Skip to content

Commit bf9a4c0

Browse files
wilhel1812heigreclaude
authored
release: 0.18.0 (#769)
* ui: align sidebar heading with Path terminology (#411) (#434) * chore: reconcile main into staging (2026-04-06 refactor prep) (#437) * fix: unblock blank simulation workflows (#179) * fix: persist blank simulation load session state * fix: allow blank simulations to continue in workspace * fix: keep blank load session persistence main-compatible * chore: bump release version to 0.10.3 * release: v0.11.0 (#214) * Deep link V2 + FastAPI calculation (#192) * fix: align multi-site overlays with radial terrain loading * fix: remove relay overlay bounding box artifact * fix: update profile when selected sites change * fix: clear map selection before add-site and default zero-select heatmap * fix: persist blank simulation load session state * fix: allow blank simulations to continue in workspace * fix: prevent fallback link after deselect and gate profile to two sites * fix: remove fallback profile link message and stale first-link fallback * fix: preserve deselect state and sync profile cursor on direction flip * fix: apply buffered overlay mask when no sites selected * fix: use simulation-wide boundary for overlays and terrain loading * fix: remove manual terrain controls and online elevation sync * sidebar: text overhaul per issue #184 * sidebar: remove (saved) label and helper line * sidebar: combine simulation name with heading * heatmap: default to contours and allow selection when no sites selected * heatmap: default to contours instead of smooth * Implement shorter deeplinks (issue #144) - New URL format: /<simulation>/<site>+<site2> or /<simulation>/<site1><><site2> - Keep Unicode characters (æøå) in URLs - Strip delimiter chars (+ < > /) from slugs - Backward compatible with old ?dl=1 format * Fix deeplinks: preserve case, remove query params, no encoding - Keep original case in URLs (Blefjell not blefjell) - Remove dl=2 and sim=xxx query params for clean URLs - Build URL manually to prevent <> encoding to %3C%3E - V2 format detected by path presence * Fix: preserve <> in URLs, encode unicode chars - Use encodeURIComponent but keep <> unencoded - Unicode chars like ø encode to %C3%B8 (browsers auto-decode) - <> stays as <> (not %3C%3E) * Fix: actually decode %3C and %3E back to <> * Fix: don't re-encode URL when copying share link * Fix: don't encode URLs - preserve emojis and unicode as-is * feat: add FastAPI calculation API with hardened Docker target * chore: ignore Python cache artifacts * feat: add configurable rate limiting to calculation API * feat(api): proxy calculation endpoint through Pages * Fix: use slugifyName for slug matching instead of toLowerCase * Add debug logging for deep link processing * Fix: decode URL-encoded slugs before matching - Decode simulation slug with decodeURIComponent before slugifyName - Decode site/link slugs before matching - Remove debug logging * Fix deep-link resolution for encoded path slugs Decode URL-encoded path segments and use canonical slug matching so shared Simulation links resolve reliably after hydration for ASCII, Unicode, and emoji names. * Use tilde delimiter in generated deep links Switch link delimiter generation to tilde to avoid browser percent-encoding in the address bar, keep legacy angle-bracket parsing support, and normalize emoji variation selectors for cleaner URLs. * feat(api): cut over calculate endpoint to app terrain logic * docs: add deep-link format specification --------- Co-authored-by: Mats <heigre@protonmail.com> * fix(deeplink): only encode link paths when a link is explicitly selected (#193) * fix(deeplink): apply multi-site selections deterministically (#194) * fix: unblock blank simulation workflows (#179) (#195) * fix: persist blank simulation load session state * fix: allow blank simulations to continue in workspace * fix: keep blank load session persistence main-compatible * chore: bump release version to 0.10.3 * docs: add staging-first milestone workflow to AGENTS.md (#196) * fix(deeplink): only encode link paths when a link is explicitly selected * fix(deeplink): apply multi-site selections deterministically * docs: add staging-first milestone workflow to AGENTS.md * feat(api): proxy calculation endpoint through Pages (#197) Co-authored-by: Mats <heigre@protonmail.com> * Revert "feat(api): proxy calculation endpoint through Pages (#197)" (#198) This reverts commit 0138c1bd1aaa8f6adc5fd26f2512540ea5593f8d. * Restore native terrain API optimization line from pre-regression commits (#199) * feat(api): add async terrain jobs endpoints and request guardrails * feat(api): process terrain jobs asynchronously with status updates * feat(api): compute terrain job results from sampled elevations * feat(api): use Copernicus terrain tiles via proxy for terrain jobs * fix(api): correct Copernicus tile naming and bucket paths * fix: fallback to Open-Meteo if Copernicus tiles fail * Revert "fix: fallback to Open-Meteo if Copernicus tiles fail" This reverts commit c50820f429dc0558743277a79aa11450090a8ef5. * refactor(api): share app propagation logic across calculate endpoints * fix(api): resolve Copernicus tile paths from tileList directories * docs(api): document Copernicus-backed terrain analysis path * feat(api): cut over calculate endpoint to app terrain logic * fix(api): stabilize terrain calculate and reduce tile decode load * Enforce async jobs path for terrain calculate requests (#200) Route terrain-mode requests from /api/v1/calculate to /api/v1/calculate/jobs and return queued 202 responses, keeping sync calculate for non-terrain mode while preventing CPU-heavy terrain sync execution. * Route terrain sync calculate requests to async jobs (#201) Enforce jobs-first terrain execution by delegating mode=terrain requests on /api/v1/calculate to the async jobs queue path and returning 202 queued responses, preventing CPU-heavy synchronous terrain runs. * Fix deep-link sharing to prefer actual site selection (#202) Clear stale selectedLinkId when selecting sites and only emit link-style share URLs when the selected sites exactly match the active link endpoints. * Fix deep-link unicode matching for emoji and Korean names (#203) Preserve Unicode and emoji semantics in slug/canonical key generation, avoid ASCII-only canonical collapse, and add emoji/Korean deep-link regression coverage. * Normalize emoji variation selectors in deep-link keys (#204) Treat FE0E/FE0F variants consistently in slug and canonical key generation so emoji deep links resolve to the correct Site regardless of URL serialization differences. * Harden deep-link site matching for emoji variations (#205) Normalize variation selectors in generated path segments and require full site slug resolution before applying selections so emoji/Korean deep links do not silently fall back to incorrect default selections. * Harden deep-link matching order for emoji/unicode selections (#206) Use exact-normalized matching before slug/canonical fallback for site/link deep-link selection and clear active selection when deep-link resolution is partial, preventing silent fallback to default first site/link. * Add deep-link debug instrumentation for selection apply (#207) Add temporary DeepLinkDebug console logs around payload parsing, slug matching, and post-apply selection state to trace why some deep links fall back to first-site selection on staging. * Apply deep-link selections after init cycle settles (#208) Prevent startup hydration from overwriting deep-link site/link selections by deferring deep-link application until the initializeCloudSync cycle has been observed and settled. * chore(deeplink): remove temporary DeepLinkDebug instrumentation (#209) * feat(onboarding): merge onboarding with simulation library into welcome modal (#152) (#210) - Rewrite onboarding content: ctrl/cmd-click links, auto-overlay switching, simplified sharing - Extract SimulationLibraryPanel reusable component from Sidebar - Create WelcomeModal: merged onboarding + simulation library - First visit: WelcomeModal opens with onboarding collapsed + library visible - Deep-link visits skip the welcome modal entirely - Returning users with no simulation auto-open the Simulation Library - ? button opens WelcomeModal with onboarding expanded - Delete old OnboardingTutorialModal * refactor(onboarding): compact welcome card, simple onboarding modal (#152) (#211) - Welcome modal: compact card with 3 action buttons (not full-screen) - Onboarding: restored as simple text modal (no library, no collapsible) - OS detection: Cmd on Mac, Ctrl elsewhere via {{MODIFIER}} placeholder - Simulation Library button in onboarding text via custom ReactMarkdown component - Trimmed onboarding: removed basemap providers, workflow, roles, security note - Added showNewSimulationRequest store state to trigger New Simulation from welcome * refactor(onboarding): inline links for Simulation Library and Site Library (#152) (#212) * feat(deeplink): live update browser URL to match current share link (#190) (#213) * fix: unblock blank simulation workflows (#179) * fix: persist blank simulation load session state * fix: allow blank simulations to continue in workspace * fix: keep blank load session persistence main-compatible * chore: bump release version to 0.10.3 * feat(deeplink): live update browser URL to match current share link (#190) Add buildDeepLinkPathname helper for pathname-only computation. Sync browser URL bar via history.replaceState when simulation or site selection changes. Clears stale deeplink when no simulation is active. Skips sync until initial deeplink resolution settles. Includes drift reconcile (0863752: unblock blank simulation workflows). * release: prepare v0.11.0 changelog and metadata * fix(api): restore no-store headers for profile and public simulation * chore: retrigger release checks --------- Co-authored-by: Mats <heigre@protonmail.com> * ci: add workflow to require milestone on completed issue close (#217) * release: v0.12.0 (#280) * Deep link V2 + FastAPI calculation (#192) * fix: align multi-site overlays with radial terrain loading * fix: remove relay overlay bounding box artifact * fix: update profile when selected sites change * fix: clear map selection before add-site and default zero-select heatmap * fix: persist blank simulation load session state * fix: allow blank simulations to continue in workspace * fix: prevent fallback link after deselect and gate profile to two sites * fix: remove fallback profile link message and stale first-link fallback * fix: preserve deselect state and sync profile cursor on direction flip * fix: apply buffered overlay mask when no sites selected * fix: use simulation-wide boundary for overlays and terrain loading * fix: remove manual terrain controls and online elevation sync * sidebar: text overhaul per issue #184 * sidebar: remove (saved) label and helper line * sidebar: combine simulation name with heading * heatmap: default to contours and allow selection when no sites selected * heatmap: default to contours instead of smooth * Implement shorter deeplinks (issue #144) - New URL format: /<simulation>/<site>+<site2> or /<simulation>/<site1><><site2> - Keep Unicode characters (æøå) in URLs - Strip delimiter chars (+ < > /) from slugs - Backward compatible with old ?dl=1 format * Fix deeplinks: preserve case, remove query params, no encoding - Keep original case in URLs (Blefjell not blefjell) - Remove dl=2 and sim=xxx query params for clean URLs - Build URL manually to prevent <> encoding to %3C%3E - V2 format detected by path presence * Fix: preserve <> in URLs, encode unicode chars - Use encodeURIComponent but keep <> unencoded - Unicode chars like ø encode to %C3%B8 (browsers auto-decode) - <> stays as <> (not %3C%3E) * Fix: actually decode %3C and %3E back to <> * Fix: don't re-encode URL when copying share link * Fix: don't encode URLs - preserve emojis and unicode as-is * feat: add FastAPI calculation API with hardened Docker target * chore: ignore Python cache artifacts * feat: add configurable rate limiting to calculation API * feat(api): proxy calculation endpoint through Pages * Fix: use slugifyName for slug matching instead of toLowerCase * Add debug logging for deep link processing * Fix: decode URL-encoded slugs before matching - Decode simulation slug with decodeURIComponent before slugifyName - Decode site/link slugs before matching - Remove debug logging * Fix deep-link resolution for encoded path slugs Decode URL-encoded path segments and use canonical slug matching so shared Simulation links resolve reliably after hydration for ASCII, Unicode, and emoji names. * Use tilde delimiter in generated deep links Switch link delimiter generation to tilde to avoid browser percent-encoding in the address bar, keep legacy angle-bracket parsing support, and normalize emoji variation selectors for cleaner URLs. * feat(api): cut over calculate endpoint to app terrain logic * docs: add deep-link format specification --------- Co-authored-by: Mats <heigre@protonmail.com> * fix(deeplink): only encode link paths when a link is explicitly selected (#193) * fix(deeplink): apply multi-site selections deterministically (#194) * fix: unblock blank simulation workflows (#179) (#195) * fix: persist blank simulation load session state * fix: allow blank simulations to continue in workspace * fix: keep blank load session persistence main-compatible * chore: bump release version to 0.10.3 * docs: add staging-first milestone workflow to AGENTS.md (#196) * fix(deeplink): only encode link paths when a link is explicitly selected * fix(deeplink): apply multi-site selections deterministically * docs: add staging-first milestone workflow to AGENTS.md * feat(api): proxy calculation endpoint through Pages (#197) Co-authored-by: Mats <heigre@protonmail.com> * Revert "feat(api): proxy calculation endpoint through Pages (#197)" (#198) This reverts commit 0138c1bd1aaa8f6adc5fd26f2512540ea5593f8d. * Restore native terrain API optimization line from pre-regression commits (#199) * feat(api): add async terrain jobs endpoints and request guardrails * feat(api): process terrain jobs asynchronously with status updates * feat(api): compute terrain job results from sampled elevations * feat(api): use Copernicus terrain tiles via proxy for terrain jobs * fix(api): correct Copernicus tile naming and bucket paths * fix: fallback to Open-Meteo if Copernicus tiles fail * Revert "fix: fallback to Open-Meteo if Copernicus tiles fail" This reverts commit c50820f429dc0558743277a79aa11450090a8ef5. * refactor(api): share app propagation logic across calculate endpoints * fix(api): resolve Copernicus tile paths from tileList directories * docs(api): document Copernicus-backed terrain analysis path * feat(api): cut over calculate endpoint to app terrain logic * fix(api): stabilize terrain calculate and reduce tile decode load * Enforce async jobs path for terrain calculate requests (#200) Route terrain-mode requests from /api/v1/calculate to /api/v1/calculate/jobs and return queued 202 responses, keeping sync calculate for non-terrain mode while preventing CPU-heavy terrain sync execution. * Route terrain sync calculate requests to async jobs (#201) Enforce jobs-first terrain execution by delegating mode=terrain requests on /api/v1/calculate to the async jobs queue path and returning 202 queued responses, preventing CPU-heavy synchronous terrain runs. * Fix deep-link sharing to prefer actual site selection (#202) Clear stale selectedLinkId when selecting sites and only emit link-style share URLs when the selected sites exactly match the active link endpoints. * Fix deep-link unicode matching for emoji and Korean names (#203) Preserve Unicode and emoji semantics in slug/canonical key generation, avoid ASCII-only canonical collapse, and add emoji/Korean deep-link regression coverage. * Normalize emoji variation selectors in deep-link keys (#204) Treat FE0E/FE0F variants consistently in slug and canonical key generation so emoji deep links resolve to the correct Site regardless of URL serialization differences. * Harden deep-link site matching for emoji variations (#205) Normalize variation selectors in generated path segments and require full site slug resolution before applying selections so emoji/Korean deep links do not silently fall back to incorrect default selections. * Harden deep-link matching order for emoji/unicode selections (#206) Use exact-normalized matching before slug/canonical fallback for site/link deep-link selection and clear active selection when deep-link resolution is partial, preventing silent fallback to default first site/link. * Add deep-link debug instrumentation for selection apply (#207) Add temporary DeepLinkDebug console logs around payload parsing, slug matching, and post-apply selection state to trace why some deep links fall back to first-site selection on staging. * Apply deep-link selections after init cycle settles (#208) Prevent startup hydration from overwriting deep-link site/link selections by deferring deep-link application until the initializeCloudSync cycle has been observed and settled. * chore(deeplink): remove temporary DeepLinkDebug instrumentation (#209) * feat(onboarding): merge onboarding with simulation library into welcome modal (#152) (#210) - Rewrite onboarding content: ctrl/cmd-click links, auto-overlay switching, simplified sharing - Extract SimulationLibraryPanel reusable component from Sidebar - Create WelcomeModal: merged onboarding + simulation library - First visit: WelcomeModal opens with onboarding collapsed + library visible - Deep-link visits skip the welcome modal entirely - Returning users with no simulation auto-open the Simulation Library - ? button opens WelcomeModal with onboarding expanded - Delete old OnboardingTutorialModal * refactor(onboarding): compact welcome card, simple onboarding modal (#152) (#211) - Welcome modal: compact card with 3 action buttons (not full-screen) - Onboarding: restored as simple text modal (no library, no collapsible) - OS detection: Cmd on Mac, Ctrl elsewhere via {{MODIFIER}} placeholder - Simulation Library button in onboarding text via custom ReactMarkdown component - Trimmed onboarding: removed basemap providers, workflow, roles, security note - Added showNewSimulationRequest store state to trigger New Simulation from welcome * refactor(onboarding): inline links for Simulation Library and Site Library (#152) (#212) * feat(deeplink): live update browser URL to match current share link (#190) (#213) * fix: unblock blank simulation workflows (#179) * fix: persist blank simulation load session state * fix: allow blank simulations to continue in workspace * fix: keep blank load session persistence main-compatible * chore: bump release version to 0.10.3 * feat(deeplink): live update browser URL to match current share link (#190) Add buildDeepLinkPathname helper for pathname-only computation. Sync browser URL bar via history.replaceState when simulation or site selection changes. Clears stale deeplink when no simulation is active. Skips sync until initial deeplink resolution settles. Includes drift reconcile (0863752: unblock blank simulation workflows). * chore: back-merge main into staging after v0.11.0 release (#215) * fix: unblock blank simulation workflows (#179) * fix: persist blank simulation load session state * fix: allow blank simulations to continue in workspace * fix: keep blank load session persistence main-compatible * chore: bump release version to 0.10.3 * release: v0.11.0 (#214) * Deep link V2 + FastAPI calculation (#192) * fix: align multi-site overlays with radial terrain loading * fix: remove relay overlay bounding box artifact * fix: update profile when selected sites change * fix: clear map selection before add-site and default zero-select heatmap * fix: persist blank simulation load session state * fix: allow blank simulations to continue in workspace * fix: prevent fallback link after deselect and gate profile to two sites * fix: remove fallback profile link message and stale first-link fallback * fix: preserve deselect state and sync profile cursor on direction flip * fix: apply buffered overlay mask when no sites selected * fix: use simulation-wide boundary for overlays and terrain loading * fix: remove manual terrain controls and online elevation sync * sidebar: text overhaul per issue #184 * sidebar: remove (saved) label and helper line * sidebar: combine simulation name with heading * heatmap: default to contours and allow selection when no sites selected * heatmap: default to contours instead of smooth * Implement shorter deeplinks (issue #144) - New URL format: /<simulation>/<site>+<site2> or /<simulation>/<site1><><site2> - Keep Unicode characters (æøå) in URLs - Strip delimiter chars (+ < > /) from slugs - Backward compatible with old ?dl=1 format * Fix deeplinks: preserve case, remove query params, no encoding - Keep original case in URLs (Blefjell not blefjell) - Remove dl=2 and sim=xxx query params for clean URLs - Build URL manually to prevent <> encoding to %3C%3E - V2 format detected by path presence * Fix: preserve <> in URLs, encode unicode chars - Use encodeURIComponent but keep <> unencoded - Unicode chars like ø encode to %C3%B8 (browsers auto-decode) - <> stays as <> (not %3C%3E) * Fix: actually decode %3C and %3E back to <> * Fix: don't re-encode URL when copying share link * Fix: don't encode URLs - preserve emojis and unicode as-is * feat: add FastAPI calculation API with hardened Docker target * chore: ignore Python cache artifacts * feat: add configurable rate limiting to calculation API * feat(api): proxy calculation endpoint through Pages * Fix: use slugifyName for slug matching instead of toLowerCase * Add debug logging for deep link processing * Fix: decode URL-encoded slugs before matching - Decode simulation slug with decodeURIComponent before slugifyName - Decode site/link slugs before matching - Remove debug logging * Fix deep-link resolution for encoded path slugs Decode URL-encoded path segments and use canonical slug matching so shared Simulation links resolve reliably after hydration for ASCII, Unicode, and emoji names. * Use tilde delimiter in generated deep links Switch link delimiter generation to tilde to avoid browser percent-encoding in the address bar, keep legacy angle-bracket parsing support, and normalize emoji variation selectors for cleaner URLs. * feat(api): cut over calculate endpoint to app terrain logic * docs: add deep-link format specification --------- Co-authored-by: Mats <heigre@protonmail.com> * fix(deeplink): only encode link paths when a link is explicitly selected (#193) * fix(deeplink): apply multi-site selections deterministically (#194) * fix: unblock blank simulation workflows (#179) (#195) * fix: persist blank simulation load session state * fix: allow blank simulations to continue in workspace * fix: keep blank load session persistence main-compatible * chore: bump release version to 0.10.3 * docs: add staging-first milestone workflow to AGENTS.md (#196) * fix(deeplink): only encode link paths when a link is explicitly selected * fix(deeplink): apply multi-site selections deterministically * docs: add staging-first milestone workflow to AGENTS.md * feat(api): proxy calculation endpoint through Pages (#197) Co-authored-by: Mats <heigre@protonmail.com> * Revert "feat(api): proxy calculation endpoint through Pages (#197)" (#198) This reverts commit 0138c1bd1aaa8f6adc5fd26f2512540ea5593f8d. * Restore native terrain API optimization line from pre-regression commits (#199) * feat(api): add async terrain jobs endpoints and request guardrails * feat(api): process terrain jobs asynchronously with status updates * feat(api): compute terrain job results from sampled elevations * feat(api): use Copernicus terrain tiles via proxy for terrain jobs * fix(api): correct Copernicus tile naming and bucket paths * fix: fallback to Open-Meteo if Copernicus tiles fail * Revert "fix: fallback to Open-Meteo if Copernicus tiles fail" This reverts commit c50820f429dc0558743277a79aa11450090a8ef5. * refactor(api): share app propagation logic across calculate endpoints * fix(api): resolve Copernicus tile paths from tileList directories * docs(api): document Copernicus-backed terrain analysis path * feat(api): cut over calculate endpoint to app terrain logic * fix(api): stabilize terrain calculate and reduce tile decode load * Enforce async jobs path for terrain calculate requests (#200) Route terrain-mode requests from /api/v1/calculate to /api/v1/calculate/jobs and return queued 202 responses, keeping sync calculate for non-terrain mode while preventing CPU-heavy terrain sync execution. * Route terrain sync calculate requests to async jobs (#201) Enforce jobs-first terrain execution by delegating mode=terrain requests on /api/v1/calculate to the async jobs queue path and returning 202 queued responses, preventing CPU-heavy synchronous terrain runs. * Fix deep-link sharing to prefer actual site selection (#202) Clear stale selectedLinkId when selecting sites and only emit link-style share URLs when the selected sites exactly match the active link endpoints. * Fix deep-link unicode matching for emoji and Korean names (#203) Preserve Unicode and emoji semantics in slug/canonical key generation, avoid ASCII-only canonical collapse, and add emoji/Korean deep-link regression coverage. * Normalize emoji variation selectors in deep-link keys (#204) Treat FE0E/FE0F variants consistently in slug and canonical key generation so emoji deep links resolve to the correct Site regardless of URL serialization differences. * Harden deep-link site matching for emoji variations (#205) Normalize variation selectors in generated path segments and require full site slug resolution before applying selections so emoji/Korean deep links do not silently fall back to incorrect default selections. * Harden deep-link matching order for emoji/unicode selections (#206) Use exact-normalized matching before slug/canonical fallback for site/link deep-link selection and clear active selection when deep-link resolution is partial, preventing silent fallback to default first site/link. * Add deep-link debug instrumentation for selection apply (#207) Add temporary DeepLinkDebug console logs around payload parsing, slug matching, and post-apply selection state to trace why some deep links fall back to first-site selection on staging. * Apply deep-link selections after init cycle settles (#208) Prevent startup hydration from overwriting deep-link site/link selections by deferring deep-link application until the initializeCloudSync cycle has been observed and settled. * chore(deeplink): remove temporary DeepLinkDebug instrumentation (#209) * feat(onboarding): merge onboarding with simulation library into welcome modal (#152) (#210) - Rewrite onboarding content: ctrl/cmd-click links, auto-overlay switching, simplified sharing - Extract SimulationLibraryPanel reusable component from Sidebar - Create WelcomeModal: merged onboarding + simulation library - First visit: WelcomeModal opens with onboarding collapsed + library visible - Deep-link visits skip the welcome modal entirely - Returning users with no simulation auto-open the Simulation Library - ? button opens WelcomeModal with onboarding expanded - Delete old OnboardingTutorialModal * refactor(onboarding): compact welcome card, simple onboarding modal (#152) (#211) - Welcome modal: compact card with 3 action buttons (not full-screen) - Onboarding: restored as simple text modal (no library, no collapsible) - OS detection: Cmd on Mac, Ctrl elsewhere via {{MODIFIER}} placeholder - Simulation Library button in onboarding text via custom ReactMarkdown component - Trimmed onboarding: removed basemap providers, workflow, roles, security note - Added showNewSimulationRequest store state to trigger New Simulation from welcome * refactor(onboarding): inline links for Simulation Library and Site Library (#152) (#212) * feat(deeplink): live update browser URL to match current share link (#190) (#213) * fix: unblock blank simulation workflows (#179) * fix: persist blank simulation load session state * fix: allow blank simulations to continue in workspace * fix: keep blank load session persistence main-compatible * chore: bump release version to 0.10.3 * feat(deeplink): live update browser URL to match current share link (#190) Add buildDeepLinkPathname helper for pathname-only computation. Sync browser URL bar via history.replaceState when simulation or site selection changes. Clears stale deeplink when no simulation is active. Skips sync until initial deeplink resolution settles. Includes drift reconcile (0863752: unblock blank simulation workflows). * release: prepare v0.11.0 changelog and metadata * fix(api): restore no-store headers for profile and public simulation * chore: retrigger release checks --------- Co-authored-by: Mats <heigre@protonmail.com> --------- Co-authored-by: Mats <heigre@protonmail.com> * chore: sync staging with main after v0.11.0 (#216) * fix: unblock blank simulation workflows (#179) * fix: persist blank simulation load session state * fix: allow blank simulations to continue in workspace * fix: keep blank load session persistence main-compatible * chore: bump release version to 0.10.3 * release: v0.11.0 (#214) * Deep link V2 + FastAPI calculation (#192) * fix: align multi-site overlays with radial terrain loading * fix: remove relay overlay bounding box artifact * fix: update profile when selected sites change * fix: clear map selection before add-site and default zero-select heatmap * fix: persist blank simulation load session state * fix: allow blank simulations to continue in workspace * fix: prevent fallback link after deselect and gate profile to two sites * fix: remove fallback profile link message and stale first-link fallback * fix: preserve deselect state and sync profile cursor on direction flip * fix: apply buffered overlay mask when no sites selected * fix: use simulation-wide boundary for overlays and terrain loading * fix: remove manual terrain controls and online elevation sync * sidebar: text overhaul per issue #184 * sidebar: remove (saved) label and helper line * sidebar: combine simulation name with heading * heatmap: default to contours and allow selection when no sites selected * heatmap: default to contours instead of smooth * Implement shorter deeplinks (issue #144) - New URL format: /<simulation>/<site>+<site2> or /<simulation>/<site1><><site2> - Keep Unicode characters (æøå) in URLs - Strip delimiter chars (+ < > /) from slugs - Backward compatible with old ?dl=1 format * Fix deeplinks: preserve case, remove query params, no encoding - Keep original case in URLs (Blefjell not blefjell) - Remove dl=2 and sim=xxx query params for clean URLs - Build URL manually to prevent <> encoding to %3C%3E - V2 format detected by path presence * Fix: preserve <> in URLs, encode unicode chars - Use encodeURIComponent but keep <> unencoded - Unicode chars like ø encode to %C3%B8 (browsers auto-decode) - <> stays as <> (not %3C%3E) * Fix: actually decode %3C and %3E back to <> * Fix: don't re-encode URL when copying share link * Fix: don't encode URLs - preserve emojis and unicode as-is * feat: add FastAPI calculation API with hardened Docker target * chore: ignore Python cache artifacts * feat: add configurable rate limiting to calculation API * feat(api): proxy calculation endpoint through Pages * Fix: use slugifyName for slug matching instead of toLowerCase * Add debug logging for deep link processing * Fix: decode URL-encoded slugs before matching - Decode simulation slug with decodeURIComponent before slugifyName - Decode site/link slugs before matching - Remove debug logging * Fix deep-link resolution for encoded path slugs Decode URL-encoded path segments and use canonical slug matching so shared Simulation links resolve reliably after hydration for ASCII, Unicode, and emoji names. * Use tilde delimiter in generated deep links Switch link delimiter generation to tilde to avoid browser percent-encoding in the address bar, keep legacy angle-bracket parsing support, and normalize emoji variation selectors for cleaner URLs. * feat(api): cut over calculate endpoint to app terrain logic * docs: add deep-link format specification --------- Co-authored-by: Mats <heigre@protonmail.com> * fix(deeplink): only encode link paths when a link is explicitly selected (#193) * fix(deeplink): apply multi-site selections deterministically (#194) * fix: unblock blank simulation workflows (#179) (#195) * fix: persist blank simulation load session state * fix: allow blank simulations to continue in workspace * fix: keep blank load session persistence main-compatible * chore: bump release version to 0.10.3 * docs: add staging-first milestone workflow to AGENTS.md (#196) * fix(deeplink): only encode link paths when a link is explicitly selected * fix(deeplink): apply multi-site selections deterministically * docs: add staging-first milestone workflow to AGENTS.md * feat(api): proxy calculation endpoint through Pages (#197) Co-authored-by: Mats <heigre@protonmail.com> * Revert "feat(api): proxy calculation endpoint through Pages (#197)" (#198) This reverts commit 0138c1bd1aaa8f6adc5fd26f2512540ea5593f8d. * Restore native terrain API optimization line from pre-regression commits (#199) * feat(api): add async terrain jobs endpoints and request guardrails * feat(api): process terrain jobs asynchronously with status updates * feat(api): compute terrain job results from sampled elevations * feat(api): use Copernicus terrain tiles via proxy for terrain jobs * fix(api): correct Copernicus tile naming and bucket paths * fix: fallback to Open-Meteo if Copernicus tiles fail * Revert "fix: fallback to Open-Meteo if Copernicus tiles fail" This reverts commit c50820f429dc0558743277a79aa11450090a8ef5. * refactor(api): share app propagation logic across calculate endpoints * fix(api): resolve Copernicus tile paths from tileList directories * docs(api): document Copernicus-backed terrain analysis path * feat(api): cut over calculate endpoint to app terrain logic * fix(api): stabilize terrain calculate and reduce tile decode load * Enforce async jobs path for terrain calculate requests (#200) Route terrain-mode requests from /api/v1/calculate to /api/v1/calculate/jobs and return queued 202 responses, keeping sync calculate for non-terrain mode while preventing CPU-heavy terrain sync execution. * Route terrain sync calculate requests to async jobs (#201) Enforce jobs-first terrain execution by delegating mode=terrain requests on /api/v1/calculate to the async jobs queue path and returning 202 queued responses, preventing CPU-heavy synchronous terrain runs. * Fix deep-link sharing to prefer actual site selection (#202) Clear stale selectedLinkId when selecting sites and only emit link-style share URLs when the selected sites exactly match the active link endpoints. * Fix deep-link unicode matching for emoji and Korean names (#203) Preserve Unicode and emoji semantics in slug/canonical key generation, avoid ASCII-only canonical collapse, and add emoji/Korean deep-link regression coverage. * Normalize emoji variation selectors in deep-link keys (#204) Treat FE0E/FE0F variants consistently in slug and canonical key generation so emoji deep links resolve to the correct Site regardless of URL serialization differences. * Harden deep-link site matching for emoji variations (#205) Normalize variation selectors in generated path segments and require full site slug resolution before applying selections so emoji/Korean deep links do not silently fall back to incorrect default selections. * Harden deep-link matching order for emoji/unicode selections (#206) Use exact-normalized matching before slug/canonical fallback for site/link deep-link selection and clear active selection when deep-link resolution is partial, preventing silent fallback to default first site/link. * Add deep-link debug instrumentation for selection apply (#207) Add temporary DeepLinkDebug console logs around payload parsing, slug matching, and post-apply selection state to trace why some deep links fall back to first-site selection on staging. * Apply deep-link selections after init cycle settles (#208) Prevent startup hydration from overwriting deep-link site/link selections by deferring deep-link application until the initializeCloudSync cycle has been observed and settled. * chore(deeplink): remove temporary DeepLinkDebug instrumentation (#209) * feat(onboarding): merge onboarding with simulation library into welcome modal (#152) (#210) - Rewrite onboarding content: ctrl/cmd-click links, auto-overlay switching, simplified sharing - Extract SimulationLibraryPanel reusable component from Sidebar - Create WelcomeModal: merged onboarding + simulation library - First visit: WelcomeModal opens with onboarding collapsed + library visible - Deep-link visits skip the welcome modal entirely - Returning users with no simulation auto-open the Simulation Library - ? button opens WelcomeModal with onboarding expanded - Delete old OnboardingTutorialModal * refactor(onboarding): compact welcome card, simple onboarding modal (#152) (#211) - Welcome modal: compact card with 3 action buttons (not full-screen) - Onboarding: restored as simple text modal (no library, no collapsible) - OS detection: Cmd on Mac, Ctrl elsewhere via {{MODIFIER}} placeholder - Simulation Library button in onboarding text via custom ReactMarkdown component - Trimmed onboarding: removed basemap providers, workflow, roles, security note - Added showNewSimulationRequest store state to trigger New Simulation from welcome * refactor(onboarding): inline links for Simulation Library and Site Library (#152) (#212) * feat(deeplink): live update browser URL to match current share link (#190) (#213) * fix: unblock blank simulation workflows (#179) * fix: persist blank simulation load session state * fix: allow blank simulations to continue in workspace * fix: keep blank load session persistence main-compatible * chore: bump release version to 0.10.3 * feat(deeplink): live update browser URL to match current share link (#190) Add buildDeepLinkPathname helper for pathname-only computation. Sync browser URL bar via history.replaceState when simulation or site selection changes. Clears stale deeplink when no simulation is active. Skips sync until initial deeplink resolution settles. Includes drift reconcile (0863752: unblock blank simulation workflows). * release: prepare v0.11.0 changelog and metadata * fix(api): restore no-store headers for profile and public simulation * chore: retrigger release checks --------- Co-authored-by: Mats <heigre@protonmail.com> --------- Co-authored-by: Mats <heigre@protonmail.com> * refactor: extract coverage state into separate Zustand store (#159) (#218) Move coverageSamples, isSimulationRecomputing, simulationProgress, simulationRunToken, and recomputeCoverage action out of appStore.ts into a dedicated coverageStore.ts. Components subscribe to coverage state via useCoverageStore, narrowing the re-render fan-out during coverage recompute lifecycle. * fix: update simulation snapshot after library entry edit (#91) (#219) updateSiteLibraryEntry was missing the updateCurrentSimulationSnapshot() call that every other mutation function makes. This caused gain, power, cable loss, antenna height, and other fields edited via the resource details popup to not reflect in the simulation preset snapshot until a full page reload. * fix: move map to background overlays and consolidate inspector (#88) (#224) * feat: add mobile tabbed map overlays for viewing workflow (#171) (#225) * fix: refine mobile full-screen map and profile panel behavior (#171) (#226) * fix: enable mobile viewport-fit and keep environment frame (#171) (#227) * fix: force true full-viewport mobile map sizing (#171) (#228) * fix: stabilize iOS viewport sizing for full-screen mobile map (#171) (#229) * fix: use layout viewport sizing for iOS full-bleed map (#171) (#230) * feat: improve mobile panel toggles and map selection controls (#171) (#231) * fix: polish mobile tabs and unify panel/header controls (#171) (#232) * fix: normalize mobile panel sizing and centralize ui icons (#171) (#233) * fix: make mobile panel and attribution layout robust (#171) (#234) * fix: use unified mobile attribution clearance offsets (#171) (#235) * fix: measure mobile overlay clearance for attribution and restore env badge (#171) (#237) * fix: reset mobile map layout and top-anchor attribution (#171) (#238) * fix: stabilize mobile full-bleed map and attribution top anchoring (#171) (#239) * fix: show multi-select toggle only on mobile (#171) (#241) * fix: use default carto normal style for provider fallback (#167) (#242) * fix: default basemap preset to carto normal-themed on fresh load (#167) (#243) * fix: set desktop sidebar and inspector width to 20vw max 400px (#240) (#244) * fix: move env badge to sidebar title row and compact user chip (#240) (#245) * fix: simplify sidebar header controls to icon-only row (#240) (#246) * fix: make inspector a right sidebar and simplify header controls (#240) (#247) * fix: move results panel into right inspector (#240) (#248) * feat: unify UI iconography with Lucide controls (#249) * fix: remove remaining icon chrome and unify expand/collapse cues (#251) * fix: correct map icon states and tighten icon accessibility (#253) * fix: use fullscreen fit icon and prevent pill shadow clipping (#254) * fix: reorder map utility icons and use stack multi-select icon (#255) * fix: center compact map utility pill on mobile (#257) * chore: reconcile main into staging (#258) * fix: unblock blank simulation workflows (#179) * fix: persist blank simulation load session state * fix: allow blank simulations to continue in workspace * fix: keep blank load session persistence main-compatible * chore: bump release version to 0.10.3 * release: v0.11.0 (#214) * Deep link V2 + FastAPI calculation (#192) * fix: align multi-site overlays with radial terrain loading * fix: remove relay overlay bounding box artifact * fix: update profile when selected sites change * fix: clear map selection before add-site and default zero-select heatmap * fix: persist blank simulation load session state * fix: allow blank simulations to continue in workspace * fix: prevent fallback link after deselect and gate profile to two sites * fix: remove fallback profile link message and stale first-link fallback * fix: preserve deselect state and sync profile cursor on direction flip * fix: apply buffered overlay mask when no sites selected * fix: use simulation-wide boundary for overlays and terrain loading * fix: remove manual terrain controls and online elevation sync * sidebar: text overhaul per issue #184 * sidebar: remove (saved) label and helper line * sidebar: combine simulation name with heading * heatmap: default to contours and allow selection when no sites selected * heatmap: default to contours instead of smooth * Implement shorter deeplinks (issue #144) - New URL format: /<simulation>/<site>+<site2> or /<simulation>/<site1><><site2> - Keep Unicode characters (æøå) in URLs - Strip delimiter chars (+ < > /) from slugs - Backward compatible with old ?dl=1 format * Fix deeplinks: preserve case, remove query params, no encoding - Keep original case in URLs (Blefjell not blefjell) - Remove dl=2 and sim=xxx query params for clean URLs - Build URL manually to prevent <> encoding to %3C%3E - V2 format detected by path presence * Fix: preserve <> in URLs, encode unicode chars - Use encodeURIComponent but keep <> unencoded - Unicode chars like ø encode to %C3%B8 (browsers auto-decode) - <> stays as <> (not %3C%3E) * Fix: actually decode %3C and %3E back to <> * Fix: don't re-encode URL when copying share link * Fix: don't encode URLs - preserve emojis and unicode as-is * feat: add FastAPI calculation API with hardened Docker target * chore: ignore Python cache artifacts * feat: add configurable rate limiting to calculation API * feat(api): proxy calculation endpoint through Pages * Fix: use slugifyName for slug matching instead of toLowerCase * Add debug logging for deep link processing * Fix: decode URL-encoded slugs before matching - Decode simulation slug with decodeURIComponent before slugifyName - Decode site/link slugs before matching - Remove debug logging * Fix deep-link resolution for encoded path slugs Decode URL-encoded path segments and use canonical slug matching so shared Simulation links resolve reliably after hydration for ASCII, Unicode, and emoji names. * Use tilde delimiter in generated deep links Switch link delimiter generation to tilde to avoid browser percent-encoding in the address bar, keep legacy angle-bracket parsing support, and normalize emoji variation selectors for cleaner URLs. * feat(api): cut over calculate endpoint to app terrain logic * docs: add deep-link format specification --------- Co-authored-by: Mats <heigre@protonmail.com> * fix(deeplink): only encode link paths when a link is explicitly selected (#193) * fix(deeplink): apply multi-site selections deterministically (#194) * fix: unblock blank simulation workflows (#179) (#195) * fix: persist blank simulation load session state * fix: allow blank simulations to continue in workspace * fix: keep blank load session persistence main-compatible * chore: bump release version to 0.10.3 * docs: add staging-first milestone workflow to AGENTS.md (#196) * fix(deeplink): only encode link paths when a link is explicitly selected * fix(deeplink): apply multi-site selections deterministically * docs: add staging-first milestone workflow to AGENTS.md * feat(api): proxy calculation endpoint through Pages (#197) Co-authored-by: Mats <heigre@protonmail.com> * Revert "feat(api): proxy calculation endpoint through Pages (#197)" (#198) This reverts commit 0138c1bd1aaa8f6adc5fd26f2512540ea5593f8d. * Restore native terrain API optimization line from pre-regression commits (#199) * feat(api): add async terrain jobs endpoints and request guardrails * feat(api): process terrain jobs asynchronously with status updates * feat(api): compute terrain job results from sampled elevations * feat(api): use Copernicus terrain tiles via proxy for terrain jobs * fix(api): correct Copernicus tile naming and bucket paths * fix: fallback to Open-Meteo if Copernicus tiles fail * Revert "fix: fallback to Open-Meteo if Copernicus tiles fail" This reverts commit c50820f429dc0558743277a79aa11450090a8ef5. * refactor(api): share app propagation logic across calculate endpoints * fix(api): resolve Copernicus tile paths from tileList directories * docs(api): document Copernicus-backed terrain analysis path * feat(api): cut over calculate endpoint to app terrain logic * fix(api): stabilize terrain calculate and reduce tile decode load * Enforce async jobs path for terrain calculate requests (#200) Route terrain-mode requests from /api/v1/calculate to /api/v1/calculate/jobs and return queued 202 responses, keeping sync calculate for non-terrain mode while preventing CPU-heavy terrain sync execution. * Route terrain sync calculate requests to async jobs (#201) Enforce jobs-first terrain execution by delegating mode=terrain requests on /api/v1/calculate to the async jobs queue path and returning 202 queued responses, preventing CPU-heavy synchronous terrain runs. * Fix deep-link sharing to prefer actual site selection (#202) Clear stale selectedLinkId when selecting sites and only emit link-style share URLs when the selected sites exactly match the active link endpoints. * Fix deep-link unicode matching for emoji and Korean names (#203) Preserve Unicode and emoji semantics in slug/canonical key generation, avoid ASCII-only canonical collapse, and add emoji/Korean deep-link regression coverage. * Normalize emoji variation selectors in deep-link keys (#204) Treat FE0E/FE0F variants consistently in slug and canonical key generation so emoji deep links resolve to the correct Site regardless of URL serialization differences. * Harden deep-link site matching for emoji variations (#205) Normalize variation selectors in generated path segments and require full site slug resolution before applying selections so emoji/Korean deep links do not silently fall back to incorrect default selections. * Harden deep-link matching order for emoji/unicode selections (#206) Use exact-normalized matching before slug/canonical fallback for site/link deep-link selection and clear active selection when deep-link resolution is partial, preventing silent fallback to default first site/link. * Add deep-link debug instrumentation for selection apply (#207) Add temporary DeepLinkDebug console logs around payload parsing, slug matching, and post-apply selection state to trace why some deep links fall back to first-site selection on staging. * Apply deep-link selections after init cycle settles (#208) Prevent startup hydration from overwriting deep-link site/link selections by deferring deep-link application until the initializeCloudSync cycle has been observed and settled. * chore(deeplink): remove temporary DeepLinkDebug instrumentation (#209) * feat(onboarding): merge onboarding with simulation library into welcome modal (#152) (#210) - Rewrite onboarding content: ctrl/cmd-click links, auto-overlay switching, simplified sharing - Extract SimulationLibraryPanel reusable component from Sidebar - Create WelcomeModal: merged onboarding + simulation library - First visit: WelcomeModal opens with onboarding collapsed + library visible - Deep-link visits skip the welcome modal entirely - Returning users with no simulation auto-open the Simulation Library - ? button opens WelcomeModal with onboarding expanded - Delete old OnboardingTutorialModal * refactor(onboarding): compact welcome card, simple onboarding modal (#152) (#211) - Welcome modal: compact card with 3 action buttons (not full-screen) - Onboarding: restored as simple text modal (no library, no collapsible) - OS detection: Cmd on Mac, Ctrl elsewhere via {{MODIFIER}} placeholder - Simulation Library button in onboarding text via custom ReactMarkdown component - Trimmed onboarding: removed basemap providers, workflow, roles, security note - Added showNewSimulationRequest store state to trigger New Simulation from welcome * refactor(onboarding): inline links for Simulation Library and Site Library (#152) (#212) * feat(deeplink): live update browser URL to match current share link (#190) (#213) * fix: unblock blank simulation workflows (#179) * fix: persist blank simulation load session state * fix: allow blank simulations to continue in workspace * fix: keep blank load session persistence main-compatible * chore: bump release version to 0.10.3 * feat(deeplink): live update browser URL to match current share link (#190) Add buildDeepLinkPathname helper for pathname-only computation. Sync browser URL bar via history.replaceState when simulation or site selection changes. Clears stale deeplink when no simulation is active. Skips sync until initial deeplink resolution settles. Includes drift reconcile (0863752: unblock blank simulation workflows). * release: prepare v0.11.0 changelog and metadata * fix(api): restore no-store headers for profile and public simulation * chore: retrigger release checks --------- Co-authored-by: Mats <heigre@protonmail.com> * ci: add workflow to require milestone on completed issue close (#217) --------- Co-authored-by: Mats <heigre@protonmail.com> * feat: add Easter holiday theme override framework (#256) (#259) * fix: expand Easter theme window across Easter week (#256) (#260) * refine map panel controls and sidebar glass treatment (#261) Move map settings into the right Map expandable, keep the utility pill on-map, and adjust panel styling/behavior so controls fit better and profile expansion hides the right inspector. * fix mobile viewport fill and tab panel offset (#262) Align mobile tab-bar height with the glass tab container to prevent panel overlap, and use fixed inset map sizing with dvh/lvh fallback so iOS keeps the map full-bleed behind browser chrome. * fix mobile panel gap and iOS map anchoring (#263) Add a consistent gap between the mobile tab bar and panel stack, and switch the mobile map shell to top/left/right anchoring with large viewport height so the background map can extend behind iOS browser chrome. * fix iOS mobile map full-bleed sizing (#264) Revert mobile map shell to full-edge anchoring and add robust viewport-height fallbacks while keeping the panel gap spacing fix, so iOS can keep the map behind browser chrome without bottom panel overlap. * fix iOS full-bleed map with webkit fallback (#265) Apply a clean mobile viewport fallback stack with for root/app/map shells and trigger map resize on visualViewport changes, while keeping the tab-to-panel spacing fix consistent. * fix iOS map full-bleed inset sizing (#266) Simplify the mobile map shell to fixed inset-only sizing and narrow WebKit fill-available fallback scope so the map is not constrained by intermediate mobile containers. * Recover iOS full-bleed map behavior (#220) (#267) * Revert "fix iOS map full-bleed inset sizing (#266)" This reverts commit 5568a0c116f7fb1433e63c7386a9bb3f8b21c15b. * fix iOS map resize clamping on safari chrome Stop resizing the map on visualViewport changes so Safari URL bar transitions do not clamp map canvas height against the visible viewport while full-bleed container sizing remains in place. * restore iOS partial-fix resize baseline (#268) Re-enable visualViewport resize map refresh to match the 3eed567 behavior, but avoid scroll-driven resize churn so Safari toolbar motion is less likely to force repeated visible-viewport clamping. * fix iOS map full-bleed canvas bounds (#269) Restore the 3eed567 viewport listener baseline and pin the internal MapLibre map/canvas stack to the mobile map shell bounds so Safari toolbar transitions no longer leave a clipped map rectangle. * add staging-only mobile viewport diagnostics (#270) Temporarily disable the staging frame border and add a staging-only viewport metrics overlay (inner/visual/map/canvas heights) to remove visual ambiguity and diagnose iOS full-bleed map clipping. * move staging viewport debug chip above mobile UI (#271) Center the staging diagnostics chip with an extremely high z-index so it remains visible above the mobile tab bar and overlays during iOS viewport debugging. * stabilize iOS map initial canvas sizing (#272) Force an immediate + delayed post-mount map resize sequence to prevent stale initial canvas bounds on iOS, and improve staging diagnostics by reading map/container/canvas heights from MapLibre directly. * repair iOS diagnostic probe and add maplibregl-wrapper override (#273) Add onLoad callback to Map component to track when MapLibre is ready for diagnostics probe, and add CSS override for .maplibregl-wrapper to force full-bleed on mobile. * fix iOS map background color and viewport transform (#274) Add background-color to maplibregl-wrapper to cover default yellow tile background, and reset viewport transform to none on mobile to prevent rendering gaps. * anchor mobile map to bottom and expand viewport diagnostics (#275) Revert the temporary map background workaround, switch the mobile map shell to bottom-anchored sizing with lvh fallback, and add top/bottom debug metrics for panel/map/canvas to pinpoint vertical offset behavior on iOS. * top-anchor iOS mobile map shell (#276) Force top anchoring for the mobile map shell and remove map-panel from WebKit fill-available fallback scope so extra viewport height extends below the visible area instead of shifting map content upward. * clean up mobile map sizing and remove diagnostics (#277) Remove temporary viewport debug instrumentation and iOS-specific map sizing experiments, restore normal fullscreen map sizing on mobile, and keep only safe-area based UI offsets with viewport-fit=cover behavior. * chore: sync unpushed staging fullscreen toggle fixes (#278) * fix path profile fullscreen toggle to only toggle profile, not map * fix: map and path profile fullscreen toggles work independently * fix: decouple map and path profile fullscreen toggles * fix: correct app shell CSS class for profile vs map expansion * release: bump version to 0.12.0 * release: trigger CI for v0.12.0 --------- Co-authored-by: Mats <heigre@protonmail.com> * Release v0.12.1 hotfix (#286) * fix: share holiday theme state via Zustand & bump to v0.12.1 Lift holidayWindowState from useThemeVariant local useState into the appStore so AppShell, MapView, and Sidebar share reactive state. Fixes Revert Theme refresh requirement and adds yellow Easter option to color theme dropdown. * chore: re-trigger CI for release/v0.12.1 * chore: update workflows to set explicit commit status checks * chore: finalize v0.12.1 version (#288) * hotfix: reconcile production auth fix and v0.12.2 in main (#334) * hotfix: restore production sign-in recovery from locked state (#24) * chore: bump version to 0.12.2 for production hotfix release * fix: align required status checks to PR head SHA (#335) * fix: publish required statuses on PR head SHA * fix: keep CI status publish working on push events * release: 0.13.0 (#368) * Deep link V2 + FastAPI calculation (#192) * fix: align multi-site overlays with radial terrain loading * fix: remove relay overlay bounding box artifact * fix: update profile when selected sites change * fix: clear map selection before add-site and default zero-select heatmap * fix: persist blank simulation load session state * fix: allow blank simulations to continue in workspace * fix: prevent fallback link after deselect and gate profile to two sites * fix: remove fallback profile link message and stale first-link fallback * fix: preserve deselect state and sync profile cursor on direction flip * fix: apply buffered overlay mask when no sites selected * fix: use simulation-wide boundary for overlays and terrain loading * fix: remove manual terrain controls and online elevation sync * sidebar: text overhaul per issue #184 * sidebar: remove (saved) label and helper line * sidebar: combine simulation name with heading * heatmap: default to contours and allow selection when no sites selected * heatmap: default to contours instead of smooth * Implement shorter deeplinks (issue #144) - New URL format: /<simulation>/<site>+<site2> or /<simulation>/<site1><><site2> - Keep Unicode characters (æøå) in URLs - Strip delimiter chars (+ < > /) from slugs - Backward compatible with old ?dl=1 format * Fix deeplinks: preserve case, remove query params, no encoding - Keep original case in URLs (Blefjell not blefjell) - Remove dl=2 and sim=xxx query params for clean URLs - Build URL manually to prevent <> encoding to %3C%3E - V2 format detected by path presence * Fix: preserve <> in URLs, encode unicode chars - Use encodeURIComponent but keep <> unencoded - Unicode chars like ø encode to %C3%B8 (browsers auto-decode) - <> stays as <> (not %3C%3E) * Fix: actually decode %3C and %3E back to <> * Fix: don't re-encode URL when copying share link * Fix: don't encode URLs - preserve emojis and unicode as-is * feat: add FastAPI calculation API with hardened Docker target * chore: ignore Python cache artifacts * feat: add configurable rate limiting to calculation API * feat(api): proxy calculation endpoint through Pages * Fix: use slugifyName for slug matching instead of toLowerCase * Add debug logging for deep link processing * Fix: decode URL-encoded slugs before matching - Decode simulation slug with decodeURIComponent before slugifyName - Decode site/link slugs before matching - Remove debug logging * Fix deep-link resolution for encoded path slugs Decode URL-encoded path segments and use canonical slug matching so shared Simulation links resolve reliably after hydration for ASCII, Unicode, and emoji names. * Use tilde delimiter in generated deep links Switch link delimiter generation to tilde to avoid browser percent-encoding in the address bar, keep legacy angle-bracket parsing support, and normalize emoji variation selectors for cleaner URLs. * feat(api): cut over calculate endpoint to app terrain logic * docs: add deep-link format specification --------- Co-authored-by: Mats <heigre@protonmail.com> * fix(deeplink): only encode link paths when a link is explicitly selected (#193) * fix(deeplink): apply multi-site selections deterministically (#194) * fix: unblock blank simulation workflows (#179) (#195) * fix: persist blank simulation load session state * fix: allow blank simulations to continue in workspace * fix: keep blank load session persistence main-compatible * chore: bump release version to 0.10.3 * docs: add staging-first milestone workflow to AGENTS.md (#196) * fix(deeplink): only encode link paths when a link is explicitly selected * fix(deeplink): apply multi-site selections deterministically * docs: add staging-first milestone workflow to AGENTS.md * feat(api): proxy calculation endpoint through Pages (#197) Co-authored-by: Mats <heigre@protonmail.com> * Revert "feat(api): proxy calculation endpoint through Pages (#197)" (#198) This reverts commit 0138c1bd1aaa8f6adc5fd26f2512540ea5593f8d. * Restore native terrain API optimization line from pre-regression commits (#199) * feat(api): add async terrain jobs endpoints and request guardrails * feat(api): process terrain jobs asynchronously with status updates * feat(api): compute terrain job results from sampled elevations * feat(api): use Copernicus terrain tiles via proxy for terrain jobs * fix(api): correct Copernicus tile naming and bucket paths * fix: fallback to Open-Meteo if Copernicus tiles fail * Revert "fix: fallback to Open-Meteo if Copernicus tiles fail" This reverts commit c50820f429dc0558743277a79aa11450090a8ef5. * refactor(api): share app propagation logic across calculate endpoints * fix(api): resolve Copernicus tile paths from tileList directories * docs(api): document Copernicus-backed terrain analysis path * feat(api): cut over calculate endpoint to app terrain logic * fix(api): stabilize terrain calculate and reduce tile decode load * Enforce async jobs path for terrain calculate requests (#200) Route terrain-mode requests from /api/v1/calculate to /api/v1/calculate/jobs and return queued 202 responses, keeping sync calculate for non-terrain mode while preventing CPU-heavy terrain sync execution. * Route terrain sync calculate requests to async jobs (#201) Enforce jobs-first terrain execution by delegating mode=terrain requests on /api/v1/calculate to the async jobs queue path and returning 202 queued responses, preventing CPU-heavy synchronous terrain runs. * Fix deep-link sharing to prefer actual site selection (#202) Clear stale selectedLinkId when selecting sites and only emit link-style share URLs when the selected sites exactly match the active link endpoints. * Fix deep-link unicode matching for emoji and Korean names (#203) Preserve Unicode and emoji semantics in slug/canonical key generation, avoid ASCII-only canonical collapse, and add emoji/Korean deep-link regression coverage. * Normalize emoji variation selectors in deep-link keys (#204) Treat FE0E/FE0F variants consistently in slug and canonical key generation so emoji deep links resolve to the correct Site regardless of URL serialization differences. * Harden deep-link site matching for emoji variations (#205) Normalize variation selectors in generated path segments and require full site slug resolution before applying selections so emoji/Korean deep links do not silently fall back to incorrect default selections. * Harden deep-link matching order for emoji/unicode selections (#206) Use exact-normalized matching before slug/canonical fallback for site/link deep-link selection and clear active selection when deep-link resolution is partial, preventing silent fallback to default first site/link. * Add deep-link debug instrumentation for selection apply (#207) Add temporary DeepLinkDebug console logs around payload parsing, slug matching, and post-apply selection state to trace why some deep links fall back to first-site selection on staging. * Apply deep-link selections after init cycle settles (#208) Prevent startup hydration from overwriting deep-link site/link selections by deferring deep-link application until the initializeCloudSync cycle has been observed and settled. * chore(deeplink): remove temporary DeepLinkDebug instrumentation (#209) * feat(onboarding): merge onboarding with simulation library into welcome modal (#152) (#210) - Rewrite onboarding content: ctrl/cmd-click links, auto-overlay switching, simplified sharing - Extract SimulationLibraryPanel reusable component from Sidebar - Create WelcomeModal: merged onboarding +…
1 parent f79e750 commit bf9a4c0

44 files changed

Lines changed: 3207 additions & 1391 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/require-milestone-on-close.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ on:
66

77
jobs:
88
enforce-milestone:
9-
if: github.event.issue.state_reason == 'completed' && github.event.issue.milestone == null && !contains(toJson(github.event.issue.labels), '\"name\":\"no-milestone-close-ok\"')
9+
if: github.event.issue.state_reason == 'completed' && github.event.issue.milestone == null && !contains(github.event.issue.labels.*.name, 'no-milestone-close-ok')
1010
runs-on: ubuntu-latest
1111
permissions:
1212
issues: write

CHANGELOG.md

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,43 @@
11
# Changelog
22

3-
All notable changes to this project are documented here in a human-readable format.
3+
## [0.18.0] - 2026-04-24
4+
5+
### Added
6+
- User location map with live camera follow: tap "Locate me" to centre the map on your GPS position; the camera follows as you move until you pan away. (#765)
7+
- Site beam visualizer popover: interactive gain/beam-pattern overlay with anchored popover and unified gain fields across antenna types. (#737, #740)
8+
- Site access editor redesign: cleaner layout, padded collaborator popover, and improved modal structure. (#733)
9+
- PanelToolbar component unifying toolbar layout and action alignment across all panels (Navigator, Inspector, chart panel, sidebar). (#616, #751)
10+
- Unified button system: `btn`, `btn-ghost`, and `btn-icon` CSS roles + shared `Button` component consolidation; removes mixed legacy button language. (#616, #748)
11+
12+
### Fixed
13+
- Notification bell replaced with Lucide Bell icon (removes emoji dependency). (#616, #763)
14+
- Map pin anchoring and stacking order corrected. (#731)
15+
- Deploy script syntax error. (#732)
16+
17+
### Internal
18+
- Removed dead UI patterns: `NotificationBanner`, `OfflineBanner` (#759), `MapInlineNotice`, `MapControls`, `FormActionRow` (#760), `terrain-alert` (#761), stale gallery class refs (#758).
19+
- `require-milestone-on-close` workflow now honours `no-milestone-close-ok` label. (#764)
20+
- README landing page simplified. (#747)
421

522
## [0.17.0] - 2026-04-17
623

724
Rolls up all merged work since `0.16.0`, including the `0.16.2` shared-site sync hotfix and the release/deploy guardrail fixes.
825

926
### Added
10-
- Panorama / single-site 360° mode with FOV controls, adaptive zoom sampling, peak labels, global peak tiles, and true shading/LOS-clipped terrain rendering. (#101)
1127
- A new `/settings` panel with per-field auto-save, plus settings layout, avatar drop-zone, and access-request refinements. (#125, #688, #689, #690, #691)
12-
- Norsk Polarinstitutt Svalbard basemap provider with topographic, satellite, and orthophoto presets. (#635)
1328
- The live in-app UI gallery plus continued componentization of badge, popover, state-dot, and ActionButton patterns across the shell. (#540, #616)
1429
- Mobile tabbed map overlays and expanded accessibility semantics for mobile tabs and markers. (#171, #430, #431, #433)
1530

1631
### Changed
17-
- Bumped the app and release line to `0.17.0` so the next milestone train starts from the current staging baseline.
32+
- Reorganized map layers.
1833
- Standardized panel/sidebar headers, ActionButton usage, and iconography across the shell, inspector, sidebar, and settings surfaces.
1934
- Continued mobile map, panel, and attribution polish, including synchronized animation timing and viewport-fit behavior.
2035
- Hardened release and deploy automation, CI guardrails, and staging-main workflow rules.
2136
- Improved local test-server startup, change polling, and developer workflow reliability.
2237

2338
### Fixed
2439
- Shared simulations now persist private Site references during sync and reload.
25-
- Deep-link handling is more stable for shared simulations, emoji/Korean names, and guest/public auth flows.
2640
- Panorama/profile sizing, path override handling, and overlay recompute/performance stability were improved.
27-
- Terrain loading, geocode throttling, meshmap MQTT/proxy caching, and other sync/network edge cases were hardened.
28-
- Production deploy verification now checks the correct release checkout and Pages project again, so tagged releases complete cleanly.
2941
- Rolled unfinished milestone work forward one step to keep the backlog aligned with the new release boundary.
3042

3143
## [0.16.2] - 2026-04-17

README.md

Lines changed: 19 additions & 211 deletions
Original file line numberDiff line numberDiff line change
@@ -1,220 +1,28 @@
11
# LinkSim
22

3-
Link planning web application for terrain-aware radio path analysis.
3+
LinkSim is a web app for terrain-aware radio path planning and simulation.
44

5-
Inspired by Radio Mobile by Roger Coude (VE2DBE).
5+
<p>
6+
<img src="./docs/assets/readme/panorama-simulation.png" alt="LinkSim panorama simulation" width="49%">
7+
<img src="./docs/assets/readme/path-profile-simulation.png" alt="LinkSim path profile simulation" width="49%">
8+
</p>
69

7-
## Repository Status
10+
## Start Here
811

9-
- License: GNU GPL v3.0 ([LICENSE](./LICENSE))
10-
- Security policy: [SECURITY.md](./SECURITY.md)
11-
- Privacy notice: [docs/legal/PRIVACY.md](./docs/legal/PRIVACY.md)
12-
- Terms and acceptable use: [docs/legal/TERMS.md](./docs/legal/TERMS.md)
13-
- Sensitive data warning: do not store secrets in app content; visibility levels are collaboration controls, not a secret vault.
14-
- Legal credits/notices:
15-
- [docs/legal/CREDITS.md](./docs/legal/CREDITS.md)
16-
- [docs/legal/THIRD_PARTY_NOTICES.md](./docs/legal/THIRD_PARTY_NOTICES.md)
17-
- [docs/legal/SOURCE_COMPLIANCE.md](./docs/legal/SOURCE_COMPLIANCE.md)
12+
| Need | Document |
13+
| --- | --- |
14+
| Use the app | [Getting Started](./docs/onboarding.md) |
15+
| Understand radio models | [RF models and sampling](./docs/rf-models-and-sampling.md) |
16+
| Run or maintain the repo | [Repository guide](./docs/repository-guide.md) |
17+
| Test changes | [Testing plan](./docs/testing-plan.md) |
18+
| Ship a release | [Release flow](./docs/release-flow.md) |
19+
| Reuse UI patterns | [UI pattern glossary](./docs/ui-pattern-glossary.md) |
20+
| Review policies | [Security](./SECURITY.md), [Privacy](./docs/legal/PRIVACY.md), [Terms](./docs/legal/TERMS.md) |
1821

19-
## Environment Model
22+
## Contribute
2023

21-
The project is operated in three stages:
24+
Start by opening a [GitHub Issue](https://github.com/wilhel1812/LinkSim/issues) or joining the [Matrix space](https://matrix.to/#/%23linksim:matrix.org).
2225

23-
1. Local dev (primary iteration environment)
24-
2. Staging (cloud validation)
25-
3. Production (live)
26+
## License
2627

27-
Operational rule:
28-
- Changes are built and tested locally first.
29-
- Then deployed to staging.
30-
- Then promoted to production.
31-
32-
## Quick Start (Local)
33-
34-
Install dependencies:
35-
36-
```bash
37-
npm install
38-
```
39-
40-
Run local edge-parity stack:
41-
42-
```bash
43-
docker compose up --build edge
44-
```
45-
46-
Open:
47-
- `http://localhost:8788`
48-
49-
Other local runtime options (legacy/optional):
50-
51-
```bash
52-
npm run dev
53-
npm run dev:edge
54-
docker compose up --build web
55-
docker compose up --build dev
56-
```
57-
58-
Default ports:
59-
- `edge`: `http://localhost:8788`
60-
- `web`: `http://localhost:8080`
61-
- `dev`: `http://localhost:5173`
62-
63-
## Build, Test, Smoke
64-
65-
Core commands:
66-
67-
```bash
68-
npm run build
69-
npm test
70-
npm run test:ci
71-
```
72-
73-
Additional smoke scripts:
74-
75-
```bash
76-
npm run smoke:edge
77-
npm run smoke:scenario
78-
npm run smoke:profile
79-
npm run smoke:fit-profile
80-
npm run smoke:itm
81-
```
82-
83-
## Calculation API
84-
85-
`/api/v1/calculate` runs directly in LinkSim Pages Functions and uses the same propagation stack as the app (`ITM` with Copernicus terrain sampling).
86-
87-
Behavior notes:
88-
89-
- Public endpoint: `https://linksim.link/api/v1/calculate`
90-
- Terrain source: Copernicus DEM via `/copernicus/30m/*`
91-
- If node ground elevation is omitted, the API samples terrain and uses that elevation with `2m` default antenna height
92-
- Result includes app-style pass/fail text, for example `LOS clear + fail at 83.39 km (-133.6 dBm after env loss)`
93-
- Edge rate limit: `CALC_API_PROXY_RATE_LIMIT_PER_MINUTE` (default `120`)
94-
95-
Example request:
96-
97-
```bash
98-
curl -X POST http://localhost:8788/api/v1/calculate \
99-
-H 'content-type: application/json' \
100-
-d '{
101-
"calculation": "link_budget",
102-
"input": {
103-
"from_site": "Site A",
104-
"to_site": "Site B",
105-
"frequency_mhz": 868,
106-
"rx_target_dbm": -110,
107-
"nodes": [
108-
{"name": "Site A", "lat": 59.9139, "lon": 10.7522},
109-
{"name": "Site B", "lat": 59.9170, "lon": 10.7600}
110-
]
111-
}
112-
}'
113-
```
114-
115-
## Deploy and Release
116-
117-
### Staging deploy
118-
119-
```bash
120-
npm run deploy:staging:main
121-
```
122-
123-
### Production deploy (guarded)
124-
125-
```bash
126-
npm run deploy:prod:main
127-
```
128-
129-
`deploy:prod:main` is blocked unless release requirements are met:
130-
- `HEAD` is tagged `v<package.json version>`
131-
- Version is bumped in `HEAD` compared to `HEAD^`
132-
133-
### Recommended release flow (use this)
134-
135-
```bash
136-
npm run release:prod
137-
```
138-
139-
Optional bump level:
140-
141-
```bash
142-
npm run release:prod -- --bump minor
143-
npm run release:prod -- --bump major
144-
```
145-
146-
What `release:prod` does:
147-
1. Bumps version in `package.json`
148-
2. Regenerates build metadata
149-
3. Commits + tags release
150-
4. Pushes `main` and tags
151-
5. Deploys staging
152-
6. Deploys production
153-
154-
Build label rules:
155-
- Local: `vX.Y.Z-alpha+<commit>`
156-
- Staging: `vX.Y.Z-beta+<commit>`
157-
- Production: `vX.Y.Z`
158-
- Same commit always keeps the same base version (`X.Y.Z`) across all environments.
159-
160-
## Cloudflare Setup Overview
161-
162-
This repo uses:
163-
- Cloudflare Pages + Functions
164-
- D1 for application data
165-
- R2 for avatar images
166-
- Cloudflare Access for authentication boundary
167-
168-
Primary configs:
169-
- Production: [wrangler.toml](./wrangler.toml)
170-
- Staging: [wrangler.staging.toml](./wrangler.staging.toml)
171-
172-
Optional basemap provider environment variables (admin-configured only):
173-
- `VITE_MAPTILER_KEY`
174-
- `VITE_STADIA_KEY`
175-
- `VITE_KARTVERKET_API_KEY`
176-
- `VITE_KARTVERKET_WMTS_BASE_URL` (optional override)
177-
- `VITE_KARTVERKET_TILE_TEMPLATE` (optional explicit template; overrides base URL)
178-
179-
Detailed setup docs:
180-
- [docs/cloudflare-auth-setup.md](./docs/cloudflare-auth-setup.md)
181-
- [docs/access-policy-templates.md](./docs/access-policy-templates.md)
182-
183-
## Staging Data Refresh
184-
185-
Refresh staging from production snapshots:
186-
187-
```bash
188-
npm run refresh:staging
189-
```
190-
191-
Or run separately:
192-
193-
```bash
194-
npm run refresh:staging:d1
195-
npm run refresh:staging:r2
196-
```
197-
198-
## Data/Service Notes
199-
200-
- Terrain data is fetched on demand and cached client-side.
201-
- API proxies and geocode endpoints include method/rate-limit safeguards.
202-
- In local runtimes without edge functions, some cloud behaviors are emulated/fallback.
203-
- Basemap provider failures auto-fallback to CARTO with a non-blocking warning.
204-
205-
## Project Structure
206-
207-
- `src/`: frontend app
208-
- `functions/`: Cloudflare Pages Functions API
209-
- `db/`: SQL schema and migration assets
210-
- `scripts/`: deploy/release/smoke tooling
211-
- `config/`: TS/Vite/Vitest configs
212-
- `docs/`: setup, legal, testing, and operations documentation
213-
- `public/`: static assets
214-
- `nginx/`: nginx config used by Docker flows
215-
216-
## Contributor Notes
217-
218-
- Keep working tree clean before deploy commands, except expected build metadata updates in `src/lib/buildInfo.ts` and `functions/_lib/buildInfo.ts`.
219-
- Prefer `npm run release:prod` over manual production deploy.
220-
- When changing auth/permissions, add or update tests in the same pass.
28+
LinkSim is licensed under [GNU GPL v3.0](./LICENSE).
2.21 MB
Loading
2.27 MB
Loading

0 commit comments

Comments
 (0)