diff --git a/.github/ISSUE_TEMPLATE/community_idea.yml b/.github/ISSUE_TEMPLATE/community_idea.yml index 0ec0865d..484c6911 100644 --- a/.github/ISSUE_TEMPLATE/community_idea.yml +++ b/.github/ISSUE_TEMPLATE/community_idea.yml @@ -24,7 +24,7 @@ body: - Feature or project idea - Other community feedback validations: - required: true + required: false - type: dropdown id: experience @@ -37,7 +37,7 @@ body: - Repeater, room server, or observer operator - Developer or documentation contributor validations: - required: true + required: false - type: input id: summary @@ -71,7 +71,7 @@ body: description: What should change? placeholder: Add... validations: - required: true + required: false - type: textarea id: context diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index c838694b..74648244 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -154,3 +154,11 @@ jobs: run: | export CHROME_PATH="$(command -v google-chrome || command -v google-chrome-stable)" npm run audit:lighthouse + + - name: Save Lighthouse reports + if: always() + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: lighthouse-reports + path: .tmp/lighthouse/*.json + retention-days: 7 diff --git a/README.md b/README.md new file mode 100644 index 00000000..e86b2355 --- /dev/null +++ b/README.md @@ -0,0 +1,73 @@ +# MeshCore Canada + +Community documentation and network tools for [meshcore.ca](https://meshcore.ca/), +in English and French. Built with MkDocs Material and small browser scripts. + +## Work locally + +Use Python 3.13 and Node.js 22.19 or newer. From the repository root: + +```sh +python -m pip install -r requirements-docs.txt +npm ci +npm run docs:build +python -m http.server 4173 --bind 127.0.0.1 --directory .tmp/site +``` + +Open `http://127.0.0.1:4173/`. Rebuild after editing. Build output belongs in +`.tmp/`, not in a commit. `npm run docs:build:preview` builds for subpath hosting. + +## Where to make changes + +- Pages: `docs/`. Update each English `.md` and French `.fr.md` pair; retain old + heading anchors when changing titles. +- Navigation, theme, and shared templates: `mkdocs.yml` and `overrides/`. +- Community listings: `data/communities.json` and `data/communities.fr.json`. + Run `python scripts/validate-communities.py --write` after editing the source. + It generates the directory pages and `docs/assets/radio-profiles.json`. +- Region tools: `docs/assets/regions/` and `docs/config/editor/`. + Follow the boundary proposal workflow; do not hand-edit generated geography. +- Broker settings: `docs/analyzer/observer-config.json`. The build generates the + broker reference table from this file, including its no-JavaScript version. +- Anonymous submissions: `tools/region-proposal-gateway/`. The site and gateway + deploy separately; check the gateway README before changing their contract. + +Never commit credentials, private keys, precise private locations, or test +submissions containing personal information. Human maintainers review changes +before publication; an automated test is not hardware or policy approval. + +## Check a change + +```sh +python scripts/validate-content.py +python scripts/validate-communities.py +python scripts/validate_community_submission.py +python -m unittest discover -s tests/content -p "test_*.py" +npm run test:content +npm run test:editor +npm run check:links +npx playwright install chromium firefox webkit +npm run test:browser +npm run audit:lighthouse +``` + +The broker-helper tests need Bash and PowerShell (`pwsh`); on Windows they use +Git Bash. They run in temporary directories, do not install software, and never +connect to a real broker. Browser submission tests intercept requests locally. + +For region, gateway, or automation changes, also run: + +```sh +python -m pip install -r scripts/requirements-regions.txt +python -m pip install -r tools/region-proposal-gateway/requirements.txt +node scripts/validate-regions.cjs +python scripts/verify-region-geometry.py +python scripts/verify-region-geometry.py --partition docs/assets/regions/canada-region-partition-digital.geojson +python -m unittest discover -s tools/region-proposal-gateway/tests +python -m unittest discover -s tests/automation +``` + +The quality workflow runs these checks on pull requests. Publishing is a separate +reviewed workflow; opening a PR does not authorize deploying it or changing brokers. +See [the September audit follow-up](maintenance/site-audit-2026-09-04.md) for the +current fixes, test coverage, and confirmations still needed from maintainers. diff --git a/docs/analyzer/broker-reference.fr.md b/docs/analyzer/broker-reference.fr.md index 5b395226..fe8aa8dc 100644 --- a/docs/analyzer/broker-reference.fr.md +++ b/docs/analyzer/broker-reference.fr.md @@ -15,8 +15,6 @@ estimated_time: 8 minutes destructive: false page_styles: - assets/styles/analyzer.css?v=20260722-2 -page_scripts: - - assets/javascripts/analyzer-broker-reference.js?v=20260722-2 --- # Paramètres de connexion des observateurs @@ -45,10 +43,13 @@ Ces valeurs proviennent de la [configuration commune des observateurs](observer- -

Chargement des paramètres officiels de connexion…

-Si le tableau ne s’affiche pas, ouvrez [observer-config.json](observer-config.json). +## Accès en lecture seule + +La [liste des comptes MQTT en lecture seule](data-collection-access.md#read-only-mqtt-accounts) indique les abonnés autorisés, dont QuinteMesh. Pour demander un accès, contactez un administrateur ci-dessous. + +Les réglages JWT de cette page concernent les **observateurs qui publient des paquets**. Les abonnés en lecture seule utilisent les identifiants et les instructions fournis par un administrateur. N’utilisez pas la clé privée d’un observateur pour cet accès. ## Administrateurs des courtiers diff --git a/docs/analyzer/broker-reference.md b/docs/analyzer/broker-reference.md index 195a60d7..c77b5be9 100644 --- a/docs/analyzer/broker-reference.md +++ b/docs/analyzer/broker-reference.md @@ -15,8 +15,6 @@ estimated_time: 8 minutes destructive: false page_styles: - assets/styles/analyzer.css?v=20260722-2 -page_scripts: - - assets/javascripts/analyzer-broker-reference.js?v=20260722-2 --- # Observer connection reference @@ -44,10 +42,13 @@ These values come from the shared [observer configuration](observer-config.json) -

Loading official connection settings…

-If the table does not load, open [observer-config.json](observer-config.json). +## Read-only access + +See the [read-only MQTT account list](data-collection-access.md#read-only-mqtt-accounts) for approved subscribers, including QuinteMesh. Ask an administrator below to request access. + +The JWT settings on this page are for **observers publishing packets**. Read-only subscribers use the credentials and connection instructions supplied by an administrator. Do not use an observer’s private key for subscriber access. ## Broker administrators diff --git a/docs/analyzer/builds/mctomqtt.fr.md b/docs/analyzer/builds/mctomqtt.fr.md index beac6c0b..491a3a58 100644 --- a/docs/analyzer/builds/mctomqtt.fr.md +++ b/docs/analyzer/builds/mctomqtt.fr.md @@ -63,8 +63,8 @@ Sur un hôte à liaison série, l’outil : Pour la capture d’un compagnon, il met à jour `~/.meshcore-packet-capture/.env.local`, crée une sauvegarde horodatée, -configure les emplacements 1 et 2, désactive les emplacements 3 à 6 et peut -redémarrer le service de capture. +met à jour les emplacements MeshCore Canada existants ou utilise des emplacements +vides. Les autres connexions sont conservées. Il peut redémarrer le service de capture. Les options d’installation téléchargent et exécutent des programmes d’installation distincts provenant des projets d’origine. Ne les utilisez pas @@ -128,7 +128,7 @@ Après avoir examiné la version actuellement publiée de l’outil, vous pouvez exécuter directement ce même fichier : ```bash -bash <(curl -fsSL https://meshcore.ca/analyzer/scripts/add-meshcore-ca-broker.sh) --device serial-host --iata YOW +bash add-meshcore-ca-broker.sh --device serial-host --iata YOW ``` La méthode de téléchargement présentée plus haut facilite l’examen et la diff --git a/docs/analyzer/builds/mctomqtt.md b/docs/analyzer/builds/mctomqtt.md index e6c5aff4..54c0bf0a 100644 --- a/docs/analyzer/builds/mctomqtt.md +++ b/docs/analyzer/builds/mctomqtt.md @@ -59,7 +59,7 @@ On a serial host, the helper: - adds the primary and backup MeshCore Canada addresses and location code; and - restarts `mctomqtt` unless `--no-restart` is used. -For companion capture, it updates `~/.meshcore-packet-capture/.env.local`, makes a timestamped backup, configures slots 1 and 2, disables slots 3–6, and may restart the capture service. +For companion capture, it updates `~/.meshcore-packet-capture/.env.local`, makes a timestamped backup, updates existing Canada broker slots or uses empty slots, and may restart the capture service. Install flags download and run separate upstream installers. Do not use them until you have reviewed the named upstream installer too. @@ -114,7 +114,7 @@ The service should stay active without repeated TLS or authentication errors. After reviewing the current published helper, you can run that same file directly: ```bash -bash <(curl -fsSL https://meshcore.ca/analyzer/scripts/add-meshcore-ca-broker.sh) --device serial-host --iata YOW +bash add-meshcore-ca-broker.sh --device serial-host --iata YOW ``` The downloaded-file method above is easier to review and recover. diff --git a/docs/analyzer/builds/mqtt-firmware.fr.md b/docs/analyzer/builds/mqtt-firmware.fr.md index ad8bd4fa..54410865 100644 --- a/docs/analyzer/builds/mqtt-firmware.fr.md +++ b/docs/analyzer/builds/mqtt-firmware.fr.md @@ -15,7 +15,8 @@ destructive: true page_styles: - assets/styles/analyzer.css?v=20260722-2 page_scripts: - - assets/javascripts/analyzer-command-builder.js?v=20260722-2 + - assets/javascripts/radio-profiles.js?v=20260904-1 + - assets/javascripts/analyzer-command-builder.js?v=20260904-1 --- # Configurer un observateur MQTT autonome @@ -100,6 +101,10 @@ propre à la communauté, la configuration canadienne de départ est : ### 3. Générer les commandes +Ces commandes remplacent les emplacements MQTT **1 et 2**. S’ils servent déjà à un +autre réseau, ajoutez le Canada dans des emplacements libres avec **Configure via USB**. +Les emplacements 3 à 6 restent inchangés. La répétition est désactivée par défaut. + L’interface en ligne de commande n’a pas de règle générale documentée pour les guillemets. Le générateur rejette les espaces, les guillemets, les barres obliques inverses, les caractères de contrôle et les autres valeurs Wi-Fi @@ -146,11 +151,14 @@ représenter de façon sûre. + +

Choisissez le profil de votre communauté. L’emplacement seul ne détermine pas les réglages radio.

+

Chargement des suggestions d’emplacements canadiens…

Le SSID et le mot de passe restent uniquement dans cette page. Ils sont effacés lorsque vous la quittez et masqués dans l’aperçu jusqu’à ce que vous choisissiez de les afficher.

@@ -175,23 +183,17 @@ sensibles : ```text set name YOW-Repeater-01 -set radio 910.525,62.5,7,5 -set path.hash.mode 2 set mqtt.iata YOW set wifi.powersave none set mqtt1.preset meshcore-ca-1 set mqtt2.preset meshcore-ca-2 -set mqtt3.preset none -set mqtt4.preset none -set mqtt5.preset none -set mqtt6.preset none set mqtt.status on set mqtt.packets on set mqtt.raw off set mqtt.rx on set mqtt.tx advert set bridge.enabled on -set repeat on +set repeat off advert ``` @@ -233,7 +235,7 @@ La carte est correctement configurée lorsque : - le code d’emplacement est exact; - les préréglages sont `meshcore-ca-1` et `meshcore-ca-2`; - la publication des paquets et le mode pont sont activés; -- le mode de hachage des chemins est `2`. +- le profil radio et la taille d’identifiant correspondent à vos choix. ## Vérifier dans CoreScope { #check-in-corescope } diff --git a/docs/analyzer/builds/mqtt-firmware.md b/docs/analyzer/builds/mqtt-firmware.md index c0145bed..f86cae55 100644 --- a/docs/analyzer/builds/mqtt-firmware.md +++ b/docs/analyzer/builds/mqtt-firmware.md @@ -15,7 +15,8 @@ destructive: true page_styles: - assets/styles/analyzer.css?v=20260722-2 page_scripts: - - assets/javascripts/analyzer-command-builder.js?v=20260722-2 + - assets/javascripts/radio-profiles.js?v=20260904-1 + - assets/javascripts/analyzer-command-builder.js?v=20260904-1 --- # Build a standalone MQTT observer @@ -88,6 +89,10 @@ Use the local mesh settings. When no community override exists, the Canadian onb ### 3. Build the commands +These commands replace MQTT slots **1 and 2**. Check those slots first; if they +serve another network, use **Configure via USB** to add Canada in unused slots. +Slots 3–6 are left unchanged. Repeating is off unless you choose to enable it. + The CLI has no documented general quoting contract. The builder rejects spaces, quotes, backslashes, control characters, and other ambiguous Wi-Fi values. Use **Configure via USB** for a network it cannot represent safely.
@@ -130,11 +135,14 @@ The CLI has no documented general quoting contract. The builder rejects spaces,
+ +

Choose your community’s profile explicitly. Location alone does not select a radio network.

+

Loading Canadian location suggestions…

SSID and password stay in this page only. They are cleared when you leave and hidden from the preview until you reveal them.

@@ -152,27 +160,23 @@ Check the non-sensitive summary and redacted preview. Reveal and copy commands o ### Enter commands by hand +These commands preserve radio settings and presets 3–6, but replace presets 1 and 2. On an existing observer, check those slots first. Choose radio values in the builder above; changes take effect after reboot. Observing does not require repeating. Enable repeating only after coordinating with nearby operators. + If you prefer manual entry, set the non-sensitive values first: ```text set name YOW-Repeater-01 -set radio 910.525,62.5,7,5 -set path.hash.mode 2 set mqtt.iata YOW set wifi.powersave none set mqtt1.preset meshcore-ca-1 set mqtt2.preset meshcore-ca-2 -set mqtt3.preset none -set mqtt4.preset none -set mqtt5.preset none -set mqtt6.preset none set mqtt.status on set mqtt.packets on set mqtt.raw off set mqtt.rx on set mqtt.tx advert set bridge.enabled on -set repeat on +set repeat off advert ``` @@ -210,7 +214,7 @@ The board is configured correctly when: - the location code is correct; - presets are `meshcore-ca-1` and `meshcore-ca-2`; - packet publishing and bridge mode are on; and -- path hash mode is `2`. +- the radio profile and path hash mode match your explicit choices. ## Verify in CoreScope diff --git a/docs/analyzer/builds/pymc.fr.md b/docs/analyzer/builds/pymc.fr.md index 3b95fab2..4fa17482 100644 --- a/docs/analyzer/builds/pymc.fr.md +++ b/docs/analyzer/builds/pymc.fr.md @@ -50,7 +50,9 @@ Notez l’état actuel du service : ```bash sudo systemctl status pymc-repeater --no-pager -sudo cp -- /etc/pymc_repeater/config.yaml /etc/pymc_repeater/config.yaml.pre-meshcore-ca +backup="/etc/pymc_repeater/config.yaml.pre-meshcore-ca.$(date -u +%Y%m%dT%H%M%S).$" +sudo cp --no-clobber -- /etc/pymc_repeater/config.yaml "$backup" +printf 'Backup: %s\n' "$backup" ``` ## Ce qui sera modifié @@ -140,7 +142,7 @@ bon état ne confirme pas que les paquets se sont rendus à CoreScope. Restaurez exactement la sauvegarde créée avant la modification : ```bash -sudo cp -- /etc/pymc_repeater/config.yaml.pre-meshcore-ca /etc/pymc_repeater/config.yaml +sudo cp -- "${backup:?Set backup to the saved backup path first}" /etc/pymc_repeater/config.yaml sudo systemctl restart pymc-repeater sudo systemctl status pymc-repeater --no-pager ``` diff --git a/docs/analyzer/builds/pymc.md b/docs/analyzer/builds/pymc.md index e027fc52..fcfec7e4 100644 --- a/docs/analyzer/builds/pymc.md +++ b/docs/analyzer/builds/pymc.md @@ -45,7 +45,9 @@ Record the current service state: ```bash sudo systemctl status pymc-repeater --no-pager -sudo cp -- /etc/pymc_repeater/config.yaml /etc/pymc_repeater/config.yaml.pre-meshcore-ca +backup="/etc/pymc_repeater/config.yaml.pre-meshcore-ca.$(date -u +%Y%m%dT%H%M%S).$" +sudo cp --no-clobber -- /etc/pymc_repeater/config.yaml "$backup" +printf 'Backup: %s\n' "$backup" ``` ## What this changes @@ -128,7 +130,7 @@ Finish with [Check your observer](../verify.md). A healthy systemd service is no Restore the exact backup made before editing: ```bash -sudo cp -- /etc/pymc_repeater/config.yaml.pre-meshcore-ca /etc/pymc_repeater/config.yaml +sudo cp -- "${backup:?Set backup to the saved backup path first}" /etc/pymc_repeater/config.yaml sudo systemctl restart pymc-repeater sudo systemctl status pymc-repeater --no-pager ``` diff --git a/docs/analyzer/data-collection-access.fr.md b/docs/analyzer/data-collection-access.fr.md index 692679e9..cf14fd79 100644 --- a/docs/analyzer/data-collection-access.fr.md +++ b/docs/analyzer/data-collection-access.fr.md @@ -70,7 +70,7 @@ MeshCore Canada n’offre pas d’abonnement direct général au courtier. L’a direct est limité à CoreScope, aux administrateurs des réseaux maillés locaux et aux personnes autorisées par les administrateurs de l’infrastructure. -## Comptes MQTT en lecture seule +## Comptes MQTT en lecture seule { #read-only-mqtt-accounts } Cet inventaire public répertorie les services qui disposent d’un accès en lecture seule aux courtiers MQTT de MeshCore Canada. Il indique le service et @@ -80,6 +80,7 @@ l’exploitant, mais jamais les mots de passe ni les jetons. |---|---|---| | Beacon (`dev.meshcore.ca`) | Exploitants de MeshCore Canada | Visualisation publique des paquets et vérification des identifiants de répéteur | | CoreScope (`live.meshcore.ca`) | Exploitants de MeshCore Canada | Outils publics pour les observateurs, les paquets, les nœuds et la carte | +| [Canadaverse](https://canadaverse.org/) | [n30nex@gmail.com](mailto:n30nex@gmail.com) | Outils régionaux du Grand Toronto et carte CartoLive | | [Quinte Mesh](https://quintemesh.ca/) | hansimgamr | Services du réseau communautaire de la région de Quinte | Les administrateurs de l’infrastructure doivent mettre ce tableau à jour chaque diff --git a/docs/analyzer/data-collection-access.md b/docs/analyzer/data-collection-access.md index 0ad308c7..3e4a3535 100644 --- a/docs/analyzer/data-collection-access.md +++ b/docs/analyzer/data-collection-access.md @@ -68,6 +68,7 @@ This public inventory lists services with read-only access to the MeshCore Canad |---|---|---| | Beacon (`dev.meshcore.ca`) | MeshCore Canada operators | Public packet viewer and repeater ID checks | | CoreScope (`live.meshcore.ca`) | MeshCore Canada operators | Public observer, packet, node, and map tools | +| [Canadaverse](https://canadaverse.org/) | [n30nex@gmail.com](mailto:n30nex@gmail.com) | GTA Regional Tools & CartoLive map | | [Quinte Mesh](https://quintemesh.ca/) | hansimgamr | Quinte-region community network services | Infrastructure administrators must update this table whenever they create or remove a read-only account. [Report a missing or outdated entry](https://github.com/MeshCore-ca/MeshCore-Canada/issues/new/choose). diff --git a/docs/analyzer/scripts/add-meshcore-ca-broker.sh b/docs/analyzer/scripts/add-meshcore-ca-broker.sh index 619ab147..f1996894 100644 --- a/docs/analyzer/scripts/add-meshcore-ca-broker.sh +++ b/docs/analyzer/scripts/add-meshcore-ca-broker.sh @@ -30,8 +30,9 @@ Supported installs: - Companion radio via meshcore-packet-capture (.env.local) Usage: - MESHCORE_CA_IATA=YOW bash <(curl -fsSL https://meshcore.ca/analyzer/scripts/add-meshcore-ca-broker.sh) - bash <(curl -fsSL https://meshcore.ca/analyzer/scripts/add-meshcore-ca-broker.sh) --iata YOW --device serial-host + curl -fsSLo add-meshcore-ca-broker.sh https://meshcore.ca/analyzer/scripts/add-meshcore-ca-broker.sh + less add-meshcore-ca-broker.sh + bash ./add-meshcore-ca-broker.sh --iata YOW --device serial-host Options: --iata CODE Real 3-letter IATA airport code. @@ -537,15 +538,28 @@ set_packetcapture_slot() { upsert_env "$file" "PACKETCAPTURE_MQTT${slot}_TOPIC_PACKETS" 'meshcore/{IATA}/{PUBLIC_KEY}/packets' } -disable_packetcapture_slot() { - local file="$1" - local slot="$2" - upsert_env "$file" "PACKETCAPTURE_MQTT${slot}_ENABLED" "false" - upsert_env "$file" "PACKETCAPTURE_MQTT${slot}_SERVER" "" +choose_packetcapture_slots() { + local file="$1" n server slot1="" slot2="" free=() + for n in 1 2 3 4 5 6; do + server="$(env_value "$file" "PACKETCAPTURE_MQTT${n}_SERVER" | tr -d '\r')" + server="${server#\"}"; server="${server%\"}" + server="${server#\'}"; server="${server%\'}" + if [ "$server" = "$BROKER1_HOST" ] && [ -z "$slot1" ]; then slot1="$n" + elif [ "$server" = "$BROKER2_HOST" ] && [ -z "$slot2" ]; then slot2="$n" + elif [ -z "$server" ]; then free+=("$n") + fi + done + if [ -z "$slot1" ] && [ "${#free[@]}" -gt 0 ]; then slot1="${free[0]}"; free=("${free[@]:1}"); fi + if [ -z "$slot2" ] && [ "${#free[@]}" -gt 0 ]; then slot2="${free[0]}"; fi + if [ -z "$slot1" ] || [ -z "$slot2" ]; then + echo "Not enough empty broker slots. Keep your existing connections, or free two slots and run again. No settings changed." >&2 + return 1 + fi + printf '%s %s\n' "$slot1" "$slot2" } patch_env_capture() { - local env_file current_iata backup n + local env_file current_iata backup slots slot1 slot2 env_file="$(packetcapture_env_path)" install_packetcapture mkdir -p "$(dirname "$env_file")" @@ -556,16 +570,15 @@ patch_env_capture() { fi require_iata + slots="$(choose_packetcapture_slots "$env_file")" || return 1 + read -r slot1 slot2 <<< "$slots" backup="$(backup_path "$env_file")" cp -p "$env_file" "$backup" chmod 0600 "$backup" upsert_env "$env_file" "PACKETCAPTURE_IATA" "$IATA" - set_packetcapture_slot "$env_file" 1 "$BROKER1_HOST" - set_packetcapture_slot "$env_file" 2 "$BROKER2_HOST" - for n in 3 4 5 6; do - disable_packetcapture_slot "$env_file" "$n" - done + set_packetcapture_slot "$env_file" "$slot1" "$BROKER1_HOST" + set_packetcapture_slot "$env_file" "$slot2" "$BROKER2_HOST" chmod 0600 "$env_file" say "Patched: $env_file" say "Backup written: $backup" diff --git a/docs/analyzer/scripts/add-meshcore-ca-packetcapture-broker.ps1 b/docs/analyzer/scripts/add-meshcore-ca-packetcapture-broker.ps1 index 9d747ea6..f0982250 100644 --- a/docs/analyzer/scripts/add-meshcore-ca-packetcapture-broker.ps1 +++ b/docs/analyzer/scripts/add-meshcore-ca-packetcapture-broker.ps1 @@ -114,7 +114,7 @@ function Set-EnvValue { $lines = Get-Content $Path } $updated = $false - $out = foreach ($line in $lines) { + $out = @(foreach ($line in $lines) { if ($line -match "^${Key}=") { if (-not $updated) { $updated = $true @@ -124,7 +124,7 @@ function Set-EnvValue { else { $line } - } + }) if (-not $updated) { $out += "${Key}=${Value}" } @@ -135,16 +135,16 @@ function Set-PacketCaptureSlot { param( [string]$Path, [int]$Slot, - [string]$Host + [string]$BrokerHost ) Set-EnvValue -Path $Path -Key "PACKETCAPTURE_MQTT${Slot}_ENABLED" -Value "true" - Set-EnvValue -Path $Path -Key "PACKETCAPTURE_MQTT${Slot}_SERVER" -Value $Host + Set-EnvValue -Path $Path -Key "PACKETCAPTURE_MQTT${Slot}_SERVER" -Value $BrokerHost Set-EnvValue -Path $Path -Key "PACKETCAPTURE_MQTT${Slot}_PORT" -Value $BrokerPort Set-EnvValue -Path $Path -Key "PACKETCAPTURE_MQTT${Slot}_TRANSPORT" -Value "websockets" Set-EnvValue -Path $Path -Key "PACKETCAPTURE_MQTT${Slot}_USE_TLS" -Value "true" Set-EnvValue -Path $Path -Key "PACKETCAPTURE_MQTT${Slot}_TLS_VERIFY" -Value "true" Set-EnvValue -Path $Path -Key "PACKETCAPTURE_MQTT${Slot}_USE_AUTH_TOKEN" -Value "true" - Set-EnvValue -Path $Path -Key "PACKETCAPTURE_MQTT${Slot}_TOKEN_AUDIENCE" -Value $Host + Set-EnvValue -Path $Path -Key "PACKETCAPTURE_MQTT${Slot}_TOKEN_AUDIENCE" -Value $BrokerHost Set-EnvValue -Path $Path -Key "PACKETCAPTURE_MQTT${Slot}_KEEPALIVE" -Value "120" Set-EnvValue -Path $Path -Key "PACKETCAPTURE_MQTT${Slot}_QOS" -Value "0" Set-EnvValue -Path $Path -Key "PACKETCAPTURE_MQTT${Slot}_RETAIN" -Value "true" @@ -152,13 +152,29 @@ function Set-PacketCaptureSlot { Set-EnvValue -Path $Path -Key "PACKETCAPTURE_MQTT${Slot}_TOPIC_PACKETS" -Value "meshcore/{IATA}/{PUBLIC_KEY}/packets" } -function Disable-PacketCaptureSlot { - param( - [string]$Path, - [int]$Slot - ) - Set-EnvValue -Path $Path -Key "PACKETCAPTURE_MQTT${Slot}_ENABLED" -Value "false" - Set-EnvValue -Path $Path -Key "PACKETCAPTURE_MQTT${Slot}_SERVER" -Value "" +function Get-PacketCaptureSlots { + param([string]$Path) + $servers = @{} + foreach ($line in Get-Content -LiteralPath $Path) { + if ($line -match '^PACKETCAPTURE_MQTT([1-6])_SERVER=(.*)$') { + $servers[[int]$Matches[1]] = $Matches[2].Trim().Trim('"', "'") + } + } + $chosen = @($null, $null) + for ($index = 0; $index -lt 2; $index++) { + $broker = @($Broker1Host, $Broker2Host)[$index] + $slot = 1..6 | Where-Object { $servers[$_] -eq $broker -and $_ -notin $chosen } | Select-Object -First 1 + $chosen[$index] = $slot + } + for ($index = 0; $index -lt 2; $index++) { + if (-not $chosen[$index]) { + $chosen[$index] = 1..6 | Where-Object { [string]::IsNullOrWhiteSpace($servers[$_]) -and $_ -notin $chosen } | Select-Object -First 1 + } + } + if (-not $chosen[0] -or -not $chosen[1]) { + throw 'Not enough empty broker slots. Free slots before trying again. No settings changed.' + } + return $chosen } function Ensure-PacketCaptureInstall { @@ -206,15 +222,13 @@ if (-not (Test-Path $envPath)) { New-Item -ItemType File -Path $envPath -Force | Out-Null } +$slots = @(Get-PacketCaptureSlots -Path $envPath) $backupPath = New-BackupPath -Path $envPath Copy-Item -Path $envPath -Destination $backupPath -Force Set-EnvValue -Path $envPath -Key "PACKETCAPTURE_IATA" -Value $Iata -Set-PacketCaptureSlot -Path $envPath -Slot 1 -Host $Broker1Host -Set-PacketCaptureSlot -Path $envPath -Slot 2 -Host $Broker2Host -foreach ($slot in 3..6) { - Disable-PacketCaptureSlot -Path $envPath -Slot $slot -} +Set-PacketCaptureSlot -Path $envPath -Slot $slots[0] -BrokerHost $Broker1Host +Set-PacketCaptureSlot -Path $envPath -Slot $slots[1] -BrokerHost $Broker2Host Write-Host "" Write-Info "Patched: $envPath" diff --git a/docs/analyzer/verify.fr.md b/docs/analyzer/verify.fr.md index 259c5434..c15b9c72 100644 --- a/docs/analyzer/verify.fr.md +++ b/docs/analyzer/verify.fr.md @@ -18,7 +18,7 @@ page_styles: # Vérifier votre observateur -Une connexion au courtier confirme seulement l’accès Internet. Votre +Une connexion ne confirme pas que les paquets arrivent à CoreScope. Votre observateur fonctionne lorsqu’un véritable paquet radio se rend jusqu’à CoreScope. diff --git a/docs/analyzer/verify.md b/docs/analyzer/verify.md index 3ebd73b6..91aadbc2 100644 --- a/docs/analyzer/verify.md +++ b/docs/analyzer/verify.md @@ -18,7 +18,7 @@ page_styles: # Check your observer -A broker connection proves only internet access. Your observer is working when +Connected does not mean packets have reached CoreScope. Your observer is working when a real radio packet reaches CoreScope. ## Follow a packet through four stages diff --git a/docs/assets/javascripts/analyzer-command-builder.js b/docs/assets/javascripts/analyzer-command-builder.js index ca0fabf0..8f54966f 100644 --- a/docs/assets/javascripts/analyzer-command-builder.js +++ b/docs/assets/javascripts/analyzer-command-builder.js @@ -78,7 +78,9 @@ number: document.getElementById("observer-number"), ssid: document.getElementById("observer-ssid"), password: document.getElementById("observer-password"), - repeat: document.getElementById("observer-repeat") + repeat: document.getElementById("observer-repeat"), + radio: document.getElementById("observer-radio"), + hash: document.getElementById("observer-hash") }; var output = document.getElementById("observer-command-output"); var status = document.getElementById("observer-copy-status"); @@ -99,6 +101,8 @@ } root.dataset.ready = "true"; + var radioProfiles = window.MeshCoreRadioProfiles; + if (radioProfiles) radioProfiles.populate(fields.radio); var exactCommands = []; var commandsRevealed = false; @@ -126,6 +130,8 @@ [tr("Board", "Carte"), fields.board.options[fields.board.selectedIndex].text], [tr("Location", "Emplacement"), iata || tr("Not set", "Non défini")], [tr("Node name", "Nom du nœud"), nodeName || tr("Not set", "Non défini")], + [tr("Radio network", "Réseau radio"), radioProfiles ? radioProfiles.label(fields.radio.value) : tr("Keep current settings", "Conserver les réglages actuels")], + [tr("Advert ID size", "Taille de l’identifiant d’annonce"), fields.hash.options[fields.hash.selectedIndex].text], [ tr("Mesh traffic", "Trafic du réseau maillé"), repeat === "on" @@ -218,18 +224,13 @@ exactCommands = [ "set name " + nodeName, - "set radio 910.525,62.5,7,5", - "set path.hash.mode 2", + ].concat(radioProfiles ? radioProfiles.commands(fields.radio.value, fields.hash.value) : [], [ "set mqtt.iata " + iata, "set wifi.ssid " + ssid, "set wifi.pwd " + password, "set wifi.powersave none", "set mqtt1.preset meshcore-ca-1", "set mqtt2.preset meshcore-ca-2", - "set mqtt3.preset none", - "set mqtt4.preset none", - "set mqtt5.preset none", - "set mqtt6.preset none", "set mqtt.status on", "set mqtt.packets on", "set mqtt.raw off", @@ -239,7 +240,7 @@ "set repeat " + repeat, "advert", "reboot" - ]; + ]); revealCommandsButton.disabled = false; showCurrentCommands(); } diff --git a/docs/assets/javascripts/radio-profiles.js b/docs/assets/javascripts/radio-profiles.js new file mode 100644 index 00000000..9b728779 --- /dev/null +++ b/docs/assets/javascripts/radio-profiles.js @@ -0,0 +1,50 @@ +(function () { + "use strict"; + var source = new URL("../radio-profiles.json", document.currentScript.src); + var french = /^fr(?:-|$)/i.test(document.documentElement.lang); + var profiles = []; + var loading; + + function commands(id, hashMode) { + var result = []; + if (id && id !== "keep") { + var profile = profiles.find(function (item) { return item.id === id; }); + if (!profile) throw new Error("Unknown radio profile"); + var radio = profile.radio; + result.push("set radio " + [radio.frequency_mhz, radio.bandwidth_khz, radio.spreading_factor, radio.coding_rate].join(",")); + } + if (["0", "1", "2"].indexOf(hashMode) !== -1) result.push("set path.hash.mode " + hashMode); + return result; + } + + function populate(select) { + if (!loading) loading = fetch(source).then(function (response) { + if (!response.ok) throw new Error("Radio profiles unavailable"); + return response.json(); + }).then(function (data) { profiles = data; }); + return loading.then(function () { + profiles.forEach(function (profile) { + var option = document.createElement("option"); + option.value = profile.id; + option.textContent = (french ? profile.name_fr : profile.name) + " · " + + profile.radio.frequency_mhz + " MHz"; + select.appendChild(option); + }); + }).catch(function () { + // Keep-current remains usable; never substitute a default when data fails. + var notice = document.createElement("p"); + notice.setAttribute("role", "status"); + notice.textContent = french + ? "Profils radio indisponibles. Les réglages actuels seront conservés." + : "Radio profiles unavailable. Current settings will be kept."; + select.after(notice); + }); + } + + function label(id) { + var profile = profiles.find(function (item) { return item.id === id; }); + return profile ? (french ? profile.name_fr : profile.name) + " · " + profile.radio.frequency_mhz + " MHz" + : (french ? "Conserver les réglages actuels" : "Keep current settings"); + } + window.MeshCoreRadioProfiles = { commands: commands, populate: populate, label: label }; +}()); diff --git a/docs/assets/javascripts/repeater-hash-check.js b/docs/assets/javascripts/repeater-hash-check.js index 03beec0b..7c315846 100644 --- a/docs/assets/javascripts/repeater-hash-check.js +++ b/docs/assets/javascripts/repeater-hash-check.js @@ -315,7 +315,7 @@ var rows = summaries.map(function (summary) { var selected = summary.bytes === selectedBytes; return '' + - '' + summary.bytes + ' ' + + '' + escapeHtml(summary.bytes === 1 ? message(language, "oneByte") : message(language, summary.bytes === 2 ? "twoBytes" : "threeBytes")) + (selected ? ' ' + escapeHtml(message(language, "selected")) + '' : '') + '' + '' + escapeHtml(summary.prefix) + '' + diff --git a/docs/assets/radio-profiles.json b/docs/assets/radio-profiles.json new file mode 100644 index 00000000..70b39d23 --- /dev/null +++ b/docs/assets/radio-profiles.json @@ -0,0 +1,27 @@ +[ + { + "id": "canada", + "name": "Canada default", + "name_fr": "Réglages canadiens par défaut", + "radio": { + "frequency_mhz": 910.525, + "bandwidth_khz": 62.5, + "spreading_factor": 7, + "coding_rate": 5 + }, + "route": "provinces/#canada-baseline" + }, + { + "id": "bc-mesh", + "name": "BC Mesh", + "name_fr": "BC Mesh", + "radio": { + "frequency_mhz": 910.425, + "bandwidth_khz": 62.5, + "spreading_factor": 7, + "coding_rate": 5 + }, + "route": "provinces/british-columbia/#community-bc-mesh", + "reviewed": "2026-08-29" + } +] diff --git a/docs/assets/regions/modules/configurator-support.js b/docs/assets/regions/modules/configurator-support.js index 897b63ec..6a3b4e34 100644 --- a/docs/assets/regions/modules/configurator-support.js +++ b/docs/assets/regions/modules/configurator-support.js @@ -25,12 +25,14 @@ var paths = Array.isArray(input.paths) ? input.paths.map(normalizeText).filter(Boolean) : []; var commands = Array.isArray(input.commands) ? input.commands.map(normalizeText).filter(Boolean) : []; var lines = [ - "MeshCore Canada repeater commissioning summary", + "MeshCore Canada repeater setup summary", "Generated: " + normalizeText(input.generatedAt), "Location label: " + (normalizeText(input.locationLabel) || "Not recorded"), "Home region: " + normalizeText(input.homeRegion), "Firmware: " + normalizeText(input.firmware), "Region budget: " + normalizeText(input.budget), + "Radio network: " + normalizeText(input.radio || "Keep current settings"), + "Advert ID size: " + normalizeText(input.hashMode || "Keep current settings"), "", "Forwarding paths:" ]; @@ -43,6 +45,8 @@ "1. Run region before saving and compare every path above.", "2. Run region save only after the paths and flood permissions are correct.", "3. Run region again after saving.", + "4. If radio settings changed, reboot and run get radio to confirm them.", + "5. If the advert ID size changed, run get path.hash.mode to confirm it.", "", "This summary omits exact coordinates, passwords, private keys, and device identifiers." ); diff --git a/docs/assets/regions/regions.css b/docs/assets/regions/regions.css index 8b05c312..9138fcc6 100644 --- a/docs/assets/regions/regions.css +++ b/docs/assets/regions/regions.css @@ -2,19 +2,25 @@ body:has([data-mcc-regions="map"]) [data-mcc-regions="map"] { margin: 0; } +/* Reserve space before any scripts run, without adding a gap for no-JavaScript readers. */ +@media (scripting: enabled) { + [data-mcc-regions="map"]:empty, + [data-mcc-regions="map"][data-mcc-loading="1"] { + min-height: 60rem; + } +} + body:has([data-mcc-regions]) { background: var(--md-default-bg-color, #ffffff); } -/* Region tools use one deliberate dark workbench in either site palette. This - keeps dense maps, command output, and status colours readable after a theme - switch while the header, navigation, and surrounding site still follow it. */ +/* Use the site's palette for the workbench as well as its surrounding navigation. */ body:has([data-mcc-regions]) .md-main, body:has([data-mcc-regions]) .md-main__inner, body:has([data-mcc-regions]) .md-content, body:has([data-mcc-regions]) .md-content__inner { - color: #dce9e7; - background: #0c151c; + color: var(--md-default-fg-color); + background: var(--md-default-bg-color); } body:has([data-mcc-regions="map"]) .md-main__inner { @@ -24,11 +30,7 @@ body:has([data-mcc-regions="map"]) .md-main__inner { body:has([data-mcc-regions]) .md-typeset h1, body:has([data-mcc-regions]) .md-typeset h2, body:has([data-mcc-regions]) .md-typeset h3 { - color: #ffffff; -} - -body:has([data-mcc-regions]) .md-main .md-typeset a { - color: var(--md-typeset-a-color, #5488e8); + color: var(--md-default-fg-color); } .mcc-visually-hidden { @@ -49,7 +51,7 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { border: 1px solid rgba(133, 179, 174, 0.24); border-left: 0.22rem solid #2f7d68; border-radius: 0.35rem; - color: #eaf3f1; + color: var(--mc-color-text); background: rgba(88, 184, 137, 0.1); } @@ -65,9 +67,9 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { padding: 0.85rem 0.95rem; border: 1px solid rgba(133, 179, 174, 0.24); border-radius: 0.35rem; - color: #eaf3f1; + color: var(--mc-color-text); text-decoration: none; - background: #101d26; + background: var(--mcc-surface); } .mcc-region-action:hover { @@ -92,17 +94,22 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { --mcc-blue: #303fa1; --mcc-gold: #f4bf5a; --mcc-red: #ff7373; - --mcc-border: rgba(159, 216, 189, 0.22); + --mcc-border: var(--mc-color-border); --mcc-soft: rgba(66, 135, 255, 0.15); - --mcc-panel: #0c151c; - --mcc-surface: #101d26; - --mcc-surface-2: #172630; - --mcc-text: #dce9e7; - --mcc-muted: #a7b9bb; + --mcc-panel: var(--md-default-bg-color); + --mcc-surface: var(--mc-color-surface); + --mcc-surface-2: var(--mc-color-surface-raised); + --mcc-text: var(--md-default-fg-color); + --mcc-muted: var(--mc-color-text-muted); + --mcc-action-text: #fff; margin: 1rem 0 2rem; color: var(--mcc-text); } +[data-md-color-scheme="slate"] [data-mcc-regions] { + --mcc-action-text: #101827; +} + /* Match the first configurator step before its progressive enhancement mounts. */ [data-mcc-regions="config"] { display: block; @@ -137,14 +144,14 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { padding: 0.35rem 0 0.25rem; border: 0; border-radius: 0; - color: #ffffff; + color: var(--mcc-text); background: transparent; } .mcc-console-header h1, .mcc-console-header h2 { margin: 0; - color: #ffffff; + color: var(--mcc-text); font-size: clamp(1.25rem, 2.1vw, 1.65rem); letter-spacing: 0; } @@ -261,7 +268,8 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { outline: 0.12rem solid color-mix(in srgb, var(--mcc-green), transparent 76%); } -.mcc-button { +.mcc-button, +.md-typeset a.mcc-button { display: inline-flex; gap: 0.35rem; align-items: center; @@ -285,7 +293,10 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { } .mcc-button:hover, -.mcc-button:focus { +.mcc-button:focus, +.md-typeset a.mcc-button:hover, +.md-typeset a.mcc-button:focus { + color: var(--mcc-action-text); background: var(--mcc-green-dark); border-color: var(--mcc-green-dark); } @@ -295,15 +306,18 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { cursor: not-allowed; } -.mcc-button-secondary { +.mcc-button-secondary, +.md-typeset a.mcc-button-secondary { color: var(--mcc-text); background: transparent; border-color: var(--mcc-border); } .mcc-button-secondary:hover, -.mcc-button-secondary:focus { - color: #ffffff; +.mcc-button-secondary:focus, +.md-typeset a.mcc-button-secondary:hover, +.md-typeset a.mcc-button-secondary:focus { + color: var(--mcc-text); background: var(--mcc-soft); border-color: var(--mcc-green); } @@ -462,7 +476,7 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { } .mcc-status-warning { - color: #f6dda8; + color: var(--mc-color-warning); background: color-mix(in srgb, var(--mcc-gold), transparent 82%); } @@ -487,7 +501,7 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { border: 1px solid var(--mcc-border); border-radius: 0.35rem; color: var(--mcc-text); - background: #09131a; + background: var(--mcc-surface); text-align: left; cursor: pointer; } @@ -574,7 +588,7 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { } .mcc-empty-state strong { - color: #ffffff; + color: var(--mcc-text); font-size: 1rem; } @@ -706,7 +720,7 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { min-width: 0; margin: 0.12rem 0 0; overflow: hidden; - color: #eaf3f1; + color: var(--mc-color-text); font-size: 0.78rem; text-overflow: ellipsis; white-space: nowrap; @@ -851,7 +865,7 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { .mcc-map-shell { display: grid; grid-template-columns: minmax(23rem, 27.5rem) minmax(0, 1fr); - height: clamp(30rem, calc(100vh - 8rem), 40rem); + height: auto; min-height: 30rem; border: 1px solid var(--mcc-border); border-radius: 0.45rem; @@ -912,7 +926,7 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { gap: 0.75rem; padding: 1rem; background: var(--mcc-panel); - overflow: auto; + overflow: visible; } .mcc-map-stage { @@ -921,20 +935,24 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { min-height: 30rem; } -.mcc-skip-map { +.md-typeset .mcc-skip-map { position: absolute; top: 0.5rem; left: 0.5rem; z-index: 1001; padding: 0.45rem 0.65rem; border-radius: 0.3rem; - color: #ffffff; + color: var(--mcc-action-text); background: var(--mcc-green-dark); - transform: translateY(calc(-100% - 1rem)); + clip-path: inset(50%); + opacity: 0; + pointer-events: none; } -.mcc-skip-map:focus { - transform: none; +.md-typeset .mcc-skip-map:focus { + clip-path: none; + opacity: 1; + pointer-events: auto; } .mcc-map-loading { @@ -952,6 +970,19 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { text-align: center; } +.md-typeset .mcc-map-progress { + position: absolute; + top: 0.5rem; + left: 3rem; + z-index: 1001; + margin: 0; + padding: 0.3rem 0.5rem; + border-radius: 0.3rem; + color: var(--mcc-text); + background: var(--mcc-surface); + font-size: 0.8rem; +} + .mcc-map-loading > div { display: grid; max-width: 28rem; @@ -990,7 +1021,7 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { .mcc-region-crumb:hover, .mcc-region-crumb:focus-visible { - color: #ffffff; + color: var(--mcc-text); text-decoration: underline; } @@ -1044,7 +1075,7 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { padding: 0 0 0.2rem; border: 0; border-radius: 0; - color: #ffffff; + color: var(--mcc-text); background: transparent; } @@ -1066,7 +1097,7 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { .mcc-panel-header h1, .mcc-panel-header h2 { margin: 0; - color: #ffffff; + color: var(--mcc-text); font-size: 1.28rem; letter-spacing: 0; } @@ -1099,9 +1130,9 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { .mcc-map-canvas .leaflet-bar a, .mcc-map-canvas .leaflet-bar a:hover { - border-color: rgba(133, 179, 174, 0.24); - color: #eaf3f1; - background: rgba(11, 20, 27, 0.92); + border-color: var(--mcc-border); + color: var(--mcc-text); + background: var(--mcc-surface); } .mcc-region-borders { @@ -1145,7 +1176,7 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { padding: 0.9rem; border: 1px solid rgba(159, 216, 189, 0.24); border-radius: 0.45rem; - color: #eaf3f1; + color: var(--mc-color-text); background: rgba(7, 16, 22, 0.92); box-shadow: 0 18px 42px rgba(0, 0, 0, 0.34); } @@ -1238,7 +1269,7 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { } .mcc-detail-node code { - color: #ffffff; + color: var(--mcc-text); font-size: 0.82rem; } @@ -1364,7 +1395,7 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { } .mcc-visible-legend-head strong { - color: #eaf3f1; + color: var(--mc-color-text); font-size: 0.76rem; } @@ -1416,7 +1447,7 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { .mcc-visible-region strong { display: block; overflow: hidden; - color: #eaf3f1; + color: var(--mc-color-text); font-size: 0.72rem; text-overflow: ellipsis; white-space: nowrap; @@ -1507,7 +1538,7 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { padding: 0.62rem 0.7rem; border: 1px solid rgba(159, 216, 189, 0.18); border-radius: 0.35rem; - color: #eaf3f1; + color: var(--mc-color-text); background: var(--mcc-surface); text-decoration: none; } @@ -1515,7 +1546,7 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { .mcc-action-button:hover, .mcc-action-button:focus { border-color: rgba(159, 216, 189, 0.42); - color: #ffffff; + color: var(--mcc-text); background: rgba(88, 184, 137, 0.11); text-decoration: none; } @@ -1572,7 +1603,7 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { .mcc-stat strong { display: block; margin-top: 0.18rem; - color: #ffffff; + color: var(--mcc-text); font-size: 1.45rem; line-height: 1; } @@ -1593,7 +1624,7 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { display: flex; gap: 0.36rem; align-items: center; - color: #ffffff; + color: var(--mcc-text); font-size: 0.86rem; } @@ -1717,8 +1748,8 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { } .mcc-region-table td { - color: #eaf3f1; - background: #101d26; + color: var(--mc-color-text); + background: var(--mcc-surface); font-size: 0.76rem; } @@ -1951,7 +1982,7 @@ ol.mcc-wizard-progress > li { } .mcc-finish-path strong { - color: #ffffff; + color: var(--mcc-text); } .mcc-finish-path small { @@ -2011,7 +2042,7 @@ ol.mcc-guide-steps > li::before { .mcc-guide-steps h4 { margin: 0; - color: #ffffff; + color: var(--mcc-text); font-size: 0.92rem; } @@ -2056,7 +2087,7 @@ ol.mcc-guide-steps > li::before { .mcc-connect-method h5 { margin: 0; - color: #ffffff; + color: var(--mcc-text); font-size: 0.84rem; } diff --git a/docs/assets/regions/regions.js b/docs/assets/regions/regions.js index 6230a5c3..75adff68 100644 --- a/docs/assets/regions/regions.js +++ b/docs/assets/regions/regions.js @@ -9,16 +9,27 @@ var displayPartitionPromise = null; var resolverPartitionPromise = null; var leafletPromise = null; - var lucidePromise = null; var activeMaps = []; - var LUCIDE_SRC = new URL("vendor/lucide.js", assetBase).href; var configuratorSupport = window.MeshCoreRegionConfiguratorSupport || {}; + var radioProfiles = window.MeshCoreRadioProfiles; var REQUEST_TIMEOUT_MS = 12000; var frenchRuntime = /^fr(?:-|$)/i.test( document.documentElement ? document.documentElement.lang || "" : "" ); var FRENCH_RUNTIME_TEXT = { + "Loading regional boundaries…": "Chargement des limites régionales…", + "Radio network": "Réseau radio", + "Advert ID size": "Taille de l’identifiant d’annonce", + "Keep current settings": "Conserver les réglages actuels", + "Choose a profile only after checking with your community. A region does not select a radio network.": "Confirmez le profil auprès de votre communauté. Une région ne détermine pas les réglages radio.", + "Radio changes take effect after reboot.": "Les changements radio prennent effet après le redémarrage.", + "Choose the region you mean:": "Choisissez la région recherchée :", + "This saved location is invalid or no longer available. Choose a region again.": "Cet emplacement est invalide ou n’est plus disponible. Choisissez une région à nouveau.", + "Setup summary": "Résumé de configuration", + "3 bytes": "3 octets", + "2 bytes": "2 octets", + "1 byte": "1 octet", "Unable to load MeshCore Canada region data": "Impossible de charger les données régionales de MeshCore Canada", "Unable to load the Canadian map layer": "Impossible de charger la couche cartographique canadienne", "Unable to load the Canadian location layer": "Impossible de charger la couche canadienne de localisation", @@ -271,12 +282,15 @@ "Open standard and change process": "Ouvrir la norme et le processus de modification", "Try again": "Réessayer", "Loading release evidence…": "Chargement des preuves de publication…", - "Select this leaf to see its full path.": "Sélectionnez cette région terminale pour voir son chemin complet.", + "Select this region to see its full path.": "Sélectionnez cette région pour voir son chemin complet.", "Province or territory": "Province ou territoire", "resolves to": "correspond à", "Aliases": "Alias", "None recorded": "Aucun", - "Planned paths": "Chemins prévus", + "Repeater paths": "Chemins du répéteur", + "Region details": "Détails de la région", + "Find a community": "Trouver une communauté", + "These are routing regions, not radio coverage boundaries.": "Ces régions servent au routage; elles ne représentent pas la portée radio.", "Configure this region": "Configurer cette région", "Copy link": "Copier le lien", "This location is outside Canada.": "Cet emplacement se trouve à l’extérieur du Canada.", @@ -379,9 +393,6 @@ [/^(.+) resolves to (.+)$/, function (match, place, region) { return place + " correspond à " + region; }], - [/^Text result: (.+)\.$/, function (match, path) { - return "Résultat textuel : " + path + "."; - }], [/^(\d+) leaf regions$/, function (match, count) { return count + " régions terminales"; }], @@ -725,40 +736,30 @@ .replace(/"/g, """); } - function icon(name) { - return ''; - } - - function loadLucide() { - if (window.lucide && typeof window.lucide.createIcons === "function") { - return Promise.resolve(window.lucide); - } - if (lucidePromise) return lucidePromise; - lucidePromise = new Promise(function (resolve) { - var script = document.createElement("script"); - script.src = LUCIDE_SRC; - script.defer = true; - script.onload = function () { resolve(window.lucide || null); }; - script.onerror = function () { resolve(null); }; - document.head.appendChild(script); - }); - return lucidePromise; - } + // Lucide 0.547.0 SVG paths; ISC/Feather MIT notices are in vendor/lucide-LICENSE. + // Include only the 17 icons used here, without downloading the full icon library. + var ICONS = { + "arrow-left": "", + "arrow-right": "", + "book-open-check": "", + "clipboard": "", + "copy": "", + "download": "", + "external-link": "", + "link": "", + "list-checks": "", + "locate-fixed": "", + "map": "", + "printer": "", + "radio-tower": "", + "search": "", + "terminal": "", + "triangle-alert": "", + "usb": "" + }; - function refreshIcons(root) { - loadLucide().then(function (lucide) { - if (!lucide || typeof lucide.createIcons !== "function") return; - try { - lucide.createIcons({ - attrs: { - "stroke-width": 2, - "aria-hidden": "true" - } - }); - } catch (err) { - // Icons are progressive enhancement; text labels remain usable. - } - }); + function icon(name) { + return ''; } function copyText(text, button, resetLabel) { @@ -779,7 +780,6 @@ window.setTimeout(function () { button.classList.remove("is-copied"); button.innerHTML = button.dataset.originalHtml || resetLabel || "Copy"; - refreshIcons(button); }, 1400); }; var fallback = function () { @@ -990,9 +990,38 @@ if (state.selectedExternalPaths && state.selectedExternalPaths.length) { params.set("external", state.selectedExternalPaths.join(",")); } + if (state.firmware) params.set("firmware", state.firmware); + if (state.radioProfile) params.set("radio", state.radioProfile); + if (state.hashMode) params.set("hash", state.hashMode); + if (state.deviceRole) params.set("role", state.deviceRole); + if (state.wizardStep) params.set("step", state.wizardStep); + if (state.finishPath) params.set("instructions", state.finishPath); return regionPageHref("map") + (params.toString() ? "?" + params.toString() : ""); } + function keepLanguageSelection(state) { + document.querySelectorAll(".md-select__link[hreflang]").forEach(function (link) { + // Transfer only the tool's public selections, never arbitrary URL fields or secrets. + link.addEventListener("click", function () { + var target = new URL(link.href); + target.search = new URL(mapHrefForState(state)).search; + link.href = target.href; + }); + }); + } + + function initialLocation(data, params) { + var tag = params.get("tag"); + var seed = tag && seedForTag(data, tag); + if (tag && (!seed || statusFor(data, tag).state === "retired")) return null; + var point = params.has("lat") || params.has("lon") + ? configuratorSupport.parseCoordinates(params.get("lat"), params.get("lon")) + : seed; + if (!point) return null; + return { lat: point.lat, lon: point.lon, tag: tag || null, countryCode: "ca", + name: String(params.get("name") || (tag && labelFor(data, tag)) || "").slice(0, 160) }; + } + function configHrefForState(state) { var mapHref = new URL(mapHrefForState(state)); return regionPageHref("config") + mapHref.search; @@ -1199,11 +1228,8 @@ }); } - function buildCommands(data, tags, firmware, includeBaseline, parentOverrides) { - var lines = []; - if (includeBaseline) { - lines = lines.concat(data.meta.baselineCommands || []); - } + function buildCommands(data, tags, firmware, settings, parentOverrides) { + var lines = radioProfiles ? radioProfiles.commands(settings.radioProfile, settings.hashMode) : []; if (firmware === "1.16") { var regionDefLine = "region def " + regionDefTokens(data, tags, parentOverrides).join(" "); @@ -1342,12 +1368,14 @@ if (!matches.length) return null; var best = matches[0]; var tied = matches.filter(function (item) { return item.score === best.score; }); + if (tied.length > 1 && best.score > 1) return null; if (tied.length > 1) { return { ambiguous: true, choices: tied.map(function (item) { var province = provinceTagFor(data, item.seed.tag); - return labelFor(data, item.seed.tag) + (province ? ", " + province.toUpperCase() : ""); + return { tag: item.seed.tag, lat: item.seed.lat, lon: item.seed.lon, countryCode: "ca", + name: labelFor(data, item.seed.tag) + (province ? ", " + province.toUpperCase() : "") }; }) }; } @@ -1424,10 +1452,24 @@ }); } + function showLocationChoices(target, choices, choose) { + if (!choices) return; + choices.forEach(function (geo) { + var button = document.createElement("button"); + button.type = "button"; + button.className = "mcc-button mcc-button-secondary"; + button.textContent = geo.name + " (" + geo.tag + ")"; + button.addEventListener("click", function () { choose(geo); }); + target.appendChild(button); + }); + } + function geocode(data, query, signal) { var localMatch = localGeocode(data, query); if (localMatch && localMatch.ambiguous) { - return Promise.reject(new Error("That name matches more than one region (" + localMatch.choices.join("; ") + "). Add a province or postal code.")); + var error = new Error("Choose the region you mean:"); + error.choices = localMatch.choices; + return Promise.reject(error); } if (localMatch && localMatch.exactLocalMatch) return Promise.resolve(localMatch); var postal = parseCanadianPostalCode(query); @@ -1606,14 +1648,12 @@ 'No region yet' + 'Choose a location first.' + ''; - refreshIcons(target); return; } var rec = recommend(data, state.resolution, state.type, state.selectedMetros, state.selectedExternalPaths); if (!rec) { target.innerHTML = '
' + icon("radio-tower") + 'No region yet
'; - refreshIcons(target); return; } if (rec.budget.tagCount > 32 || rec.budget.responseBytes > 172) { @@ -1622,11 +1662,10 @@ 'Too many regions selected' + 'This selection uses ' + esc(rec.budget.tagCount) + ' tags and ' + esc(rec.budget.responseBytes) + ' bytes. Remove regions until it fits the 32-tag and 172-byte limits.' + ''; - refreshIcons(target); return; } var firmware = state.firmware || data.meta.defaultFirmware || "1.16"; - var commands = buildCommands(data, rec.tags, firmware, state.includeBaseline, rec.parentOverrides); + var commands = buildCommands(data, rec.tags, firmware, state, rec.parentOverrides); var technicalCommands = commands.concat(["region", "region save", "region"]); var titleTag = state.resolution.primary.seed.tag; var statusNotes = rec.notes.map(function (note) { @@ -1638,7 +1677,8 @@ var firmwareLabel = firmware === "1.16" ? "v1.16+" : firmware === "1.15" ? "v1.15.x" : "v1.14.x"; var guided = state.finishPath === "guided"; var verificationCommands = ["region"]; - if (state.includeBaseline) verificationCommands.push("get radio"); + if (state.radioProfile !== "keep") verificationCommands.push("get radio"); + if (state.hashMode !== "keep") verificationCommands.push("get path.hash.mode"); var expectedPaths = rec.paths.map(function (path) { return labelledPath(data, path); }); var expectedPathMarkup = '
' + expectedPaths.map(function (path) { return "" + esc(path) + ""; @@ -1687,7 +1727,7 @@ '
' + '

Save:

' + '
' + - '

' + (state.includeBaseline + '

' + (state.radioProfile !== "keep" ? 'Restart the device, reconnect, then run these final checks:' : 'Run this once more to confirm the saved region:') + '

' + '
' + verificationCommands.map(function (line) { @@ -1736,10 +1776,12 @@ (!guided ? '' : '') + '
' + scopeNotice + + '

' + esc(radioProfiles ? radioProfiles.label(state.radioProfile) : "Keep current settings") + + (state.radioProfile !== "keep" ? ' — Radio changes take effect after reboot.' : '') + '

' + technicalDetails + resultBody + '
' + - '

Commissioning record

Download or print a summary without exact coordinates, credentials, or device identifiers.

' + + '

Setup summary

Download or print a summary without exact coordinates, credentials, or device identifiers.

' + '
' + '
' + '
' + @@ -1769,7 +1811,6 @@ printCommissioningSummary(data, state, printSummary); }); } - refreshIcons(target); } function seedForTag(data, tag) { @@ -1795,7 +1836,7 @@ if (!rec) return null; if (rec.budget.tagCount > 32 || rec.budget.responseBytes > 172) return null; var firmware = state.firmware || data.meta.defaultFirmware || "1.16"; - return buildCommands(data, rec.tags, firmware, state.includeBaseline, rec.parentOverrides) + return buildCommands(data, rec.tags, firmware, state, rec.parentOverrides) .concat(["region", "region save", "region"]); } @@ -1811,6 +1852,8 @@ homeRegion: labelledPath(data, ancestryFor(data, homeTag)), firmware: state.firmware === "1.16" ? "v1.16+" : "v" + state.firmware + ".x", budget: rec.budget.tagCount + " / 32 tags, " + rec.budget.responseBytes + " / 172 bytes", + radio: radioProfiles ? radioProfiles.label(state.radioProfile) : "Keep current settings", + hashMode: state.hashMode === "keep" ? "Keep current settings" : (state.hashMode === "0" ? "1 byte" : String(Number(state.hashMode) + 1) + " bytes"), paths: rec.paths.map(function (path) { return labelledPath(data, path); }), commands: commands }); @@ -1929,7 +1972,6 @@ copyText(commands.join("\n"), copyCommands, "Copy commands"); }); } - refreshIcons(target); } function refreshTool(data, els, state, afterRefresh) { @@ -2035,12 +2077,13 @@ '' + '
' + '
' + + '' + + '' + + '

Choose a profile only after checking with your community. A region does not select a radio network.

' + + '' + + '' + '
' + - 'Radio and firmware options' + - '
' + - '' + - '' + - '
' + + 'Firmware version' + '

Firmware version

' + '
' + '' + @@ -2066,7 +2109,6 @@ function initConfig(el, data) { el.innerHTML = toolUi(); - refreshIcons(el); var state = { lat: null, lon: null, @@ -2077,7 +2119,8 @@ deviceRole: "repeater", type: "residential", firmware: data.meta.defaultFirmware || "1.16", - includeBaseline: true, + radioProfile: "keep", + hashMode: "keep", selectedMetros: [], selectedExternalPaths: [], canGenerate: false, @@ -2141,6 +2184,8 @@ '
Node
' + esc(deviceLabels[state.deviceRole] || deviceLabels.repeater) + '
' + '
Place
' + esc(state.name || labelFor(data, state.resolution.primary.seed.tag)) + '
' + '
Home region
' + esc(labelledPath(data, ancestryFor(data, state.resolution.primary.seed.tag))) + '
' + + '
Radio network
' + esc(radioProfiles ? radioProfiles.label(state.radioProfile) : "Keep current settings") + '
' + + '
Advert ID size
' + (state.hashMode === "keep" ? 'Keep current settings' : state.hashMode === "0" ? '1 byte' : esc(Number(state.hashMode) + 1) + ' bytes') + '
' + '
Budget
' + esc(rec.budget.tagCount) + ' / 32 tags · ' + esc(rec.budget.responseBytes) + ' / 172 bytes
' + '' + '

Forwarding paths

    ' + paths + '
' + @@ -2179,7 +2224,7 @@ heading.focus({ preventScroll: true }); } } - refreshIcons(el); + updateMapLinks(); } function selectFinishPath(path) { @@ -2190,6 +2235,7 @@ button.setAttribute("aria-pressed", active ? "true" : "false"); }); renderResult(data, els.result, state); + updateMapLinks(); } function advanceStep() { @@ -2333,12 +2379,12 @@ if (err && err.name === "AbortError") return; state.canGenerate = false; setStatus(els.status, esc(err.message || "Location lookup failed"), "error"); + showLocationChoices(els.status, err.choices, useGeo); refreshTool(data, els, state, updateMapLinks); }).finally(function () { if (activeGeocodeController !== thisController) return; els.locate.disabled = false; els.locate.innerHTML = icon("search") + "Find"; - refreshIcons(els.locate); }); } @@ -2419,14 +2465,13 @@ input.addEventListener("change", function () { state.deviceRole = input.value; if (state.deviceRole === "advanced" && els.technicalSettings) els.technicalSettings.open = true; + updateMapLinks(); }); }); - el.querySelectorAll("input[name='mcc-device-path']").forEach(function (input) { - input.addEventListener("change", function () { - state.includeBaseline = input.value === "new"; - renderResult(data, els.result, state); - }); - }); + var profileSelect = el.querySelector("#mcc-radio-profile"); + var hashSelect = el.querySelector("#mcc-hash-mode"); + profileSelect.addEventListener("change", function () { state.radioProfile = profileSelect.value; updateMapLinks(); }); + hashSelect.addEventListener("change", function () { state.hashMode = hashSelect.value; updateMapLinks(); }); el.querySelectorAll("input[name='mcc-type']").forEach(function (input) { input.addEventListener("change", function () { state.type = input.value; @@ -2440,13 +2485,52 @@ input.addEventListener("change", function () { state.firmware = input.value; renderResult(data, els.result, state); + updateMapLinks(); }); }); renderConfigRegionBrowser(state.browseTag); updateMapLinks(); var initialParams = new URLSearchParams(window.location.search); + keepLanguageSelection(state); + var profileReady = radioProfiles ? radioProfiles.populate(profileSelect) : Promise.resolve(); + profileReady.then(function () { + if (Array.from(profileSelect.options).some(function (option) { return option.value === initialParams.get("radio"); })) { + state.radioProfile = profileSelect.value = initialParams.get("radio"); + updateMapLinks(); + if (state.wizardStep === 4) showStep(4); + } + }); + [["firmware", "mcc-firmware", ["1.14", "1.15", "1.16"]], + ["deviceRole", "mcc-device-role", ["repeater", "room", "advanced"]]].forEach(function (entry) { + var value = initialParams.get(entry[0] === "deviceRole" ? "role" : entry[0]); + if (entry[2].indexOf(value) !== -1) { + state[entry[0]] = value; + el.querySelector("input[name='" + entry[1] + "'][value='" + value + "']").checked = true; + } + }); + if (["0", "1", "2"].indexOf(initialParams.get("hash")) !== -1) state.hashMode = hashSelect.value = initialParams.get("hash"); + if (initialParams.get("instructions") === "technical") selectFinishPath("technical"); + var location = initialLocation(data, initialParams); var initialQuery = (initialParams.get("place") || "").trim(); - if (initialQuery) { + if (location) { + els.input.value = location.name; + useGeo(location).then(function () { + if (!state.canGenerate) return; + if (initialParams.get("type") === "large") { + state.type = "high-site"; + el.querySelector("input[name='mcc-type'][value='high-site']").checked = true; + state.selectedMetros = String(initialParams.get("regions") || "").split(",").filter(function (tag) { return Boolean(seedForTag(data, tag)); }); + state.selectedExternalPaths = selectedExternalRegionPaths(data, String(initialParams.get("external") || "").split(",")).map(function (record) { return record.id; }); + } + state.maxStep = 4; + refreshTool(data, els, state, updateMapLinks); + showStep(/^[1-4]$/.test(initialParams.get("step")) ? Number(initialParams.get("step")) : 3); + }); + } else if (initialParams.has("tag") || initialParams.has("lat") || initialParams.has("lon")) { + state.maxStep = 2; + showStep(2); + setStatus(els.status, "This saved location is invalid or no longer available. Choose a region again.", "warning"); + } else if (initialQuery) { state.maxStep = 2; els.input.value = initialQuery; showStep(2); @@ -2502,11 +2586,11 @@ '
' + '
' + '' + - '
' + - '

Browse by province or territory

' + + '
' + + 'Browse by province or territory' + '
' + '
' + - '
' + + '' + '' + @@ -2514,6 +2598,7 @@ '
' + 'Skip the interactive map' + '
' + + '' + '
' + '
' + icon("map") + '

Loading interactive map…

' + '

Loading Canadian boundaries and OpenStreetMap tiles.

' + @@ -2536,7 +2621,6 @@ '
' + '' + '
'; - refreshIcons(el); var mapParams = new URLSearchParams(window.location.search); var requestedLargeCoverage = mapParams.get("type") === "large"; @@ -2558,7 +2642,8 @@ jurisdictionTag: null, type: requestedLargeCoverage ? "high-site" : "residential", firmware: data.meta.defaultFirmware || "1.16", - includeBaseline: true, + radioProfile: "keep", + hashMode: ["0", "1", "2"].indexOf(mapParams.get("hash")) !== -1 ? mapParams.get("hash") : "keep", selectedMetros: requestedLargeCoverage ? requestedCanadianRegions : [], selectedExternalPaths: requestedLargeCoverage ? requestedExternalPaths : [], canGenerate: false, @@ -2583,6 +2668,7 @@ mapLoading: el.querySelector("[data-role='map-loading']"), mapLoadStatus: el.querySelector("[data-role='map-load-status']"), mapReadyStatus: el.querySelector("[data-role='map-ready-status']"), + boundaryStatus: el.querySelector("[data-role='map-boundary-status']"), mapArea: el.querySelector("[data-role='map-area']"), canvas: el.querySelector("[data-role='map-canvas']"), auditStatus: el.querySelector("[data-role='audit-status']"), @@ -2648,7 +2734,6 @@ 'Download catalog' + 'Open standard and change process' + '
'; - refreshIcons(els.auditContent); auditLoaded = true; }).catch(function (error) { els.auditStatus.hidden = false; @@ -2681,7 +2766,7 @@ '' + esc(labelFor(data, child)) + '' + (nested ? leafCount + ' subregions' : child.toUpperCase() + ' · region') + ''; - }).join("") : '

Select this leaf to see its full path.

'; + }).join("") : '

Select this region to see its full path.

'; if (!map || !browseLayer) return; browseLayer.clearLayers(); if (tag !== (data.meta.rootTag || "can") && data.partitionByTag) { @@ -2728,22 +2813,28 @@ return normalizeLocationSearch(alias) !== normalizeLocationSearch(tag) && normalizeLocationSearch(alias) !== normalizeLocationSearch(labelFor(data, tag)); }); + aliases = aliases.filter(function (alias, index) { + return aliases.findIndex(function (item) { return normalizeLocationSearch(item) === normalizeLocationSearch(alias); }) === index; + }); + var communityUrl = new URL("../provinces/", regionPageHref("config")); + communityUrl.searchParams.set("community", labelFor(data, provinceTagFor(data, tag))); els.resultSection.hidden = false; els.textResult.innerHTML = '

' + esc(state.name) + ' resolves to ' + esc(labelFor(data, tag)) + ' (' + esc(tag) + ').

' + '

' + esc(labelledPath(data, ancestryFor(data, tag))) + '

' + - '
Province or territory
' + esc(labelFor(data, provinceTagFor(data, tag))) + '
' + + '

These are routing regions, not radio coverage boundaries.

' + + '

Find a community

' + + '
Region details
Province or territory
' + esc(labelFor(data, provinceTagFor(data, tag))) + '
' + '
Status
' + esc(statusLabel(statusFor(data, tag).state || "draft")) + '
' + '
Aliases
' + esc(aliases.length ? aliases.join(", ") : "None recorded") + '
' + - '
Planned paths
' + esc(rec ? rec.paths.length : 1) + '
' + + '
Repeater paths
' + esc(rec ? rec.paths.length : 1) + '
' + '
Configure this region' + '
'; var copyLink = els.textResult.querySelector("[data-action='copy-region-link']"); if (copyLink) copyLink.addEventListener("click", function () { copyText(mapHrefForState(state), copyLink, "Copy link"); }); - refreshIcons(els.textResult); } function finishGeo(geo, forcedTag, recenter, requestId) { @@ -2768,8 +2859,7 @@ if (!state.canGenerate) { setStatus(els.status, "No Canadian region contains that point. Browse the list instead.", "warning"); } else { - var path = labelledPath(data, ancestryFor(data, state.detailTag)); - setStatus(els.status, "Text result: " + esc(path) + ".", "info"); + setStatus(els.status, "Region found.", "info"); renderRegionBrowser(state.detailTag, false); } renderTextResult(); @@ -2812,11 +2902,11 @@ .catch(function (error) { if (error && error.name === "AbortError") return; setStatus(els.status, esc(error.message || "Location lookup failed"), "error"); + showLocationChoices(els.status, error.choices, function (geo) { return useGeo(geo, true, geo.tag); }); }).finally(function () { if (activeGeocodeController !== thisController) return; els.locate.disabled = false; els.locate.innerHTML = icon("search") + "Find"; - refreshIcons(els.locate); }); } @@ -2841,33 +2931,47 @@ mapIsLoading = true; els.loadMap.hidden = true; els.mapStage.setAttribute("aria-busy", "true"); + els.boundaryStatus.hidden = false; els.mapLoadStatus.textContent = "Loading Canadian boundaries and map tools…"; els.mapReadyStatus.textContent = "Loading the interactive region map."; - Promise.all([loadDisplayPartition(), loadLeaflet()]).then(function (loaded) { - applyGeneratedPartition(data, loaded[0], null); - var L = loaded[1]; + loadLeaflet().then(function (L) { + L.Icon.Default.imagePath = new URL("vendor/leaflet/images/", assetBase).href; els.mapArea.hidden = false; map = L.map(els.canvas, { minZoom: 3, maxZoom: 13 }); + var loadingMap = map; activeMaps.push({ container: el, map: map }); - map.fitBounds(data.meta.map.bounds || [[41.5, -141.5], [83.5, -52]], { padding: [24, 24], maxZoom: 4 }); + var initialRec = state.canGenerate && recommend(data, state.resolution, state.type, state.selectedMetros, state.selectedExternalPaths); + var initialFeatures = initialRec && data.resolverByTag + ? initialRec.leaves.map(function (tag) { return data.resolverByTag[tag]; }).filter(Boolean) : []; + var initialBounds = initialFeatures.length ? L.geoJSON(initialFeatures).getBounds() : null; + map.fitBounds(initialBounds && initialBounds.isValid() ? initialBounds : data.meta.map.bounds || [[41.5, -141.5], [83.5, -52]], + { padding: [28, 28], maxZoom: initialFeatures.length ? 9 : 4, animate: false }); var tileLayer = L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", { maxZoom: 19, attribution: '© OpenStreetMap contributors' }).addTo(map); - L.geoJSON(data.partitionRegions, { - style: function (feature) { - return { color: "#aeb8ff", opacity: 0.8, weight: 1, fillColor: colorForTag(feature.properties.tag), fillOpacity: 0.2 }; - }, - onEachFeature: function (feature, layer) { - layer.bindTooltip('' + esc(feature.properties.tag.toUpperCase()) + ' - ' + esc(feature.properties.label)); - layer.on("click", function (event) { - if (event.originalEvent) L.DomEvent.stopPropagation(event.originalEvent); - useGeo({ lat: event.latlng.lat, lon: event.latlng.lng, name: feature.properties.label, countryCode: "ca", tag: feature.properties.tag }, false, feature.properties.tag); - }); - } - }).addTo(map); browseLayer = L.geoJSON(null, { interactive: false, style: { color: "#ffd166", opacity: 1, weight: 3, fillOpacity: 0 } }).addTo(map); selectedLayer = L.geoJSON(null, { interactive: false, style: { color: "#ffffff", opacity: 1, weight: 4, dashArray: "8 5", fillColor: "#4287ff", fillOpacity: 0.34 } }).addTo(map); + updateMapVisuals(false); + // Paint tiles first; the much larger boundary overlay can arrive independently. + var boundariesReady = loadDisplayPartition().then(function (partition) { + if (map !== loadingMap) return; + applyGeneratedPartition(data, partition, null); + L.geoJSON(data.partitionRegions, { + style: function (feature) { + return { color: "#aeb8ff", opacity: 0.8, weight: 1, fillColor: colorForTag(feature.properties.tag), fillOpacity: 0.2 }; + }, + onEachFeature: function (feature, layer) { + layer.bindTooltip('' + esc(feature.properties.tag.toUpperCase()) + ' - ' + esc(feature.properties.label)); + layer.on("click", function (event) { + if (event.originalEvent) L.DomEvent.stopPropagation(event.originalEvent); + useGeo({ lat: event.latlng.lat, lon: event.latlng.lng, name: feature.properties.label, countryCode: "ca", tag: feature.properties.tag }, false, feature.properties.tag); + }); + } + }).addTo(map); + renderRegionBrowser(state.browseTag, false); + updateMapVisuals(false); + }); map.on("click", function (event) { useGeo({ lat: event.latlng.lat, @@ -2876,10 +2980,8 @@ countryCode: "ca" }, false, null); }); - renderRegionBrowser(state.browseTag, false); - updateMapVisuals(Boolean(state.canGenerate)); - window.setTimeout(function () { map.invalidateSize(); }, 0); - return new Promise(function (resolve, reject) { + window.setTimeout(function () { if (map === loadingMap) map.invalidateSize(); }, 0); + var tilesReady = new Promise(function (resolve, reject) { var settled = false; var timeout = window.setTimeout(function () { if (settled) return; @@ -2890,6 +2992,7 @@ if (settled) return; settled = true; window.clearTimeout(timeout); + if (map === loadingMap) els.mapLoading.hidden = true; resolve(); }); tileLayer.once("load", function () { @@ -2899,8 +3002,10 @@ reject(new Error("OpenStreetMap tiles could not load. Search and the region list still work.")); }); }); + return Promise.all([tilesReady, boundariesReady]); }).then(function () { els.mapLoading.hidden = true; + els.boundaryStatus.hidden = true; els.mapLoadStatus.textContent = ""; els.mapStage.setAttribute("aria-busy", "false"); els.mapReadyStatus.textContent = "Interactive region map loaded."; @@ -2911,9 +3016,13 @@ try { map.remove(); } catch (cleanupError) { /* Leaflet may have failed during setup. */ } activeMaps = activeMaps.filter(function (entry) { return entry.map !== failedMap; }); map = null; + marker = null; + browseLayer = null; + selectedLayer = null; } els.mapArea.hidden = true; els.mapLoading.hidden = false; + els.boundaryStatus.hidden = true; els.loadMap.hidden = false; mapIsLoading = false; els.mapLoadStatus.textContent = error.message || "The map could not load. Search and the region list still work."; @@ -2938,21 +3047,54 @@ els.coordinates.addEventListener("click", useCoordinates); els.loadMap.addEventListener("click", loadInteractiveMap); renderRegionBrowser(state.browseTag, false); - renderRegionTable(els.table, data, function (tag) { chooseRegionNode(tag); }); - window.setTimeout(loadInteractiveMap, 0); - - var initialLat = Number(mapParams.get("lat")); - var initialLon = Number(mapParams.get("lon")); - var hasInitialLocation = mapParams.has("lat") && mapParams.has("lon") && - Number.isFinite(initialLat) && Number.isFinite(initialLon) && - initialLat >= -90 && initialLat <= 90 && initialLon >= -180 && initialLon <= 180; - if (hasInitialLocation) { - var requestedTag = slug(mapParams.get("tag")); - if (!data.hierarchy[requestedTag] || !seedForTag(data, requestedTag)) requestedTag = null; - var initialName = String(mapParams.get("name") || "").slice(0, 160) || - initialLat.toFixed(4) + ", " + initialLon.toFixed(4); - els.input.value = initialName; - useGeo({ lat: initialLat, lon: initialLon, name: initialName, countryCode: "ca", tag: requestedTag }, false, requestedTag); + var regionList = el.querySelector("#mcc-region-list"); + function ensureRegionTable() { + if (!els.table.firstChild) renderRegionTable(els.table, data); + } + regionList.addEventListener("toggle", function () { + if (regionList.open) ensureRegionTable(); + }); + el.querySelector(".mcc-skip-map").addEventListener("click", function () { + regionList.open = true; + ensureRegionTable(); + }); + function observeInteractiveMap() { + // Text lookup is useful on its own; download the display layer only when it is visible. + if ("IntersectionObserver" in window) { + var mapObserver = new IntersectionObserver(function (entries) { + if (entries.some(function (entry) { return entry.isIntersecting; })) { + mapObserver.disconnect(); + loadInteractiveMap(); + } + }); + mapObserver.observe(els.mapStage); + } else { + window.setTimeout(loadInteractiveMap, 0); + } + } + + keepLanguageSelection(state); + // Load the same validated profile list used by the configurator before carrying a choice back. + if (radioProfiles) radioProfiles.populate(document.createElement("select")).then(function () { + try { + radioProfiles.commands(mapParams.get("radio"), "keep"); + state.radioProfile = mapParams.get("radio") || "keep"; + } catch (_) { state.radioProfile = "keep"; } + renderTextResult(); + }); + if (["1.14", "1.15", "1.16"].indexOf(mapParams.get("firmware")) !== -1) state.firmware = mapParams.get("firmware"); + if (["repeater", "room", "advanced"].indexOf(mapParams.get("role")) !== -1) state.deviceRole = mapParams.get("role"); + if (["guided", "technical"].indexOf(mapParams.get("instructions")) !== -1) state.finishPath = mapParams.get("instructions"); + var initialGeo = initialLocation(data, mapParams); + if (initialGeo) { + els.input.value = initialGeo.name; + // Let the result card settle before measuring whether the map is on screen. + useGeo(initialGeo, false, initialGeo.tag).then(observeInteractiveMap); + } else { + if (mapParams.has("tag") || mapParams.has("lat") || mapParams.has("lon")) { + setStatus(els.status, "This saved location is invalid or no longer available. Choose a region again.", "warning"); + } + observeInteractiveMap(); } setMapMode(mapParams.get("view") === "audit" ? "audit" : "explore"); } @@ -3023,7 +3165,6 @@ "" + (row.basis === "established" ? "Established" : "Proposed") + "" + ""; }).join(""); - refreshIcons(el); } input.addEventListener("input", draw); province.addEventListener("change", draw); @@ -3052,7 +3193,6 @@ '' + '
'; renderRegionTable(el.querySelector("[data-role='dashboard-table']"), data); - refreshIcons(el); } function initRegions() { diff --git a/docs/assets/regions/vendor/README.txt b/docs/assets/regions/vendor/README.txt index 3eeaf85d..af3f3773 100644 --- a/docs/assets/regions/vendor/README.txt +++ b/docs/assets/regions/vendor/README.txt @@ -14,3 +14,7 @@ The CSS differs from upstream only by git CRLF→LF normalization. UMD distribution from npm. - `lucide.js`: `8c6fb02591ab1afdf712fa06bc9ac41124f48a729cd07aa1b6eec705f7f92f34` + +The region tools embed only their 17 SVG icons in regions.js. They no longer +download the full Lucide library. The original bundle is retained as the +reference for the icon consistency test; see lucide-LICENSE for its notices. diff --git a/docs/assets/regions/vendor/lucide-LICENSE b/docs/assets/regions/vendor/lucide-LICENSE new file mode 100644 index 00000000..46e69621 --- /dev/null +++ b/docs/assets/regions/vendor/lucide-LICENSE @@ -0,0 +1,39 @@ +ISC License + +Copyright (c) for portions of Lucide are held by Cole Bemis 2013-2023 as part of Feather (MIT). All other copyright (c) for Lucide are held by Lucide Contributors 2025. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +--- + +The MIT License (MIT) (for portions derived from Feather) + +Copyright (c) 2013-2023 Cole Bemis + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/docs/config/editor/issue.js b/docs/config/editor/issue.js index ebbc024f..36992ed6 100644 --- a/docs/config/editor/issue.js +++ b/docs/config/editor/issue.js @@ -159,7 +159,8 @@ export function validateSubmissionConfig(value, endpoint = DEFAULT_SUBMISSION_EN version: SUBMISSION_CONTRACT_VERSION, endpoint: cleanEndpoint(endpoint), turnstileSiteKey: siteKey, - turnstileAction: action + turnstileAction: action, + ...(value.communityIdeaOptionalDetails === true ? { communityIdeaOptionalDetails: true } : {}) }); } diff --git a/docs/config/index.fr.md b/docs/config/index.fr.md index f6c0ac9e..13c0c3cd 100644 --- a/docs/config/index.fr.md +++ b/docs/config/index.fr.md @@ -14,10 +14,11 @@ tested_with: difficulty: intermediate estimated_time: 5-10 minutes page_styles: - - assets/regions/regions.css?v=20260820-1 + - assets/regions/regions.css?v=20260904-1 page_scripts: - - assets/regions/modules/configurator-support.js?v=20260722-2 - - assets/regions/regions.js?v=20260820-1 + - assets/javascripts/radio-profiles.js?v=20260904-1 + - assets/regions/modules/configurator-support.js?v=20260904-1 + - assets/regions/regions.js?v=20260904-1 hide: - navigation - toc diff --git a/docs/config/index.md b/docs/config/index.md index 2a5a9745..c7ee9003 100644 --- a/docs/config/index.md +++ b/docs/config/index.md @@ -14,10 +14,11 @@ tested_with: difficulty: intermediate estimated_time: 5-10 minutes page_styles: - - assets/regions/regions.css?v=20260820-1 + - assets/regions/regions.css?v=20260904-1 page_scripts: - - assets/regions/modules/configurator-support.js?v=20260722-2 - - assets/regions/regions.js?v=20260820-1 + - assets/javascripts/radio-profiles.js?v=20260904-1 + - assets/regions/modules/configurator-support.js?v=20260904-1 + - assets/regions/regions.js?v=20260904-1 hide: - navigation - toc diff --git a/docs/config/map.fr.md b/docs/config/map.fr.md index e63b50c7..4e2fa778 100644 --- a/docs/config/map.fr.md +++ b/docs/config/map.fr.md @@ -15,10 +15,11 @@ tested_with: region_catalog: national-partition-2026-07-19 difficulty: beginner page_styles: - - assets/regions/regions.css?v=20260820-1 + - assets/regions/regions.css?v=20260904-1 page_scripts: - - assets/regions/modules/configurator-support.js?v=20260722-2 - - assets/regions/regions.js?v=20260820-1 + - assets/javascripts/radio-profiles.js?v=20260904-1 + - assets/regions/modules/configurator-support.js?v=20260904-1 + - assets/regions/regions.js?v=20260904-1 hide: - toc --- diff --git a/docs/config/map.md b/docs/config/map.md index df02ec96..cee67591 100644 --- a/docs/config/map.md +++ b/docs/config/map.md @@ -15,10 +15,11 @@ tested_with: region_catalog: national-partition-2026-07-19 difficulty: beginner page_styles: - - assets/regions/regions.css?v=20260820-1 + - assets/regions/regions.css?v=20260904-1 page_scripts: - - assets/regions/modules/configurator-support.js?v=20260722-2 - - assets/regions/regions.js?v=20260820-1 + - assets/javascripts/radio-profiles.js?v=20260904-1 + - assets/regions/modules/configurator-support.js?v=20260904-1 + - assets/regions/regions.js?v=20260904-1 hide: - toc --- diff --git a/docs/config/standard.fr.md b/docs/config/standard.fr.md index c71f9027..b7ec1d86 100644 --- a/docs/config/standard.fr.md +++ b/docs/config/standard.fr.md @@ -6,7 +6,7 @@ audience: - region-maintainer task: understand-region-standard scope: canada-baseline -status: verified +status: draft owner: region-maintainers last_reviewed: 2026-07-19 review_by: 2026-10-19 @@ -17,6 +17,18 @@ difficulty: advanced # Définition des régions et autorité de MeshCore Canada +## Pour configurer votre appareil + +1. [Trouvez votre région](map.md). +2. Confirmez les réglages radio auprès de votre [communauté](../provinces/index.md). +3. Sur un répéteur, [préparez les chemins régionaux](index.md), vérifiez-les, puis enregistrez-les. + +Une région organise le routage; elle ne garantit pas la portée radio. Ottawa–Gatineau et Lloydminster utilisent plusieurs chemins pour leur zone partagée. + +Le reste de cette page décrit les règles proposées et les contrôles destinés aux responsables du registre. Les modifications de limites nécessitent un examen communautaire. + +## Référence du registre + Cette norme définit les régions MeshCore du Canada. Elle explique comment les emplacements et les limites sont attribués, qui approuve les changements et comment les conflits entre sources sont résolus. @@ -29,7 +41,7 @@ comment les conflits entre sources sont résolus. | Entrée sémantique actuelle | Canada MeshCore Region Strategy v1.1.1 | | Entrée actuelle pour les limites communautaires | Instantané MeshMapper Canada, 2026-07-12 | | Preuve opérationnelle actuelle | Instantané canadien de densité radio protégeant la vie privée, 2026-07-15 UTC; instantané agrégé des routes Canada–États-Unis, 2026-07-18 | -| Adoption | Devient normative après approbation et fusion par MeshCore Canada | +| Adoption | Examen communautaire; publier cette page ne signifie pas que tous les réseaux l’ont adoptée | Statistique Canada emploie en français les termes **aire de diffusion (AD)**, **subdivision de recensement (SDR)**, **division de recensement (DR)** et diff --git a/docs/config/standard.md b/docs/config/standard.md index 7a6745e5..5d774bfd 100644 --- a/docs/config/standard.md +++ b/docs/config/standard.md @@ -6,7 +6,7 @@ audience: - region-maintainer task: understand-region-standard scope: canada-baseline -status: verified +status: draft owner: region-maintainers last_reviewed: 2026-07-19 review_by: 2026-10-19 @@ -17,6 +17,18 @@ difficulty: advanced # MeshCore Canada region definition and authority +## Setting up a device? + +1. [Find your region](map.md). +2. Confirm the radio settings with your [community](../provinces/index.md). +3. For a repeater, [prepare its region paths](index.md), check them, then save. + +A region organizes routing; it does not guarantee radio coverage. Ottawa–Gatineau and Lloydminster use several paths for their shared area. + +The rest of this page documents proposed registry rules and maintainer checks. Boundary changes still require community review. + +## Registry reference + This standard defines Canada's MeshCore regions. It explains how locations and boundaries are assigned, who approves changes, and how source conflicts are resolved. @@ -29,7 +41,7 @@ resolved. | Current semantic input | Canada MeshCore Region Strategy v1.1.1 | | Current community boundary input | MeshMapper Canada snapshot, 2026-07-12 | | Current operational evidence | Privacy-safe Canadian radio-density snapshot, 2026-07-15 UTC; aggregate Canada–U.S. route snapshot, 2026-07-18 | -| Adoption | Becomes normative when approved and merged by MeshCore Canada | +| Adoption | Community review; publishing this page does not establish network-wide adoption | !!! important "What is authoritative today?" This page and the generated national partition are proposed for review. The candidate assigns every digital DA exactly once and the public map renders only dissolved leaf regions. No raw source polygon or approximate circle is a boundary. The partition becomes operationally authoritative only after community review and every release check in this standard passes. diff --git a/docs/hardware/recommended-antenna.fr.md b/docs/hardware/recommended-antenna.fr.md index 5dfef261..2daa7cb1 100644 --- a/docs/hardware/recommended-antenna.fr.md +++ b/docs/hardware/recommended-antenna.fr.md @@ -56,9 +56,9 @@ Ce sont des antennes SMA, plus compactes, qui ont pourtant constamment offert un | Produit | Connecteur | Coût (CAD) | Lien | |---|---|---|---| -| Gizont 167CM 915MHz SMA M | SMA | 12 $ | [Space Hedgehog (boutique locale)](https://space-hedgehog.com/products/gizont-915mhz-antenna?variant=51602989711416) | -| Gizont 167CM 915MHz SMA M | SMA | 10,53 $ | [AliExpress](https://www.aliexpress.com/item/1005004607615001.html) | -| Gizont 167CM 915MHz RP-SMA M | RP-SMA | 10,53 $ | [AliExpress](https://www.aliexpress.com/item/1005004607615001.html) | +| Gizont 915 MHz SMA M | SMA | 12 $ | [Space Hedgehog (boutique locale)](https://space-hedgehog.com/products/gizont-915mhz-antenna?variant=51602989711416) | +| Gizont 915 MHz SMA M | SMA | 10,53 $ | [AliExpress](https://www.aliexpress.com/item/1005004607615001.html) | +| Gizont 915 MHz RP-SMA M | RP-SMA | 10,53 $ | [AliExpress](https://www.aliexpress.com/item/1005004607615001.html) | | LINX ANT-916-CW-HW-SMA | SMA | 14,65 $ | [DigiKey](https://www.digikey.ca/en/products/detail/te-connectivity-linx/ANT-916-CW-HW-SMA/2694126?s=N4IgTCBcDaIDIEkByANABAQSQFQLQE4BGANlwGEB1XACSoGUBZDEAXQF8g) | | Taoglas TI.09.A.0111 | SMA | 17,47 $ | [DigiKey](https://www.digikey.ca/en/products/detail/taoglas-limited/TI-09-A-0111/2332695?s=N4IgTCBcDaICoEMD2BzANggzgAjgSQDoAGATgIEFiBGGkAXQF8g) | | Seeed Studio LoRa Antenna Kit | SMA | 6,79 $ | [Seeed Studio](https://www.seeedstudio.com/LoRa-Antenna-Kit-for-reTerminal-DM-p-5714.html) | diff --git a/docs/hardware/recommended-antenna.md b/docs/hardware/recommended-antenna.md index 7cb821c0..db36afe1 100644 --- a/docs/hardware/recommended-antenna.md +++ b/docs/hardware/recommended-antenna.md @@ -56,9 +56,9 @@ These are SMA antennas and are more compact, yet they've consistently shown exce | Product | Connector | Cost (CAD) | Link | |---|---|---|---| -| Gizont 167CM 915MHz SMA M | SMA | $12 | [Space Hedgehog (local store)](https://space-hedgehog.com/products/gizont-915mhz-antenna?variant=51602989711416) | -| Gizont 167CM 915MHz SMA M | SMA | $10.53 | [AliExpress](https://www.aliexpress.com/item/1005004607615001.html) | -| Gizont 167CM 915MHz RP-SMA M | RP-SMA | $10.53 | [AliExpress](https://www.aliexpress.com/item/1005004607615001.html) | +| Gizont 915 MHz SMA M | SMA | $12 | [Space Hedgehog (local store)](https://space-hedgehog.com/products/gizont-915mhz-antenna?variant=51602989711416) | +| Gizont 915 MHz SMA M | SMA | $10.53 | [AliExpress](https://www.aliexpress.com/item/1005004607615001.html) | +| Gizont 915 MHz RP-SMA M | RP-SMA | $10.53 | [AliExpress](https://www.aliexpress.com/item/1005004607615001.html) | | LINX ANT-916-CW-HW-SMA | SMA | $14.65 | [DigiKey](https://www.digikey.ca/en/products/detail/te-connectivity-linx/ANT-916-CW-HW-SMA/2694126?s=N4IgTCBcDaIDIEkByANABAQSQFQLQE4BGANlwGEB1XACSoGUBZDEAXQF8g) | | Taoglas TI.09.A.0111 | SMA | $17.47 | [DigiKey](https://www.digikey.ca/en/products/detail/taoglas-limited/TI-09-A-0111/2332695?s=N4IgTCBcDaICoEMD2BzANggzgAjgSQDoAGATgIEFiBGGkAXQF8g) | | Seeed Studio LoRa Antenna Kit | SMA | $6.79 | [Seeed Studio](https://www.seeedstudio.com/LoRa-Antenna-Kit-for-reTerminal-DM-p-5714.html) | diff --git a/docs/hardware/recommended-companions.fr.md b/docs/hardware/recommended-companions.fr.md index 5cbac16e..4a7dd1e1 100644 --- a/docs/hardware/recommended-companions.fr.md +++ b/docs/hardware/recommended-companions.fr.md @@ -81,11 +81,11 @@ Il s’agit d’un rôle de **compagnon** et il exige un téléphone intelligent | **Carte LoRa** | Heltec T114 (ensemble avec écran) | 45,99 $ | [AliExpress](https://www.aliexpress.com/item/1005007916299029.html) | | **Câble IPEX à SMA à angle droit** | SMA-KW 2PCS 8cm | 4,67 $ | [AliExpress](https://www.aliexpress.com/item/1005009270132403.html) | | **Pile** | MakerFocus LiPo 3,7 V 3000 mAh (paquet de 4), connecteur Micro JST 1.5 avec circuit de protection | 34,34 $ | [MakerFocus](https://www.makerfocus.com/products/makerfocus-3-7v-3000mah-lithium-rechargeable-battery-1s-3c-lipo-battery-pack-of-4?variant=44823607541998) | -| **Antenne** | Gizont 167CM 915MHz SMA M | 10,68 $ | [AliExpress](https://www.aliexpress.com/item/1005004607615001.html) (assurez-vous de choisir la bonne antenne à l’ouverture du lien) | +| **Antenne** | Gizont 915 MHz SMA M | 10,68 $ | [AliExpress](https://www.aliexpress.com/item/1005004607615001.html) (assurez-vous de choisir la bonne antenne à l’ouverture du lien) | -*Coût total approximatif :* **95,68 $ CAD** +*Total de cet exemple d’achat :* **95,68 $ CAD**, dont un lot de quatre piles et deux câbles. Ce n’est pas le coût par appareil. *Les prix datent du 2 septembre 2026. Consultez les pages liées pour vérifier les prix, l’expédition, les droits de douane et la disponibilité.* diff --git a/docs/hardware/recommended-companions.md b/docs/hardware/recommended-companions.md index e212c5ef..4893b417 100644 --- a/docs/hardware/recommended-companions.md +++ b/docs/hardware/recommended-companions.md @@ -81,11 +81,11 @@ This is a **companion node** role and requires a smartphone. The MeshCore app co | **LoRa board** | Heltec T114 (bundle with screen) | $45.99 | [AliExpress](https://www.aliexpress.com/item/1005007916299029.html) | | **Right-angle IPEX to SMA pigtail cable** | SMA-KW 2PCS 8cm | $4.67 | [AliExpress](https://www.aliexpress.com/item/1005009270132403.html) | | **Battery** | MakerFocus 3.7V 3000mAh LiPo (pack of 4), Micro JST 1.5 connection with protection board | $34.34 | [MakerFocus](https://www.makerfocus.com/products/makerfocus-3-7v-3000mah-lithium-rechargeable-battery-1s-3c-lipo-battery-pack-of-4?variant=44823607541998) | -| **Antenna** | Gizont 167CM 915MHz SMA M | $10.68 | [AliExpress](https://www.aliexpress.com/item/1005004607615001.html) (make sure you select the right antenna when opening the link) | +| **Antenna** | Gizont 915 MHz SMA M | $10.68 | [AliExpress](https://www.aliexpress.com/item/1005004607615001.html) (make sure you select the right antenna when opening the link) | -*Approximate total cost:* **$95.68 CAD** +*Example shopping total:* **$95.68 CAD**, including a four-battery pack and two pigtails. This is not the per-device cost. *Prices are dated September 2, 2026. Check the linked pages for current prices, shipping, duties, and availability.* diff --git a/docs/hardware/repeater-mounting-options.fr.md b/docs/hardware/repeater-mounting-options.fr.md index 38cf9890..4af68583 100644 --- a/docs/hardware/repeater-mounting-options.fr.md +++ b/docs/hardware/repeater-mounting-options.fr.md @@ -138,11 +138,9 @@ sous tension en hauteur.
-Aucun intervalle d’inspection pancanadien n’a été approuvé. Avant le -déploiement, consignez un intervalle propre au site et les conditions qui -déclenchent une inspection supplémentaire, comme des conditions -météorologiques extrêmes, des travaux à proximité, une infiltration d’eau ou -un changement du comportement radio. +Fixez un calendrier d’inspection adapté au site. Inspectez aussi l’installation +après une tempête, des travaux à proximité, une infiltration d’eau ou un changement +du comportement radio. Pour chaque inspection, consignez la date, le nom de la personne qui l’a effectuée, les photographies, l’état de la quincaillerie et des câbles, l’état diff --git a/docs/hardware/repeater-mounting-options.md b/docs/hardware/repeater-mounting-options.md index 96d4d9d1..b433677f 100644 --- a/docs/hardware/repeater-mounting-options.md +++ b/docs/hardware/repeater-mounting-options.md @@ -110,7 +110,7 @@ If anything moves, leaks, loosens, corrodes, changes electrically, or fails its
-No Canada-wide inspection interval has been approved. Before deployment, record a site-specific interval and the conditions that trigger an extra inspection, such as severe weather, nearby work, water entry, or changed radio behaviour. +Set an inspection schedule for the site. Inspect again after severe weather, nearby work, water entry, or changed radio behaviour. Record each inspection date, inspector, photographs, hardware/cable condition, enclosure condition, radio verification, corrective work, and next due date. diff --git a/docs/hardware/repeater-solar-1w-diy-build.fr.md b/docs/hardware/repeater-solar-1w-diy-build.fr.md index b7f93ee0..bea7a777 100644 --- a/docs/hardware/repeater-solar-1w-diy-build.fr.md +++ b/docs/hardware/repeater-solar-1w-diy-build.fr.md @@ -39,7 +39,7 @@ facultative, ses mesures et ses limites climatiques n’ont pas été reproduite
Radio
GOME Ikoka Stick 0.4.0
Alimentation
Waveshare Solar Power Manager
-
Micrologiciel
Ni épinglé ni reproduit
+
Micrologiciel
Version non confirmée; fonctionnement non vérifié
Entretien
À définir pendant l’examen du site
@@ -52,7 +52,7 @@ de trafic. Utilisez le [modèle de 300 mW à l’état d’ébauche](repeater-solar-300mw-diy-build.md) ou un système préassemblé examiné, sauf si une personne responsable du réseau -et une personne responsable de l’examen du matériel et des RF conviennent que +et une personne qualifiée en électronique et en RF conviennent que cette expérience répond à un besoin mesuré. ## Avant de commencer @@ -63,8 +63,8 @@ cette expérience répond à un besoin mesuré.
  • Le besoin du réseau local et les effets sur les autres régions sont documentés.
  • Le matériel Ikoka exact et la cible du micrologiciel du répéteur sont confirmés et récupérables par USB.
  • -
  • Une personne responsable de l’examen du matériel et de l’électricité approuve la pile, le chargeur, le panneau, le câblage, la protection, la télémétrie facultative et le plan thermique du boîtier.
  • -
  • Une personne responsable de l’examen RF approuve la radio, le filtre, la ligne de transmission RF, l’antenne, les connecteurs et le site.
  • +
  • Une personne qualifiée en électronique approuve la pile, le chargeur, le panneau, le câblage, la protection, la télémétrie facultative et le plan thermique du boîtier.
  • +
  • Une personne qualifiée en RF approuve la radio, le filtre, la ligne de transmission RF, l’antenne, les connecteurs et le site.
  • Un examen de la structure et du site couvre la masse finale, la surface du panneau, le montage, le vent, la glace, la neige, le passage des câbles et l’accès.
  • La construction restera sous surveillance sur l’établi jusqu’à l’approbation de son dossier d’essai.
@@ -152,7 +152,7 @@ maintien de la pile et la qualité d’impression avant l’utilisation.

Étape 4 — Examiner la modification du démarrage du gestionnaire d’alimentation

Ne pontez pas les pastilles du bouton de démarrage du gestionnaire d’alimentation. Cette modification n’a pas été reproduite ni examinée.

-

Avant d’envisager tout changement, exigez qu’une personne responsable de l’examen du matériel approuve le schéma exact, les modes de défaillance, les répercussions sur la garantie, la méthode de soudage, l’essai de démarrage et le retour en arrière.

+

Avant d’envisager tout changement, exigez qu’une personne qualifiée en électronique approuve le schéma exact, les modes de défaillance, les répercussions sur la garantie, la méthode de soudage, l’essai de démarrage et le retour en arrière.

Pont de fil non vérifié sur les pastilles du bouton de démarrage du gestionnaire solaire
Modification non vérifiée. Ne la reproduisez pas à partir de cette photographie.
@@ -212,7 +212,7 @@ devinés.
  • Consignez les pièces exactes, les révisions, les documents sources, le schéma, les photographies, la polarité et les valeurs mesurées.
  • Utilisez des méthodes supervisées de première mise sous tension et de limitation du courant adaptées au matériel examiné.
  • Confirmez que l’appareil démarre après l’application de chaque source d’alimentation prévue et après une coupure d’alimentation contrôlée.
  • -
  • Confirmez que l’identité, le micrologiciel et les réglages de radio, de région, de parcours, d’annonce et d’accès du répéteur résistent au redémarrage.
  • +
  • Confirmez que l’identité, le micrologiciel et les réglages de radio, de région, de parcours, d’annonce et d’accès du répéteur sont conservés après le redémarrage.
  • Confirmez qu’un compagnon à proximité reçoit une annonce et que l’essai d’acheminement local prévu réussit.
  • Mesurez le comportement RF et électrique avec la méthode approuvée par la personne responsable de l’examen; ne déduisez pas la puissance de sortie à partir d’un seul réglage du micrologiciel.
  • Effectuez sous surveillance un essai de recharge et de charge qui couvre les conditions examinées sans dépasser les limites des composants.
  • @@ -221,7 +221,7 @@ devinés. Ne déployez pas l’appareil avant qu’une personne responsable de l’examen du -matériel et de l’électricité, une personne responsable de l’examen RF et le +matériel et de l’électricité, une personne qualifiée en RF et le propriétaire du site aient approuvé le dossier d’essai complet. ## Récupération et retour en arrière diff --git a/docs/hardware/repeater-solar-300mw-diy-build.fr.md b/docs/hardware/repeater-solar-300mw-diy-build.fr.md index eb61317f..c445ed2d 100644 --- a/docs/hardware/repeater-solar-300mw-diy-build.fr.md +++ b/docs/hardware/repeater-solar-300mw-diy-build.fr.md @@ -201,7 +201,7 @@ chargeur, l’appareil de protection, la carte radio et le panneau exacts. Ne montez pas l’appareil avant qu’il ait réussi toutes les vérifications et -qu’une personne responsable de l’examen du matériel ait approuvé le dossier du +qu’une personne qualifiée en électronique ait approuvé le dossier du site. ## Récupération et retour en arrière diff --git a/docs/javascripts/community-submission.js b/docs/javascripts/community-submission.js index 065d25d3..7499d0bf 100644 --- a/docs/javascripts/community-submission.js +++ b/docs/javascripts/community-submission.js @@ -54,12 +54,12 @@ function boundedText(value, maximum, label, required = false, multiline = false) } export function buildCommunityIdea(data) { - const category = boundedText(data.category, 80, "Contribution type", true); - const experience = boundedText(data.experience, 80, "MeshCore experience", true); + const category = boundedText(data.category || "Other community feedback", 80, "Contribution type"); + const experience = boundedText(data.experience, 80, "MeshCore experience"); if (!COMMUNITY_CATEGORIES.includes(category)) { throw new Error("Choose a valid contribution type."); } - if (!MESHCORE_EXPERIENCE_LEVELS.includes(experience)) { + if (experience && !MESHCORE_EXPERIENCE_LEVELS.includes(experience)) { throw new Error("Choose a valid MeshCore experience level."); } if (data.publicAcknowledged !== true) { @@ -72,7 +72,7 @@ export function buildCommunityIdea(data) { experience, summary: boundedText(data.summary, 100, "Short title", true), need: boundedText(data.need, 2000, "What is difficult now", true, true), - idea: boundedText(data.idea, 2000, "What would help", true, true), + idea: boundedText(data.idea, 2000, "What would help", false, true), publicAcknowledged: true }; const optional = { @@ -83,9 +83,19 @@ export function buildCommunityIdea(data) { Object.entries(optional).forEach(([key, value]) => { if (value) proposal[key] = value; }); + if (data.sourcePage) proposal.sourcePage = normalizeSourcePage(data.sourcePage); return proposal; } +export function normalizeSourcePage(value) { + const url = new URL(value); + if (url.origin !== "https://meshcore.ca" || url.username || url.password || url.search || + !/^\/[a-zA-Z0-9/_.-]*$/.test(url.pathname) || !/^(?:#[a-zA-Z0-9_-]+)?$/.test(url.hash) || url.href.length > 400) { + throw new Error("Choose a valid source page."); + } + return url.href; +} + function section(heading, value, fallback = "Not provided") { return `## ${heading}\n\n${value || fallback}`; } @@ -99,6 +109,7 @@ export function buildSubmissionText(proposal) { section("Problem", proposal.need), section("Suggested change", proposal.idea), section("Additional context", proposal.context), + ...(proposal.sourcePage ? [section("Source page", proposal.sourcePage)] : []), section( "Public contact", proposal.followUp, @@ -135,6 +146,7 @@ export function buildFrenchSubmissionText(proposal) { frenchSection("Problème", proposal.need), frenchSection("Changement proposé", proposal.idea), frenchSection("Autres précisions", proposal.context), + ...(proposal.sourcePage ? [frenchSection("Page concernée", proposal.sourcePage)] : []), frenchSection( "Contact public", proposal.followUp, @@ -148,9 +160,7 @@ export function buildManualGithubLink( proposal, sourcePage = COMMUNITY_SOURCE_PAGE, ) { - if (![COMMUNITY_SOURCE_PAGE, COMMUNITY_FRENCH_SOURCE_PAGE].includes(sourcePage)) { - throw new Error("Choose a valid source page."); - } + sourcePage = normalizeSourcePage(proposal.sourcePage || sourcePage); const params = new URLSearchParams({ template: "community_idea.yml", title: `[Community idea] ${proposal.summary}`, diff --git a/docs/javascripts/submission-form.js b/docs/javascripts/submission-form.js index a4f151b4..80f81dc6 100644 --- a/docs/javascripts/submission-form.js +++ b/docs/javascripts/submission-form.js @@ -312,6 +312,7 @@ idea: value("submission-idea"), context: value("submission-context"), followUp: value("submission-follow-up"), + sourcePage: form.elements.source_page.value, publicAcknowledged: document.getElementById("submission-public").checked }; } @@ -422,6 +423,9 @@ config = await loaded.transport.fetchSubmissionConfig({ endpoint: loaded.community.COMMUNITY_SUBMISSION_ENDPOINT }); + // The gateway must support the shorter form before it can accept its payload. + // Older deployments still offer the copy/GitHub path, without a failed POST. + if (!config.communityIdeaOptionalDetails) throw new Error("Submission service update required"); turnstile = await loaded.transport.loadTurnstile(); if (widgetId === null) { setAntiSpamStatus("Complete the check."); @@ -577,6 +581,19 @@ elements.antiSpamRetry.addEventListener("click", initialiseSubmission); initialiseCharacterCounters(); restoreDraft(); + loadModules().then(function (loaded) { + const params = new URLSearchParams(window.location.search); + try { + if (params.has("source_page")) form.elements.source_page.value = loaded.community.normalizeSourcePage(params.get("source_page")); + } catch (_) { /* Ignore malformed context; the form remains usable. */ } + const community = params.get("community"); + if (community && /^[a-z0-9-]{1,80}$/.test(community) && !document.getElementById("submission-context").value) { + document.getElementById("submission-context").value = "Community listing: " + community; + document.getElementById("submission-category").value = "Regional community information"; + } else if (params.has("source_page") && !document.getElementById("submission-category").value) { + document.getElementById("submission-category").value = "Documentation correction"; + } + }).catch(function () { /* GitHub fallback remains available. */ }); updateDraftControls(); updateActions(); })(); diff --git a/docs/meshcore/flash-companion.fr.md b/docs/meshcore/flash-companion.fr.md index c0dd026b..fdec5835 100644 --- a/docs/meshcore/flash-companion.fr.md +++ b/docs/meshcore/flash-companion.fr.md @@ -69,9 +69,9 @@ officiel. Il faut un navigateur compatible avec 2. Sélectionnez le modèle exact de la carte. 3. Sélectionnez **Companion Radio (Bluetooth)**. 4. Sélectionnez la version du micrologiciel destinée à cette carte. -5. Cliquez sur **Enter DFU Mode**. Le programme devrait indiquer qu’il a trouvé un appareil dans le mode attendu. +5. Suivez les étapes de connexion propres à votre carte. Utilisez **Enter DFU Mode** seulement si cette cible le demande. Le programme devrait indiquer qu’il a trouvé un appareil dans le mode attendu. 6. Vérifiez de nouveau le matériel et le micrologiciel sélectionnés. -7. Cliquez sur **Erase Flash**, puis attendez le message confirmant la réussite de l’effacement. +7. Pour une première installation ou une réinitialisation, sauvegardez d’abord. Cliquez sur **Erase Flash** seulement si les instructions de la carte le demandent. Pour une mise à jour, laissez l’effacement désactivé sauf indication contraire dans les notes de version. 8. Cliquez sur **Flash** et attendez le message de fin avant de débrancher l’appareil. ## Récupération en cas d’échec @@ -105,7 +105,7 @@ sauf si votre méthode d’essai locale exige un autre réglage. 1. Confirmez que l’application se reconnecte et affiche le nom de l’appareil et les réglages radio attendus. 2. Envoyez un message d’essai dans le canal **Public**. -3. Une réponse comme **Heard X Repeats** indique qu’au moins un répéteur a signalé l’avoir reçu. Un simple résultat **Sent** ne prouve pas que les réglages sont incorrects; rendez-vous dans une zone où la couverture est connue ou demandez à la communauté locale de vous aider à faire l’essai. +3. Une réponse comme **Heard X Repeats** indique qu’votre compagnon a entendu une retransmission du message. Un simple résultat **Sent** ne prouve pas que les réglages sont incorrects; rendez-vous dans une zone où la couverture est connue ou demandez à la communauté locale de vous aider à faire l’essai. La configuration n’est terminée que si les réglages enregistrés résistent à un redémarrage et qu’un essai local réussit. diff --git a/docs/meshcore/flash-companion.md b/docs/meshcore/flash-companion.md index 9c2cebb0..636f5993 100644 --- a/docs/meshcore/flash-companion.md +++ b/docs/meshcore/flash-companion.md @@ -62,9 +62,9 @@ Use the official [MeshCore Web Flasher](https://meshcore.io/flasher). The flashe 2. Select the exact hardware model. 3. Select **Companion Radio (Bluetooth)**. 4. Select the firmware version intended for that board. -5. Click **Enter DFU Mode**. The flasher should report that it found a device in the expected mode. +5. Follow the flasher’s connection steps for your board. Use **Enter DFU Mode** only when that target asks for it. The flasher should report that it found a device in the expected mode. 6. Recheck the hardware and firmware selections. -7. Click **Erase Flash**, then wait for a successful erase message. +7. For a first installation or deliberate reset, back up first. Click **Erase Flash** only if the board’s instructions require it. For an update, leave erase off unless its release notes require a reset. 8. Click **Flash** and wait for the completion message before disconnecting the device. ## Recovery if flashing fails @@ -89,7 +89,7 @@ The optional **Message Settings → Auto Reset Path** preference affects how the 1. Confirm the app reconnects and shows the expected device name and radio settings. 2. Send a test message in the **Public** channel. -3. A response such as **Heard X Repeats** indicates that at least one repeater reported hearing it. A plain **Sent** result is not proof that the settings are wrong; move to a known coverage area or ask the local community to help test. +3. A response such as **Heard X Repeats** indicates that your companion heard a rebroadcast of the message. A plain **Sent** result is not proof that the settings are wrong; move to a known coverage area or ask the local community to help test. Do not consider the setup complete until the saved settings survive a reboot and a local test succeeds. diff --git a/docs/meshcore/flash-repeater.fr.md b/docs/meshcore/flash-repeater.fr.md index d752fb26..acb3b1cd 100644 --- a/docs/meshcore/flash-repeater.fr.md +++ b/docs/meshcore/flash-repeater.fr.md @@ -21,7 +21,7 @@ page_styles: - assets/styles/devices-builds.css?v=20260728-1 - assets/styles/repeater-hash-check.css?v=20260820-1 page_scripts: - - assets/javascripts/repeater-hash-check.js?v=20260820-1 + - assets/javascripts/repeater-hash-check.js?v=20260904-1 --- # Programmer, configurer et tester un répéteur sur l’établi @@ -89,7 +89,7 @@ guide ou d’une capture d’écran. 3. Activez son mode de chargeur d’amorçage UF2. Sur une RAK4631, il faut normalement appuyer deux fois sur le bouton près du port USB; pour les autres cartes, utilisez la méthode de réinitialisation documentée. 4. Confirmez qu’un lecteur USB apparaît et examinez `INFO.TXT` pour vérifier l’identité de la carte. 5. Copiez le fichier UF2 correspondant sur ce lecteur. Le lecteur peut se déconnecter pendant le redémarrage de la carte. -6. Revenez au mode de chargeur d’amorçage et confirmez que `INFO.TXT` indique la version `0.9.2` avant de continuer. +6. Revenez au mode de chargeur d’amorçage et confirmez que `INFO.TXT` indique la carte et la version du chargeur d’amorçage correspondant à la version choisie avant de continuer. Si l’identité de la carte ou la version attendue ne correspond pas, arrêtez et récupérez l’appareil par USB avant de programmer MeshCore. @@ -103,9 +103,9 @@ officiel dans un navigateur compatible avec 1. Branchez le répéteur par USB. 2. Sélectionnez le modèle exact du matériel. 3. Sélectionnez **Repeater** comme rôle et choisissez la version destinée à cette carte. -4. Cliquez sur **Enter DFU Mode** et attendez que le programme trouve la carte. +4. Suivez les étapes de connexion propres à votre carte. Utilisez **Enter DFU Mode** seulement si cette cible le demande. 5. Vérifiez de nouveau la carte et le rôle sélectionnés. -6. Cliquez sur **Erase Flash** et attendez le message confirmant la réussite de l’effacement. +6. Pour une première installation ou une réinitialisation, sauvegardez d’abord. Cliquez sur **Erase Flash** seulement si les instructions de la carte le demandent. Pour une mise à jour, laissez l’effacement désactivé sauf indication contraire dans les notes de version. 7. Cliquez sur **Flash** et attendez la fin de l’opération avant de débrancher l’appareil. Si la programmation échoue après l’effacement, n’effacez pas l’appareil à diff --git a/docs/meshcore/flash-repeater.md b/docs/meshcore/flash-repeater.md index fbac57fe..245c270a 100644 --- a/docs/meshcore/flash-repeater.md +++ b/docs/meshcore/flash-repeater.md @@ -21,7 +21,7 @@ page_styles: - assets/styles/devices-builds.css?v=20260728-1 - assets/styles/repeater-hash-check.css?v=20260820-1 page_scripts: - - assets/javascripts/repeater-hash-check.js?v=20260820-1 + - assets/javascripts/repeater-hash-check.js?v=20260904-1 --- # Flash, configure, and bench-test a repeater @@ -54,7 +54,7 @@ Never post the private key or passwords in an issue, screenshot, log, or chat. I ## What flashing changes -An erase replaces firmware and can delete identity and settings. The setup also writes radio, advert, path-hash, access, location, and region values that affect the shared network. +Flashing replaces firmware. Erasing can also delete identity and settings. The setup also writes radio, advert, path-hash, access, location, and region values that affect the shared network. ## Recovery plan @@ -74,7 +74,7 @@ hardware. Do not use a filename copied from an older guide or screenshot. 3. Enter its UF2 bootloader mode. On a RAK4631 this is normally done by double-pressing the button beside USB; other boards use their documented reset method. 4. Confirm a USB drive appears and inspect `INFO.TXT` so the board identity is what you expect. 5. Copy the matching UF2 file to that drive. The drive may disconnect as the board reboots. -6. Re-enter bootloader mode and confirm `INFO.TXT` reports bootloader version `0.9.2` before continuing. +6. Re-enter bootloader mode and confirm `INFO.TXT` reports the board and bootloader version from the release you selected before continuing. If the board identity or expected version does not match, stop and recover over USB before flashing MeshCore. @@ -85,9 +85,9 @@ Use the official [MeshCore Web Flasher](https://meshcore.io/flasher) in a browse 1. Connect the repeater by USB. 2. Select the exact hardware model. 3. Select **Repeater** as the role and choose the intended version for that board. -4. Click **Enter DFU Mode** and wait for the flasher to find the board. +4. Follow the flasher’s connection steps for your board. Use **Enter DFU Mode** only when that target asks for it. 5. Recheck the selected board and role. -6. Click **Erase Flash** and wait for a successful erase message. +6. For a first installation or deliberate reset, back up first. Click **Erase Flash** only if the board’s instructions require it. For an update, leave erase off unless its release notes require a reset. 7. Click **Flash** and wait for completion before disconnecting. If flashing fails after erase, do not repeatedly erase. Refresh the page, return the board to DFU mode, verify the target again, and retry **Flash**. Use the board's USB recovery process if it no longer appears. diff --git a/docs/meshcore/flash-room-server.fr.md b/docs/meshcore/flash-room-server.fr.md index d6bf66a0..5cd88565 100644 --- a/docs/meshcore/flash-room-server.fr.md +++ b/docs/meshcore/flash-room-server.fr.md @@ -67,9 +67,9 @@ n’utilisez pas les fichiers d’une autre carte. 1. Ouvrez le [programme de mise à jour Web MeshCore](https://meshcore.io/flasher) officiel. 2. Sélectionnez le modèle exact de l’appareil. 3. Sélectionnez **Room Server** et la version destinée à cette carte. -4. Cliquez sur **Enter DFU Mode** et attendez que l’appareil attendu apparaisse. +4. Suivez les étapes de connexion propres à votre carte. Utilisez **Enter DFU Mode** seulement si cette cible le demande. 5. Vérifiez de nouveau le matériel et le rôle sélectionnés. -6. Cliquez sur **Erase Flash** et attendez le message confirmant la réussite de l’effacement. +6. Pour une première installation ou une réinitialisation, sauvegardez d’abord. Cliquez sur **Erase Flash** seulement si les instructions de la carte le demandent. Pour une mise à jour, laissez l’effacement désactivé sauf indication contraire dans les notes de version. 7. Cliquez sur **Flash** et attendez la fin de l’opération avant de débrancher l’appareil. Si la programmation échoue après l’effacement, laissez l’appareil branché, diff --git a/docs/meshcore/flash-room-server.md b/docs/meshcore/flash-room-server.md index e186e13b..d4babb94 100644 --- a/docs/meshcore/flash-room-server.md +++ b/docs/meshcore/flash-room-server.md @@ -54,9 +54,9 @@ Keep the exact board's USB recovery method, backed-up identity/settings, a known 1. Open the official [MeshCore Web Flasher](https://meshcore.io/flasher). 2. Select the exact device model. 3. Select **Room Server** and the intended version for that board. -4. Click **Enter DFU Mode** and wait for the expected device to appear. +4. Follow the flasher’s connection steps for your board. Use **Enter DFU Mode** only when that target asks for it. 5. Recheck the hardware and role selections. -6. Click **Erase Flash** and wait for a successful erase message. +6. For a first installation or deliberate reset, back up first. Click **Erase Flash** only if the board’s instructions require it. For an update, leave erase off unless its release notes require a reset. 7. Click **Flash** and wait for completion before disconnecting. If flashing fails after erase, leave the device connected, refresh the flasher, re-enter DFU mode, confirm the board and role, and retry **Flash**. Use the board's documented USB recovery process if it is no longer detected. diff --git a/docs/meshcore/general-faq.fr.md b/docs/meshcore/general-faq.fr.md index 645ad23a..22878599 100644 --- a/docs/meshcore/general-faq.fr.md +++ b/docs/meshcore/general-faq.fr.md @@ -34,8 +34,8 @@ source actuelle. ### Quels réglages radio dois-je utiliser au Canada? -Pour un répéteur, utilisez le [configurateur de répéteur](../config/index.md). Il -fournit les réglages actuels pour l’emplacement choisi. +Utilisez le profil radio de votre communauté. Le [configurateur de répéteur](../config/index.md) +trouve les chemins régionaux, mais conserve les réglages radio tant que vous ne choisissez pas de profil. Quel que soit le rôle de l’appareil, consultez d’abord le [répertoire des communautés](../provinces/index.md). Lorsqu’une communauté @@ -43,10 +43,10 @@ publie des réglages locaux différents, suivez-les. ### Qu’est-ce que le mode de hachage des parcours? -Il détermine la taille des identifiants utilisés dans les parcours d’annonce. -La norme canadienne des régions et le configurateur fournissent le réglage -actuel d’un répéteur. Utilisez leur résultat plutôt que de copier une ancienne -commande provenant d’une discussion ou d’une capture d’écran. +Il détermine la taille des identifiants dans les parcours d’annonce : 1, 2 ou 3 octets. +MeshCore Canada recommande 3 octets pour réduire les collisions. Les versions 1.14 +et suivantes peuvent relayer des parcours de tailles différentes; les nœuds voisins +n’ont pas tous besoin d’utiliser la même taille d’identifiant. [Lire la norme des régions](../config/standard.md). diff --git a/docs/meshcore/general-faq.md b/docs/meshcore/general-faq.md index 1c9c919e..9f1e1e0c 100644 --- a/docs/meshcore/general-faq.md +++ b/docs/meshcore/general-faq.md @@ -34,18 +34,17 @@ of repeating values that may change. ### Which radio settings should I use in Canada? -Use the [repeater configurator](../config/index.md) for a repeater. It returns -the current settings for the location you select. +Use your community’s radio profile. The [repeater configurator](../config/index.md) +finds region paths, but keeps your radio settings unless you choose a profile. For any role, first check the [community directory](../provinces/index.md). Follow a published local override when one exists. ### What is path hash mode? -It controls the size of the identifiers used in advert paths. The Canadian -region standard and configurator provide the current setting for a repeater. -Use their output instead of copying an old command from a discussion or -screenshot. +It sets the size of the identifiers in this node’s advert paths: 1, 2, or 3 bytes. +MeshCore Canada recommends 3 bytes to reduce collisions. Firmware 1.14 and newer +can forward mixed sizes; neighbouring nodes do not all need the same ID size. [Read the region standard](../config/standard.md). diff --git a/docs/meshcore/general-howto.fr.md b/docs/meshcore/general-howto.fr.md index e2aad6c1..7b625e77 100644 --- a/docs/meshcore/general-howto.fr.md +++ b/docs/meshcore/general-howto.fr.md @@ -20,6 +20,7 @@ destructive: false Choisissez une tâche. Les captures montrent une version de l’application mobile MeshCore; les libellés et leur emplacement peuvent différer. +- [Envoyer votre premier message](#send-your-first-message) - [Partager votre lien de contact](#share-your-contact-link) - [Importer un lien de contact](#import-a-contact-link) - [Tracer un parcours](#trace-a-path) @@ -36,6 +37,16 @@ MeshCore; les libellés et leur emplacement peuvent différer.
    +## Envoyer votre premier message { #send-your-first-message } + +1. Connectez l’application à votre compagnon et confirmez les réglages radio locaux. +2. Ouvrez le canal de votre communauté ou choisissez un contact importé. +3. Envoyez une courte salutation et demandez une réponse. + +Une réponse confirme la communication dans les deux sens. L’indicateur d’envoi +ne suffit pas à confirmer la réception. Sans réponse, vérifiez avec une personne +à proximité avant d’envoyer d’autres essais. + ## Partager votre lien de contact { #share-your-contact-link } Utilisez cette procédure lorsqu’une autre personne veut vous ajouter comme diff --git a/docs/meshcore/general-howto.md b/docs/meshcore/general-howto.md index d3d5aa30..23a7d3b4 100644 --- a/docs/meshcore/general-howto.md +++ b/docs/meshcore/general-howto.md @@ -20,6 +20,7 @@ destructive: false Choose a task. The screenshots show one version of the MeshCore mobile app, so labels and positions may differ. +- [Send your first message](#send-your-first-message) - [Share your contact link](#share-your-contact-link) - [Import a contact link](#import-a-contact-link) - [Trace a path](#trace-a-path) @@ -36,6 +37,15 @@ labels and positions may differ.
    +## Send your first message + +1. Connect the app to your companion and confirm the local radio settings. +2. Open the channel used by your community, or choose an imported contact. +3. Send a short greeting and ask for a reply. + +A reply confirms two-way communication. A sent indicator alone does not confirm +delivery. If nobody replies, check with a nearby operator before sending more tests. + ## Share your contact link Use this when another person needs to add you as a contact. diff --git a/docs/meshcore/generate-repeater-id.fr.md b/docs/meshcore/generate-repeater-id.fr.md index 0071e789..8bd51bba 100644 --- a/docs/meshcore/generate-repeater-id.fr.md +++ b/docs/meshcore/generate-repeater-id.fr.md @@ -22,7 +22,7 @@ page_styles: - assets/styles/devices-builds.css?v=20260728-1 - assets/styles/repeater-hash-check.css?v=20260820-1 page_scripts: - - assets/javascripts/repeater-hash-check.js?v=20260820-1 + - assets/javascripts/repeater-hash-check.js?v=20260904-1 --- # Changer l’identifiant d’un répéteur en mode 1 octet diff --git a/docs/meshcore/generate-repeater-id.md b/docs/meshcore/generate-repeater-id.md index 217bb453..08a47dd4 100644 --- a/docs/meshcore/generate-repeater-id.md +++ b/docs/meshcore/generate-repeater-id.md @@ -22,7 +22,7 @@ page_styles: - assets/styles/devices-builds.css?v=20260728-1 - assets/styles/repeater-hash-check.css?v=20260820-1 page_scripts: - - assets/javascripts/repeater-hash-check.js?v=20260820-1 + - assets/javascripts/repeater-hash-check.js?v=20260904-1 --- # Change a repeater ID in 1-byte mode diff --git a/docs/privacy.fr.md b/docs/privacy.fr.md index 944d0b20..3a552fbd 100644 --- a/docs/privacy.fr.md +++ b/docs/privacy.fr.md @@ -43,6 +43,13 @@ limitation du débit. Aucune période de conservation n’a encore été publié ## Données enregistrées sur votre appareil +Changer de langue ou passer entre la carte et le configurateur conserve votre +sélection dans l’URL de destination. Elle peut contenir les coordonnées saisies, +un lieu, des chemins régionaux et un profil radio. Ces URL peuvent apparaître dans +l’historique du navigateur et les journaux du serveur. Ne partagez pas un lien qui +contient un emplacement privé. Les résumés téléchargés omettent les coordonnées +exactes et les identifiants de connexion. + Les listes de configuration, les brouillons d’idées et le dernier code de région Beacon choisi dans l’outil de vérification sont enregistrés uniquement dans votre navigateur lorsque vous utilisez ces fonctions. Les clés publiques diff --git a/docs/privacy.md b/docs/privacy.md index b18db609..f9647749 100644 --- a/docs/privacy.md +++ b/docs/privacy.md @@ -42,6 +42,12 @@ period has not yet been published. ## Saved on your device +Switching language or moving between the region map and configurator carries your +selection in the destination URL. This can include entered coordinates, a location +label, region paths, and radio-profile choices. These URLs may appear in browser +history and server access logs. Do not share a link containing a private location. +Downloaded setup summaries omit exact coordinates and credentials. + Setup checklists, idea drafts, and the last Beacon region code selected in the repeater-ID checker are saved in your browser only when you use those features. Repeater public keys, passwords, private keys, anti-spam tokens, and location diff --git a/docs/provinces/alberta.fr.md b/docs/provinces/alberta.fr.md index 0f445769..05cf45d9 100644 --- a/docs/provinces/alberta.fr.md +++ b/docs/provinces/alberta.fr.md @@ -39,7 +39,7 @@ sauf si une fiche indique des réglages locaux différents.
    Réglages
    Réglages par défaut du Canada
    -
    Dernière vérification
    +
    Fiche révisée
    2026-08-29

    Coordonnées

    @@ -56,7 +56,7 @@ sauf si une fiche indique des réglages locaux différents.

    Vérification : Effectuée le 2026-08-29

    -

    Mettre cette fiche à jour

    +

    Mettre cette fiche à jour

    @@ -68,7 +68,7 @@ sauf si une fiche indique des réglages locaux différents.
    Réglages
    Réglages par défaut du Canada
    -
    Dernière vérification
    +
    Fiche révisée
    2026-08-29

    Coordonnées

    @@ -82,7 +82,7 @@ sauf si une fiche indique des réglages locaux différents.

    Vérification : Effectuée le 2026-08-29

    -

    Mettre cette fiche à jour

    +

    Mettre cette fiche à jour

    @@ -93,7 +93,7 @@ sauf si une fiche indique des réglages locaux différents.
    Réglages
    Réglages par défaut du Canada
    -
    Dernière vérification
    +
    Fiche révisée
    2026-08-29

    Coordonnées

    @@ -104,7 +104,7 @@ sauf si une fiche indique des réglages locaux différents.

    Vérification : Certains liens restent à vérifier

    -

    Mettre cette fiche à jour

    +

    Mettre cette fiche à jour

    @@ -116,7 +116,7 @@ sauf si une fiche indique des réglages locaux différents.
    Réglages
    Réglages par défaut du Canada
    -
    Dernière vérification
    +
    Fiche révisée
    2026-08-29

    Coordonnées

    @@ -130,7 +130,7 @@ sauf si une fiche indique des réglages locaux différents.

    Vérification : Effectuée le 2026-08-29

    -

    Mettre cette fiche à jour

    +

    Mettre cette fiche à jour

    @@ -142,7 +142,7 @@ sauf si une fiche indique des réglages locaux différents.
    Réglages
    Réglages par défaut du Canada
    -
    Dernière vérification
    +
    Fiche révisée
    2026-08-29

    Coordonnées

    @@ -155,7 +155,7 @@ sauf si une fiche indique des réglages locaux différents.

    Vérification : Effectuée le 2026-08-29

    -

    Mettre cette fiche à jour

    +

    Mettre cette fiche à jour

    @@ -167,7 +167,7 @@ sauf si une fiche indique des réglages locaux différents.
    Réglages
    Réglages par défaut du Canada
    -
    Dernière vérification
    +
    Fiche révisée
    2026-08-29

    Coordonnées

    @@ -180,7 +180,7 @@ sauf si une fiche indique des réglages locaux différents.

    Vérification : Effectuée le 2026-08-29

    -

    Mettre cette fiche à jour

    +

    Mettre cette fiche à jour

    @@ -192,7 +192,7 @@ sauf si une fiche indique des réglages locaux différents.
    Réglages
    Réglages par défaut du Canada
    -
    Dernière vérification
    +
    Fiche révisée
    2026-08-29

    Coordonnées

    @@ -209,7 +209,7 @@ sauf si une fiche indique des réglages locaux différents.

    Vérification : Certains liens restent à vérifier

    -

    Mettre cette fiche à jour

    +

    Mettre cette fiche à jour

    @@ -220,7 +220,7 @@ sauf si une fiche indique des réglages locaux différents.
    Réglages
    Réglages par défaut du Canada
    -
    Dernière vérification
    +
    Fiche révisée
    2026-08-29

    Coordonnées

    @@ -231,7 +231,7 @@ sauf si une fiche indique des réglages locaux différents.

    Vérification : Certains liens restent à vérifier

    -

    Mettre cette fiche à jour

    +

    Mettre cette fiche à jour

    @@ -242,7 +242,7 @@ sauf si une fiche indique des réglages locaux différents.
    Réglages
    Réglages par défaut du Canada
    -
    Dernière vérification
    +
    Fiche révisée
    2026-08-29

    Coordonnées

    @@ -253,7 +253,7 @@ sauf si une fiche indique des réglages locaux différents.

    Vérification : Effectuée le 2026-08-29

    -

    Mettre cette fiche à jour

    +

    Mettre cette fiche à jour

    diff --git a/docs/provinces/alberta.md b/docs/provinces/alberta.md index 48a696b4..fd540cf6 100644 --- a/docs/provinces/alberta.md +++ b/docs/provinces/alberta.md @@ -39,7 +39,7 @@ card lists different local settings.
    Settings
    Uses the Canada defaults
    -
    Last verified
    +
    Listing reviewed
    2026-08-29

    Contacts

    @@ -54,9 +54,9 @@ card lists different local settings.
  • Website: Monitoring tools (external)
  • -Contact check: Verified on 2026-08-29 +Contact check: Links checked on 2026-08-29

    -

    Update this listing

    +

    Update this listing

    @@ -68,7 +68,7 @@ card lists different local settings.
    Settings
    Uses the Canada defaults
    -
    Last verified
    +
    Listing reviewed
    2026-08-29

    Contacts

    @@ -80,9 +80,9 @@ card lists different local settings.
  • Website: WAeV live map (external)
  • -Contact check: Verified on 2026-08-29 +Contact check: Links checked on 2026-08-29

    -

    Update this listing

    +

    Update this listing

    @@ -93,7 +93,7 @@ card lists different local settings.
    Settings
    Uses the Canada defaults
    -
    Last verified
    +
    Listing reviewed
    2026-08-29

    Contacts

    @@ -104,7 +104,7 @@ card lists different local settings.

    Contact check: Some links still need review

    -

    Update this listing

    +

    Update this listing

    @@ -116,7 +116,7 @@ card lists different local settings.
    Settings
    Uses the Canada defaults
    -
    Last verified
    +
    Listing reviewed
    2026-08-29

    Contacts

    @@ -128,9 +128,9 @@ card lists different local settings.
  • Website: Recommended Calgary RX channels (external)
  • -Contact check: Verified on 2026-08-29 +Contact check: Links checked on 2026-08-29

    -

    Update this listing

    +

    Update this listing

    @@ -142,7 +142,7 @@ card lists different local settings.
    Settings
    Uses the Canada defaults
    -
    Last verified
    +
    Listing reviewed
    2026-08-29

    Contacts

    @@ -153,9 +153,9 @@ card lists different local settings.
  • MeshMapper: Edmonton network map (external)
  • -Contact check: Verified on 2026-08-29 +Contact check: Links checked on 2026-08-29

    -

    Update this listing

    +

    Update this listing

    @@ -167,7 +167,7 @@ card lists different local settings.
    Settings
    Uses the Canada defaults
    -
    Last verified
    +
    Listing reviewed
    2026-08-29

    Contacts

    @@ -178,9 +178,9 @@ card lists different local settings.
  • MeshMapper: Edmonton network map (external)
  • -Contact check: Verified on 2026-08-29 +Contact check: Links checked on 2026-08-29

    -

    Update this listing

    +

    Update this listing

    @@ -192,7 +192,7 @@ card lists different local settings.
    Settings
    Uses the Canada defaults
    -
    Last verified
    +
    Listing reviewed
    2026-08-29

    Contacts

    @@ -209,7 +209,7 @@ card lists different local settings.

    Contact check: Some links still need review

    -

    Update this listing

    +

    Update this listing

    @@ -220,7 +220,7 @@ card lists different local settings.
    Settings
    Uses the Canada defaults
    -
    Last verified
    +
    Listing reviewed
    2026-08-29

    Contacts

    @@ -231,7 +231,7 @@ card lists different local settings.

    Contact check: Some links still need review

    -

    Update this listing

    +

    Update this listing

    @@ -242,7 +242,7 @@ card lists different local settings.
    Settings
    Uses the Canada defaults
    -
    Last verified
    +
    Listing reviewed
    2026-08-29

    Contacts

    @@ -251,9 +251,9 @@ card lists different local settings.
  • Website: MeshMonitoring (external)
  • -Contact check: Verified on 2026-08-29 +Contact check: Links checked on 2026-08-29

    -

    Update this listing

    +

    Update this listing

    @@ -261,7 +261,7 @@ card lists different local settings.

    Telegram: Alberta topic in MeshCore Canada (external)

    -

    Contact check: Verified on 2026-08-29

    +

    Contact check: Links checked on 2026-08-29

    ## Add or update a listing diff --git a/docs/provinces/british-columbia.fr.md b/docs/provinces/british-columbia.fr.md index fab08e78..744294ce 100644 --- a/docs/provinces/british-columbia.fr.md +++ b/docs/provinces/british-columbia.fr.md @@ -43,7 +43,7 @@ sauf si une fiche indique des réglages locaux différents.
    Réglages
    Préréglage radio : Custom; Valeurs radio brutes : 910.425 MHz / 62.5 kHz / SF7 / CR5
    -
    Dernière vérification
    +
    Fiche révisée
    2026-08-29
    @@ -57,7 +57,7 @@ sauf si une fiche indique des réglages locaux différents.

    Vérification : Effectuée le 2026-08-29

    -

    Mettre cette fiche à jour

    +

    Mettre cette fiche à jour

    @@ -68,7 +68,7 @@ sauf si une fiche indique des réglages locaux différents.
    Réglages
    Réglages par défaut du Canada
    -
    Dernière vérification
    +
    Fiche révisée
    2026-08-29

    Coordonnées

    @@ -78,7 +78,7 @@ sauf si une fiche indique des réglages locaux différents.

    Vérification : Effectuée le 2026-08-29

    -

    Mettre cette fiche à jour

    +

    Mettre cette fiche à jour

    diff --git a/docs/provinces/british-columbia.md b/docs/provinces/british-columbia.md index 1ef7f6d0..b5a68166 100644 --- a/docs/provinces/british-columbia.md +++ b/docs/provinces/british-columbia.md @@ -42,7 +42,7 @@ card lists different local settings.
    Settings
    Radio preset: Custom; Raw radio values: 910.425 MHz / 62.5 kHz / SF7 / CR5
    -
    Last verified
    +
    Listing reviewed
    2026-08-29
    @@ -54,9 +54,9 @@ card lists different local settings.
  • Website: Ridgeline network map and tools (external)
  • -Contact check: Verified on 2026-08-29 +Contact check: Links checked on 2026-08-29

    -

    Update this listing

    +

    Update this listing

    @@ -67,7 +67,7 @@ card lists different local settings.
    Settings
    Uses the Canada defaults
    -
    Last verified
    +
    Listing reviewed
    2026-08-29

    Contacts

    @@ -75,9 +75,9 @@ card lists different local settings.
  • Website: Salish Mesh website (external)
  • -Contact check: Verified on 2026-08-29 +Contact check: Links checked on 2026-08-29

    -

    Update this listing

    +

    Update this listing

    diff --git a/docs/provinces/index.fr.md b/docs/provinces/index.fr.md index 9092deb4..d2cd6660 100644 --- a/docs/provinces/index.fr.md +++ b/docs/provinces/index.fr.md @@ -78,7 +78,7 @@ complète fonctionne sans carte, autorisation de localisation ni compte GitHub.

    Lower Mainland et île de Vancouver

    Province ou territoire : Colombie-Britannique

    Réglages : Réglages locaux différents — Préréglage radio : Custom; Valeurs radio brutes : 910.425 MHz / 62.5 kHz / SF7 / CR5

    -

    Dernière vérification : 2026-08-29

    +

    Fiche révisée : 2026-08-29

    @@ -92,7 +92,7 @@ complète fonctionne sans carte, autorisation de localisation ni compte GitHub.

    Mer des Salish et environs

    Province ou territoire : Colombie-Britannique

    Réglages : Réglages par défaut du Canada

    -

    Dernière vérification : 2026-08-29

    +

    Fiche révisée : 2026-08-29

    @@ -107,7 +107,7 @@ complète fonctionne sans carte, autorisation de localisation ni compte GitHub.

    Développement du réseau maillé LoRa hors réseau de l’Alberta, exploité par la communauté

    Province ou territoire : Alberta

    Réglages : Réglages par défaut du Canada

    -

    Dernière vérification : 2026-08-29

    +

    Fiche révisée : 2026-08-29

    • Site Web : AlbertaMesh.ca (externe)
    • Discord : Discord d’Alberta MeshCore (externe)
    • @@ -129,7 +129,7 @@ complète fonctionne sans carte, autorisation de localisation ni compte GitHub.

      Utilise l’identifiant régional YYC partagé avec le réseau régional de Calgary

      Province ou territoire : Alberta

      Réglages : Réglages par défaut du Canada

      -

      Dernière vérification : 2026-08-29

      +

      Fiche révisée : 2026-08-29

      • Site Web : Réseau MeshCore d’Airdrie (externe)
      • Site Web : Guide de configuration d’Airdrie (externe)
      • @@ -147,7 +147,7 @@ complète fonctionne sans carte, autorisation de localisation ni compte GitHub.

        Calgary et environs

        Province ou territoire : Alberta

        Réglages : Réglages par défaut du Canada

        -

        Dernière vérification : 2026-08-29

        +

        Fiche révisée : 2026-08-29

        • Telegram : Sujet sur Calgary dans Mesh Alberta (externe)
        • Discord : Canal régional Canada — Calgary, Alberta et environs sur le Discord de MeshCore
        • @@ -163,7 +163,7 @@ complète fonctionne sans carte, autorisation de localisation ni compte GitHub.

          Première région lancée sur AlbertaMesh.ca

          Province ou territoire : Alberta

          Réglages : Réglages par défaut du Canada

          -

          Dernière vérification : 2026-08-29

          +

          Fiche révisée : 2026-08-29

          • Site Web : Guide de la communauté de Calgary (externe)
          • Discord : Discord d’Alberta MeshCore (externe)
          • @@ -182,7 +182,7 @@ complète fonctionne sans carte, autorisation de localisation ni compte GitHub.

            Communauté régionale d’Edmonton au sein d’Alberta MeshCore

            Province ou territoire : Alberta

            Réglages : Réglages par défaut du Canada

            -

            Dernière vérification : 2026-08-29

            +

            Fiche révisée : 2026-08-29

            • Site Web : Edmonton sur AlbertaMesh.ca (externe)
            • Site Web : Guide de configuration d’Edmonton (externe)
            • @@ -200,7 +200,7 @@ complète fonctionne sans carte, autorisation de localisation ni compte GitHub.

              Communauté du réseau maillé d’Edmonton axée sur MeshCore

              Province ou territoire : Alberta

              Réglages : Réglages par défaut du Canada

              -

              Dernière vérification : 2026-08-29

              +

              Fiche révisée : 2026-08-29

              • Site Web : Bien démarrer (externe)
              • Site Web : Réglages par défaut de MeshCore (externe)
              • @@ -218,7 +218,7 @@ complète fonctionne sans carte, autorisation de localisation ni compte GitHub.

                Relier Lethbridge, un nœud à la fois

                Province ou territoire : Alberta

                Réglages : Réglages par défaut du Canada

                -

                Dernière vérification : 2026-08-29

                +

                Fiche révisée : 2026-08-29

                • Site Web : Site Web de YQLMesh (externe)
                • Site Web : Page régionale de Lethbridge (externe)
                • @@ -239,7 +239,7 @@ complète fonctionne sans carte, autorisation de localisation ni compte GitHub.

                  Sud de l’Alberta, y compris Cardston, Magrath, Raymond et les environs

                  Province ou territoire : Alberta

                  Réglages : Réglages par défaut du Canada

                  -

                  Dernière vérification : 2026-08-29

                  +

                  Fiche révisée : 2026-08-29

                  • Discord : Canal régional Canada — Sud de l’Alberta sur le Discord de MeshCore
                  • Telegram : Sujet sur Cardston dans Mesh Alberta (externe)
                  • @@ -254,7 +254,7 @@ complète fonctionne sans carte, autorisation de localisation ni compte GitHub.

                    Calgary

                    Province ou territoire : Alberta

                    Réglages : Réglages par défaut du Canada

                    -

                    Dernière vérification : 2026-08-29

                    +

                    Fiche révisée : 2026-08-29

                    • Discord : Discord de YYC MeshCore (externe)
                    • Site Web : MeshMonitoring (externe)
                    • @@ -269,7 +269,7 @@ complète fonctionne sans carte, autorisation de localisation ni compte GitHub.

                      Centre de la Saskatchewan

                      Province ou territoire : Saskatchewan

                      Réglages : Réglages par défaut du Canada

                      -

                      Dernière vérification : 2026-08-29

                      +

                      Fiche révisée : 2026-08-29

                      @@ -283,7 +283,7 @@ complète fonctionne sans carte, autorisation de localisation ni compte GitHub.

                      Regina et sud de la Saskatchewan

                      Province ou territoire : Saskatchewan

                      Réglages : Réglages par défaut du Canada

                      -

                      Dernière vérification : 2026-08-29

                      +

                      Fiche révisée : 2026-08-29

                      Voir les détails de la fiche

                      @@ -296,7 +296,7 @@ complète fonctionne sans carte, autorisation de localisation ni compte GitHub.

                      Est de l’Ontario et ouest du Québec

                      Province ou territoire : Ontario

                      Réglages : Réglages par défaut du Canada

                      -

                      Dernière vérification : 2026-08-29

                      +

                      Fiche révisée : 2026-08-29

                      • Discord : Discord de Greater Ottawa Mesh Enthusiasts (externe)
                      • Site Web : Site Web d’Ottawa Mesh (externe)
                      • @@ -311,7 +311,7 @@ complète fonctionne sans carte, autorisation de localisation ni compte GitHub.

                        Sud de l’Ontario

                        Province ou territoire : Ontario

                        Réglages : Réglages par défaut du Canada

                        -

                        Dernière vérification : 2026-08-29

                        +

                        Fiche révisée : 2026-08-29

                        @@ -325,7 +325,7 @@ complète fonctionne sans carte, autorisation de localisation ni compte GitHub.

                        Région de Quinte, y compris Belleville, Trenton, le comté de Prince Edward et les environs

                        Province ou territoire : Ontario

                        Réglages : Réglages par défaut du Canada

                        -

                        Dernière vérification : 2026-08-29

                        +

                        Fiche révisée : 2026-08-29

                        • Discord : Discord de Quinte Mesh Network (externe)
                        • Site Web : Site Web de Quinte Mesh Network (externe)
                        • @@ -340,7 +340,7 @@ complète fonctionne sans carte, autorisation de localisation ni compte GitHub.

                          Charlevoix, y compris La Malbaie

                          Province ou territoire : Québec

                          Réglages : Réglages par défaut du Canada

                          -

                          Dernière vérification : 2026-08-29

                          +

                          Fiche révisée : 2026-08-29

                          @@ -354,7 +354,7 @@ complète fonctionne sans carte, autorisation de localisation ni compte GitHub.

                          Québec

                          Province ou territoire : Québec

                          Réglages : Réglages par défaut du Canada

                          -

                          Dernière vérification : 2026-08-29

                          +

                          Fiche révisée : 2026-08-29

                          @@ -368,7 +368,7 @@ complète fonctionne sans carte, autorisation de localisation ni compte GitHub.

                          Grand Montréal

                          Province ou territoire : Québec

                          Réglages : Réglages par défaut du Canada

                          -

                          Dernière vérification : 2026-08-29

                          +

                          Fiche révisée : 2026-08-29

                          @@ -382,7 +382,7 @@ complète fonctionne sans carte, autorisation de localisation ni compte GitHub.

                          Ville de Québec

                          Province ou territoire : Québec

                          Réglages : Réglages par défaut du Canada

                          -

                          Dernière vérification : 2026-08-29

                          +

                          Fiche révisée : 2026-08-29

                          @@ -396,7 +396,7 @@ complète fonctionne sans carte, autorisation de localisation ni compte GitHub.

                          Saguenay–Lac-Saint-Jean (YTF)

                          Province ou territoire : Québec

                          Réglages : Réglages par défaut du Canada

                          -

                          Dernière vérification : 2026-08-29

                          +

                          Fiche révisée : 2026-08-29

                          • Facebook : Facebook du Réseau Mesh du Saguenay–Lac-Saint-Jean YTF (externe)
                          • MeshMapper : Carte MeshMapper du Réseau Mesh du Saguenay–Lac-Saint-Jean YTF (externe)
                          • @@ -411,7 +411,7 @@ complète fonctionne sans carte, autorisation de localisation ni compte GitHub.

                            Montréal

                            Province ou territoire : Québec

                            Réglages : Réglages par défaut du Canada

                            -

                            Dernière vérification : 2026-08-29

                            +

                            Fiche révisée : 2026-08-29

                            @@ -425,7 +425,7 @@ complète fonctionne sans carte, autorisation de localisation ni compte GitHub.

                            Sud du Nouveau-Brunswick, y compris Fredericton, Saint John, Moncton et les environs

                            Province ou territoire : Nouveau-Brunswick

                            Réglages : Réglages par défaut du Canada

                            -

                            Dernière vérification : 2026-08-29

                            +

                            Fiche révisée : 2026-08-29

                            @@ -439,7 +439,7 @@ complète fonctionne sans carte, autorisation de localisation ni compte GitHub.

                            Comté de Lunenburg

                            Province ou territoire : Nouvelle-Écosse

                            Réglages : Réglages par défaut du Canada

                            -

                            Dernière vérification : 2026-08-29

                            +

                            Fiche révisée : 2026-08-29

                            diff --git a/docs/provinces/index.md b/docs/provinces/index.md index eed47df0..7ba6b8f2 100644 --- a/docs/provinces/index.md +++ b/docs/provinces/index.md @@ -78,7 +78,7 @@ works without a map, location permission, or a GitHub account.

                            Lower Mainland and Vancouver Island

                            Province: British Columbia

                            Settings: Different local settings — Radio preset: Custom; Raw radio values: 910.425 MHz / 62.5 kHz / SF7 / CR5

                            -

                            Last verified: 2026-08-29

                            +

                            Listing reviewed: 2026-08-29

                            @@ -92,7 +92,7 @@ works without a map, location permission, or a GitHub account.

                            Salish Sea and surrounding area

                            Province: British Columbia

                            Settings: Uses the Canada defaults

                            -

                            Last verified: 2026-08-29

                            +

                            Listing reviewed: 2026-08-29

                            @@ -107,7 +107,7 @@ works without a map, location permission, or a GitHub account.

                            Building Alberta's community-operated off-grid LoRa mesh network

                            Province: Alberta

                            Settings: Uses the Canada defaults

                            -

                            Last verified: 2026-08-29

                            +

                            Listing reviewed: 2026-08-29

                            • Website: AlbertaMesh.ca (external)
                            • Discord: Alberta MeshCore Discord (external)
                            • @@ -129,7 +129,7 @@ works without a map, location permission, or a GitHub account.

                              Uses the YYC regional identifier shared with the Calgary regional network

                              Province: Alberta

                              Settings: Uses the Canada defaults

                              -

                              Last verified: 2026-08-29

                              +

                              Listing reviewed: 2026-08-29

                              • Website: Airdrie MeshCore Network (external)
                              • Website: Airdrie configuration guide (external)
                              • @@ -147,7 +147,7 @@ works without a map, location permission, or a GitHub account.

                                Calgary and area

                                Province: Alberta

                                Settings: Uses the Canada defaults

                                -

                                Last verified: 2026-08-29

                                +

                                Listing reviewed: 2026-08-29

                                • Telegram: Calgary topic in Mesh Alberta (external)
                                • Discord: Canada - Calgary, Alberta & Area regional channel in the MeshCore Discord
                                • @@ -163,7 +163,7 @@ works without a map, location permission, or a GitHub account.

                                  Initial AlbertaMesh.ca launch region

                                  Province: Alberta

                                  Settings: Uses the Canada defaults

                                  -

                                  Last verified: 2026-08-29

                                  +

                                  Listing reviewed: 2026-08-29

                                  • Website: Calgary community guide (external)
                                  • Discord: Alberta MeshCore Discord (external)
                                  • @@ -182,7 +182,7 @@ works without a map, location permission, or a GitHub account.

                                    Edmonton regional community within Alberta MeshCore

                                    Province: Alberta

                                    Settings: Uses the Canada defaults

                                    -

                                    Last verified: 2026-08-29

                                    +

                                    Listing reviewed: 2026-08-29

                                    • Website: Edmonton on AlbertaMesh.ca (external)
                                    • Website: Edmonton configuration guide (external)
                                    • @@ -200,7 +200,7 @@ works without a map, location permission, or a GitHub account.

                                      Edmonton mesh network community focused on MeshCore

                                      Province: Alberta

                                      Settings: Uses the Canada defaults

                                      -

                                      Last verified: 2026-08-29

                                      +

                                      Listing reviewed: 2026-08-29

                                      • Website: Getting started (external)
                                      • Website: MeshCore defaults (external)
                                      • @@ -218,7 +218,7 @@ works without a map, location permission, or a GitHub account.

                                        Connecting Lethbridge, one node at a time

                                        Province: Alberta

                                        Settings: Uses the Canada defaults

                                        -

                                        Last verified: 2026-08-29

                                        +

                                        Listing reviewed: 2026-08-29

                                        • Website: YQLMesh website (external)
                                        • Website: Lethbridge regional page (external)
                                        • @@ -239,7 +239,7 @@ works without a map, location permission, or a GitHub account.

                                          Southern Alberta, including Cardston, Magrath, Raymond, and nearby areas

                                          Province: Alberta

                                          Settings: Uses the Canada defaults

                                          -

                                          Last verified: 2026-08-29

                                          +

                                          Listing reviewed: 2026-08-29

                                          • Discord: Canada - Southern Alberta regional channel in the MeshCore Discord
                                          • Telegram: Cardston topic in Mesh Alberta (external)
                                          • @@ -254,7 +254,7 @@ works without a map, location permission, or a GitHub account.

                                            Calgary

                                            Province: Alberta

                                            Settings: Uses the Canada defaults

                                            -

                                            Last verified: 2026-08-29

                                            +

                                            Listing reviewed: 2026-08-29

                                            • Discord: YYC MeshCore Discord (external)
                                            • Website: MeshMonitoring (external)
                                            • @@ -269,7 +269,7 @@ works without a map, location permission, or a GitHub account.

                                              Central Saskatchewan

                                              Province: Saskatchewan

                                              Settings: Uses the Canada defaults

                                              -

                                              Last verified: 2026-08-29

                                              +

                                              Listing reviewed: 2026-08-29

                                              @@ -283,7 +283,7 @@ works without a map, location permission, or a GitHub account.

                                              Regina and Southern Saskatchewan

                                              Province: Saskatchewan

                                              Settings: Uses the Canada defaults

                                              -

                                              Last verified: 2026-08-29

                                              +

                                              Listing reviewed: 2026-08-29

                                              View listing details

                                              @@ -296,7 +296,7 @@ works without a map, location permission, or a GitHub account.

                                              Eastern Ontario and Western Quebec

                                              Province: Ontario

                                              Settings: Uses the Canada defaults

                                              -

                                              Last verified: 2026-08-29

                                              +

                                              Listing reviewed: 2026-08-29

                                              • Discord: Greater Ottawa Mesh Enthusiasts Discord (external)
                                              • Website: Ottawa Mesh website (external)
                                              • @@ -311,7 +311,7 @@ works without a map, location permission, or a GitHub account.

                                                Southern Ontario

                                                Province: Ontario

                                                Settings: Uses the Canada defaults

                                                -

                                                Last verified: 2026-08-29

                                                +

                                                Listing reviewed: 2026-08-29

                                                @@ -325,7 +325,7 @@ works without a map, location permission, or a GitHub account.

                                                Quinte Region, including Belleville, Trenton, Prince Edward County, and nearby areas

                                                Province: Ontario

                                                Settings: Uses the Canada defaults

                                                -

                                                Last verified: 2026-08-29

                                                +

                                                Listing reviewed: 2026-08-29

                                                • Discord: Quinte Mesh Network Discord (external)
                                                • Website: Quinte Mesh Network website (external)
                                                • @@ -340,7 +340,7 @@ works without a map, location permission, or a GitHub account.

                                                  Charlevoix, including La Malbaie

                                                  Province: Quebec

                                                  Settings: Uses the Canada defaults

                                                  -

                                                  Last verified: 2026-08-29

                                                  +

                                                  Listing reviewed: 2026-08-29

                                                  @@ -354,7 +354,7 @@ works without a map, location permission, or a GitHub account.

                                                  Quebec

                                                  Province: Quebec

                                                  Settings: Uses the Canada defaults

                                                  -

                                                  Last verified: 2026-08-29

                                                  +

                                                  Listing reviewed: 2026-08-29

                                                  @@ -368,7 +368,7 @@ works without a map, location permission, or a GitHub account.

                                                  Greater Montreal

                                                  Province: Quebec

                                                  Settings: Uses the Canada defaults

                                                  -

                                                  Last verified: 2026-08-29

                                                  +

                                                  Listing reviewed: 2026-08-29

                                                  @@ -382,7 +382,7 @@ works without a map, location permission, or a GitHub account.

                                                  Quebec City

                                                  Province: Quebec

                                                  Settings: Uses the Canada defaults

                                                  -

                                                  Last verified: 2026-08-29

                                                  +

                                                  Listing reviewed: 2026-08-29

                                                  @@ -396,7 +396,7 @@ works without a map, location permission, or a GitHub account.

                                                  Saguenay–Lac-Saint-Jean (YTF)

                                                  Province: Quebec

                                                  Settings: Uses the Canada defaults

                                                  -

                                                  Last verified: 2026-08-29

                                                  +

                                                  Listing reviewed: 2026-08-29

                                                  • Facebook: Réseau Mesh du Saguenay Lac st-Jean YTF Facebook (external)
                                                  • MeshMapper: Réseau Mesh du Saguenay Lac st-Jean YTF MeshMapper (external)
                                                  • @@ -411,7 +411,7 @@ works without a map, location permission, or a GitHub account.

                                                    Montreal

                                                    Province: Quebec

                                                    Settings: Uses the Canada defaults

                                                    -

                                                    Last verified: 2026-08-29

                                                    +

                                                    Listing reviewed: 2026-08-29

                                                    @@ -425,7 +425,7 @@ works without a map, location permission, or a GitHub account.

                                                    Fredericton, Saint John, Moncton, and nearby areas

                                                    Province: New Brunswick

                                                    Settings: Uses the Canada defaults

                                                    -

                                                    Last verified: 2026-08-29

                                                    +

                                                    Listing reviewed: 2026-08-29

                                                    @@ -439,7 +439,7 @@ works without a map, location permission, or a GitHub account.

                                                    Lunenburg County

                                                    Province: Nova Scotia

                                                    Settings: Uses the Canada defaults

                                                    -

                                                    Last verified: 2026-08-29

                                                    +

                                                    Listing reviewed: 2026-08-29

                                                    diff --git a/docs/provinces/new-brunswick.fr.md b/docs/provinces/new-brunswick.fr.md index 332cba82..92d5c11e 100644 --- a/docs/provinces/new-brunswick.fr.md +++ b/docs/provinces/new-brunswick.fr.md @@ -38,7 +38,7 @@ sauf si une fiche indique des réglages locaux différents.
                                                    Réglages
                                                    Réglages par défaut du Canada
                                                    -
                                                    Dernière vérification
                                                    +
                                                    Fiche révisée
                                                    2026-08-29

                                                    @@ -51,7 +51,7 @@ Ce groupe est en formation. Communiquez avec lui pour savoir ce qui fonctionne e

                                                    Vérification : Pas encore effectuée

                                                    -

                                                    Mettre cette fiche à jour

                                                    +

                                                    Mettre cette fiche à jour

                                                    diff --git a/docs/provinces/new-brunswick.md b/docs/provinces/new-brunswick.md index 7a74d698..1688a88e 100644 --- a/docs/provinces/new-brunswick.md +++ b/docs/provinces/new-brunswick.md @@ -38,7 +38,7 @@ card lists different local settings.
                                                    Settings
                                                    Uses the Canada defaults
                                                    -
                                                    Last verified
                                                    +
                                                    Listing reviewed
                                                    2026-08-29

                                                    @@ -51,7 +51,7 @@ This group is forming. Contact it to learn what is working and where help is nee

                                                    Contact check: Not yet verified

                                                    -

                                                    Update this listing

                                                    +

                                                    Update this listing

                                                    diff --git a/docs/provinces/nova-scotia.fr.md b/docs/provinces/nova-scotia.fr.md index fc8ab022..59f03290 100644 --- a/docs/provinces/nova-scotia.fr.md +++ b/docs/provinces/nova-scotia.fr.md @@ -38,7 +38,7 @@ sauf si une fiche indique des réglages locaux différents.
                                                    Réglages
                                                    Réglages par défaut du Canada
                                                    -
                                                    Dernière vérification
                                                    +
                                                    Fiche révisée
                                                    2026-08-29

                                                    Coordonnées

                                                    @@ -48,7 +48,7 @@ sauf si une fiche indique des réglages locaux différents.

                                                    Vérification : Effectuée le 2026-08-29

                                                    -

                                                    Mettre cette fiche à jour

                                                    +

                                                    Mettre cette fiche à jour

                                                    diff --git a/docs/provinces/nova-scotia.md b/docs/provinces/nova-scotia.md index 6b47c73b..fa56513f 100644 --- a/docs/provinces/nova-scotia.md +++ b/docs/provinces/nova-scotia.md @@ -38,7 +38,7 @@ card lists different local settings.
                                                    Settings
                                                    Uses the Canada defaults
                                                    -
                                                    Last verified
                                                    +
                                                    Listing reviewed
                                                    2026-08-29

                                                    Contacts

                                                    @@ -46,9 +46,9 @@ card lists different local settings.
                                                  • Website: Lunenburg County Mesh website (external)

                                                  -Contact check: Verified on 2026-08-29 +Contact check: Links checked on 2026-08-29

                                                  -

                                                  Update this listing

                                                  +

                                                  Update this listing

                                                  diff --git a/docs/provinces/ontario.fr.md b/docs/provinces/ontario.fr.md index 3bfdf0c4..e4424e1b 100644 --- a/docs/provinces/ontario.fr.md +++ b/docs/provinces/ontario.fr.md @@ -38,7 +38,7 @@ sauf si une fiche indique des réglages locaux différents.
                                                  Réglages
                                                  Réglages par défaut du Canada
                                                  -
                                                  Dernière vérification
                                                  +
                                                  Fiche révisée
                                                  2026-08-29

                                                  Coordonnées

                                                  @@ -49,7 +49,7 @@ sauf si une fiche indique des réglages locaux différents.

                                                  Vérification : Effectuée le 2026-08-29

                                                  -

                                                  Mettre cette fiche à jour

                                                  +

                                                  Mettre cette fiche à jour

                                                  @@ -60,7 +60,7 @@ sauf si une fiche indique des réglages locaux différents.
                                                  Réglages
                                                  Réglages par défaut du Canada
                                                  -
                                                  Dernière vérification
                                                  +
                                                  Fiche révisée
                                                  2026-08-29

                                                  Coordonnées

                                                  @@ -70,7 +70,7 @@ sauf si une fiche indique des réglages locaux différents.

                                                  Vérification : Effectuée le 2026-08-29

                                                  -

                                                  Mettre cette fiche à jour

                                                  +

                                                  Mettre cette fiche à jour

                                                  @@ -81,7 +81,7 @@ sauf si une fiche indique des réglages locaux différents.
                                                  Réglages
                                                  Réglages par défaut du Canada
                                                  -
                                                  Dernière vérification
                                                  +
                                                  Fiche révisée
                                                  2026-08-29

                                                  Coordonnées

                                                  @@ -92,7 +92,7 @@ sauf si une fiche indique des réglages locaux différents.

                                                  Vérification : Effectuée le 2026-08-29

                                                  -

                                                  Mettre cette fiche à jour

                                                  +

                                                  Mettre cette fiche à jour

                                                  diff --git a/docs/provinces/ontario.md b/docs/provinces/ontario.md index 9786fd4e..c21d2354 100644 --- a/docs/provinces/ontario.md +++ b/docs/provinces/ontario.md @@ -38,7 +38,7 @@ card lists different local settings.
                                                  Settings
                                                  Uses the Canada defaults
                                                  -
                                                  Last verified
                                                  +
                                                  Listing reviewed
                                                  2026-08-29

                                                  Contacts

                                                  @@ -47,9 +47,9 @@ card lists different local settings.
                                                • Website: Ottawa Mesh website (external)

                                                -Contact check: Verified on 2026-08-29 +Contact check: Links checked on 2026-08-29

                                                -

                                                Update this listing

                                                +

                                                Update this listing

                                                @@ -60,7 +60,7 @@ card lists different local settings.
                                                Settings
                                                Uses the Canada defaults
                                                -
                                                Last verified
                                                +
                                                Listing reviewed
                                                2026-08-29

                                                Contacts

                                                @@ -68,9 +68,9 @@ card lists different local settings.
                                              • Discord: GTA+-Lora-Meshes Discord (external)

                                              -Contact check: Verified on 2026-08-29 +Contact check: Links checked on 2026-08-29

                                              -

                                              Update this listing

                                              +

                                              Update this listing

                                              @@ -81,7 +81,7 @@ card lists different local settings.
                                              Settings
                                              Uses the Canada defaults
                                              -
                                              Last verified
                                              +
                                              Listing reviewed
                                              2026-08-29

                                              Contacts

                                              @@ -90,9 +90,9 @@ card lists different local settings.
                                            • Website: Quinte Mesh Network website (external)

                                            -Contact check: Verified on 2026-08-29 +Contact check: Links checked on 2026-08-29

                                            -

                                            Update this listing

                                            +

                                            Update this listing

                                            diff --git a/docs/provinces/quebec.fr.md b/docs/provinces/quebec.fr.md index d71872b3..3eec9f39 100644 --- a/docs/provinces/quebec.fr.md +++ b/docs/provinces/quebec.fr.md @@ -38,7 +38,7 @@ sauf si une fiche indique des réglages locaux différents.
                                            Réglages
                                            Réglages par défaut du Canada
                                            -
                                            Dernière vérification
                                            +
                                            Fiche révisée
                                            2026-08-29

                                            Coordonnées

                                            @@ -49,7 +49,7 @@ sauf si une fiche indique des réglages locaux différents.

                                            Vérification : Effectuée le 2026-08-29

                                            -

                                            Mettre cette fiche à jour

                                            +

                                            Mettre cette fiche à jour

                                            @@ -60,7 +60,7 @@ sauf si une fiche indique des réglages locaux différents.
                                            Réglages
                                            Réglages par défaut du Canada
                                            -
                                            Dernière vérification
                                            +
                                            Fiche révisée
                                            2026-08-29

                                            Coordonnées

                                            @@ -70,7 +70,7 @@ sauf si une fiche indique des réglages locaux différents.

                                            Vérification : Effectuée le 2026-08-29

                                            -

                                            Mettre cette fiche à jour

                                            +

                                            Mettre cette fiche à jour

                                            @@ -81,7 +81,7 @@ sauf si une fiche indique des réglages locaux différents.
                                            Réglages
                                            Réglages par défaut du Canada
                                            -
                                            Dernière vérification
                                            +
                                            Fiche révisée
                                            2026-08-29

                                            Coordonnées

                                            @@ -91,7 +91,7 @@ sauf si une fiche indique des réglages locaux différents.

                                            Vérification : Effectuée le 2026-08-29

                                            -

                                            Mettre cette fiche à jour

                                            +

                                            Mettre cette fiche à jour

                                            @@ -102,7 +102,7 @@ sauf si une fiche indique des réglages locaux différents.
                                            Réglages
                                            Réglages par défaut du Canada
                                            -
                                            Dernière vérification
                                            +
                                            Fiche révisée
                                            2026-08-29

                                            Coordonnées

                                            @@ -112,7 +112,7 @@ sauf si une fiche indique des réglages locaux différents.

                                            Vérification : Effectuée le 2026-08-29

                                            -

                                            Mettre cette fiche à jour

                                            +

                                            Mettre cette fiche à jour

                                            @@ -123,7 +123,7 @@ sauf si une fiche indique des réglages locaux différents.
                                            Réglages
                                            Réglages par défaut du Canada
                                            -
                                            Dernière vérification
                                            +
                                            Fiche révisée
                                            2026-08-29

                                            Coordonnées

                                            @@ -134,7 +134,7 @@ sauf si une fiche indique des réglages locaux différents.

                                            Vérification : Certains liens restent à vérifier

                                            -

                                            Mettre cette fiche à jour

                                            +

                                            Mettre cette fiche à jour

                                            @@ -145,7 +145,7 @@ sauf si une fiche indique des réglages locaux différents.
                                            Réglages
                                            Réglages par défaut du Canada
                                            -
                                            Dernière vérification
                                            +
                                            Fiche révisée
                                            2026-08-29

                                            Coordonnées

                                            @@ -155,7 +155,7 @@ sauf si une fiche indique des réglages locaux différents.

                                            Vérification : Effectuée le 2026-08-29

                                            -

                                            Mettre cette fiche à jour

                                            +

                                            Mettre cette fiche à jour

                                            diff --git a/docs/provinces/quebec.md b/docs/provinces/quebec.md index c38b871b..f060bb93 100644 --- a/docs/provinces/quebec.md +++ b/docs/provinces/quebec.md @@ -38,7 +38,7 @@ card lists different local settings.
                                            Settings
                                            Uses the Canada defaults
                                            -
                                            Last verified
                                            +
                                            Listing reviewed
                                            2026-08-29

                                            Contacts

                                            @@ -47,9 +47,9 @@ card lists different local settings.

                                          Listing contact: pifane

                                          -Contact check: Verified on 2026-08-29 +Contact check: Links checked on 2026-08-29

                                          -

                                          Update this listing

                                          +

                                          Update this listing

                                          @@ -60,7 +60,7 @@ card lists different local settings.
                                          Settings
                                          Uses the Canada defaults
                                          -
                                          Last verified
                                          +
                                          Listing reviewed
                                          2026-08-29

                                          Contacts

                                          @@ -68,9 +68,9 @@ card lists different local settings.
                                        • Telegram: Mesh Quebec Telegram (external)

                                        -Contact check: Verified on 2026-08-29 +Contact check: Links checked on 2026-08-29

                                        -

                                        Update this listing

                                        +

                                        Update this listing

                                        @@ -81,7 +81,7 @@ card lists different local settings.
                                        Settings
                                        Uses the Canada defaults
                                        -
                                        Last verified
                                        +
                                        Listing reviewed
                                        2026-08-29

                                        Contacts

                                        @@ -89,9 +89,9 @@ card lists different local settings.
                                      • Website: Montreal Mesh website (external)

                                      -Contact check: Verified on 2026-08-29 +Contact check: Links checked on 2026-08-29

                                      -

                                      Update this listing

                                      +

                                      Update this listing

                                      @@ -102,7 +102,7 @@ card lists different local settings.
                                      Settings
                                      Uses the Canada defaults
                                      -
                                      Last verified
                                      +
                                      Listing reviewed
                                      2026-08-29

                                      Contacts

                                      @@ -110,9 +110,9 @@ card lists different local settings.
                                    • Discord: Réseau Mesh de la Capitale YQB Discord (external)

                                    -Contact check: Verified on 2026-08-29 +Contact check: Links checked on 2026-08-29

                                    -

                                    Update this listing

                                    +

                                    Update this listing

                                    @@ -123,7 +123,7 @@ card lists different local settings.
                                    Settings
                                    Uses the Canada defaults
                                    -
                                    Last verified
                                    +
                                    Listing reviewed
                                    2026-08-29

                                    Contacts

                                    @@ -134,7 +134,7 @@ card lists different local settings.

                                    Contact check: Some links still need review

                                    -

                                    Update this listing

                                    +

                                    Update this listing

                                    @@ -145,7 +145,7 @@ card lists different local settings.
                                    Settings
                                    Uses the Canada defaults
                                    -
                                    Last verified
                                    +
                                    Listing reviewed
                                    2026-08-29

                                    Contacts

                                    @@ -153,9 +153,9 @@ card lists different local settings.
                                  • Website: Réseau Libre website (external)

                                  -Contact check: Verified on 2026-08-29 +Contact check: Links checked on 2026-08-29

                                  -

                                  Update this listing

                                  +

                                  Update this listing

                                  diff --git a/docs/provinces/saskatchewan.fr.md b/docs/provinces/saskatchewan.fr.md index 2df0c32f..23c733fb 100644 --- a/docs/provinces/saskatchewan.fr.md +++ b/docs/provinces/saskatchewan.fr.md @@ -38,7 +38,7 @@ sauf si une fiche indique des réglages locaux différents.
                                  Réglages
                                  Réglages par défaut du Canada
                                  -
                                  Dernière vérification
                                  +
                                  Fiche révisée
                                  2026-08-29

                                  Coordonnées

                                  @@ -48,7 +48,7 @@ sauf si une fiche indique des réglages locaux différents.

                                  Vérification : Effectuée le 2026-08-29

                                  -

                                  Mettre cette fiche à jour

                                  +

                                  Mettre cette fiche à jour

                                  @@ -59,11 +59,11 @@ sauf si une fiche indique des réglages locaux différents.
                                  Réglages
                                  Réglages par défaut du Canada
                                  -
                                  Dernière vérification
                                  +
                                  Fiche révisée
                                  2026-08-29

                                  Aucune coordonnée publique n’a encore été fournie.

                                  -

                                  Mettre cette fiche à jour

                                  +

                                  Mettre cette fiche à jour

                                  diff --git a/docs/provinces/saskatchewan.md b/docs/provinces/saskatchewan.md index 6bb1798e..768ef475 100644 --- a/docs/provinces/saskatchewan.md +++ b/docs/provinces/saskatchewan.md @@ -38,7 +38,7 @@ card lists different local settings.
                                  Settings
                                  Uses the Canada defaults
                                  -
                                  Last verified
                                  +
                                  Listing reviewed
                                  2026-08-29

                                  Contacts

                                  @@ -46,9 +46,9 @@ card lists different local settings.
                                • Telegram: Mesh Saskatchewan Telegram (external)

                                -Contact check: Verified on 2026-08-29 +Contact check: Links checked on 2026-08-29

                                -

                                Update this listing

                                +

                                Update this listing

                                @@ -59,11 +59,11 @@ card lists different local settings.
                                Settings
                                Uses the Canada defaults
                                -
                                Last verified
                                +
                                Listing reviewed
                                2026-08-29

                                No public contact has been provided yet.

                                -

                                Update this listing

                                +

                                Update this listing

                                diff --git a/docs/resources/glossary.fr.md b/docs/resources/glossary.fr.md index a41936c9..49a9807f 100644 --- a/docs/resources/glossary.fr.md +++ b/docs/resources/glossary.fr.md @@ -91,9 +91,8 @@ utiliser chaque paramètre ou appareil. : Tout appareil MeshCore présent sur le réseau. **Observateur** -: Une radio ou un service sur un système hôte qui écoute le trafic MeshCore - et transmet des données du réseau aux outils publics. Il ne relaie pas le - trafic du réseau. +: Une radio ou un service qui transmet des données MeshCore aux outils publics. + L’observation et la répétition sont des fonctions distinctes; un appareil peut faire les deux. **Préréglage** : Un ensemble nommé de paramètres radio. diff --git a/docs/resources/glossary.md b/docs/resources/glossary.md index ec02c5f4..51875491 100644 --- a/docs/resources/glossary.md +++ b/docs/resources/glossary.md @@ -83,7 +83,7 @@ the linked guides explain how to use each setting or device. **Observer** : A radio or host service that listens for MeshCore traffic and sends network - data to public tools. It does not relay mesh traffic. + data to public tools. Observing and repeating are separate functions; a node can do both when configured for it. **Path hash mode** : The MeshCore setting that controls the size of identifiers in advert paths. diff --git a/docs/start/companion.fr.md b/docs/start/companion.fr.md index 8f5a3f38..75d0b7e7 100644 --- a/docs/start/companion.fr.md +++ b/docs/start/companion.fr.md @@ -26,6 +26,8 @@ Un appareil compagnon sert à envoyer et à recevoir des messages personnels. Il ne relaie pas le trafic des autres personnes. La plupart se jumellent à une application; certains possèdent leur propre écran et leurs propres commandes. +[Installer et configurer un compagnon](../meshcore/flash-companion.md){ .md-button .md-button--primary } + ## Avant de commencer - Consultez les [appareils compagnons recommandés](../hardware/recommended-companions.md). @@ -40,25 +42,11 @@ Le guide lié remplace le micrologiciel de l’appareil et le configure comme compagnon. Arrêtez-vous avant l’effacement si vous ne pouvez pas récupérer les renseignements dont vous avez besoin. -
                                -

                                Liste de configuration

                                -

                                Cette liste est enregistrée uniquement dans ce navigateur.

                                -
                                  -
                                1. -
                                2. -
                                3. -
                                4. -
                                5. -
                                6. -
                                -
                                + ## Installer le micrologiciel du compagnon -Suivez le guide [Reprogrammer et configurer un appareil -compagnon](../meshcore/flash-companion.md). Il explique comment choisir -l’appareil, établir la connexion dans le navigateur, installer le micrologiciel -et récupérer l’appareil en cas de problème. +Suivez le guide [Installer et configurer un compagnon](../meshcore/flash-companion.md). ## Choisir les bons paramètres radio @@ -89,3 +77,16 @@ modification du micrologiciel. Consultez les messagerie courantes. [Trouvez votre communauté](../provinces/index.md), puis échangez un message d’essai avec une personne à proximité. Si personne ne voit l’annonce, [obtenez de l’aide](get-help.md). + +
                                +

                                Liste de vérification

                                +

                                Cette liste est enregistrée uniquement dans ce navigateur.

                                +
                                  +
                                1. +
                                2. +
                                3. +
                                4. +
                                5. +
                                6. +
                                +
                                diff --git a/docs/start/companion.md b/docs/start/companion.md index 90eddc10..87c6e5c3 100644 --- a/docs/start/companion.md +++ b/docs/start/companion.md @@ -25,6 +25,8 @@ requires: A companion is a personal messaging device. It does not route traffic for other users. Most pair with an app; some have their own screen and controls. +[Install and configure a companion](../meshcore/flash-companion.md){ .md-button .md-button--primary } + ## Before you start - Check the [recommended companion options](../hardware/recommended-companions.md). @@ -37,24 +39,11 @@ other users. Most pair with an app; some have their own screen and controls. The linked flashing guide replaces the device firmware and configures it as a companion. Stop before erasing if you cannot recover information you need. -
                                -

                                Setup checklist

                                -

                                Checks are saved only in this browser.

                                -
                                  -
                                1. -
                                2. -
                                3. -
                                4. -
                                5. -
                                6. -
                                -
                                + ## Flash the companion -Follow [Flash and configure a companion](../meshcore/flash-companion.md). Use -that guide for device selection, browser connection, flashing, and recovery -steps. +Follow [Flash and configure a companion](../meshcore/flash-companion.md). ## Use the right radio settings @@ -76,6 +65,19 @@ The companion is ready when: Use the [companion verification checklist](verify.md#companion). +
                                +

                                Setup checklist

                                +

                                Checks are saved only in this browser.

                                +
                                  +
                                1. +
                                2. +
                                3. +
                                4. +
                                5. +
                                6. +
                                +
                                + ## What's next Keep the local settings recorded and check them again after firmware changes. diff --git a/docs/start/observer.fr.md b/docs/start/observer.fr.md index 1c1338f3..ed34d20f 100644 --- a/docs/start/observer.fr.md +++ b/docs/start/observer.fr.md @@ -23,13 +23,16 @@ requires: # Commencer avec un observateur Un observateur écoute le réseau au moyen d’une radio MeshCore et transmet les -données captées à CoreScope. Il ne relaie pas le trafic du réseau. Il peut +données captées à CoreScope. Il n’a pas besoin de relayer le trafic, mais un +même appareil peut remplir les deux fonctions. Il peut fonctionner dans le micrologiciel d’une radio, sur un ordinateur qui reste allumé ou sur un système de domotique. Avant de l’activer, lisez [quelles données le service recueille et qui peut y accéder](../analyzer/data-collection-access.md). +[Choisir une méthode d’observation](../analyzer/intro.md){ .md-button .md-button--primary } + ## Avant de commencer - Ouvrez l’outil [Choisir une méthode d’observation](../analyzer/intro.md). @@ -46,19 +49,7 @@ Selon la méthode choisie, la configuration peut remplacer le micrologiciel de la radio, installer un service sur le système hôte ou ajouter une intégration domotique. Elle permet aussi la transmission publique de données du réseau. -
                                -

                                Liste de configuration

                                -

                                Cette liste est enregistrée uniquement dans ce navigateur.

                                -
                                  -
                                1. -
                                2. -
                                3. -
                                4. -
                                5. -
                                6. -
                                7. -
                                -
                                + ## Configurer l’observateur @@ -94,3 +85,17 @@ d’identifiants, de système hôte ou de micrologiciel. Ouvrez [Vérifier votre observateur](../analyzer/verify.md) pour consulter son état détaillé. S’il est absent ou silencieux, [obtenez de l’aide](get-help.md). + +
                                +

                                Liste de vérification

                                +

                                Cette liste est enregistrée uniquement dans ce navigateur.

                                +
                                  +
                                1. +
                                2. +
                                3. +
                                4. +
                                5. +
                                6. +
                                7. +
                                +
                                diff --git a/docs/start/observer.md b/docs/start/observer.md index 2fa7f749..7be1ec65 100644 --- a/docs/start/observer.md +++ b/docs/start/observer.md @@ -23,12 +23,14 @@ requires: # Start with an observer An observer listens through a MeshCore radio and sends network data to -CoreScope. It does not route mesh traffic. It can run on radio firmware, an -always-on computer, or a home-automation host. +CoreScope. Observing does not require repeating; a node can do both if configured +for it. Observers can run on radio firmware, an always-on computer, or a home-automation host. Read [what the service collects and who can access it](../analyzer/data-collection-access.md) before enabling an observer. +[Choose an observer setup](../analyzer/intro.md){ .md-button .md-button--primary } + ## Before you start - Open the [observer method chooser](../analyzer/intro.md). @@ -43,19 +45,7 @@ Depending on the method, setup may replace radio firmware, install a host service, or add a home-automation integration. It also enables public network data publishing. -
                                -

                                Setup checklist

                                -

                                Checks are saved only in this browser.

                                -
                                  -
                                1. -
                                2. -
                                3. -
                                4. -
                                5. -
                                6. -
                                7. -
                                -
                                + ## Set up the observer @@ -84,6 +74,20 @@ The observer is working when: Use the [observer verification checklist](verify.md#observer). +
                                +

                                Setup checklist

                                +

                                Checks are saved only in this browser.

                                +
                                  +
                                1. +
                                2. +
                                3. +
                                4. +
                                5. +
                                6. +
                                7. +
                                +
                                + ## What's next Check the observer after radio, network, credential, host, or firmware changes. diff --git a/docs/start/repeater.fr.md b/docs/start/repeater.fr.md index d3b927f5..73f895a3 100644 --- a/docs/start/repeater.fr.md +++ b/docs/start/repeater.fr.md @@ -21,7 +21,7 @@ requires: page_styles: - assets/styles/repeater-hash-check.css?v=20260820-1 page_scripts: - - assets/javascripts/repeater-hash-check.js?v=20260820-1 + - assets/javascripts/repeater-hash-check.js?v=20260904-1 --- # Commencer avec un répéteur @@ -30,6 +30,8 @@ Un répéteur améliore la couverture en relayant le trafic des autres personnes Puisqu’il s’agit d’une installation fixe, planifiez l’alimentation, l’antenne, l’accès et l’entretien avant de l’installer. +[Installer et configurer un répéteur](../meshcore/flash-repeater.md){ .md-button .md-button--primary } + ## Avant de commencer - Consultez les [répéteurs recommandés](../hardware/recommended-repeaters.md). @@ -44,25 +46,10 @@ Le guide installe le micrologiciel du répéteur et configure la radio, l’identité, les annonces et la région. Faites les essais sur l’établi avant toute installation difficile d’accès. -
                                -

                                Liste de configuration

                                -

                                Cette liste est enregistrée uniquement dans ce navigateur.

                                -
                                  -
                                1. -
                                2. -
                                3. -
                                4. -
                                5. -
                                6. -
                                -
                                ## Installer le micrologiciel du répéteur -Suivez le guide [Reprogrammer et configurer un -répéteur](../meshcore/flash-repeater.md). Il présente les choix propres à chaque -carte, les sauvegardes à faire, les situations où il faut s’arrêter et la -méthode de récupération. +Suivez le guide [Reprogrammer et configurer un répéteur](../meshcore/flash-repeater.md). ## Choisir les bons paramètres radio et régionaux @@ -105,3 +92,16 @@ Consultez les conseils sur les [antennes](../hardware/recommended-antenna.md) et les [méthodes de fixation](../hardware/repeater-mounting-options.md) avant l’installation finale. Si une vérification échoue, [obtenez de l’aide](get-help.md). + +
                                +

                                Liste de vérification

                                +

                                Cette liste est enregistrée uniquement dans ce navigateur.

                                +
                                  +
                                1. +
                                2. +
                                3. +
                                4. +
                                5. +
                                6. +
                                +
                                diff --git a/docs/start/repeater.md b/docs/start/repeater.md index 29ef1490..2d5b9220 100644 --- a/docs/start/repeater.md +++ b/docs/start/repeater.md @@ -21,7 +21,7 @@ requires: page_styles: - assets/styles/repeater-hash-check.css?v=20260820-1 page_scripts: - - assets/javascripts/repeater-hash-check.js?v=20260820-1 + - assets/javascripts/repeater-hash-check.js?v=20260904-1 --- # Start with a repeater @@ -30,6 +30,8 @@ A repeater extends coverage by routing traffic for other users. Because it is fixed infrastructure, plan its power, antenna, access, and maintenance before installation. +[Install and configure a repeater](../meshcore/flash-repeater.md){ .md-button .md-button--primary } + ## Before you start - Check [recommended repeater options](../hardware/recommended-repeaters.md). @@ -42,24 +44,10 @@ installation. The guide installs repeater firmware and sets radio, identity, adverts, and regional settings. Test on the bench before a difficult installation. -
                                -

                                Setup checklist

                                -

                                Checks are saved only in this browser.

                                -
                                  -
                                1. -
                                2. -
                                3. -
                                4. -
                                5. -
                                6. -
                                -
                                ## Flash the repeater -Follow [Flash and configure a repeater](../meshcore/flash-repeater.md). Use the -board-specific decisions, backup guidance, stop conditions, and recovery path -in that guide. +Follow [Flash and configure a repeater](../meshcore/flash-repeater.md). ## Use the right radio and region settings @@ -88,6 +76,19 @@ The repeater is ready to install when: Use the [repeater verification checklist](verify.md#repeater). +
                                +

                                Setup checklist

                                +

                                Checks are saved only in this browser.

                                +
                                  +
                                1. +
                                2. +
                                3. +
                                4. +
                                5. +
                                6. +
                                +
                                + ## What's next Keep a record of the site, owner, recovery access, settings, and last diff --git a/docs/start/room-server.fr.md b/docs/start/room-server.fr.md index c0bf6c40..59c183f7 100644 --- a/docs/start/room-server.fr.md +++ b/docs/start/room-server.fr.md @@ -25,6 +25,8 @@ Un serveur de salon garde un salon partagé accessible. Il ne remplace pas un répéteur : utilisez un répéteur pour acheminer le trafic et améliorer la couverture. +[Installer et configurer un serveur de salon](../meshcore/flash-room-server.md){ .md-button .md-button--primary } + ## Avant de commencer - Confirmez que l’outil de reprogrammation offre le micrologiciel @@ -40,18 +42,7 @@ couverture. Le guide lié remplace le micrologiciel et configure l’identité du serveur de salon, les identifiants d’accès et les paramètres radio. -
                                -

                                Liste de configuration

                                -

                                Cette liste est enregistrée uniquement dans ce navigateur.

                                -
                                  -
                                1. -
                                2. -
                                3. -
                                4. -
                                5. -
                                6. -
                                -
                                + ## Installer le micrologiciel du serveur de salon @@ -89,3 +80,16 @@ membre de la communauté à proximité de faire un essai avec un deuxième compagnon. Revérifiez la découverte et l’accès des invités après tout changement de micrologiciel, d’identifiants ou de paramètres radio. En cas d’échec, [obtenez de l’aide](get-help.md). + +
                                +

                                Liste de vérification

                                +

                                Cette liste est enregistrée uniquement dans ce navigateur.

                                +
                                  +
                                1. +
                                2. +
                                3. +
                                4. +
                                5. +
                                6. +
                                +
                                diff --git a/docs/start/room-server.md b/docs/start/room-server.md index ecddcab9..c6fcb923 100644 --- a/docs/start/room-server.md +++ b/docs/start/room-server.md @@ -24,6 +24,8 @@ requires: A room server keeps a shared room available. It does not replace a repeater; use a repeater for routing and coverage. +[Install and configure a room server](../meshcore/flash-room-server.md){ .md-button .md-button--primary } + ## Before you start - Confirm the flasher offers room-server firmware for the device. @@ -36,18 +38,7 @@ use a repeater for routing and coverage. The linked guide replaces the firmware and sets room-server identity, access credentials, and radio settings. -
                                -

                                Setup checklist

                                -

                                Checks are saved only in this browser.

                                -
                                  -
                                1. -
                                2. -
                                3. -
                                4. -
                                5. -
                                6. -
                                -
                                + ## Flash the room server @@ -76,6 +67,19 @@ The room server is ready when: Use the [room-server verification checklist](verify.md#room-server). +
                                +

                                Setup checklist

                                +

                                Checks are saved only in this browser.

                                +
                                  +
                                1. +
                                2. +
                                3. +
                                4. +
                                5. +
                                6. +
                                +
                                + ## What's next Record who maintains the room and how users get support. Ask a nearby community diff --git a/docs/submit-idea.fr.md b/docs/submit-idea.fr.md index 1083c466..966d5be2 100644 --- a/docs/submit-idea.fr.md +++ b/docs/submit-idea.fr.md @@ -16,7 +16,7 @@ estimated_time: 3-5 minutes page_styles: - stylesheets/extra.css?v=20260722-2 page_scripts: - - javascripts/submission-form.js?v=20260722-2 + - javascripts/submission-form.js?v=20260904-1 hide: - toc --- @@ -54,13 +54,33 @@ hide:

                                Décrivez votre idée

                                -

                                Remplissez les cinq champs obligatoires.

                                +

                                Ajoutez un titre et décrivez le problème ou l’idée. Le reste est facultatif.

                                +
                                + + +
                                + +
                                +
                                + + +
                                + +
                                + + +
                                +
                                + +
                                + Ajouter des précisions Facultatif +
                                - @@ -74,7 +94,7 @@ hide:
                                - @@ -85,26 +105,7 @@ hide:
                                -
                                - - -
                                - -
                                -
                                - - -
                                - -
                                - - -
                                -
                                -
                                - Ajouter des précisions Facultatif -
                                diff --git a/docs/submit-idea.md b/docs/submit-idea.md index f3857b37..13524e10 100644 --- a/docs/submit-idea.md +++ b/docs/submit-idea.md @@ -16,7 +16,7 @@ estimated_time: 3-5 minutes page_styles: - stylesheets/extra.css?v=20260722-2 page_scripts: - - javascripts/submission-form.js?v=20260722-2 + - javascripts/submission-form.js?v=20260904-1 hide: - toc --- @@ -54,13 +54,33 @@ hide:

                                Describe your idea

                                -

                                Fill five required fields.

                                +

                                Add a title and describe the problem or idea. Everything else is optional.

                                +
                                + + +
                                + +
                                +
                                + + +
                                + +
                                + + +
                                +
                                + +
                                + Add context Optional +
                                - @@ -74,7 +94,7 @@ hide:
                                - @@ -85,26 +105,7 @@ hide:
                                -
                                - - -
                                - -
                                -
                                - - -
                                - -
                                - - -
                                -
                                -
                                - Add context Optional -
                                diff --git a/maintenance/site-audit-2026-09-04.md b/maintenance/site-audit-2026-09-04.md new file mode 100644 index 00000000..620bff8a --- /dev/null +++ b/maintenance/site-audit-2026-09-04.md @@ -0,0 +1,63 @@ +# September 2026 site audit follow-up + +This change follows the full English/French site audit against main +`c7569011b2a52546282ca034f0ff678b95cfba64`. It fixes the configuration and usability +problems without changing region boundaries, broker accounts, or deployment targets. + +## Changes by audit finding + +| Finding | Implemented change | Verification | +|---|---|---| +| A01 — Radio defaults | Configurator and observer commands preserve current radio/hash settings. Explicit Canada and BC Mesh profiles come from the community source. | Profile unit tests and bilingual command-generation journeys. | +| A02 — Broker slots | Helpers reuse Canada slots or choose free slots; unrelated connections survive. No-room failures leave the file untouched. Backups are unique. | Bash/PowerShell empty, one-line, occupied, existing, full, rerun, and restore fixtures. | +| A03 — Map handoff | Location, shared/extra paths, firmware, and radio choices reach configuration. Invalid saved locations offer recovery. | Bilingual map-to-config tests. | +| A04 — Language state | Map/config language links preserve supported selections, not arbitrary query fields. | Language-switch journey and source-page validation. | +| A05 — Ambiguous searches | Partial registry matches no longer block city lookup; genuine ambiguous matches offer selectable results. | Québec geocoder fixture and Victoria choices. | +| A06 — Observer role | Repeating defaults off and is separate from observing. Existing MQTT slots 3–6 are no longer cleared. | Command-builder tests and bilingual role/glossary review. | +| A07 — Region clarity | Shorter labels distinguish routing regions from coverage and radio networks; results link to communities. | Result/handoff journeys and source review. | +| A08 — Accessible tools | Theme-aware map surfaces and focus colours; province browsing is collapsible. Map controls no longer need a nested scrollbar, and the keyboard skip link cannot overlap buttons. | Light/dark focused-control contrast checks and desktop/mobile page sweep. | +| A09 — Fewer setup detours | Direct setup buttons precede checklists; unrelated automatic next-role links are removed. First-message steps precede app tracing. | Setup/content tests and existing route/anchor checks. | +| A10 — Language | Shorter EN/FR setup and maintenance wording; corrected observer definitions and byte labels. | Bilingual content checks and runtime journeys. | +| A11 — Region standard | Operator quick start precedes technical rules; draft status and incomplete network adoption are explicit. | Content/anchor checks; no authority edits. | +| A12 — Directory | Update links carry the exact community and page. Labels separate listing review from link checks. All 24 listings remain. | Generated-data validation and feedback-prefill tests. | +| A13 — Feedback | Title, description, and public acknowledgement are sufficient. Source-page context survives preview and submission. | Client/server schema tests, simulated submissions, older-gateway fallback. | +| A14 — Flashing | Erase and DFU steps are conditional; bootloader verification refers to the selected release, not a contradictory fixed version. | Content safety tests; no hardware was flashed. | +| A15 — Hardware wording | Removed an unverified antenna-length claim; distinguished shopping-basket costs from per-device cost; shortened French and maintenance prose. | Content review; experimental-build warnings remain. | +| A16 — Broker reference | Broker table is generated into HTML and works without JavaScript. Access inventory and existing admins remain linked. | Static-table and no-JavaScript browser tests. | +| A17 — Contributor/testing gaps | Added a contributor README, safety regressions, and mobile map/editor performance coverage. The map and region table load when visible. Tiles appear before the boundary overlay, and 17 inline icons replace the full icon-library download. Page scripts no longer block the initial render. | Quality workflow with downloadable Lighthouse reports; budgets and throttling profiles are unchanged. The audit server uses HTTP gzip, matching the live GitHub Pages responses. | + +The wording pass is targeted, not a claim that every sentence needed rewriting. +Existing URLs, community contributions, downloadable build files, and unresolved +safety warnings are retained. + +## Required rollout order + +The anonymous submission gateway deploys separately from the static site. +Deploy its backward-compatible change first and verify that `/config` reports +`communityIdeaOptionalDetails: true`; then publish the site. See the +[gateway runbook](../tools/region-proposal-gateway/README.md#short-form-rollout-order). +An older gateway cannot accept the shorter payload, so the site offers copy/GitHub +fallbacks instead of attempting a failing anonymous submission. The boundary-editor +contract is unchanged. No deployment is part of this PR. + +## Human confirmations still needed + +- **Regional maintainers:** formal adoption of the registry and the decision on + boundary proposal #63. Published data is not evidence of network-wide adoption. +- **Community contacts:** current local radio profiles, overlap between communities, + missing contact/language details, and the scope of earlier verification dates. + No new verification dates or associations are invented here. +- **Build contributors:** exact hardware revisions, schematics, safe readings, + climate limits, and reproduction evidence for the unfinished solar guides. + The 1 W guide remains experimental; software checks do not certify hardware. +- **Infrastructure operators:** subscriber authentication details and retention/ + deletion policy. This PR does not change credentials or promise a retention period. + +## Review checklist + +- Inspect explicit Canada/BC Mesh commands and keep-current defaults. +- Check the map → configuration → French journey on a phone and desktop. +- Review short-form feedback in both languages, including the originating page. +- Confirm the gateway rollout prerequisite before merging for publication. +- Read the actual quality-workflow result for this PR; this document describes + coverage and does not substitute for a passing run. diff --git a/mkdocs.yml b/mkdocs.yml index d9196540..9f10ce58 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -34,7 +34,6 @@ theme: - navigation.tabs.sticky - navigation.sections - navigation.path - - navigation.footer - navigation.top - search.suggest - search.highlight diff --git a/overrides/main.html b/overrides/main.html index 8597bae6..26c836bd 100644 --- a/overrides/main.html +++ b/overrides/main.html @@ -39,7 +39,7 @@ {{ super() }} {% if page and page.meta %} {% for script in page.meta.page_scripts or [] %} - + {% endfor %} {% for module in page.meta.page_modules or [] %} diff --git a/overrides/partials/page-feedback.html b/overrides/partials/page-feedback.html index 398e2d2c..c6bc612f 100644 --- a/overrides/partials/page-feedback.html +++ b/overrides/partials/page-feedback.html @@ -2,11 +2,11 @@ {% set page_locale = i18n_page_locale | default(config.theme.language, true) %} {% endif %} diff --git a/scripts/add-meshcore-ca-broker.sh b/scripts/add-meshcore-ca-broker.sh index 39a3cce8..dcc582da 100644 --- a/scripts/add-meshcore-ca-broker.sh +++ b/scripts/add-meshcore-ca-broker.sh @@ -30,8 +30,9 @@ Supported installs: - Companion radio via meshcore-packet-capture (.env.local) Usage: - MESHCORE_CA_IATA=YOW bash <(curl -fsSL https://live.meshcore.ca/scripts/add-meshcore-ca-broker.sh) - bash <(curl -fsSL https://live.meshcore.ca/scripts/add-meshcore-ca-broker.sh) --iata YOW --device serial-host + curl -fsSLo add-meshcore-ca-broker.sh https://meshcore.ca/analyzer/scripts/add-meshcore-ca-broker.sh + less add-meshcore-ca-broker.sh + bash ./add-meshcore-ca-broker.sh --iata YOW --device serial-host Options: --iata CODE Real 3-letter IATA airport code. @@ -531,15 +532,28 @@ set_packetcapture_slot() { upsert_env "$file" "PACKETCAPTURE_MQTT${slot}_TOPIC_PACKETS" 'meshcore/{IATA}/{PUBLIC_KEY}/packets' } -disable_packetcapture_slot() { - local file="$1" - local slot="$2" - upsert_env "$file" "PACKETCAPTURE_MQTT${slot}_ENABLED" "false" - upsert_env "$file" "PACKETCAPTURE_MQTT${slot}_SERVER" "" +choose_packetcapture_slots() { + local file="$1" n server slot1="" slot2="" free=() + for n in 1 2 3 4 5 6; do + server="$(env_value "$file" "PACKETCAPTURE_MQTT${n}_SERVER" | tr -d '\r')" + server="${server#\"}"; server="${server%\"}" + server="${server#\'}"; server="${server%\'}" + if [ "$server" = "$BROKER1_HOST" ] && [ -z "$slot1" ]; then slot1="$n" + elif [ "$server" = "$BROKER2_HOST" ] && [ -z "$slot2" ]; then slot2="$n" + elif [ -z "$server" ]; then free+=("$n") + fi + done + if [ -z "$slot1" ] && [ "${#free[@]}" -gt 0 ]; then slot1="${free[0]}"; free=("${free[@]:1}"); fi + if [ -z "$slot2" ] && [ "${#free[@]}" -gt 0 ]; then slot2="${free[0]}"; fi + if [ -z "$slot1" ] || [ -z "$slot2" ]; then + echo "Not enough empty broker slots. Keep your existing connections, or free two slots and run again. No settings changed." >&2 + return 1 + fi + printf '%s %s\n' "$slot1" "$slot2" } patch_env_capture() { - local env_file current_iata backup n + local env_file current_iata backup slots slot1 slot2 env_file="$(packetcapture_env_path)" install_packetcapture mkdir -p "$(dirname "$env_file")" @@ -550,16 +564,15 @@ patch_env_capture() { fi require_iata + slots="$(choose_packetcapture_slots "$env_file")" || return 1 + read -r slot1 slot2 <<< "$slots" backup="$(backup_path "$env_file")" cp -p "$env_file" "$backup" chmod 0600 "$backup" upsert_env "$env_file" "PACKETCAPTURE_IATA" "$IATA" - set_packetcapture_slot "$env_file" 1 "$BROKER1_HOST" - set_packetcapture_slot "$env_file" 2 "$BROKER2_HOST" - for n in 3 4 5 6; do - disable_packetcapture_slot "$env_file" "$n" - done + set_packetcapture_slot "$env_file" "$slot1" "$BROKER1_HOST" + set_packetcapture_slot "$env_file" "$slot2" "$BROKER2_HOST" chmod 0600 "$env_file" say "Patched: $env_file" say "Backup written: $backup" diff --git a/scripts/postprocess-site.mjs b/scripts/postprocess-site.mjs index 76f9c6fc..41aed695 100644 --- a/scripts/postprocess-site.mjs +++ b/scripts/postprocess-site.mjs @@ -210,6 +210,20 @@ function fallbackUrl(siteRoot, file, siteBaseUrl) { return new URL(route, siteBaseUrl).href; } +export function brokerReferenceRows(config, french = false) { + const escape = (value) => String(value).replaceAll("&", "&").replaceAll("<", "<").replaceAll('"', """); + if (config.schema_version !== 1 || config.brokers?.length !== 2 || !config.brokers.every((broker) => + /^mqtt[12]\.meshcore\.ca$/.test(broker.host) && broker.port === 443 && broker.tls === true && + broker.verify_tls === true && broker.transport === "websockets" && broker.token_audience === broker.host)) { + throw new Error("Invalid observer broker configuration"); + } + return config.brokers.map((broker) => "" + [ + broker.id === "primary" ? (french ? "Principal" : "Primary") : (french ? "Secours" : "Backup"), + broker.host, broker.port, "WebSockets", french ? "Requis; vérifier les certificats" : "Required; verify certificates", + broker.token_audience, + ].map((value) => `${escape(value)}`).join("") + "").join("\n"); +} + async function hashArtifact(siteRoot) { const files = (await walk(siteRoot)) .map((file) => ({ @@ -273,6 +287,11 @@ export async function postprocessSite(siteDirectory, options = {}) { let html = await readFile(file, "utf8"); const originalHtml = html; const pageName = relative(siteRoot, file).split(sep).join("/"); + if (html.includes('')) { + const config = JSON.parse(await readFile(join(siteRoot, "analyzer/observer-config.json"), "utf8")); + html = html.replace('', + `${brokerReferenceRows(config, pageName.startsWith("fr/"))}`); + } html = rewriteAlternateLinks(html, siteBaseUrl, pageName); if (options.noIndexAll) html = ensureNoIndex(html, file); if (html !== originalHtml) await writeFile(file, html, "utf8"); diff --git a/scripts/run-lighthouse.mjs b/scripts/run-lighthouse.mjs index 21e3ed02..8fef374d 100644 --- a/scripts/run-lighthouse.mjs +++ b/scripts/run-lighthouse.mjs @@ -2,7 +2,7 @@ import { spawn } from "node:child_process"; import { mkdir, writeFile } from "node:fs/promises"; import process from "node:process"; import { launch } from "chrome-launcher"; -import lighthouse, { desktopConfig } from "lighthouse"; +import lighthouse, { desktopConfig, defaultConfig } from "lighthouse"; import { normalizeSiteBaseUrl, resolveSiteRoute } from "../tests/browser/site-route.mjs"; const suppliedBaseUrl = process.env.LIGHTHOUSE_BASE_URL; @@ -14,7 +14,12 @@ const routes = [ ["config", "/config/"], ["submit-idea", "/submit-idea/"], ["fr-home", "/fr/"], - ["fr-config", "/fr/config/"] + ["fr-config", "/fr/config/"], + ["map-result", "/config/map/?tag=ott"], + ["editor", "/config/editor/"], + ["mobile-map-result", "/config/map/?tag=ott", "mobile"], + ["mobile-fr-map-result", "/fr/config/map/?tag=ott", "mobile"], + ["mobile-editor", "/config/editor/", "mobile"] ]; const budgets = { performance: 0.8, @@ -31,7 +36,7 @@ function startServer() { const command = process.platform === "win32" ? "python.exe" : "python3"; return spawn( command, - ["-m", "http.server", "4174", "--bind", "127.0.0.1", "--directory", ".tmp/site"], + ["scripts/serve-audit-site.py", "--port", "4174", "--directory", ".tmp/site"], { stdio: "ignore", windowsHide: true } ); } @@ -81,7 +86,7 @@ async function run() { if (!warmup) throw new Error(`Lighthouse warm-up returned no result for ${warmupUrl}`); const failures = []; - for (const [name, route] of routes) { + for (const [name, route, device] of routes) { const url = resolveSiteRoute(baseUrl, route); const html = await (await fetch(url)).text(); const intentionallyNoIndex = hasNoIndex(html); @@ -90,7 +95,7 @@ async function run() { output: "json", logLevel: "error", onlyCategories: Object.keys(budgets) - }, desktopConfig); + }, device === "mobile" ? defaultConfig : desktopConfig); if (!result) throw new Error(`Lighthouse returned no result for ${url}`); await writeFile( diff --git a/scripts/serve-audit-site.py b/scripts/serve-audit-site.py new file mode 100644 index 00000000..da0c6955 --- /dev/null +++ b/scripts/serve-audit-site.py @@ -0,0 +1,59 @@ +"""Local-only Lighthouse server with the HTTP gzip used by GitHub Pages.""" + +import argparse +import gzip +import io +from functools import partial +from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer +from pathlib import Path +from urllib.parse import urlsplit + + +def accepts_gzip(header): + for item in header.lower().split(","): + name, *options = (part.strip() for part in item.split(";")) + if name != "gzip": + continue + for option in options: + if option.startswith("q="): + try: + return float(option[2:]) > 0 + except ValueError: + return False + return True + return False + + +class AuditHandler(SimpleHTTPRequestHandler): + def send_head(self): + path = Path(self.translate_path(self.path)) + if path.is_dir() and urlsplit(self.path).path.endswith("/"): + path = path / "index.html" + compressible = path.suffix.lower() in { + ".html", ".css", ".js", ".json", ".geojson", ".svg", ".xml", ".txt" + } + if not (compressible and path.is_file() and accepts_gzip(self.headers.get("Accept-Encoding", ""))): + return super().send_head() + try: + payload = gzip.compress(path.read_bytes(), compresslevel=6, mtime=0) + except OSError: + return super().send_head() + self.send_response(200) + self.send_header("Content-Type", self.guess_type(str(path))) + self.send_header("Content-Encoding", "gzip") + self.send_header("Vary", "Accept-Encoding") + self.send_header("Content-Length", str(len(payload))) + self.end_headers() + return io.BytesIO(payload) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--directory", default=".tmp/site", type=Path) + parser.add_argument("--port", default=4174, type=int) + args = parser.parse_args() + if not args.directory.is_dir(): + parser.error("Build the site before starting the audit server.") + handler = partial(AuditHandler, directory=str(args.directory.resolve())) + with ThreadingHTTPServer(("127.0.0.1", args.port), handler) as server: + server.serve_forever() diff --git a/scripts/validate-communities.py b/scripts/validate-communities.py index 95814a68..003b31dc 100644 --- a/scripts/validate-communities.py +++ b/scripts/validate-communities.py @@ -13,7 +13,7 @@ from datetime import date from pathlib import Path from typing import Any -from urllib.parse import urlparse +from urllib.parse import urlparse, quote ROOT = Path(__file__).resolve().parents[1] @@ -590,8 +590,8 @@ def contact_check_label(contacts: list[dict[str, Any]]) -> str: if health == {"verified"}: checked_dates = {contact["last_checked"] for contact in contacts} if len(checked_dates) == 1: - return f"Verified on {checked_dates.pop()}" - return "All links verified" + return f"Links checked on {checked_dates.pop()}" + return "All links checked" if "verified" in health: return "Some links still need review" return "Not yet verified" @@ -648,7 +648,7 @@ def render_directory_card(community: dict[str, Any], page: dict[str, Any]) -> st f'{html.escape(page["title"].title())}

                                ' ), f"

                                Settings: {render_settings(community, compact=True)}

                                ", - f"

                                Last verified: {verification_label(community)}

                                ", + f"

                                Listing reviewed: {verification_label(community)}

                                ", '
                                  ', *render_contacts(community), "
                                ", @@ -826,7 +826,7 @@ def render_community_card(community: dict[str, Any], metadata: dict[str, Any]) - "
                                ", "
                                Settings
                                ", f"
                                {render_settings(community)}
                                ", - "
                                Last verified
                                ", + "
                                Listing reviewed
                                ", f"
                                {verification_label(community)}
                                ", "
                                ", ] @@ -876,7 +876,7 @@ def render_community_card(community: dict[str, Any], metadata: dict[str, Any]) - lines.extend( [ ( - '

                                ' + f'

                                ' "Update this listing

                                " ), "", @@ -1155,7 +1155,7 @@ def contact_check_label_fr(contacts: list[dict[str, Any]]) -> str: checked_dates = {contact["last_checked"] for contact in contacts} if len(checked_dates) == 1: return f"Effectuée le {checked_dates.pop()}" - return "Tous les liens sont vérifiés" + return "Tous les liens ont été vérifiés" if "verified" in health: return "Certains liens restent à vérifier" return "Pas encore effectuée" @@ -1219,7 +1219,7 @@ def render_directory_card_fr( f'{html.escape(page_translation["title"])}

                                ' ), f"

                                Réglages : {render_settings_fr(community, compact=True)}

                                ", - f"

                                Dernière vérification : {verification_label_fr(community)}

                                ", + f"

                                Fiche révisée : {verification_label_fr(community)}

                                ", '
                                  ', *render_contacts_fr(community), "
                                ", @@ -1435,7 +1435,7 @@ def render_community_card_fr( '
                                ', "
                                Réglages
                                ", f"
                                {render_settings_fr(community)}
                                ", - "
                                Dernière vérification
                                ", + "
                                Fiche révisée
                                ", f"
                                {verification_label_fr(community)}
                                ", "
                                ", ] @@ -1491,7 +1491,7 @@ def render_community_card_fr( lines.extend( [ ( - '

                                ' + f'

                                ' "Mettre cette fiche à jour

                                " ), "", @@ -1645,6 +1645,16 @@ def render_province_page_fr( def generated_pages(data: dict[str, Any], french: dict[str, Any]) -> dict[Path, str]: pages = {PROVINCES_DIR / "index.md": render_index(data)} + profiles = [{"id": "canada", "name": "Canada default", "name_fr": "Réglages canadiens par défaut", + "radio": data["national_defaults"]["raw_radio"], "route": "provinces/#canada-baseline"}] + for community in data["communities"]: + radio = community["settings"]["overrides"].get("raw_radio") + if radio and community.get("verified_at"): + profiles.append({"id": community["id"], "name": community["name"], + "name_fr": community["name"], "radio": radio, + "route": community["canonical_route"].lstrip("/"), + "reviewed": community["verified_at"]}) + pages[ROOT / "docs/assets/radio-profiles.json"] = json.dumps(profiles, ensure_ascii=False, indent=2) for page in data["directory_pages"]: pages[PROVINCES_DIR / f"{page['slug']}.md"] = render_province_page(data, page) pages[PROVINCES_DIR / "index.fr.md"] = render_index_fr(data, french) diff --git a/tests/browser/audit-regressions.spec.mjs b/tests/browser/audit-regressions.spec.mjs new file mode 100644 index 00000000..c50e75fe --- /dev/null +++ b/tests/browser/audit-regressions.spec.mjs @@ -0,0 +1,233 @@ +import { expect, test } from "@playwright/test"; +import AxeBuilder from "@axe-core/playwright"; +import { siteRoute } from "./site-route.mjs"; +import { submissionSha256 } from "../../docs/config/editor/issue.js"; + +for (const locale of ["", "fr/"]) { + test(`${locale || "en/"} region setup preserves radio until explicitly selected`, async ({ page }) => { + await page.goto(siteRoute(`/${locale}config/?tag=mvrd&step=4`)); + const result = page.locator('[data-role="result"]'); + await expect(result).toContainText("region def can bc mvrd"); + await expect(result).not.toContainText("set radio"); + await expect(result).not.toContainText("set path.hash.mode"); + await page.locator('[data-go-step="3"]').click(); + await page.locator("#mcc-radio-profile").selectOption("bc-mesh"); + await page.locator("#mcc-hash-mode").selectOption("2"); + await expect(page.locator('[data-action="view-map"]').first()).toHaveAttribute("href", /radio=bc-mesh&hash=2/); + await page.locator('[data-wizard-step="3"] [data-next-step]').click(); + await expect(result).toContainText("set radio 910.425,62.5,7,5"); + await expect(result).toContainText("set path.hash.mode 2"); + await expect(page.locator('[data-role="review-summary"]')).toContainText("910.425"); + await page.locator('[data-go-step="3"]').click(); + await page.locator("#mcc-radio-profile").selectOption("canada"); + await page.locator('[data-wizard-step="3"] [data-next-step]').click(); + await expect(result).toContainText("set radio 910.525,62.5,7,5"); + }); + + test(`${locale || "en/"} map handoff retains shared and extra region paths`, async ({ page }) => { + await page.goto(siteRoute(`/${locale}config/map/?tag=ott&type=large®ions=mtl&firmware=1.15`)); + const link = page.locator('[data-role="map-text-result"] .mcc-detail-actions a'); + await expect(link).toBeVisible(); + await link.click(); + await expect(page.locator('[data-role="selected-region"]')).toContainText("Ottawa"); + await page.locator('[data-wizard-step="3"] [data-next-step]').click(); + const result = page.locator('[data-role="result"]'); + await expect(result).toContainText("region put ott"); + await expect(result).toContainText("region put gatout"); + await expect(result).toContainText("region put mtl"); + await expect(result).not.toContainText("set radio"); + }); + + test(`${locale || "en/"} invalid saved location offers recovery instead of commands`, async ({ page }) => { + await page.goto(siteRoute(`/${locale}config/?tag=unknown&lat=999&lon=0`)); + await expect(page.locator('[data-role="status"]')).toHaveText(/invalid|invalide/); + await expect(page.locator('[data-go-step="4"]')).toBeDisabled(); + }); + + test(`${locale || "en/"} map loads when visible and its keyboard shortcut stays out of the way`, async ({ page }, testInfo) => { + const displayRequests = []; + page.on("request", (request) => { + if (request.url().endsWith("/canada-region-partition.geojson")) displayRequests.push(request.url()); + }); + await page.route("https://tile.openstreetmap.org/**", (route) => route.fulfill({ + contentType: "image/png", + body: Buffer.from("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=", "base64") + })); + await page.goto(siteRoute(`/${locale}config/map/?tag=ott`)); + await expect(page.locator('[data-role="map-status"]')).toHaveText(locale ? "Région trouvée." : "Region found."); + const shortcut = page.locator(".mcc-skip-map"); + await expect(shortcut).toHaveCSS("pointer-events", "none"); + await expect(page.locator('[data-role="map-region-table"]')).toBeEmpty(); + const panel = page.locator(".mcc-map-panel"); + expect(await panel.evaluate((element) => element.scrollHeight <= element.clientHeight + 1)).toBeTruthy(); + if (testInfo.project.name.startsWith("mobile-")) { + await page.evaluate(() => new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve)))); + expect(displayRequests).toHaveLength(0); + } + await page.locator(".mcc-map-stage").scrollIntoViewIfNeeded(); + await expect(page.locator('[data-role="map-loading"]')).toBeHidden(); + expect(displayRequests).toHaveLength(1); + const marker = page.locator(".leaflet-marker-icon"); + await expect(marker).toBeVisible(); + await expect(marker).toHaveAttribute("src", /\/vendor\/leaflet\/images\/marker-icon(?:-2x)?\.png$/); + await expect.poll(() => marker.evaluate((image) => image.naturalWidth)).toBeGreaterThan(0); + await shortcut.focus(); + await expect(shortcut).toHaveCSS("opacity", "1"); + await expect(shortcut).toHaveCSS("pointer-events", "auto"); + const contrast = await new AxeBuilder({ page }).include(".mcc-map-stage").withRules(["color-contrast"]).analyze(); + expect(contrast.violations).toEqual([]); + await shortcut.press("Enter"); + await expect(page.locator("#mcc-region-list")).toHaveAttribute("open", ""); + await expect(page.locator('[data-role="table-filter"]')).toBeVisible(); + }); + + for (const scheme of ["default", "slate"]) { + test(`${locale || "en/"} ${scheme} focused map controls remain readable`, async ({ page }) => { + await page.goto(siteRoute(`/${locale}config/map/?tag=ott`)); + await expect(page.locator('[data-role="map-text-result"]')).toContainText("Ottawa"); + await page.locator(`input[data-md-color-scheme="${scheme}"]`).evaluate((element) => element.click()); + const find = page.locator('[data-action="map-locate"]'); + await find.focus(); + await find.hover(); + const results = await new AxeBuilder({ page }).include("[data-mcc-regions]").withRules(["color-contrast"]).analyze(); + expect(results.violations).toEqual([]); + const background = await page.locator(".md-main").evaluate((element) => getComputedStyle(element).backgroundColor); + expect(background).toBe(scheme === "default" ? "rgb(255, 255, 255)" : "rgb(15, 22, 35)"); + }); + } + + test(`${locale || "en/"} broker reference works without JavaScript`, async ({ browser }) => { + const context = await browser.newContext({ javaScriptEnabled: false }); + try { + const page = await context.newPage(); + await page.goto(siteRoute(`/${locale}analyzer/broker-reference/`)); + await expect(page.locator("#broker-reference-body tr")).toHaveCount(2); + await expect(page.locator("#broker-reference-body")).toContainText("mqtt1.meshcore.ca"); + await expect(page.locator('article a[href*="data-collection-access/#read-only-mqtt-accounts"]')).toBeVisible(); + } finally { await context.close(); } + }); +} + +test("language switch keeps selected region, firmware, radio, and review step", async ({ page }) => { + await page.goto(siteRoute("/config/?tag=mvrd&step=4&firmware=1.15&radio=bc-mesh&hash=2")); + await expect(page.locator('[data-role="result"]')).toContainText("set radio 910.425"); + await page.locator(".md-select > button").click(); + await page.locator('.md-select__link[hreflang="fr"]').click(); + await expect(page.locator('[data-role="result"]')).toContainText("set radio 910.425"); + await expect(page.locator('[data-role="result"]')).toContainText("region put mvrd bc"); + await expect(page.locator('[data-wizard-step="4"]')).toBeVisible(); + await expect(page.locator("html")).toHaveAttribute("lang", "fr"); +}); + +test("map tiles appear while a slow boundary overlay is still loading", async ({ page }) => { + let releaseBoundary; + const boundaryGate = new Promise(resolve => { releaseBoundary = resolve; }); + await page.route("**/canada-region-partition.geojson", async route => { + await boundaryGate; + await route.continue(); + }); + await page.route("https://tile.openstreetmap.org/**", route => route.fulfill({ + contentType: "image/png", + body: Buffer.from("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=", "base64") + })); + try { + await page.goto(siteRoute("/config/map/?tag=ott")); + await page.locator(".mcc-map-stage").scrollIntoViewIfNeeded(); + await expect(page.locator(".leaflet-tile-loaded").first()).toBeVisible(); + await expect(page.locator('[data-role="map-loading"]')).toBeHidden(); + await expect(page.locator('[data-role="map-boundary-status"]')).toBeVisible(); + releaseBoundary(); + await expect(page.locator(".mcc-map-stage")).toHaveAttribute("aria-busy", "false"); + await expect(page.locator('[data-role="map-boundary-status"]')).toBeHidden(); + } finally { releaseBoundary(); } +}); + +test("map retry restores the selected marker after a tile failure", async ({ page }) => { + let tilesAvailable = false; + await page.route("https://tile.openstreetmap.org/**", route => tilesAvailable ? route.fulfill({ + contentType: "image/png", + body: Buffer.from("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=", "base64") + }) : route.abort()); + await page.goto(siteRoute("/config/map/?tag=ott")); + await page.locator(".mcc-map-stage").scrollIntoViewIfNeeded(); + const retry = page.locator('[data-action="load-map"]'); + await expect(retry).toBeVisible(); + tilesAvailable = true; + await retry.click(); + await expect(page.locator(".mcc-map-stage")).toHaveAttribute("aria-busy", "false"); + await expect(page.locator(".leaflet-marker-icon")).toBeVisible(); + await expect(page.locator('[data-role="map-text-result"]')).toContainText("Ottawa"); +}); + +test("Québec partial matches reach city search and true ambiguity offers buttons", async ({ page }) => { + let lookedUp = false; + await page.route("https://nominatim.openstreetmap.org/**", async (route) => { + lookedUp = true; + await route.fulfill({ json: [{ lat: "46.8139", lon: "-71.2080", display_name: "Québec, Québec, Canada", address: { country_code: "ca", state: "Quebec" } }] }); + }); + await page.goto(siteRoute("/fr/config/map/")); + const input = page.locator('[data-role="map-input"]'); + await input.fill("Québec"); + await page.locator('[data-action="map-locate"]').click(); + await expect(page.locator('[data-role="map-text-result"]')).toContainText("Québec"); + expect(lookedUp).toBeTruthy(); + await input.fill("Victoria"); + await page.locator('[data-action="map-locate"]').click(); + const choices = page.locator('[data-role="map-status"] button'); + await expect(choices.first()).toBeVisible(); + expect(await choices.count()).toBeGreaterThan(1); + await choices.first().click(); + await expect(page.locator('[data-role="map-text-result"]')).toBeVisible(); +}); + +test("feedback remembers its page and accepts a title and description", async ({ page }) => { + await page.goto(siteRoute("/start/repeater/")); + await page.locator(".mc-page-feedback a").click(); + await expect(page.locator('input[name="source_page"]')).toHaveValue("https://meshcore.ca/start/repeater/"); + await page.locator("#submission-summary").fill("Clarify one instruction"); + await page.locator("#submission-need").fill("The step needs a clearer example."); + await page.locator("#submission-public").check(); + await page.locator("#review-submission").click(); + await expect(page.locator("#submission-preview")).toContainText("https://meshcore.ca/start/repeater/"); + await expect(page.locator("#submission-error-summary")).toBeHidden(); +}); + +for (const locale of ["", "fr/"]) { + for (const modernGateway of [true, false]) { + test(`${locale || "en/"} short feedback ${modernGateway ? "submits to a compatible gateway" : "offers a safe older-gateway fallback"}`, async ({ page }) => { + let posted = null; + await page.addInitScript(() => { + window.turnstile = { render(_container, options) { setTimeout(() => options.callback("fixture-token"), 0); return "fixture"; }, reset() {} }; + }); + await page.route("https://api.meshcore.ca:21323/api/meshcore-canada/submissions**", async (route) => { + const request = route.request(); + if (request.method() === "GET") return route.fulfill({ json: { + version: 1, turnstileSiteKey: "fixture-site-key", turnstileAction: "meshcore_submission", + ...(modernGateway ? { communityIdeaOptionalDetails: true } : {}) + } }); + posted = request.postDataJSON().submission; + return route.fulfill({ json: { ok: true, issueNumber: 123, issueUrl: "https://github.com/MeshCore-ca/MeshCore-Canada/issues/123", + submissionSha256: await submissionSha256(posted), duplicate: false } }); + }); + await page.goto(siteRoute(`/${locale}start/repeater/`)); + await page.locator(".mc-page-feedback a").click(); + await expect(page.locator('input[name="source_page"]')).toHaveValue(`https://meshcore.ca/${locale}start/repeater/`); + await page.locator("#submission-summary").fill("Clarify one instruction"); + await page.locator("#submission-need").fill("The step needs a clearer example."); + await page.locator("#submission-public").check(); + await page.locator("#review-submission").click(); + if (modernGateway) { + await page.locator("#submit-community-idea").click(); + await expect(page.locator("#submission-result")).toHaveAttribute("data-state", "success"); + expect(posted.experience).toBe(""); + expect(posted.idea).toBe(""); + expect(posted.sourcePage).toBe(`https://meshcore.ca/${locale}start/repeater/`); + } else { + await expect(page.locator("#submission-anti-spam-status")).toHaveAttribute("data-state", "error"); + await expect(page.locator("#submit-community-idea")).toBeDisabled(); + await expect(page.locator("#open-github-submission")).toHaveAttribute("href", /github.com/); + expect(posted).toBeNull(); + } + }); + } +} diff --git a/tests/browser/critical-journeys.spec.mjs b/tests/browser/critical-journeys.spec.mjs index 800d78c0..17f1b834 100644 --- a/tests/browser/critical-journeys.spec.mjs +++ b/tests/browser/critical-journeys.spec.mjs @@ -244,6 +244,7 @@ test("region map loads OpenStreetMap tiles without a consent gate", async ({ pag await page.goto(siteRoute("/config/map/"), { waitUntil: "domcontentloaded" }); await expect(page.locator("[data-action='tile-consent']")).toHaveCount(0); await expect(page.getByText(/Allow OpenStreetMap tiles/i)).toHaveCount(0); + await page.locator(".mcc-map-stage").scrollIntoViewIfNeeded(); await expect(page.locator("[data-role='map-area']")).toBeVisible(); await expect(page.locator("[data-role='map-loading']")).toBeHidden(); await expect(page.locator(".leaflet-tile-loaded").first()).toBeVisible(); diff --git a/tests/content/analyzer-journey.test.mjs b/tests/content/analyzer-journey.test.mjs index a5d250f0..76f76aac 100644 --- a/tests/content/analyzer-journey.test.mjs +++ b/tests/content/analyzer-journey.test.mjs @@ -129,6 +129,8 @@ test("privacy pages state ownership, access, the account inventory, and the unkn for (const service of [ "Beacon (`dev.meshcore.ca`)", "CoreScope (`live.meshcore.ca`)", + "[Canadaverse](https://canadaverse.org/)", + "[n30nex@gmail.com](mailto:n30nex@gmail.com)", "[Quinte Mesh](https://quintemesh.ca/)", ]) { assert.ok(source.includes(service), `English inventory missing ${service}`); @@ -136,6 +138,8 @@ test("privacy pages state ownership, access, the account inventory, and the unkn } assert.ok(source.includes("hansimgamr"), "English inventory missing the Quinte Mesh operator"); assert.ok(french.includes("hansimgamr"), "French inventory missing the Quinte Mesh operator"); + assert.ok(source.includes("GTA Regional Tools & CartoLive map"), "English inventory missing the Canadaverse purpose"); + assert.ok(french.includes("Outils régionaux du Grand Toronto et carte CartoLive"), "French inventory missing the Canadaverse purpose"); assert.match(source, /update this table whenever they create or remove a read-only account/i); assert.match(french, /mettre ce tableau à jour chaque\s+fois qu’ils créent ou retirent un compte en lecture seule/i); }); @@ -160,7 +164,7 @@ test("broker reference lists every administrator and public contact in both lang test("verification distinguishes connectivity from an observed packet", () => { const source = read("docs/analyzer/verify.md"); - assert.match(source, /broker connection proves only/i); + assert.match(source, /Connected does not mean packets have reached CoreScope/i); assert.match(source, /Follow a packet through four stages/); for (const stage of ["Radio:", "Observer:", "Observer view:", "Packet view:"]) { assert.match(source, new RegExp(stage)); @@ -236,7 +240,7 @@ test("standalone builder is external, fail-closed, redacted, and non-persistent" const script = read("docs/assets/javascripts/analyzer-command-builder.js"); assert.doesNotMatch(page, /