efficient-shell 4.137.0 → 4.137.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/LICENSE +6 -17
  2. package/README.md +3 -1
  3. package/dist/Browser-ZX5OTOKK.js +43 -0
  4. package/dist/Calculator-4SQGOQFQ.js +29 -0
  5. package/dist/ConfirmDialog-NPKW2XHM.js +1 -0
  6. package/dist/CurrencyConverter-2KOM7L6V.js +21 -0
  7. package/dist/Documents-IZTIQYPG.js +52 -0
  8. package/dist/Files-B63KKXGZ.js +89 -0
  9. package/dist/Notepad-WA77V45J.js +25 -0
  10. package/dist/PdfViewer-RBRGEKWF.js +1 -0
  11. package/dist/PomodoroTimer-SDUBKXUS.js +99 -0
  12. package/dist/Preview-D4KOA4OI.js +187 -0
  13. package/dist/Sidebar-Y57LYXXI.js +23 -0
  14. package/dist/Spreadsheet-JX5E6WH6.js +37 -0
  15. package/dist/Stock-XAQ5WMUZ.js +16 -0
  16. package/dist/Weather-HLAGJ5LV.js +60 -0
  17. package/dist/WorldClock-IRI4VCTA.js +32 -0
  18. package/dist/apps/index.js +1 -151
  19. package/dist/chunk-3A46NCXK.js +29 -0
  20. package/dist/chunk-44UO7FYU.js +11 -0
  21. package/dist/chunk-466CM2TJ.js +1 -0
  22. package/dist/chunk-57MIENLJ.js +1 -0
  23. package/dist/chunk-5CRAF2AD.js +2 -0
  24. package/dist/chunk-A2OQLLYK.js +6 -0
  25. package/dist/chunk-B43AGQET.js +4 -0
  26. package/dist/chunk-BR22X7E5.js +1 -0
  27. package/dist/chunk-BUTSU7FO.js +1 -0
  28. package/dist/chunk-D65T4H6J.js +1 -0
  29. package/dist/chunk-ESL5TDNA.js +1 -0
  30. package/dist/chunk-FBDZGO3V.js +1 -0
  31. package/dist/chunk-FHTLC7HH.js +1 -0
  32. package/dist/chunk-FQLMX3IE.js +948 -0
  33. package/dist/chunk-H562R4GX.js +34 -0
  34. package/dist/chunk-JN4WXTWJ.js +106 -0
  35. package/dist/chunk-LVDRBRYU.js +1 -0
  36. package/dist/chunk-LWLCNQY7.js +1 -0
  37. package/dist/chunk-MAIHBBWT.js +1 -0
  38. package/dist/chunk-NPJH5MD7.js +13 -0
  39. package/dist/chunk-OCSOMMY2.js +1 -0
  40. package/dist/chunk-OQDWLHZI.js +1 -0
  41. package/dist/chunk-OWSAMA3I.js +23 -0
  42. package/dist/chunk-PH3PRIFP.js +5 -0
  43. package/dist/chunk-TAWKWSPN.js +9 -0
  44. package/dist/chunk-U4GTZPXP.js +1 -0
  45. package/dist/chunk-UB4JIOLD.js +2 -0
  46. package/dist/chunk-UZAJ5BF6.js +9 -0
  47. package/dist/chunk-VEGMDXT4.js +1 -0
  48. package/dist/chunk-VTVSMRFX.js +1 -0
  49. package/dist/chunk-VVAGSKW7.js +1 -0
  50. package/dist/chunk-XO43FJRM.js +19 -0
  51. package/dist/chunk-Y7BOLDRM.js +69 -0
  52. package/dist/file-intake/index.js +1 -4
  53. package/dist/index.js +639 -7473
  54. package/dist/map/index.js +20 -540
  55. package/dist/markdown/index.js +6 -184
  56. package/dist/markup/index.js +1 -135
  57. package/dist/page-tabs/index.js +21 -709
  58. package/dist/sounds-OMHXVVQ4.js +1 -0
  59. package/dist/ui/index.js +1 -19
  60. package/package.json +4 -3
  61. package/dist/Browser-ZNX4WFUH.js +0 -482
  62. package/dist/Browser-ZNX4WFUH.js.map +0 -1
  63. package/dist/Calculator-6FGNKOXU.js +0 -192
  64. package/dist/Calculator-6FGNKOXU.js.map +0 -1
  65. package/dist/ConfirmDialog-NPQMIGUR.js +0 -4
  66. package/dist/ConfirmDialog-NPQMIGUR.js.map +0 -1
  67. package/dist/CurrencyConverter-FF7EXRYL.js +0 -236
  68. package/dist/CurrencyConverter-FF7EXRYL.js.map +0 -1
  69. package/dist/Documents-H3R7XSBH.js +0 -441
  70. package/dist/Documents-H3R7XSBH.js.map +0 -1
  71. package/dist/Files-ZHJIAUC2.js +0 -805
  72. package/dist/Files-ZHJIAUC2.js.map +0 -1
  73. package/dist/Notepad-VE7ISWRQ.js +0 -410
  74. package/dist/Notepad-VE7ISWRQ.js.map +0 -1
  75. package/dist/PdfViewer-SQBFYAGW.js +0 -5
  76. package/dist/PdfViewer-SQBFYAGW.js.map +0 -1
  77. package/dist/PomodoroTimer-IR3WREED.js +0 -633
  78. package/dist/PomodoroTimer-IR3WREED.js.map +0 -1
  79. package/dist/Preview-DAQGRNRM.js +0 -2959
  80. package/dist/Preview-DAQGRNRM.js.map +0 -1
  81. package/dist/Sidebar-YW6H4NDJ.js +0 -291
  82. package/dist/Sidebar-YW6H4NDJ.js.map +0 -1
  83. package/dist/Spreadsheet-HOCPHJOC.js +0 -519
  84. package/dist/Spreadsheet-HOCPHJOC.js.map +0 -1
  85. package/dist/Stock-LVRQPURG.js +0 -158
  86. package/dist/Stock-LVRQPURG.js.map +0 -1
  87. package/dist/Weather-LZQGVDJL.js +0 -431
  88. package/dist/Weather-LZQGVDJL.js.map +0 -1
  89. package/dist/WorldClock-XPD7WOZT.js +0 -202
  90. package/dist/WorldClock-XPD7WOZT.js.map +0 -1
  91. package/dist/apps/index.js.map +0 -1
  92. package/dist/chunk-5O42JXQE.js +0 -26
  93. package/dist/chunk-5O42JXQE.js.map +0 -1
  94. package/dist/chunk-5RPTS3BT.js +0 -70
  95. package/dist/chunk-5RPTS3BT.js.map +0 -1
  96. package/dist/chunk-6XJEJWNJ.js +0 -61
  97. package/dist/chunk-6XJEJWNJ.js.map +0 -1
  98. package/dist/chunk-7CCHEEYC.js +0 -94
  99. package/dist/chunk-7CCHEEYC.js.map +0 -1
  100. package/dist/chunk-BE7FZOFU.js +0 -17
  101. package/dist/chunk-BE7FZOFU.js.map +0 -1
  102. package/dist/chunk-COWYSHG2.js +0 -79
  103. package/dist/chunk-COWYSHG2.js.map +0 -1
  104. package/dist/chunk-D7PYW2QS.js +0 -265
  105. package/dist/chunk-D7PYW2QS.js.map +0 -1
  106. package/dist/chunk-DVZWYWSD.js +0 -26
  107. package/dist/chunk-DVZWYWSD.js.map +0 -1
  108. package/dist/chunk-E7TB6WD2.js +0 -153
  109. package/dist/chunk-E7TB6WD2.js.map +0 -1
  110. package/dist/chunk-FIHOGVUP.js +0 -416
  111. package/dist/chunk-FIHOGVUP.js.map +0 -1
  112. package/dist/chunk-GA35NEND.js +0 -56
  113. package/dist/chunk-GA35NEND.js.map +0 -1
  114. package/dist/chunk-HUBJAUGG.js +0 -316
  115. package/dist/chunk-HUBJAUGG.js.map +0 -1
  116. package/dist/chunk-I2TAAJHS.js +0 -465
  117. package/dist/chunk-I2TAAJHS.js.map +0 -1
  118. package/dist/chunk-ICTRCKUE.js +0 -3710
  119. package/dist/chunk-ICTRCKUE.js.map +0 -1
  120. package/dist/chunk-IPXN646P.js +0 -14
  121. package/dist/chunk-IPXN646P.js.map +0 -1
  122. package/dist/chunk-IRWSRJ4K.js +0 -48
  123. package/dist/chunk-IRWSRJ4K.js.map +0 -1
  124. package/dist/chunk-JOPU6WPY.js +0 -504
  125. package/dist/chunk-JOPU6WPY.js.map +0 -1
  126. package/dist/chunk-MGGY74RY.js +0 -14
  127. package/dist/chunk-MGGY74RY.js.map +0 -1
  128. package/dist/chunk-PVPPAWVM.js +0 -46
  129. package/dist/chunk-PVPPAWVM.js.map +0 -1
  130. package/dist/chunk-R7QGMK6N.js +0 -102
  131. package/dist/chunk-R7QGMK6N.js.map +0 -1
  132. package/dist/chunk-RT6DSB5Y.js +0 -143
  133. package/dist/chunk-RT6DSB5Y.js.map +0 -1
  134. package/dist/chunk-TRCWINHF.js +0 -105
  135. package/dist/chunk-TRCWINHF.js.map +0 -1
  136. package/dist/chunk-TW7LPMVR.js +0 -16
  137. package/dist/chunk-TW7LPMVR.js.map +0 -1
  138. package/dist/chunk-UTYKLGGT.js +0 -195
  139. package/dist/chunk-UTYKLGGT.js.map +0 -1
  140. package/dist/chunk-VNJJCH5A.js +0 -822
  141. package/dist/chunk-VNJJCH5A.js.map +0 -1
  142. package/dist/chunk-XJHZWSKV.js +0 -1103
  143. package/dist/chunk-XJHZWSKV.js.map +0 -1
  144. package/dist/chunk-Y3GYJ7YR.js +0 -549
  145. package/dist/chunk-Y3GYJ7YR.js.map +0 -1
  146. package/dist/chunk-YA4EE7ST.js +0 -14107
  147. package/dist/chunk-YA4EE7ST.js.map +0 -1
  148. package/dist/chunk-YDHXWCCJ.js +0 -145
  149. package/dist/chunk-YDHXWCCJ.js.map +0 -1
  150. package/dist/chunk-YE6GUDGP.js +0 -548
  151. package/dist/chunk-YE6GUDGP.js.map +0 -1
  152. package/dist/chunk-YNKK7NE3.js +0 -171
  153. package/dist/chunk-YNKK7NE3.js.map +0 -1
  154. package/dist/chunk-YPO6RDMX.js +0 -20
  155. package/dist/chunk-YPO6RDMX.js.map +0 -1
  156. package/dist/chunk-ZS7PMGUW.js +0 -7
  157. package/dist/chunk-ZS7PMGUW.js.map +0 -1
  158. package/dist/file-intake/index.js.map +0 -1
  159. package/dist/index.js.map +0 -1
  160. package/dist/map/index.js.map +0 -1
  161. package/dist/markdown/index.js.map +0 -1
  162. package/dist/markup/index.js.map +0 -1
  163. package/dist/page-tabs/index.js.map +0 -1
  164. package/dist/sounds-NT4DEZGD.js +0 -3
  165. package/dist/sounds-NT4DEZGD.js.map +0 -1
  166. package/dist/ui/index.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/map/attribution.ts","../../src/map/maplibre.ts","../../src/map/useDarkTheme.ts","../../src/map/EntityMap.tsx","../../src/map/points.ts","../../src/map/MapOverlays.tsx","../../src/map/useMapPoints.ts","../../src/map/LocationsMap.tsx"],"names":["jsx","jsxs","useRef","useEffect","useState"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Keep the map's credit line (\"OpenFreeMap © OpenMapTiles Data from\n * OpenStreetMap\") folded behind its (i) button until someone clicks it —\n * expanded, it runs across the bottom of a compact map.\n *\n * MapLibre's compact attribution control unfolds ITSELF once: the first time\n * the attribution text arrives with the style's sources it adds\n * `maplibregl-compact` together with `maplibregl-compact-show`, which can be\n * well after the map's `load` (folding on `load` or `idle` lost that race). So\n * this watches the control and removes `maplibregl-compact-show` the first\n * time it appears — the same class MapLibre itself drops when the map is\n * dragged — then stops watching. From then on the (i) toggles the text as\n * usual: MapLibre only unfolds automatically while the control is not yet\n * compact, and after this it always is.\n *\n * Returns the cleanup that disconnects the watcher if the map unmounts first.\n */\nexport const COMPACT_SHOW = 'maplibregl-compact-show';\n\nexport function foldAttributionOnce(control: Element | null): () => void {\n if (!control) return () => {};\n const fold = () => {\n if (!control.classList.contains(COMPACT_SHOW)) return false;\n control.classList.remove(COMPACT_SHOW);\n return true;\n };\n if (fold()) return () => {};\n const watcher = new MutationObserver(() => {\n if (fold()) watcher.disconnect();\n });\n watcher.observe(control, { attributes: true, attributeFilter: ['class'] });\n return () => watcher.disconnect();\n}\n","/**\n * MapLibre, fetched only when a map is on screen: `EntityMap` calls\n * `loadMapLibre()` as it mounts, so its ~1 MB (and its stylesheet) never rides\n * in a page that shows no map. `maplibre-gl` is an OPTIONAL peer of this\n * package, installed by the portals that draw maps, and it must stay behind\n * this dynamic import — `scripts/verify-dist.mjs` fails a build that reaches\n * it statically.\n *\n * The worker. MapLibre 6 starts a MODULE worker from a URL it is given, and\n * that worker (`maplibre-gl-worker.mjs`) imports `./maplibre-gl-shared.mjs`\n * beside it — so the host serves both files, unhashed, next to each other,\n * under the installed version:\n *\n * /maplibre-gl/<version>/maplibre-gl-worker.mjs\n * /maplibre-gl/<version>/maplibre-gl-shared.mjs\n *\n * The versioned path means an upgrade can never pair a cached old worker with\n * a new main thread. In the Next.js portals that is a static route handler,\n * `app/maplibre-gl/[version]/[file]/route.ts`, copying the two files verbatim\n * from `node_modules/maplibre-gl/dist` at export time.\n */\ntype MapLibre = typeof import('maplibre-gl');\n\nexport const MAPLIBRE_WORKER_FILE = 'maplibre-gl-worker.mjs';\n\n/** Where the host serves the worker for a given MapLibre version. */\nexport const mapLibreWorkerUrl = (version: string) => `/maplibre-gl/${version}/${MAPLIBRE_WORKER_FILE}`;\n\nlet loading: Promise<MapLibre> | null = null;\n\n/** MapLibre, loaded once per page; a failed load is retried by the next map. */\nexport function loadMapLibre(): Promise<MapLibre> {\n loading ??= Promise.all([import('maplibre-gl'), import('maplibre-gl/dist/maplibre-gl.css')])\n .then(([maplibre]) => {\n maplibre.setWorkerUrl(mapLibreWorkerUrl(maplibre.getVersion()));\n return maplibre;\n })\n .catch((error: unknown) => {\n loading = null;\n throw error;\n });\n return loading;\n}\n","import { useSyncExternalStore } from 'react';\nimport { rootIsDark, ROOT_THEME_ATTRIBUTES } from '../utils/rootTheme';\n\n/**\n * True while the page is in the dark theme; re-renders when it changes. The\n * map canvas cannot follow a stylesheet — it swaps its tile style — so it has\n * to be told.\n *\n * Reads `<html>` rather than calling `useTheme`: every host stamps the theme\n * there (the desktop shell through `useTheme`, the routed portals through their\n * own theme toggle, a next-themes app as a `dark` class), while `useTheme` is\n * the desktop's and owns side effects (it removes the attribute on unmount).\n * `rootIsDark` accepts either marker, exactly as ui.css does.\n */\nfunction subscribe(onChange: () => void): () => void {\n const watcher = new MutationObserver(onChange);\n watcher.observe(document.documentElement, { attributes: true, attributeFilter: ROOT_THEME_ATTRIBUTES });\n return () => watcher.disconnect();\n}\n\nconst isDark = rootIsDark;\n\nexport function useDarkTheme(): boolean {\n return useSyncExternalStore(subscribe, isDark, () => false);\n}\n","import { useCallback, useEffect, useRef, useState, type ReactNode } from 'react';\nimport type { GeoJSONSource, Map as MapLibreMap, Marker } from 'maplibre-gl';\nimport { foldAttributionOnce } from './attribution';\nimport { loadMapLibre } from './maplibre';\nimport { useDarkTheme } from './useDarkTheme';\n\n/**\n * Records on a map. One pin per placed record: a button carrying a glyph (or\n * an icon) and, for a screen reader, the record's name in words, never the\n * colour alone. The one map every EFFICIENT portal draws — the admin portal's\n * Customer / Supplier maps and Trip Planner, the dealer portal's Address Book.\n *\n * Ported from Foyerboard's leads map, which draws its map the way\n * whoson.today does: MapLibre over OpenFreeMap's vector tiles — free,\n * keyless, OpenStreetMap data (the same data the backend geocodes against), a\n * light and a dark style. The tiles load straight from tiles.openfreemap.org,\n * so a host with a Content-Security-Policy must allow that origin.\n *\n * MapLibre is fetched only once the map mounts (`loadMapLibre`, which also\n * says how the host serves its worker), and the map is made once per mount;\n * pins, the selection, the theme and the fitted view are separate effects so\n * a re-render never rebuilds the map.\n *\n * It also draws ROUTES — the Trip Planner's days, each a line under the pins\n * (solid along the streets, dashed where it is only a straight-line estimate),\n * again after a theme change, which replaces the style and every layer on it.\n */\n\nexport interface MapPin {\n key: string;\n label: string;\n lat: number;\n lng: number;\n /** One or two characters drawn on the pin (when it has no `icon`). */\n glyph: string;\n /** An icon drawn instead of the glyph: SVG path data, 24 × 24, stroked —\n * the admin Trip Planner's stage icons. */\n icon?: readonly string[];\n /** A small tag on the pin's corner — a visit's place in the day's order. */\n badge?: string;\n /** Tone classes for the badge; white by default. */\n badgeTone?: string;\n /** Tone classes (background + text) — see `pinTone`. */\n tone: string;\n /** The pin marks an area, not the address: drawn dashed and faded. */\n approximate?: boolean;\n /** Shown for context only (another day's visits on the Trip Planner): drawn faint. */\n faded?: boolean;\n}\n\n/** A line on the map: a day's road, or its straight-line estimate. */\nexport interface MapRoute {\n key: string;\n /** [lng, lat] pairs, in order. */\n coordinates: [number, number][];\n /** Straight lines between the points, not the road: drawn dashed. */\n estimate: boolean;\n /** Context only (another day): drawn faint. */\n faded?: boolean;\n}\n\nconst ROUTE_SOURCE = 'routes';\n// The GeoJSON MapLibre's source takes, from its own types (no direct @types/geojson dependency).\ntype RouteData = Exclude<Parameters<GeoJSONSource['setData']>[0], string>;\n\n/**\n * The accent the theme gives a blue control, read off a probe element — the\n * shell's themes remap `text-blue-600` to their accent, and the map canvas\n * cannot read a class, only a colour.\n */\nfunction accentColor(): string {\n const probe = document.createElement('span');\n probe.className = 'text-blue-600';\n probe.style.display = 'none';\n document.body.appendChild(probe);\n const color = getComputedStyle(probe).color || '#2563eb';\n probe.remove();\n return toRgb(color);\n}\n\n/**\n * Any CSS colour the browser knows, as `rgb()` — what MapLibre can parse.\n * Tailwind v4's default palette computes to `oklch(…)`, which MapLibre\n * silently fails to read, drawing the route in nothing at all (only the\n * themes that remap blue to an `rgb` accent showed a line).\n */\nfunction toRgb(css: string): string {\n const ctx = document.createElement('canvas').getContext('2d');\n if (!ctx) return '#2563eb';\n ctx.fillStyle = '#2563eb';\n ctx.fillStyle = css;\n ctx.fillRect(0, 0, 1, 1);\n const [r, g, b] = ctx.getImageData(0, 0, 1, 1).data;\n return `rgb(${r}, ${g}, ${b})`;\n}\n\nconst STYLES = {\n light: 'https://tiles.openfreemap.org/styles/positron',\n dark: 'https://tiles.openfreemap.org/styles/dark',\n} as const;\n\n/** Where the view opens before it has fitted the pins: the whole world. */\nconst WORLD: { center: [number, number]; zoom: number } = { center: [30, 20], zoom: 1 };\n\ntype MapLibre = typeof import('maplibre-gl');\n\nconst SVG_NS = 'http://www.w3.org/2000/svg';\n\nfunction iconElement(paths: readonly string[], selected: boolean): SVGSVGElement {\n const svg = document.createElementNS(SVG_NS, 'svg');\n svg.setAttribute('viewBox', '0 0 24 24');\n svg.setAttribute('fill', 'none');\n svg.setAttribute('stroke', 'currentColor');\n svg.setAttribute('stroke-width', '2');\n svg.setAttribute('aria-hidden', 'true');\n svg.setAttribute('class', selected ? 'h-4 w-4' : 'h-3.5 w-3.5');\n for (const d of paths) {\n const path = document.createElementNS(SVG_NS, 'path');\n path.setAttribute('stroke-linecap', 'round');\n path.setAttribute('stroke-linejoin', 'round');\n path.setAttribute('d', d);\n svg.appendChild(path);\n }\n return svg;\n}\n\nfunction pinElement(pin: MapPin, selected: boolean): HTMLButtonElement {\n const el = document.createElement('button');\n el.type = 'button';\n el.title = pin.approximate ? `${pin.label} (approximate)` : pin.label;\n el.setAttribute('aria-label', pin.approximate ? `${pin.label}, approximate location` : pin.label);\n el.dataset.pin = pin.key;\n el.className = [\n 'relative grid place-items-center rounded-full border-2 border-white font-semibold leading-none shadow-md cursor-pointer',\n pin.tone,\n selected ? 'h-8 w-8 text-xs ring-2 ring-gray-900 ring-offset-1' : 'h-6 w-6 text-[10px]',\n pin.approximate ? 'border-dashed opacity-80' : '',\n pin.faded && !selected ? 'opacity-40' : '',\n ].join(' ');\n if (pin.icon?.length) el.appendChild(iconElement(pin.icon, selected));\n else el.textContent = pin.glyph;\n if (pin.badge) {\n const badge = document.createElement('span');\n badge.setAttribute('aria-hidden', 'true');\n badge.className = [\n 'absolute -right-2 -top-2 grid h-4 min-w-4 place-items-center rounded-full px-0.5 text-[9px] font-semibold leading-none shadow',\n pin.badgeTone ?? 'bg-white text-gray-900',\n ].join(' ');\n badge.textContent = pin.badge;\n el.appendChild(badge);\n }\n return el;\n}\n\nexport default function EntityMap({\n pins,\n selectedKey,\n focus,\n onSelect,\n ariaLabel,\n className = '',\n children,\n routes,\n fitTo,\n attribution,\n}: {\n pins: MapPin[];\n selectedKey: string | null;\n /** Bump `n` to fly to a pin (the list's \"show on map\"). */\n focus: { key: string; n: number } | null;\n onSelect: (key: string) => void;\n ariaLabel: string;\n className?: string;\n /** Drawn over the map: the selected record's card, the legend. */\n children?: ReactNode;\n /** Lines under the pins — the Trip Planner's day routes. */\n routes?: MapRoute[];\n /** Fit the view to these pins only (the day being planned); all pins when unset. */\n fitTo?: string[] | null;\n /** Extra credit line for the attribution (HTML), e.g. the router's. Read once, at mount. */\n attribution?: string;\n}) {\n const box = useRef<HTMLDivElement>(null);\n const map = useRef<MapLibreMap | null>(null);\n const lib = useRef<MapLibre | null>(null);\n const markers = useRef<Marker[]>([]);\n const fitted = useRef('');\n const [ready, setReady] = useState(false);\n const [failed, setFailed] = useState(false);\n const theme = useDarkTheme() ? 'dark' : 'light';\n // What the map's own listeners (made once) call: the latest render's props.\n const latest = useRef({ onSelect, pins, selectedKey });\n useEffect(() => { latest.current = { onSelect, pins, selectedKey }; });\n\n /** Pan (never zoom) to the chosen pin when it is out of view — a panel\n * opening beside the map narrows it, and can cover the very pin chosen.\n * Reads refs only, so the resize observer made at mount can call it. */\n const revealSelected = useCallback(() => {\n const m = map.current;\n const { pins: shown, selectedKey: chosen } = latest.current;\n const pin = chosen ? shown.find((p) => p.key === chosen) : undefined;\n if (!m || !pin || m.getBounds().contains([pin.lng, pin.lat])) return;\n const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n m.easeTo({ center: [pin.lng, pin.lat], duration: reduced ? 0 : 400 });\n }, []);\n\n // The map itself, once per mount.\n useEffect(() => {\n let cancelled = false;\n let created: MapLibreMap | null = null;\n let observer: ResizeObserver | null = null;\n let unfoldWatch: () => void = () => {};\n void (async () => {\n try {\n const maplibre = await loadMapLibre();\n if (cancelled || !box.current) return;\n lib.current = maplibre;\n created = new maplibre.Map({\n container: box.current,\n style: STYLES[theme],\n center: WORLD.center,\n zoom: WORLD.zoom,\n attributionControl: attribution\n ? { compact: true, customAttribution: attribution }\n : { compact: true },\n dragRotate: false,\n pitchWithRotate: false,\n touchPitch: false,\n maxPitch: 0,\n });\n created.touchZoomRotate.disableRotation();\n created.keyboard.disableRotation();\n created.addControl(new maplibre.NavigationControl({ showCompass: false }), 'top-right');\n // Tiles that do not come (offline, the tile host down) leave a blank\n // map; the pins still stand on it and the list has everything.\n created.on('error', (event) => console.warn('[map]', event.error?.message));\n // The credit line starts folded behind its (i) — see attribution.ts.\n unfoldWatch = foldAttributionOnce(box.current.querySelector('.maplibregl-ctrl-attrib'));\n // A shell window resizes without a window resize event, which is all\n // MapLibre listens for — without this the canvas keeps its first size.\n const current = created;\n observer = new ResizeObserver(() => { current.resize(); revealSelected(); });\n observer.observe(box.current);\n map.current = created;\n setReady(true);\n } catch (error) {\n console.error('[map] could not start', error);\n if (!cancelled) setFailed(true);\n }\n })();\n return () => {\n cancelled = true;\n observer?.disconnect();\n unfoldWatch();\n markers.current.forEach((m) => m.remove());\n markers.current = [];\n created?.remove();\n map.current = null;\n };\n // The theme is applied by the effect below; the map is made once.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // The theme's tiles.\n const shownTheme = useRef(theme);\n useEffect(() => {\n if (!ready || !map.current || shownTheme.current === theme) return;\n shownTheme.current = theme;\n map.current.setStyle(STYLES[theme]);\n }, [ready, theme]);\n\n // The pins — the selected one last, so it sits on top.\n useEffect(() => {\n const m = map.current;\n const maplibre = lib.current;\n if (!ready || !m || !maplibre) return;\n markers.current.forEach((marker) => marker.remove());\n markers.current = [];\n const ordered = [...pins].sort((a, b) => Number(a.key === selectedKey) - Number(b.key === selectedKey));\n for (const pin of ordered) {\n const el = pinElement(pin, pin.key === selectedKey);\n el.addEventListener('click', (event) => {\n event.stopPropagation();\n latest.current.onSelect(pin.key);\n });\n markers.current.push(new maplibre.Marker({ element: el, anchor: 'center' }).setLngLat([pin.lng, pin.lat]).addTo(m));\n }\n }, [ready, pins, selectedKey]);\n\n // Keep a newly chosen pin in view.\n useEffect(() => {\n if (!ready || !selectedKey) return;\n const frame = requestAnimationFrame(revealSelected);\n return () => cancelAnimationFrame(frame);\n }, [ready, selectedKey, revealSelected]);\n\n // Fit the pins when WHICH pins are shown changes — not on every redraw, so\n // a poll that adds one located address doesn't yank the view the user panned.\n useEffect(() => {\n const m = map.current;\n const maplibre = lib.current;\n const target = fitTo ? pins.filter((p) => fitTo.includes(p.key)) : pins;\n if (!ready || !m || !maplibre || target.length === 0) return;\n const key = target.map((p) => `${p.key}@${p.lat},${p.lng}`).sort().join('|');\n if (fitted.current === key) return;\n const first = fitted.current === '';\n fitted.current = key;\n if (target.length === 1) {\n m.jumpTo({ center: [target[0].lng, target[0].lat], zoom: 13 });\n return;\n }\n const bounds = new maplibre.LngLatBounds([target[0].lng, target[0].lat], [target[0].lng, target[0].lat]);\n for (const p of target) bounds.extend([p.lng, p.lat]);\n m.fitBounds(bounds, { padding: 48, maxZoom: 14, animate: !first });\n }, [ready, pins, fitTo]);\n\n // The routes, as lines under the pins. A theme change replaces the style —\n // and with it every source and layer — so they are drawn again on each\n // `style.load`, not only when the routes change.\n useEffect(() => {\n const m = map.current;\n if (!ready || !m) return;\n const data: RouteData = {\n type: 'FeatureCollection',\n features: (routes ?? []).filter((r) => r.coordinates.length >= 2).map((r) => ({\n type: 'Feature',\n properties: { key: r.key, estimate: r.estimate, faded: !!r.faded },\n geometry: { type: 'LineString', coordinates: r.coordinates },\n })),\n };\n // Idempotent: each piece (the source, each layer) is made if missing and\n // updated if there, so a draw cut short by a style still loading never\n // leaves a source without its layers.\n const draw = () => {\n const color = accentColor();\n const opacity = ['case', ['get', 'faded'], 0.25, 0.85] as unknown as number;\n const source = m.getSource<GeoJSONSource>(ROUTE_SOURCE);\n if (source) source.setData(data);\n else m.addSource(ROUTE_SOURCE, { type: 'geojson', data });\n if (m.getLayer('routes-road')) m.setPaintProperty('routes-road', 'line-color', color);\n else m.addLayer({\n id: 'routes-road', type: 'line', source: ROUTE_SOURCE,\n filter: ['!', ['get', 'estimate']],\n layout: { 'line-join': 'round', 'line-cap': 'round' },\n paint: { 'line-color': color, 'line-width': 4, 'line-opacity': opacity },\n });\n if (m.getLayer('routes-estimate')) m.setPaintProperty('routes-estimate', 'line-color', color);\n else m.addLayer({\n id: 'routes-estimate', type: 'line', source: ROUTE_SOURCE,\n filter: ['get', 'estimate'],\n layout: { 'line-join': 'round', 'line-cap': 'round' },\n paint: { 'line-color': color, 'line-width': 3, 'line-dasharray': [1.5, 1.5], 'line-opacity': opacity },\n });\n };\n // Not gated on `isStyleLoaded()`: that is false whenever TILES are still\n // loading (after any pan or zoom), which skipped the redraw and left a\n // reordered day showing its old line. Before the style itself is in, the\n // map refuses sources and layers — `style.load` draws then.\n const tryDraw = () => {\n try {\n draw();\n } catch (error) {\n console.debug('[map] routes wait for the style', error);\n }\n };\n tryDraw();\n m.on('style.load', tryDraw);\n // A style that finished loading between this effect's first try and its\n // listener would never fire `style.load` again; `idle` catches it.\n m.once('idle', tryDraw);\n return () => {\n m.off('style.load', tryDraw);\n m.off('idle', tryDraw);\n };\n }, [ready, routes, theme]);\n\n // Fly to a pin the list asked to show.\n useEffect(() => {\n const m = map.current;\n if (!ready || !m || !focus) return;\n const pin = pins.find((p) => p.key === focus.key);\n if (!pin) return;\n const to = { center: [pin.lng, pin.lat] as [number, number], zoom: Math.max(m.getZoom(), 13) };\n if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) m.jumpTo(to);\n else m.flyTo({ ...to, duration: 600 });\n // `focus.n` is what asks; the pins changing under it does not.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [ready, focus?.n]);\n\n return (\n <div className={`relative min-h-72 overflow-hidden rounded-lg border border-gray-200 bg-gray-100 ${className}`}>\n {/* Out of flow in a box of its own: MapLibre makes its container `position: relative`. */}\n <div className=\"absolute inset-0\">\n <div ref={box} className=\"h-full w-full\" role=\"region\" aria-label={ariaLabel} />\n </div>\n {failed && (\n <p className=\"absolute inset-0 grid place-items-center p-6 text-center text-sm text-gray-500\">\n The map could not be drawn here. The list has everything it shows.\n </p>\n )}\n {children}\n </div>\n );\n}\n","/**\n * Map points — the wire shape every EFFICIENT map endpoint answers with, and\n * the pure logic over it: what a point says about itself, how its pin looks,\n * and the counts over a list of them. Kept free of React and MapLibre so it is\n * testable without WebGL.\n *\n * The shape is efficient-backend's `logistics.serializers.MapPointsSerializer`\n * (the `address-geocoding` capability): the admin portal's Customer / Supplier\n * maps and Trip Planner, and the dealer portal's Address Book. Every record\n * comes back, pinned or not — a point off the map has null `lat` / `lng` and a\n * `placement` saying why, so a list beside the map can explain it instead of\n * silently dropping the row. The backend never makes a request wait on the\n * geocoder: an address it has not seen is `pending`, and the caller asks again\n * while `pending > 0` (`mapPointsRefetchInterval`).\n *\n * Moved here from the admin portal (`src/components/map/mapPoints.ts`) so the\n * dealer portal draws the same map rather than a second one.\n */\n\n/**\n * located — the geocoder matched the street\n * approximate — only the suburb / postcode / city matched; the pin marks the area\n * pending — still being looked up (poll again)\n * not_found — the geocoder found nothing for this address\n * failed — the map service could not answer; retried later\n * no_address — too little to look up: no city or postcode (and none written into the street)\n */\nexport type MapPlacement = 'located' | 'approximate' | 'pending' | 'not_found' | 'failed' | 'no_address';\n\n/**\n * One record on (or off) a map. `kind` is the record type the endpoint pinned\n * (`customer`, `delivery_location`, `supplier`, `warehouse`, the Trip Planner's\n * `office` / `trip_place` / `trip_stop` …); a portal narrows it to the kinds it\n * reads with the type parameter.\n */\nexport interface MapPoint<K extends string = string> {\n kind: K;\n id: string;\n label: string;\n /** CID / MID where the record has one. */\n code: string;\n /** What the pin is coloured by: the customer type, the supplier type, or the kind. */\n category: string;\n /** The address as one line. */\n address: string;\n placement: MapPlacement;\n lat: number | null;\n lng: number | null;\n /** What the geocoder matched, when placed. */\n matched: string;\n}\n\nexport interface MapPoints<P extends MapPoint = MapPoint> {\n points: P[];\n /** Points still being looked up — poll again while this is above zero. */\n pending: number;\n}\n\n/** How often to ask again while addresses are still being located. */\nexport const PENDING_POLL_MS = 3000;\n\n/** A react-query `refetchInterval` for a map endpoint: every few seconds while\n * the backend is still locating addresses, not at all once every point is settled. */\nexport const mapPointsRefetchInterval = (data: Pick<MapPoints, 'pending'> | undefined): number | false =>\n (data?.pending ? PENDING_POLL_MS : false);\n\n/** A point's identity across kinds — a customer and one of its delivery\n * locations are different pins even if two ids ever collided. */\nexport const pointKey = (p: Pick<MapPoint, 'kind' | 'id'>) => `${p.kind}:${p.id}`;\n\n/** On the map = placed by the geocoder AND carrying coordinates. */\nexport const isOnMap = <P extends MapPoint>(p: P): p is P & { lat: number; lng: number } =>\n (p.placement === 'located' || p.placement === 'approximate') && p.lat != null && p.lng != null;\n\n/** Why a point is not (exactly) where its address is — null when it is. */\nexport function placementNote(placement: MapPlacement): string | null {\n switch (placement) {\n case 'located': return null;\n case 'approximate': return 'Approximate: only the suburb or postcode matched';\n case 'pending': return 'Locating…';\n case 'not_found': return 'Not on the map: address not found';\n case 'failed': return 'Not on the map: the map service could not be reached';\n case 'no_address': return 'Not on the map: no city or postcode';\n }\n}\n\n/** A pin colour and the legend's word for it. */\nexport interface MapCategory {\n label: string;\n /** Tone classes (background + text) for the pin and its legend swatch. */\n tone: string;\n}\n\n/**\n * Pin colours by category. Customers follow the admin Customers list's Type\n * badge families (importer, dealer purple, retail green); a record's\n * secondary locations (delivery locations, warehouses) are amber.\n *\n * Importer and supplier are SKY, not blue: this package's themes remap\n * `bg-blue-600` to the theme's accent (themes.css), so in the green theme\n * importer pins came out the same green as retail. None of the other families\n * here is remapped — a categorical colour must not follow the accent the way\n * a button does.\n *\n * A portal that names a category differently passes its own map with the same\n * tones (the dealer's own head office is its \"Billing address\", not \"Dealer\").\n */\nexport const MAP_CATEGORIES: Readonly<Record<string, MapCategory>> = {\n importer: { label: 'Importer', tone: 'bg-sky-600 text-white' },\n dealer: { label: 'Dealer', tone: 'bg-purple-600 text-white' },\n retail: { label: 'Retail', tone: 'bg-green-600 text-white' },\n supplier: { label: 'Supplier', tone: 'bg-sky-600 text-white' },\n delivery_location: { label: 'Delivery location', tone: 'bg-amber-600 text-white' },\n warehouse: { label: 'Warehouse', tone: 'bg-amber-600 text-white' },\n};\nconst DEFAULT_TONE = 'bg-gray-700 text-white';\n/** The legend's order. */\nconst CATEGORY_ORDER = ['importer', 'dealer', 'retail', 'supplier', 'delivery_location', 'warehouse'];\n\n/** Suppliers colour by the kind rather than their free-text type (Factory,\n * Shipping Forwarder…), which has no fixed palette to map onto. */\nexport function pinCategory(p: Pick<MapPoint, 'kind' | 'category'>): string {\n if (p.kind === 'supplier') return 'supplier';\n if (p.kind === 'customer') return p.category || 'importer';\n return p.kind;\n}\n\nexport const categoryTone = (category: string, categories: Readonly<Record<string, MapCategory>> = MAP_CATEGORIES): string =>\n categories[category]?.tone ?? DEFAULT_TONE;\n\nexport const categoryLabel = (category: string, categories: Readonly<Record<string, MapCategory>> = MAP_CATEGORIES): string =>\n categories[category]?.label ?? category;\n\nexport const pinTone = (p: Pick<MapPoint, 'kind' | 'category'>, categories?: Readonly<Record<string, MapCategory>>): string =>\n categoryTone(pinCategory(p), categories);\n\n/**\n * The character on a pin. A profile map numbers its extra locations (1, 2, 3 —\n * the same numbers the list shows) and marks the head office with its\n * category's initial; a list-wide map shows each record's category initial.\n */\nexport function pinGlyph(p: MapPoint, number: number | null, categories: Readonly<Record<string, MapCategory>> = MAP_CATEGORIES): string {\n if (number != null) return String(number);\n return (categories[pinCategory(p)]?.label ?? '').charAt(0).toUpperCase();\n}\n\n/** The kinds a profile map numbers: a record's secondary locations. */\nexport const SECONDARY_KINDS: readonly string[] = ['delivery_location', 'warehouse'];\n\n/**\n * The numbers a profile map gives its secondary locations, in list order; the\n * record itself gets none. An unplaced location keeps its number, so the list\n * and the pins never renumber as addresses resolve.\n */\nexport function locationNumbers(points: MapPoint[], kinds: readonly string[] = SECONDARY_KINDS): Map<string, number> {\n const numbers = new Map<string, number>();\n let n = 0;\n for (const p of points) {\n if (kinds.includes(p.kind)) numbers.set(pointKey(p), ++n);\n }\n return numbers;\n}\n\nexport interface MapSummary {\n onMap: number;\n approximate: number;\n pending: number;\n off: number;\n}\n\nexport function summarize(points: MapPoint[]): MapSummary {\n const summary: MapSummary = { onMap: 0, approximate: 0, pending: 0, off: 0 };\n for (const p of points) {\n if (isOnMap(p)) {\n summary.onMap += 1;\n if (p.placement === 'approximate') summary.approximate += 1;\n } else if (p.placement === 'pending') summary.pending += 1;\n else summary.off += 1;\n }\n return summary;\n}\n\n/** \"12 on the map · 2 approximate · 3 not on the map\" — the list header. */\nexport function summaryText(s: MapSummary): string {\n const parts = [`${s.onMap} on the map`];\n if (s.approximate) parts.push(`${s.approximate} approximate`);\n if (s.pending) parts.push(`${s.pending} locating`);\n if (s.off) parts.push(`${s.off} not on the map`);\n return parts.join(' · ');\n}\n\n/** The categories present among the placed points, in a stable order — the\n * legend. Categories outside the palette follow, in the order first seen. */\nexport function legendCategories(points: MapPoint[]): string[] {\n const present = [...new Set(points.filter(isOnMap).map(pinCategory))];\n const known = CATEGORY_ORDER.filter((c) => present.includes(c));\n return [...known, ...present.filter((c) => !CATEGORY_ORDER.includes(c))];\n}\n\n/**\n * The records a compact map can't pin, each with its reason, for the notes\n * drawn over the map (a map with no list beside it). Pending points are left\n * out — they are counted as \"Locating…\" instead.\n */\nexport function offMapNotes(points: MapPoint[], numbers?: ReadonlyMap<string, number>): { key: string; text: string }[] {\n return points\n .filter((p) => !isOnMap(p) && p.placement !== 'pending')\n .map((p) => {\n const n = numbers?.get(pointKey(p));\n const note = placementNote(p.placement)?.replace(/^Not on the map: /, '') ?? '';\n return { key: pointKey(p), text: `${n != null ? `${n}. ` : ''}${p.label}: ${note}` };\n });\n}\n","import Button from '../forms/Button';\nimport { MAP_CATEGORIES, categoryLabel, categoryTone, pinCategory, placementNote, type MapCategory, type MapPoint } from './points';\n\n/**\n * What sits over an `EntityMap` — the legend and the chosen record's card.\n * Both take the portal's category map (`MAP_CATEGORIES` unless it names a\n * category its own way), so the legend's words and the card's match the pins.\n */\n\n/** Which colour means what; drawn only when the map shows more than one.\n * `floating` pins it to the map's top-left corner; off, the caller places it\n * (a compact map stacks its notes under it). */\nexport function MapLegend({ categories, palette = MAP_CATEGORIES, floating = true }: {\n categories: string[];\n palette?: Readonly<Record<string, MapCategory>>;\n floating?: boolean;\n}) {\n if (categories.length < 2) return null;\n return (\n <div className={`${floating ? 'absolute left-3 top-3 z-10 ' : ''}flex flex-wrap gap-x-3 gap-y-1 rounded-lg border border-gray-200 bg-white px-2.5 py-1.5 text-xs text-gray-600 shadow-sm`}>\n {categories.map((c) => (\n <span key={c} className=\"inline-flex items-center gap-1.5\">\n <span aria-hidden className={`h-2.5 w-2.5 rounded-full ${categoryTone(c, palette)}`} />\n {categoryLabel(c, palette)}\n </span>\n ))}\n </div>\n );\n}\n\n/** The chosen record's card, over the map's bottom edge. */\nexport function SelectedCard({ point, number, onOpen, onClose, palette = MAP_CATEGORIES }: {\n point: MapPoint;\n number: number | null;\n /** Open the record's own page or window; null when it has none. */\n onOpen: (() => void) | null;\n onClose: () => void;\n palette?: Readonly<Record<string, MapCategory>>;\n}) {\n const note = placementNote(point.placement);\n const category = pinCategory(point);\n return (\n <section aria-label={`${point.label}, chosen on the map`}\n className=\"absolute bottom-3 left-3 right-3 z-10 max-w-md space-y-1.5 rounded-lg border border-gray-200 bg-white p-3 shadow-md\">\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"min-w-0\">\n <h3 className=\"text-sm font-semibold text-gray-900 [overflow-wrap:anywhere]\">\n {number != null && <span className=\"mr-1 text-gray-400\">{number}.</span>}\n {point.label}\n {point.code && <span className=\"ml-1.5 text-xs font-normal text-gray-400\">{point.code}</span>}\n </h3>\n <p className=\"text-xs text-gray-500\">{palette[category] ? categoryLabel(category, palette) : point.category}</p>\n </div>\n <button type=\"button\" onClick={onClose} aria-label=\"Close\" className=\"rounded p-0.5 text-gray-400 hover:text-gray-600\">\n <svg aria-hidden className=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2}><path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M6 18L18 6M6 6l12 12\" /></svg>\n </button>\n </div>\n <p className=\"text-sm text-gray-700 [overflow-wrap:anywhere]\">{point.address || '—'}</p>\n {note && <p className=\"text-xs text-gray-500\">{note}</p>}\n {point.matched && point.placement === 'approximate' && (\n <p className=\"text-xs text-gray-400 [overflow-wrap:anywhere]\">Placed at: {point.matched}</p>\n )}\n {onOpen && (\n <div className=\"flex justify-end\">\n <Button variant=\"secondary\" size=\"sm\" onClick={onOpen}>Open</Button>\n </div>\n )}\n </section>\n );\n}\n","import { useQuery, type QueryKey } from '@tanstack/react-query';\nimport { mapPointsRefetchInterval, type MapPoints } from './points';\n\n/**\n * A map endpoint's points, asked again every few seconds while the backend is\n * still locating addresses (`pending > 0`) and not at all once every point is\n * settled. The backend never makes the request wait on the geocoder, so a\n * first visit shows the list at once and the pins arrive as they are found.\n */\nexport function useMapPoints<T extends MapPoints>(\n queryKey: QueryKey,\n queryFn: () => Promise<T>,\n { staleTime }: { staleTime?: number } = {},\n) {\n return useQuery({\n queryKey,\n queryFn,\n staleTime,\n refetchInterval: (query) => mapPointsRefetchInterval(query.state.data),\n });\n}\n","import { useEffect, useMemo, useRef, useState } from 'react';\nimport type { QueryKey } from '@tanstack/react-query';\nimport EntityMap, { type MapPin } from './EntityMap';\nimport { MapLegend, SelectedCard } from './MapOverlays';\nimport {\n MAP_CATEGORIES, isOnMap, legendCategories, locationNumbers, offMapNotes, pinGlyph, pinTone, pointKey, summarize,\n summaryText, type MapCategory, type MapPoint, type MapPoints,\n} from './points';\nimport { useMapPoints } from './useMapPoints';\n\n/** How many off-map records the notes box names before it says \"+N more\". */\nconst NOTES_SHOWN = 3;\n\n/**\n * A record's Locations map — compact, with no list beside it, because the\n * page it sits on already lists the locations. The admin portal puts it on the\n * customer / supplier Details tab beside the score (a customer's head office\n * and delivery locations, a supplier's address and warehouses); the dealer\n * portal puts it on the Address Book (the billing address and the delivery\n * addresses). What a list would say lives over the map: the legend,\n * \"Locating…\" while addresses are looked up, and a note for each record that\n * has no pin and why. Clicking a pin shows its card.\n *\n * `refreshSignal` changes when the records it pins change elsewhere on the\n * page (a delivery location added or edited, the address edited), and the\n * points are asked again; `staleTime: 0` does the same whenever it remounts.\n *\n * Moved from the admin portal's `ProfileLocationsMap`.\n */\nexport default function LocationsMap({\n queryKey,\n queryFn,\n refreshSignal,\n onOpen,\n ariaLabel,\n title = 'Locations',\n palette = MAP_CATEGORIES,\n numbers: givenNumbers,\n className = '',\n mapClassName = 'min-h-[240px] flex-1',\n}: {\n queryKey: QueryKey;\n queryFn: () => Promise<MapPoints>;\n refreshSignal?: unknown;\n /** Open a point's own page or window; null for points that have none. */\n onOpen?: (point: MapPoint) => ((() => void) | null);\n ariaLabel: string;\n /** The heading over the map. */\n title?: string;\n /** How the portal names and colours each category (the legend, the card, the head-office initial). */\n palette?: Readonly<Record<string, MapCategory>>;\n /** The number each pin shows, by `pointKey` — the numbers the page's own list\n * shows. Unset: the secondary locations numbered in the endpoint's order. */\n numbers?: ReadonlyMap<string, number>;\n className?: string;\n /** Sizing for the map box (it grows to fill the section by default). */\n mapClassName?: string;\n}) {\n const { data, isError, refetch } = useMapPoints(queryKey, queryFn, { staleTime: 0 });\n const mounted = useRef(false);\n useEffect(() => {\n if (!mounted.current) {\n mounted.current = true;\n return;\n }\n void refetch();\n // Only a change of the signal asks again; `refetch` is stable.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [refreshSignal]);\n\n const [selectedKey, setSelectedKey] = useState<string | null>(null);\n const points = useMemo(() => data?.points ?? [], [data]);\n const numbers = useMemo(() => givenNumbers ?? locationNumbers(points), [givenNumbers, points]);\n const pins: MapPin[] = useMemo(() => points.filter(isOnMap).map((p) => ({\n key: pointKey(p),\n label: p.label,\n lat: p.lat,\n lng: p.lng,\n glyph: pinGlyph(p, numbers.get(pointKey(p)) ?? null, palette),\n tone: pinTone(p, palette),\n approximate: p.placement === 'approximate',\n })), [points, numbers, palette]);\n const notes = useMemo(() => offMapNotes(points, numbers), [points, numbers]);\n const legend = useMemo(() => legendCategories(points), [points]);\n const pending = data?.pending ?? 0;\n const selected = points.find((p) => pointKey(p) === selectedKey) ?? null;\n const open = selected && onOpen ? onOpen(selected) : null;\n\n return (\n <section aria-label={title} className={`flex min-w-0 flex-col ${className}`}>\n {/* The score beside it heads its card with the period picker (30px tall);\n the same row height keeps the two boxes' tops level. */}\n <div className=\"flex min-h-[30px] items-center justify-between mb-2\">\n <h3 className=\"text-sm font-semibold text-gray-900\">{title}</h3>\n {data && <span className=\"text-xs text-gray-500\">{summaryText(summarize(points))}</span>}\n </div>\n <EntityMap pins={pins} selectedKey={selectedKey} focus={null} onSelect={setSelectedKey}\n ariaLabel={ariaLabel} className={mapClassName}>\n {/* Top-left, under the legend: the bottom edge belongs to the map's\n OpenStreetMap credit and the chosen record's card. */}\n <div className=\"absolute left-3 top-3 z-10 flex max-w-[70%] flex-col items-start gap-1.5\">\n <MapLegend categories={legend} palette={palette} floating={false} />\n {isError && (\n <div className=\"flex items-center gap-2 rounded-lg border border-gray-200 bg-white px-2.5 py-1.5 text-xs text-gray-600 shadow-sm\">\n Couldn’t load the locations.\n <button type=\"button\" onClick={() => void refetch()} className=\"font-medium text-blue-600 hover:text-blue-800 hover:underline\">\n Try again\n </button>\n </div>\n )}\n {!isError && (pending > 0 || notes.length > 0) && (\n <div className=\"max-w-full space-y-0.5 rounded-lg border border-gray-200 bg-white px-2.5 py-1.5 text-xs shadow-sm\" aria-live=\"polite\">\n {pending > 0 && (\n <p className=\"flex items-center gap-1.5 text-gray-500\">\n <svg className=\"h-3 w-3 animate-spin\" fill=\"none\" viewBox=\"0 0 24 24\"><circle className=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" strokeWidth=\"4\" /><path className=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z\" /></svg>\n Locating {pending} address{pending === 1 ? '' : 'es'}…\n </p>\n )}\n {notes.slice(0, NOTES_SHOWN).map((n) => (\n <p key={n.key} className=\"truncate text-amber-700\" title={n.text}>{n.text}</p>\n ))}\n {notes.length > NOTES_SHOWN && (\n <p className=\"text-gray-500\">+{notes.length - NOTES_SHOWN} more not on the map</p>\n )}\n </div>\n )}\n </div>\n {selected && (\n <SelectedCard point={selected} number={numbers.get(pointKey(selected)) ?? null} onOpen={open}\n onClose={() => setSelectedKey(null)} palette={palette} />\n )}\n </EntityMap>\n </section>\n );\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/markdown/index.tsx"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { memo, useMemo, useRef, useState } from 'react';\nimport type { ReactNode } from 'react';\nimport ReactMarkdown, { type Components } from 'react-markdown';\nimport remarkBreaks from 'remark-breaks';\nimport remarkGfm from 'remark-gfm';\n\nimport { useBrowserLayoutEffect } from '../shell/hydration';\n\n/**\n * The full Markdown renderer — CommonMark + GFM, behind its own subpath.\n *\n * ## Why this is not in the UI kit\n *\n * `efficient-shell/ui` promises to import nothing but React, and two checks\n * hold that line: `tests/uiEntryIsPeerFree.test.ts` walks the source graph and\n * `scripts/verify-dist.mjs` walks the built one (only the second catches a peer\n * arriving through a shared chunk). That promise is what lets the till, which\n * has no `@heroicons/react` installed at all, take the kit.\n *\n * The kit's `Markdown` was written under that constraint — a regex renderer,\n * because a regex was all it could afford. It is still there, still correct for\n * what it covers, and still what `HelpCenter` falls back to. But a regex cannot\n * nest, so it cannot grow tables inside list items, task lists, footnotes, or\n * anything else with structure, and the day arrived when a body needed more.\n *\n * So: a second entry, with a real parser, whose peers are OPTIONAL. A consumer\n * that never imports `efficient-shell/markdown` never installs react-markdown\n * and never pays a byte for it — the same bargain `efficient-shell/markup`\n * already strikes from the other direction. The package's `dependencies` stay\n * empty, which is the promise underneath all of the others.\n *\n * ## What is deliberately NOT here\n *\n * - **Syntax highlighting.** Shiki and Prism each cost more than this whole\n * package. Code blocks are monospace with their own horizontal scroll, and\n * the info string is carried through as `data-language` so a host that wants\n * highlighting can add it without a fork.\n * - **`rehype-raw`.** Adding it would re-open raw HTML, and raw HTML in a body\n * a customer typed is an XSS surface. The dialect below closes it instead.\n * - **A sanitiser.** Not needed, and worth stating so nobody adds one out of\n * caution: no raw HTML is parsed, and react-markdown's default `urlTransform`\n * neutralises `javascript:` URLs. There is no HTML string anywhere in this\n * pipeline to sanitise — the output is React elements throughout.\n * - **MDX.** The input is a string a person or a service wrote, not a module.\n */\n\n// ── Dialect ────────────────────────────────────────────────────────────────\n\n/**\n * Four constructs disabled, each because a body that predates markdown would\n * otherwise render surprisingly. Every one of these was found in stored copy,\n * not imagined:\n *\n * - `codeIndented` — a pasted, quoted email is indented four spaces and would\n * become a code block.\n * - `htmlFlow` / `htmlText` — `<John>` and `<script>` must render as the text\n * they are. react-markdown DROPS raw-HTML nodes silently, so without this a\n * name in angle brackets simply disappears from the message.\n * - `setextUnderline` — `Title` followed by `----` is a horizontal rule under a\n * line of text far more often than it is a heading.\n *\n * Fenced ``` blocks and `<https://…>` autolinks are untouched.\n */\nfunction remarkDialect(this: unknown) {\n const data = (this as { data: () => { micromarkExtensions?: unknown[] } }).data();\n (data.micromarkExtensions ??= []).push({\n disable: { null: ['codeIndented', 'htmlFlow', 'htmlText', 'setextUnderline'] },\n });\n}\n\n/**\n * The plugin lists, exported for a host assembling its own pipeline.\n *\n * `note` carries `remark-breaks`; `article` does not, and the difference is\n * about who wrote the text. A note is typed into a box, where pressing Return\n * means a line break. An article is authored markdown whose source is hard\n * wrapped at ~78 columns, where a single newline is a soft wrap and honouring\n * it would shred every paragraph.\n */\nexport const MARKDOWN_PLUGINS = {\n note: [remarkGfm, remarkBreaks, remarkDialect],\n article: [remarkGfm, remarkDialect],\n} as const;\n\n// ── Shared element renderers ───────────────────────────────────────────────\n\n/**\n * Colours here are utility classes that `ui.css` REMAPS under\n * `[data-theme=\"dark\"]` — this package's dark mode is not a Tailwind variant.\n * Two consequences worth carrying in your head when editing this file:\n *\n * 1. A class with no remap stays light-on-light. `tests/markdown.test.tsx`\n * fails the build rather than let one through.\n * 2. `bg-gray-200`, never `bg-gray-100`, for anything drawn ON a panel:\n * `bg-gray-100` remaps to `--surface`, and so does `bg-white`, so a chip\n * using it is the exact colour of the surface behind it. It was invisible\n * in dark mode in the kit renderer for a year.\n */\nconst linkRenderer: Components['a'] = ({ node: _n, ...props }) => (\n <a {...props} target=\"_blank\" rel=\"noopener noreferrer\" className=\"text-blue-600 hover:underline break-all\" />\n);\n\nconst codeRenderer: Components['code'] = ({ node: _n, ...props }) => (\n <code className=\"rounded bg-gray-200 px-1 py-0.5 font-mono text-[0.85em] text-gray-800\" {...props} />\n);\n\n/**\n * `pre` un-chips the `code` inside it: one `code` renderer serves both inline\n * spans and block content, so the block case has to undo the chip's own\n * background, padding and size.\n */\nconst preRenderer = (extra: string): Components['pre'] =>\n ({ node: _n, ...props }) => (\n <pre\n className={\n `${extra} overflow-x-auto rounded-lg border border-gray-200 bg-gray-50 font-mono text-xs ` +\n '[&>code]:bg-transparent [&>code]:p-0 [&>code]:[font-size:inherit]'\n }\n {...props}\n />\n );\n\nconst tableRenderers: Pick<Components, 'th' | 'td'> = {\n th: ({ node: _n, ...props }) => (\n <th className=\"border border-gray-200 bg-gray-50 px-2 py-1 text-left font-semibold text-gray-700\" {...props} />\n ),\n td: ({ node: _n, ...props }) => <td className=\"border border-gray-200 px-2 py-1 align-top\" {...props} />,\n};\n\n// ── variant: note ──────────────────────────────────────────────────────────\n\n/**\n * Timeline cards, message bodies, bug-report descriptions — anything a person\n * or a service TYPED, shown inside a card that has other things in it.\n *\n * Headings cap one step above body text. An author writing `# heading` in a\n * note means emphasis, not a document outline, and a 30px line inside a\n * timeline card reads as a rendering fault.\n */\nconst NOTE_COMPONENTS: Components = {\n a: linkRenderer,\n h1: ({ node: _n, ...props }) => <h1 className=\"text-base font-semibold text-gray-900\" {...props} />,\n h2: ({ node: _n, ...props }) => <h2 className=\"text-sm font-semibold text-gray-900\" {...props} />,\n h3: ({ node: _n, ...props }) => <h3 className=\"text-sm font-semibold text-gray-700\" {...props} />,\n h4: ({ node: _n, ...props }) => <h4 className=\"text-sm font-semibold text-gray-700\" {...props} />,\n h5: ({ node: _n, ...props }) => <h5 className=\"text-sm font-semibold text-gray-700\" {...props} />,\n h6: ({ node: _n, ...props }) => <h6 className=\"text-sm font-semibold text-gray-700\" {...props} />,\n ul: ({ node: _n, ...props }) => <ul className=\"list-disc pl-5\" {...props} />,\n ol: ({ node: _n, ...props }) => <ol className=\"list-decimal pl-5\" {...props} />,\n code: codeRenderer,\n pre: preRenderer('p-2'),\n blockquote: ({ node: _n, ...props }) => (\n <blockquote className=\"border-l-2 border-gray-300 pl-2.5 text-gray-600\" {...props} />\n ),\n hr: ({ node: _n, ...props }) => <hr className=\"border-gray-200\" {...props} />,\n table: ({ node: _n, ...props }) => (\n <div className=\"overflow-x-auto\">\n <table className=\"text-xs border-collapse\" {...props} />\n </div>\n ),\n ...tableRenderers,\n img: ({ node: _n, alt, ...props }) => (\n <img {...props} alt={alt ?? ''} loading=\"lazy\" className=\"max-w-full max-h-64 rounded-lg border border-gray-200\" />\n ),\n};\n\n/** The note variant's set, with `resolveImageSrc` wired the same way the\n * article's is — a resolver that silently applied to one variant and not the\n * other was a broken image with no signal. */\nfunction noteComponents(resolveImageSrc?: (src: string) => string): Components {\n if (!resolveImageSrc) return NOTE_COMPONENTS;\n return {\n ...NOTE_COMPONENTS,\n img: ({ node: _n, src, alt, ...props }) => {\n const raw = typeof src === 'string' ? src : undefined;\n return (\n <img\n {...props}\n src={raw ? resolveImageSrc(raw) : raw}\n alt={alt ?? ''}\n loading=\"lazy\"\n className=\"max-w-full max-h-64 rounded-lg border border-gray-200\"\n />\n );\n },\n };\n}\n\n// ── variant: article ───────────────────────────────────────────────────────\n\n/**\n * A screenshot that has not been uploaded yet shows a labelled box rather than\n * a broken-image icon: most seeded article screenshots do not exist, and a\n * broken icon per step reads as a bug where the box reads as intent.\n *\n * Spans, not divs — react-markdown puts a lone image inside a `<p>`.\n */\nfunction ArticleImage({ src, alt }: { src?: string; alt?: string }) {\n const [failed, setFailed] = useState(false);\n const label = (alt ?? '').replace(/^screenshot:\\s*/i, '');\n if (!src || failed) {\n return (\n <span className=\"my-3 flex items-start gap-2 rounded-lg border border-dashed border-gray-300 bg-gray-50 px-3 py-2.5\">\n <svg\n className=\"mt-0.5 h-4 w-4 shrink-0 text-gray-400\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path d=\"M3 6a1.5 1.5 0 0 1 1.5-1.5h1.1l.4-.9a1 1 0 0 1 .9-.6h6.2a1 1 0 0 1 .9.6l.4.9h1.1A1.5 1.5 0 0 1 18 6v8.5A1.5 1.5 0 0 1 16.5 16h-13A1.5 1.5 0 0 1 2 14.5V6Zm7.5 1.5a3.25 3.25 0 1 0 0 6.5 3.25 3.25 0 0 0 0-6.5Z\" />\n </svg>\n <span className=\"text-xs text-gray-500\">{label || 'Screenshot'}</span>\n </span>\n );\n }\n return (\n <img\n src={src}\n alt={alt ?? ''}\n loading=\"lazy\"\n onError={() => setFailed(true)}\n className=\"my-3 block max-w-full rounded-lg border border-gray-200\"\n />\n );\n}\n\n/**\n * Help articles and other authored documentation: full heading scale, airier\n * blocks, `>` styled as a callout rather than an email-quote bar, and a capped\n * measure so a wide window does not produce 200-character lines.\n */\nfunction articleComponents(resolveImageSrc?: (src: string) => string): Components {\n return {\n a: linkRenderer,\n h1: ({ node: _n, ...props }) => (\n <h1 className=\"mt-6 mb-3 text-xl font-semibold text-gray-900 first:mt-0\" {...props} />\n ),\n h2: ({ node: _n, ...props }) => (\n <h2 className=\"mt-6 mb-2.5 border-b border-gray-200 pb-1.5 text-lg font-semibold text-gray-900 first:mt-0\" {...props} />\n ),\n h3: ({ node: _n, ...props }) => (\n <h3 className=\"mt-5 mb-2 text-base font-semibold text-gray-900 first:mt-0\" {...props} />\n ),\n h4: ({ node: _n, ...props }) => (\n <h4 className=\"mt-4 mb-1.5 text-sm font-semibold text-gray-900 first:mt-0\" {...props} />\n ),\n h5: ({ node: _n, ...props }) => (\n <h5 className=\"mt-4 mb-1.5 text-sm font-semibold text-gray-700 first:mt-0\" {...props} />\n ),\n h6: ({ node: _n, ...props }) => (\n <h6 className=\"mt-4 mb-1.5 text-sm font-semibold text-gray-700 first:mt-0\" {...props} />\n ),\n p: ({ node: _n, ...props }) => <p className=\"my-2.5 leading-relaxed\" {...props} />,\n ul: ({ node: _n, ...props }) => <ul className=\"my-2.5 list-disc space-y-1 pl-5\" {...props} />,\n ol: ({ node: _n, ...props }) => <ol className=\"my-2.5 list-decimal space-y-1 pl-5\" {...props} />,\n // A numbered walkthrough has multi-paragraph steps (text, then a\n // screenshot); paragraphs inside an item stay tighter than the gap between\n // items, or the numbering stops reading as a sequence.\n li: ({ node: _n, ...props }) => <li className=\"leading-relaxed [&>p]:my-1\" {...props} />,\n code: codeRenderer,\n pre: preRenderer('my-3 p-3'),\n blockquote: ({ node: _n, ...props }) => (\n <blockquote\n className=\"my-3 rounded-r-lg border-l-4 border-blue-400/60 bg-blue-50 px-3 py-2 text-gray-700 [&>p]:my-1\"\n {...props}\n />\n ),\n hr: ({ node: _n, ...props }) => <hr className=\"my-4 border-gray-200\" {...props} />,\n table: ({ node: _n, ...props }) => (\n <div className=\"my-3 overflow-x-auto\">\n <table className=\"border-collapse text-sm\" {...props} />\n </div>\n ),\n ...tableRenderers,\n img: ({ node: _n, src, alt }) => {\n const raw = typeof src === 'string' ? src : undefined;\n return <ArticleImage src={raw && resolveImageSrc ? resolveImageSrc(raw) : raw} alt={alt} />;\n },\n };\n}\n\n// ── Component ──────────────────────────────────────────────────────────────\n\nexport type MarkdownVariant = 'note' | 'article';\n\nexport interface MarkdownProps {\n /** The markdown source. */\n children: string;\n /**\n * `note` (default) — typed copy inside a card: capped headings, single\n * newlines honoured as line breaks, tight blocks.\n * `article` — authored documentation: full heading scale, soft wraps, callout\n * blockquotes, screenshot placeholders, a capped measure.\n */\n variant?: MarkdownVariant;\n /**\n * Collapse to ~6 text lines with a fade. The \"Show more\" control belongs to\n * the caller; this only clamps. The fade appears only when the clamped\n * content actually OVERFLOWS, measured after layout — a note that is long in\n * characters but short on screen would otherwise get a fade under nothing.\n *\n * Needs `efficient-shell/ui.css` for `.rosmd-clamp` / `.rosmd-fade`.\n */\n clamp?: boolean;\n /**\n * Rewrite a relative image `src` before it is fetched. Article bodies\n * reference screenshots by a repo-relative path that only the host knows how\n * to serve.\n */\n resolveImageSrc?: (src: string) => string;\n /** Override individual element renderers. Merged over the variant's set. */\n components?: Partial<Components>;\n className?: string;\n}\n\n/**\n * Render markdown as React elements.\n *\n * No HTML string is produced at any point, and no raw HTML is parsed, so there\n * is nothing here to sanitise — see the note at the top of this file before\n * reaching for DOMPurify.\n */\nfunction Markdown({\n children,\n variant = 'note',\n clamp = false,\n resolveImageSrc,\n components,\n className = '',\n}: MarkdownProps) {\n const ref = useRef<HTMLDivElement>(null);\n const [overflowing, setOverflowing] = useState(false);\n\n useBrowserLayoutEffect(() => {\n const el = ref.current;\n if (!clamp || !el) {\n setOverflowing(false);\n return;\n }\n const check = () => setOverflowing(el.scrollHeight > el.clientHeight + 1);\n check();\n // A ResizeObserver rather than a one-off measure: the same note reflows\n // when its window is resized, and a fade that was correct at mount is\n // wrong the moment it is.\n const ro = new ResizeObserver(check);\n ro.observe(el);\n return () => ro.disconnect();\n }, [clamp, children]);\n\n const resolved = useMemo<Components>(\n () => ({\n ...(variant === 'article' ? articleComponents(resolveImageSrc) : noteComponents(resolveImageSrc)),\n ...components,\n }),\n [variant, resolveImageSrc, components],\n );\n\n // `className` wins over the variant's own ink and type size.\n //\n // Both are ordinary utilities on the same element, so the one that wins is\n // decided by the order Tailwind emits them, not by the order they appear in\n // the attribute — a host asking for `text-green-900` on a green panel would\n // be gambling. The alternative is an important utility at every call site,\n // and the two syntaxes for that (v3's `!text-green-900`, v4's\n // `text-green-900!`) are each silently INERT in the other version, which is\n // a bad thing to ask a caller to get right for a colour.\n //\n // So: a host that names one simply gets it.\n const named = ` ${className} `;\n const ink = /\\stext-(?:[a-z]+-\\d{2,3}(?:\\/\\d+)?|black|white|inherit)\\s/.test(named)\n ? ''\n : 'text-gray-800';\n const size = /\\stext-(?:xs|sm|base|lg|[2-9]?xl|\\[[^\\]]+\\])\\s/.test(named) ? '' : 'text-sm';\n\n return (\n <div\n ref={ref}\n className={[\n variant === 'article' ? 'max-w-3xl break-words' : 'break-words space-y-1.5',\n size,\n ink,\n clamp ? 'rosmd-clamp' : '',\n clamp && overflowing ? 'rosmd-fade' : '',\n className,\n ]\n .filter(Boolean)\n .join(' ')}\n >\n <ReactMarkdown remarkPlugins={MARKDOWN_PLUGINS[variant] as never} components={resolved}>\n {children}\n </ReactMarkdown>\n </div>\n );\n}\n\nconst MemoMarkdown = memo(Markdown);\n\nexport { MemoMarkdown as Markdown };\nexport default MemoMarkdown;\n\nexport type { Components as MarkdownComponents } from 'react-markdown';\n\n/** Re-exported so a host can render a placeholder in its own layout. */\nexport type MarkdownNode = ReactNode;\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/markup/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * The editorial markup rule — ONE grammar, shared by every product that lets a\n * human type formatted copy into a plain text box.\n *\n * Why this lives in efficient-shell, and why behind its own subpath:\n * the rule is consumed by the admin portal (agent messages + the campaign\n * designer) AND by the public storefront, which has ten dependencies in total\n * and cannot afford the shell's peer graph (a 3D viewer, a PDF renderer, a DXF\n * viewer, an xlsx parser, a Word converter). So this module is deliberately\n * FRAMEWORK-FREE: no React, no JSX, no DOM, no imports at all. `efficient-shell/markup`\n * therefore adds a few hundred bytes of first-party code to a bundle and nothing\n * else — see the subpath's own tsup entry.\n *\n * It is also the ONE subpath whose `exports` entry carries a `default`\n * condition alongside `import`, and that is deliberate rather than untidy. The\n * root and `./apps` are React component entries; a bundler resolves them and\n * nothing else has any business to. This module is consumed by build scripts\n * too — the storefront's media-manifest generator reaches it through its Puck\n * config — and those run under `tsx`, which resolves through Node's CJS\n * loader. With only `types` and `import` declared, that resolution fails with\n * ERR_PACKAGE_PATH_NOT_EXPORTED, which reads like a missing file rather than a\n * missing condition. A module this portable should answer to any resolver.\n *\n * WHAT IS SHARED IS THE RULE, NOT THE RENDERING. Each product renders the token\n * list its own way, because it must: the web uses CSS classes bound to theme\n * tokens, email must inline every style (mail clients drop classes). Two\n * renderers, one grammar, one writer ({@link applyMark}) — so a toolbar button\n * and a parser can never disagree about what a delimiter means.\n *\n * ## The delimiters\n *\n * Standard markdown, in every product:\n *\n * **phrase** bold\n * _phrase_ italic\n * ~~phrase~~ strikethrough\n * ==phrase== highlight — the brand accent colour, applied to a selection\n *\n * Plus a per-product LEGACY set ({@link STOREFRONT_MARKUP} /\n * {@link CAMPAIGN_MARKUP}) that keeps already-published copy rendering exactly\n * as it does today. Those legacy rules are the only part of this grammar that is\n * meant to be deleted: once stored content has been converted, drop the rule\n * from the product's rule list and nothing else changes.\n *\n * ## Two deliberate delimiter choices\n *\n * 1. ITALIC IS `_phrase_`, NOT `*phrase*`. In the storefront and the campaign\n * designer a single asterisk already means the ACCENT colour, on 704 live\n * instances. Standard markdown says italic. Both cannot be true, so the\n * asterisk keeps its existing meaning and italic takes markdown's OTHER\n * italic marker. Nothing published moves.\n * 2. `_` NEVER FIRES INSIDE A WORD. Without that guard, a mail merge line\n * holding `{{first_name}}` and `{{last_name}}` would italicise everything\n * between the two underscores. The guard is CommonMark's own rule for `_`\n * (an intraword underscore is literal) and it is checked with plain character\n * tests rather than a lookbehind, because a lookbehind is a parse error on\n * Safari below 16.4 and this code ships to a public storefront.\n *\n * ## One KNOWN divergence from the standard named above\n *\n * `__phrase__` IS ITALIC HERE. CommonMark reads a doubled underscore as STRONG;\n * this grammar has a single `_` rule, so the outer pair matches, the inner\n * underscores are ordinary characters, and `__all__` comes back as `_all_`.\n *\n * Left alone deliberately, and recorded rather than fixed. A census of stored\n * copy found 14 rows in this shape and every one of them is the literal link\n * key `__all__`, which no renderer is handed — so nothing published is being\n * mis-drawn today. Adding a `__` rule to buy CommonMark parity would change how\n * a stored `_` pair parses for the benefit of no existing string, and this\n * module's promise is that the grammar grows while nothing published moves.\n * Bold is `**phrase**` in every product; that is the form the toolbar writes\n * and the form to reach for. Pinned by a test, so it stays a decision rather\n * than becoming a discovery.\n */\n\n/** A style a toolbar button can apply. The first five are inline marks; the\n * last four are the composer-only affordances (a link and the three line\n * prefixes), which only a renderer that draws block content can honour. */\nexport type MarkStyle =\n | 'bold' | 'italic' | 'strike' | 'code' | 'highlight'\n | 'link' | 'bullet' | 'number' | 'quote';\n\n/** What a parsed run MEANS. `accent` is the storefront/campaign brand colour —\n * the legacy `*phrase*` — and `highlight` is its standard-markdown successor;\n * both resolve to the same paint, which is what makes converting stored copy\n * from one to the other invisible. */\nexport type InlineKind =\n | 'text' | 'bold' | 'italic' | 'strike' | 'code' | 'highlight' | 'accent';\n\n// ── Writing: the toolbar's half of the rule ────────────────────────────────\n\nconst INLINE: Record<string, { fence: string; placeholder: string }> = {\n bold: { fence: '**', placeholder: 'bold text' },\n italic: { fence: '_', placeholder: 'italic text' },\n strike: { fence: '~~', placeholder: 'struck out' },\n code: { fence: '`', placeholder: 'code' },\n highlight: { fence: '==', placeholder: 'highlighted text' },\n};\n\nconst LINE_PREFIX: Record<string, (i: number) => string> = {\n bullet: () => '- ',\n number: (i) => `${i + 1}. `,\n quote: () => '> ',\n};\n\nexport interface WrapResult {\n text: string;\n /** Where the caret should land — inside the new marks, or after them. */\n selectionStart: number;\n selectionEnd: number;\n}\n\n/**\n * Apply a style to `text[start:end]`, returning the new text and where the\n * selection should sit. Toggling is deliberate: pressing Bold on already-bold\n * text unwraps it, so the shortcut behaves like every other editor.\n *\n * Unwrapping only ever recognises the style's OWN fence. An italic button that\n * also unwrapped `*phrase*` would silently strip an accent run in the storefront\n * and the campaign designer, where the asterisk means colour, not slant.\n */\nexport function applyMark(\n text: string, start: number, end: number, style: MarkStyle,\n): WrapResult {\n const selected = text.slice(start, end);\n\n if (style === 'link') {\n const label = selected || 'link text';\n const snippet = `[${label}](https://)`;\n return {\n text: text.slice(0, start) + snippet + text.slice(end),\n // Land the caret on the URL — the label is usually already right.\n // Offset spans \"[\", the label, \"](\" — three characters plus the label.\n selectionStart: start + label.length + 3,\n selectionEnd: start + snippet.length - 1,\n };\n }\n\n const prefix = LINE_PREFIX[style];\n if (prefix) {\n // Line styles apply to every line the selection touches.\n const from = text.lastIndexOf('\\n', Math.max(start - 1, 0)) + 1;\n const toIdx = text.indexOf('\\n', end);\n const to = toIdx === -1 ? text.length : toIdx;\n const block = text.slice(from, to);\n const lines = block.split('\\n');\n const marked = lines.every((l, i) => l.startsWith(prefix(i)));\n const next = lines\n .map((l, i) => (marked ? l.slice(prefix(i).length) : prefix(i) + l))\n .join('\\n');\n return {\n text: text.slice(0, from) + next + text.slice(to),\n selectionStart: from,\n selectionEnd: from + next.length,\n };\n }\n\n const { fence, placeholder } = INLINE[style];\n const before = text.slice(Math.max(0, start - fence.length), start);\n const after = text.slice(end, end + fence.length);\n if (before === fence && after === fence) { // already marked → unwrap\n return {\n text: text.slice(0, start - fence.length) + selected + text.slice(end + fence.length),\n selectionStart: start - fence.length,\n selectionEnd: start - fence.length + selected.length,\n };\n }\n const inner = selected || placeholder;\n return {\n text: text.slice(0, start) + fence + inner + fence + text.slice(end),\n selectionStart: start + fence.length,\n selectionEnd: start + fence.length + inner.length,\n };\n}\n\n/** One toolbar button. `label` is a text glyph for hosts that draw type\n * (the message composer); hosts with an icon set map `style` to their own.\n * `shortcut` is the bare key the host combines with its platform modifier. */\nexport interface MarkupTool {\n style: MarkStyle;\n label: string;\n title: string;\n shortcut?: string;\n}\n\n/** Every button, in the order a writer reaches for them. */\nexport const MARKUP_TOOLS: readonly MarkupTool[] = [\n { style: 'bold', label: 'B', title: 'Bold (⌘B)', shortcut: 'b' },\n { style: 'italic', label: 'I', title: 'Italic (⌘I)', shortcut: 'i' },\n { style: 'strike', label: 'S', title: 'Strikethrough' },\n { style: 'highlight', label: '◆', title: 'Highlight in the brand color' },\n { style: 'code', label: '‹›', title: 'Code' },\n { style: 'link', label: '🔗', title: 'Link (⌘K)', shortcut: 'k' },\n { style: 'bullet', label: '•', title: 'Bulleted list' },\n { style: 'number', label: '1.', title: 'Numbered list' },\n { style: 'quote', label: '❝', title: 'Quote' },\n];\n\n/** The tools a host shows, resolved to descriptors in {@link MARKUP_TOOLS}\n * order. Keeps a host from hand-rebuilding a descriptor (and drifting on a\n * tooltip) just to pick a subset. */\nexport function markupTools(styles: readonly MarkStyle[]): MarkupTool[] {\n return MARKUP_TOOLS.filter((t) => styles.includes(t.style));\n}\n\n// ── Reading: the parser's half of the rule ─────────────────────────────────\n\n/**\n * One delimited run. `open`/`close` are literal strings; a run's inner text may\n * never contain the first character of `close`, which is what keeps this\n * equivalent to the \"asterisk, run of non-asterisks, asterisk\" regexes the\n * products used before — the same leftmost match, the same single close\n * candidate, so today's stored copy parses exactly as it always has.\n */\nexport interface InlineRule {\n readonly open: string;\n readonly close: string;\n readonly kind: Exclude<InlineKind, 'text'>;\n /** CommonMark's `_` rule: neither delimiter may touch a word character, so\n * `snake_case` and `{{first_name}}` stay literal. */\n readonly wordBoundary?: boolean;\n /**\n * Characters that may not FOLLOW the close. A run whose closing delimiter\n * butts up against one of these does not fire, and both delimiters stay\n * literal.\n *\n * Two legacy rules need it, for the same reason: a delimiter that is also\n * ordinary punctuation will otherwise claim text that was never markup. `#`\n * must not pair across rank markers (\"Ranked #1 and #2\"), and `[` must not\n * claim the label of a markdown link (\"[terms](https://…)\"), where the URL\n * would be stranded outside the run and printed to the reader.\n *\n * A character SET rather than a flag per case, so the next collision is one\n * more rule field and no new branch in {@link tokenizeInline}.\n */\n readonly notBefore?: string;\n}\n\n/**\n * The standard-markdown marks, shared by every product.\n *\n * `**` and `*` cannot collide even though they share a prefix: a `*` followed\n * immediately by another `*` is an EMPTY accent run, which no rule matches, so\n * at any one position exactly one of the two can fire. Order between them is\n * therefore not observable — which is worth stating, because the obvious\n * assumption (\"longest delimiter must be listed first\") would make a future\n * reader think this array's order is load-bearing when the empty-run rule is\n * what actually does the work.\n */\nexport const STANDARD_MARKUP: readonly InlineRule[] = [\n { open: '**', close: '**', kind: 'bold' },\n { open: '==', close: '==', kind: 'highlight' },\n { open: '~~', close: '~~', kind: 'strike' },\n { open: '_', close: '_', kind: 'italic', wordBoundary: true },\n];\n\n/**\n * The buttons a COPY FIELD offers — a storefront section's copy box and a\n * campaign block's copy box show exactly these, so a merchant meets one toolbar\n * wherever they type.\n *\n * DERIVED, not listed. A copy field renders inline runs only — no lists, no\n * block quote, no anchor — so the set it may advertise is precisely the tools\n * whose output this grammar parses back. Writing that as a literal list was the\n * bug's hiding place: the list and the grammar were two hand-maintained facts\n * that agreed by luck, and nothing failed when a tool wrote a delimiter the\n * parser read as something else. Now a style with no rule cannot appear here,\n * whatever anyone types into the array.\n *\n * It resolves to bold, italic, strike and highlight. `code` is the one that\n * surprises: it is an inline mark and it has a fence, but no rule reads a\n * backtick, so a copy field would print `` `word` `` verbatim — and a button\n * whose output the renderer draws as literal text is worse than no button.\n *\n * Declared here, below the rules, because it now depends on them.\n */\nexport const COPY_FIELD_TOOLS: readonly MarkStyle[] = MARKUP_TOOLS\n .map((tool) => tool.style)\n .filter((style) => STANDARD_MARKUP.some((rule) => rule.kind === style));\n\n/**\n * The storefront's rule set: standard markdown plus the ONE legacy run it has\n * published — `*phrase*` in the theme accent. Delete that last entry once\n * stored copy has been converted to `==phrase==`; both kinds already paint the\n * same, so the deletion is invisible.\n */\nexport const STOREFRONT_MARKUP: readonly InlineRule[] = [\n ...STANDARD_MARKUP,\n { open: '*', close: '*', kind: 'accent' },\n];\n\n/**\n * The campaign designer's rule set: standard markdown plus its three legacy\n * runs — `#phrase#` bold, `*phrase*` accent, `[phrase]` italic. The bold guard\n * is the one the email parser already carried: a `#` followed by a digit does\n * not close a run, so \"Ranked #1 and #2\" is literal while \"Save #20%#\" bolds.\n *\n * The italic guard exists because `[phrase]` collides with the one piece of\n * markdown syntax every writer already knows. Without it, \"Read [our\n * terms](https://…) first\" italicises the label AND prints the bare URL to the\n * reader — the words come back wearing a mark nobody chose. It is not a\n * toolbar problem: a merchant typing an ordinary link into a copy box hits it,\n * which is why the guard is on the RULE and not on which buttons a field\n * offers. `[phrase]` on its own is untouched, so every published line still\n * reads exactly as it did.\n */\nexport const CAMPAIGN_MARKUP: readonly InlineRule[] = [\n ...STANDARD_MARKUP,\n { open: '*', close: '*', kind: 'accent' },\n { open: '#', close: '#', kind: 'bold', notBefore: '0123456789' },\n { open: '[', close: ']', kind: 'italic', notBefore: '(' },\n];\n\n/** A parsed run. `text` is the inner text with the delimiters removed, so a\n * caller that only wants words ({@link stripInline}) joins the texts and a\n * caller that paints ({@link InlineKind}) switches on the kind. */\nexport interface InlineToken {\n kind: InlineKind;\n text: string;\n}\n\n/** True for a character that a `_` may not touch. Whitespace and ASCII\n * punctuation are safe neighbours; everything else — letters, digits, and any\n * non-ASCII script — counts as part of a word, so `汉_字_` stays literal too.\n * Deliberately conservative: a character wrongly called word-ish only ever\n * leaves an underscore rendering as it does today. */\nfunction wordish(ch: string): boolean {\n return !/\\s/.test(ch) && !/[!-\\/:-@[-`{-~]/.test(ch);\n}\n\n/**\n * Split authored text into its runs.\n *\n * The returned tokens ALTERNATE, starting and ending with a `text` token that\n * may be empty — exactly the shape `String.split` with one capture group\n * produced in the parsers this replaces. That contract is load-bearing, not\n * cosmetic: the storefront wraps every segment (including the empty ones) in a\n * `<span>`, so preserving the empty tokens is what keeps a published page's\n * markup byte-for-byte identical.\n *\n * A lone delimiter with no partner stays literal — the products' copy contains\n * real stray asterisks, and swallowing one would silently eat a word.\n */\nexport function tokenizeInline(\n value: string, rules: readonly InlineRule[],\n): InlineToken[] {\n const out: InlineToken[] = [];\n let buf = '';\n let i = 0;\n\n while (i < value.length) {\n const hit = matchAt(value, i, rules);\n if (hit) {\n out.push({ kind: 'text', text: buf });\n buf = '';\n out.push({ kind: hit.rule.kind, text: hit.inner });\n i = hit.end;\n continue;\n }\n buf += value[i];\n i += 1;\n }\n out.push({ kind: 'text', text: buf });\n return out;\n}\n\n/** The first rule that opens at `i` AND finds its close, or null. */\nfunction matchAt(value: string, i: number, rules: readonly InlineRule[]) {\n for (const rule of rules) {\n if (!value.startsWith(rule.open, i)) continue;\n if (rule.wordBoundary && i > 0 && wordish(value[i - 1])) continue;\n\n const from = i + rule.open.length;\n const closeAt = value.indexOf(rule.close, from);\n if (closeAt < from + 1) continue; // no close, or an empty run\n\n // The inner text may not contain the close's leading character: one close\n // candidate per opening, which is what the regexes this replaces did.\n const inner = value.slice(from, closeAt);\n if (inner.includes(rule.close[0])) continue;\n\n const end = closeAt + rule.close.length;\n const next = value[end];\n if (rule.notBefore && next !== undefined && rule.notBefore.includes(next)) continue;\n if (rule.wordBoundary && next !== undefined && wordish(next)) continue;\n\n return { rule, inner, end };\n }\n return null;\n}\n\n/**\n * The plain words of an authored string — every paired run unwrapped to its\n * inner text, every unpaired delimiter left alone.\n *\n * This is the ONLY correct way to fill an `alt`, an `aria-label` or a\n * structured-data field from authored copy. Deleting a delimiter character by\n * hand — \"replace every asterisk with nothing\" — breaks the moment the grammar\n * grows a marker, and it disagrees with the renderer about a stray asterisk,\n * which matters: search engines expect structured data to match the text a\n * visitor actually sees.\n */\nexport function stripInline(value: string, rules: readonly InlineRule[]): string {\n return tokenizeInline(value, rules).map((t) => t.text).join('');\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/page-tabs/context.ts","../../src/page-tabs/tabsModel.ts","../../src/page-tabs/PageTabsProvider.tsx","../../src/page-tabs/PageTabsOutlet.tsx","../../src/page-tabs/PageTabBar.tsx","../../src/page-tabs/hooks.ts"],"names":["setTitle","setDirty","Activity","jsx","TabFrame","useRef","useEffect","useMemo","useLocation","useContext","jsxs","Fragment","useNavigate","useCallback"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * The two contexts page tabs run on, in their own module so the provider, the\n * outlet, the strip and the page hooks can all import them without importing\n * each other.\n */\nimport { createContext, type ReactNode } from 'react';\nimport type { Location } from 'react-router-dom';\n\nimport type { PageTab, TabsState } from './tabsModel';\n\n/** What the consumer's route table says about an address. */\nexport interface PageTabRoute {\n /** The tab's name until the page names it (`usePageTabTitle`). */\n title: string;\n /** Identity; defaults to the pathname. Give two addresses the same key to\n * make them one tab. */\n key?: string;\n /** Drawn before the title in the strip. Never persisted — re-resolved from\n * the address, so a restored tab gets today's icon. */\n icon?: ReactNode;\n}\n\n/** The parts of a location a route decision may depend on. */\nexport type TabLocation = Pick<Location, 'pathname' | 'search' | 'hash'>;\n\n/** Return null for an address that is never a tab: a redirect on its way\n * somewhere, a page that must not stay open behind others. */\nexport type ResolvePageTab = (location: TabLocation) => PageTabRoute | null;\n\nexport interface CloseTabOptions {\n /** Go here instead of the neighbouring tab (a form that just created its\n * record goes to the record). */\n to?: string;\n /** Skip the unsaved-changes question — the page knows its work is saved. */\n force?: boolean;\n}\n\nexport interface PageTabsContextValue {\n enabled: boolean;\n state: TabsState;\n /** The tab the address is on right now, derived during render — a frame\n * must not wait an effect to learn it is the visible one. */\n currentKey: string | null;\n resolve: ResolvePageTab;\n keyFor: (location: TabLocation) => string | null;\n locationFor: (key: string) => Location | undefined;\n idFor: (key: string, part: 'tab' | 'panel') => string;\n activate: (key: string) => void;\n close: (key: string, options?: CloseTabOptions) => Promise<boolean>;\n closeMany: (scope: 'others' | 'right' | 'all', key: string | null) => Promise<void>;\n setTitle: (key: string, title: string) => void;\n setDirty: (key: string, dirty: boolean) => void;\n visibleTabs: PageTab[];\n}\n\nexport const PageTabsContext = createContext<PageTabsContextValue | null>(null);\n\n/** Which tab a subtree belongs to. `activeRef` is written in a layout effect,\n * which runs before a hidden subtree's passive effect clean-ups — so a\n * clean-up can tell \"my tab was hidden\" (keep my state) from \"my page was\n * replaced while on screen\" (drop it). */\nexport interface PageTabFrame {\n key: string;\n activeRef: { current: boolean };\n}\n\nexport const PageTabFrameContext = createContext<PageTabFrame | null>(null);\n","/**\n * The page-tabs state, as plain functions over plain data.\n *\n * Kept free of React and of the router so the rules that decide what a user\n * sees — which tab a click lands on, which one goes when there are too many,\n * what survives a reload — are tested directly rather than through a rendered\n * strip. `PageTabsProvider` holds a `TabsState` and calls these; nothing else\n * mutates one.\n *\n * A tab is an OPEN PAGE, identified by `key` (by default the page's pathname,\n * so `/orders?status=open` and `/orders?status=done` are the same tab showing\n * different filters, while `/orders/41` and `/orders/42` are two tabs — the\n * two windows the desktop used to give them). `path` is the full address the\n * tab was last at, which is where clicking it goes back to.\n */\n\nexport interface PageTab {\n /** Identity. Two addresses with the same key are the same tab. */\n key: string;\n /** Where the tab was last: pathname + search. Never the fragment — see\n * `pathOf` in PageTabsProvider. */\n path: string;\n /** What the strip shows. The route's name until the page supplies its own\n * (`usePageTabTitle`), after which navigation within the tab keeps it. */\n title: string;\n /** Who set `title`: the route table, or the page itself. */\n titleFrom: 'route' | 'page';\n /** Pinned tabs sit first and cannot be closed — a home page. */\n pinned: boolean;\n /** Recency, for choosing which tab to close when there are too many. A\n * counter rather than a clock, so it is deterministic under test. */\n lastActive: number;\n}\n\nexport interface TabsState {\n tabs: PageTab[];\n /** The tab on screen, or null while the address is not a tab at all (a\n * redirect on its way somewhere, a page the consumer keeps out of tabs). */\n activeKey: string | null;\n /** Tabs rendered at least once this session. A tab restored from storage is\n * not rendered until it is first opened — restoring ten tabs must not run\n * ten pages' worth of rendering and chunk loading behind the one in view. */\n mounted: string[];\n /** Unsaved changes, by tab. Never persisted: a reload loses them anyway. */\n dirty: string[];\n /** A tab whose close is waiting for navigation to leave it. Closing the tab\n * on screen is two updates — the state, and the address — and they do not\n * land in the same render; the tab stays mounted (but out of the strip)\n * until the address has moved, so the page never unmounts and remounts in\n * between. */\n closing: string | null;\n /** Monotonic counter feeding `lastActive`. */\n clock: number;\n}\n\nexport interface TabEntry {\n key: string;\n path: string;\n title: string;\n}\n\nexport const DEFAULT_MAX_TABS = 12;\n\nexport function emptyState(): TabsState {\n return { tabs: [], activeKey: null, mounted: [], dirty: [], closing: null, clock: 0 };\n}\n\nconst without = (list: string[], key: string) => list.filter((k) => k !== key);\nconst withKey = (list: string[], key: string) => (list.includes(key) ? list : [...list, key]);\n\n/** Pinned first, in pin order; then the rest in the order they were opened. */\nfunction orderPinned(tabs: PageTab[], pinnedKeys: string[]): PageTab[] {\n const pinned = pinnedKeys\n .map((key) => tabs.find((t) => t.key === key))\n .filter((t): t is PageTab => Boolean(t));\n return [...pinned, ...tabs.filter((t) => !pinnedKeys.includes(t.key))];\n}\n\n/**\n * The address moved to `entry`: activate its tab, opening one if needed.\n *\n * An existing tab takes the new address (a filter changed, a hash moved) and\n * keeps a title its page set. A new tab is appended — at the end, where the\n * strip's newest tab always is — and if that takes the count past `maxTabs`\n * the least recently used tab that can go does: not pinned, not on screen,\n * not holding unsaved changes. When nothing can go, the strip runs long\n * rather than throwing work away.\n */\nexport function visit(state: TabsState, entry: TabEntry, maxTabs = DEFAULT_MAX_TABS): TabsState {\n const clock = state.clock + 1;\n const existing = state.tabs.find((t) => t.key === entry.key);\n let tabs: PageTab[];\n if (existing) {\n tabs = state.tabs.map((t) =>\n t.key === entry.key\n ? {\n ...t,\n path: entry.path,\n title: t.titleFrom === 'page' ? t.title : entry.title || t.title,\n lastActive: clock,\n }\n : t,\n );\n } else {\n tabs = [\n ...state.tabs,\n { key: entry.key, path: entry.path, title: entry.title, titleFrom: 'route', pinned: false, lastActive: clock },\n ];\n }\n let next: TabsState = {\n ...state,\n tabs,\n activeKey: entry.key,\n mounted: withKey(state.mounted, entry.key),\n clock,\n };\n // A pending close completes once the address has left that tab.\n if (next.closing && next.closing !== entry.key) next = removeTab(next, next.closing);\n else if (next.closing === entry.key) next = { ...next, closing: null };\n return evictOverflow(next, maxTabs);\n}\n\n/** The address is not a tab (a redirect in flight, an excluded page). */\nexport function visitTransient(state: TabsState): TabsState {\n let next: TabsState = { ...state, activeKey: null };\n if (next.closing) next = removeTab(next, next.closing);\n return next;\n}\n\nfunction evictOverflow(state: TabsState, maxTabs: number): TabsState {\n let next = state;\n while (next.tabs.length > maxTabs) {\n const candidates = next.tabs\n .filter((t) => !t.pinned && t.key !== next.activeKey && !next.dirty.includes(t.key) && t.key !== next.closing)\n .sort((a, b) => a.lastActive - b.lastActive);\n if (candidates.length === 0) break;\n next = removeTab(next, candidates[0].key);\n }\n return next;\n}\n\n/** Drop a tab outright. Callers decide about unsaved changes first. */\nexport function removeTab(state: TabsState, key: string): TabsState {\n return {\n ...state,\n tabs: state.tabs.filter((t) => t.key !== key),\n mounted: without(state.mounted, key),\n dirty: without(state.dirty, key),\n activeKey: state.activeKey === key ? null : state.activeKey,\n closing: state.closing === key ? null : state.closing,\n };\n}\n\n/**\n * Where to go when the tab on screen closes: the tab to its right, else the\n * one to its left — the browser's rule, so the strip does not jump.\n */\nexport function neighbourOf(state: TabsState, key: string): PageTab | null {\n const visible = state.tabs.filter((t) => t.key !== state.closing || t.key === key);\n const i = visible.findIndex((t) => t.key === key);\n if (i < 0) return null;\n return visible[i + 1] ?? visible[i - 1] ?? null;\n}\n\n/** Mark the tab on screen as closing; it goes when the address moves on. */\nexport function beginClose(state: TabsState, key: string): TabsState {\n return { ...state, closing: key };\n}\n\n/** The keys a bulk close would take, before any unsaved-changes check. */\nexport function keysToClose(\n state: TabsState,\n scope: 'others' | 'right' | 'all',\n key: string | null,\n): string[] {\n const closable = state.tabs.filter((t) => !t.pinned);\n if (scope === 'all') return closable.map((t) => t.key);\n if (scope === 'others') return closable.filter((t) => t.key !== key).map((t) => t.key);\n const i = state.tabs.findIndex((t) => t.key === key);\n if (i < 0) return [];\n return state.tabs.slice(i + 1).filter((t) => !t.pinned).map((t) => t.key);\n}\n\nexport function setTitle(state: TabsState, key: string, title: string): TabsState {\n const tab = state.tabs.find((t) => t.key === key);\n if (!tab || !title || (tab.title === title && tab.titleFrom === 'page')) return state;\n return {\n ...state,\n tabs: state.tabs.map((t) => (t.key === key ? { ...t, title, titleFrom: 'page' } : t)),\n };\n}\n\nexport function setDirty(state: TabsState, key: string, dirty: boolean): TabsState {\n const has = state.dirty.includes(key);\n if (dirty === has) return state;\n return { ...state, dirty: dirty ? [...state.dirty, key] : without(state.dirty, key) };\n}\n\n/**\n * Make sure every pinned tab exists and they lead the strip.\n *\n * Runs on every start, so a home page added to `pinned` after a user's tabs\n * were saved still appears, and one removed from it stops being pinned (it\n * stays open as an ordinary tab rather than vanishing under the user).\n */\nexport function ensurePinned(state: TabsState, pinned: TabEntry[]): TabsState {\n const pinnedKeys = pinned.map((p) => p.key);\n let tabs = state.tabs.map((t) => ({ ...t, pinned: pinnedKeys.includes(t.key) }));\n for (const p of pinned) {\n if (!tabs.some((t) => t.key === p.key)) {\n tabs = [...tabs, { key: p.key, path: p.path, title: p.title, titleFrom: 'route', pinned: true, lastActive: 0 }];\n }\n }\n return { ...state, tabs: orderPinned(tabs, pinnedKeys) };\n}\n\n// ── Persistence ────────────────────────────────────────────────────────────\n\n/** What survives a reload: which pages were open, where, and what they were\n * called. Not the page state — that is the page's to restore. */\ninterface StoredTabs {\n v: 1;\n tabs: { key: string; path: string; title: string; titleFrom: 'route' | 'page' }[];\n}\n\nexport function serialize(state: TabsState): string {\n const stored: StoredTabs = {\n v: 1,\n tabs: state.tabs\n .filter((t) => t.key !== state.closing)\n .map(({ key, path, title, titleFrom }) => ({ key, path, title, titleFrom })),\n };\n return JSON.stringify(stored);\n}\n\n/**\n * Read saved tabs back, re-deciding each one against today's routes.\n *\n * `resolve` is the consumer's route table: a saved page that is no longer a\n * tab (retired, renamed, now excluded) is dropped, and one whose key rule\n * changed takes its new key. Anything unreadable yields no tabs — a corrupt\n * entry must not stop the portal from starting.\n */\nexport function restore(raw: string | null, resolve: (path: string) => TabEntry | null): PageTab[] {\n if (!raw) return [];\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return [];\n }\n const stored = parsed as Partial<StoredTabs> | null;\n if (!stored || stored.v !== 1 || !Array.isArray(stored.tabs)) return [];\n const out: PageTab[] = [];\n for (const item of stored.tabs) {\n // An app path only. `//host/…` is protocol-relative: another origin.\n if (!item || typeof item.path !== 'string' || !item.path.startsWith('/') || item.path.startsWith('//')) continue;\n // A fragment is never kept (see `pathOf`); drop one a hand-edited or\n // foreign value carries on the way in.\n const hashAt = item.path.indexOf('#');\n const path = hashAt >= 0 ? item.path.slice(0, hashAt) : item.path;\n const entry = resolve(path);\n if (!entry || out.some((t) => t.key === entry.key)) continue;\n const pageTitle = item.titleFrom === 'page' && typeof item.title === 'string' && item.title;\n out.push({\n key: entry.key,\n path,\n title: pageTitle || entry.title,\n titleFrom: pageTitle ? 'page' : 'route',\n pinned: false,\n lastActive: 0,\n });\n }\n return out;\n}\n","/**\n * PageTabsProvider — keeps a strip of open pages in step with the router.\n *\n * A routed app shows one page at a time: open an order from a list and the\n * list is gone, with its filters, its scroll position and anything half typed.\n * The desktop shell answered that with windows; a portal that dropped the\n * desktop for plain pages lost it. Page tabs put it back the way admin\n * dashboards commonly do it: every page opened stays open as a tab under the\n * header, the pages behind the other tabs stay alive, and clicking a tab goes\n * back to exactly where it was.\n *\n * The address stays the source of truth. The provider never renders a page\n * the router did not route to: it watches the location, and a location its\n * `resolve` names becomes (or re-activates) a tab. Clicking a tab navigates to\n * that tab's last address, so Back, Forward, bookmarks and deep links behave\n * as they do without tabs. `PageTabsOutlet` does the keeping-alive and\n * `PageTabBar` draws the strip; this component is only the state between them.\n *\n * Mount it inside the router, once, keyed on the signed-in user:\n *\n * <PageTabsProvider key={user.id} storageKey={`tabs:u${user.id}`} resolve={…}>\n *\n * The key matters. The saved strip belongs to one user, and a provider that\n * outlived a sign-out would show the next user the last one's pages.\n *\n * Unsaved changes ask before a tab closes (`usePageTabDirty` marks them) and\n * hold the browser tab open while they exist. The question is asked through\n * the kit's `confirm`, so `ConfirmProvider` must be mounted — or pass\n * `confirmDiscard` to ask some other way.\n */\nimport {\n useCallback,\n useEffect,\n useId,\n useMemo,\n useReducer,\n useRef,\n type ReactNode,\n} from 'react';\nimport { useLocation, useNavigate, type Location } from 'react-router-dom';\n\nimport { confirm } from '../shell/ConfirmDialog';\nimport { useBrowserLayoutEffect } from '../shell/hydration';\nimport { useShellStrings } from '../shell/strings';\nimport {\n PageTabsContext,\n type CloseTabOptions,\n type PageTabsContextValue,\n type ResolvePageTab,\n type TabLocation,\n} from './context';\nimport {\n DEFAULT_MAX_TABS,\n beginClose,\n emptyState,\n ensurePinned,\n keysToClose,\n neighbourOf,\n removeTab,\n restore,\n serialize,\n setDirty as setDirtyIn,\n setTitle as setTitleIn,\n visit,\n visitTransient,\n type PageTab,\n type TabEntry,\n type TabsState,\n} from './tabsModel';\n\nexport interface PageTabsProviderProps {\n children: ReactNode;\n /** The route table: whether an address is a tab, and what it is called. */\n resolve: ResolvePageTab;\n /** Addresses that are always open, first in the strip, and cannot be\n * closed — the portal's home page. */\n pinned?: string[];\n /** localStorage key for the open tabs. Scope it to the user. Omit to keep\n * tabs for the life of the page only. */\n storageKey?: string;\n /** Past this many, opening a page closes the least recently used tab that\n * has no unsaved changes. */\n maxTabs?: number;\n /** Off, there is no strip and no keeping-alive: the outlet renders the\n * current page alone. For phones, where a strip of tabs has no room. */\n enabled?: boolean;\n /** Ask before discarding unsaved changes. Defaults to the kit's `confirm`. */\n confirmDiscard?: (tabs: PageTab[]) => Promise<boolean>;\n}\n\ntype Action =\n | { type: 'visit'; entry: TabEntry; maxTabs: number }\n | { type: 'transient' }\n | { type: 'close'; keys: string[]; closing: string | null }\n | { type: 'title'; key: string; title: string }\n | { type: 'dirty'; key: string; dirty: boolean };\n\nfunction reducer(state: TabsState, action: Action): TabsState {\n switch (action.type) {\n case 'visit':\n return visit(state, action.entry, action.maxTabs);\n case 'transient':\n return visitTransient(state);\n case 'close': {\n let next = state;\n for (const key of action.keys) next = removeTab(next, key);\n return action.closing ? beginClose(next, action.closing) : next;\n }\n case 'title':\n return setTitleIn(state, action.key, action.title);\n case 'dirty':\n return setDirtyIn(state, action.key, action.dirty);\n }\n}\n\n/**\n * What a tab remembers of an address: the path and the query, never the\n * fragment. A fragment is where credentials travel precisely because it is\n * never sent to a server — an impersonation hand-off (`#_imp=<token>`), an\n * OAuth implicit grant. A tab that kept it would write the token into\n * localStorage and put it back in the address bar every time the tab was\n * clicked. An in-page anchor is the only thing lost, and a tab switch that\n * lands at the top of the section rather than on the anchor is a fair price.\n */\nconst pathOf = (location: TabLocation) => `${location.pathname}${location.search}`;\n\n/** A path back into the parts `resolve` reads. Relative to nothing: every\n * stored path starts with `/`. */\nexport function parseTabPath(path: string): TabLocation {\n const hashAt = path.indexOf('#');\n const hash = hashAt >= 0 ? path.slice(hashAt) : '';\n const rest = hashAt >= 0 ? path.slice(0, hashAt) : path;\n const searchAt = rest.indexOf('?');\n return {\n pathname: searchAt >= 0 ? rest.slice(0, searchAt) : rest,\n search: searchAt >= 0 ? rest.slice(searchAt) : '',\n hash,\n };\n}\n\nfunction readStorage(key: string | undefined): string | null {\n if (!key) return null;\n try {\n return localStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nexport default function PageTabsProvider({\n children,\n resolve,\n pinned = [],\n storageKey,\n maxTabs = DEFAULT_MAX_TABS,\n enabled = true,\n confirmDiscard,\n}: PageTabsProviderProps) {\n const location = useLocation();\n const navigate = useNavigate();\n const strings = useShellStrings().pageTabs;\n const idPrefix = useId();\n\n // `resolve` is usually an inline function; hold the latest in a ref so the\n // callbacks below stay stable without asking the consumer to memoise it.\n const resolveRef = useRef(resolve);\n resolveRef.current = resolve;\n\n const entryFor = useCallback((loc: TabLocation): TabEntry | null => {\n const route = resolveRef.current(loc);\n if (!route) return null;\n return { key: route.key ?? loc.pathname, path: pathOf(loc), title: route.title };\n }, []);\n\n const [state, dispatch] = useReducer(reducer, undefined, (): TabsState => {\n const restored = restore(readStorage(storageKey), (path) => entryFor(parseTabPath(path)));\n const pinnedEntries = pinned\n .map((path) => entryFor(parseTabPath(path)))\n .filter((e): e is TabEntry => Boolean(e));\n let initial = ensurePinned({ ...emptyState(), tabs: restored }, pinnedEntries);\n // The first address is visited here, not in an effect, so the first paint\n // already has its tab — no frame of \"no tab\" before the page appears.\n const entry = enabled ? entryFor(location) : null;\n initial = entry ? visit(initial, entry, maxTabs) : visitTransient(initial);\n return initial;\n });\n\n // The async handlers (close waits on a dialog) read the state as it is when\n // they resume, not as it was when they started.\n const stateRef = useRef(state);\n stateRef.current = state;\n\n // Each tab's last location, so a hidden tab keeps rendering the address it\n // was at (with its router state) rather than the current one.\n const locations = useRef(new Map<string, Location>());\n const current = enabled ? entryFor(location) : null;\n const currentKey = current?.key ?? null;\n\n useBrowserLayoutEffect(() => {\n if (!enabled) return;\n const entry = entryFor(location);\n if (entry) {\n locations.current.set(entry.key, location);\n dispatch({ type: 'visit', entry, maxTabs });\n } else {\n dispatch({ type: 'transient' });\n }\n }, [location, enabled, maxTabs, entryFor]);\n\n // Forget the locations of tabs that are gone.\n useEffect(() => {\n const open = new Set(state.tabs.map((t) => t.key));\n for (const key of locations.current.keys()) if (!open.has(key)) locations.current.delete(key);\n }, [state.tabs]);\n\n const serialized = useMemo(() => serialize(state), [state]);\n useEffect(() => {\n if (!storageKey) return;\n try {\n localStorage.setItem(storageKey, serialized);\n } catch {\n /* storage full or unavailable — the strip just won't survive a reload */\n }\n }, [serialized, storageKey]);\n\n // Unsaved work anywhere holds the browser tab, as a dirty window did.\n const anyDirty = state.dirty.length > 0;\n useEffect(() => {\n if (!anyDirty) return;\n const hold = (e: BeforeUnloadEvent) => {\n e.preventDefault();\n // Chrome still needs returnValue set to show the prompt.\n e.returnValue = '';\n };\n window.addEventListener('beforeunload', hold);\n return () => window.removeEventListener('beforeunload', hold);\n }, [anyDirty]);\n\n const ids = useRef(new Map<string, number>());\n const idFor = useCallback(\n (key: string, part: 'tab' | 'panel') => {\n let n = ids.current.get(key);\n if (n === undefined) {\n n = ids.current.size;\n ids.current.set(key, n);\n }\n return `${idPrefix}-page-${part}-${n}`;\n },\n [idPrefix],\n );\n\n const askDiscard = useCallback(\n (tabs: PageTab[]) => {\n if (confirmDiscard) return confirmDiscard(tabs);\n return confirm({\n title: strings.discardTitle,\n message: tabs.length === 1\n ? strings.discardOne\n : strings.discardCount?.(tabs.length) ?? `${tabs.length} ${strings.discardMany}`,\n confirmLabel: strings.discardConfirm,\n cancelLabel: strings.keepEditing,\n variant: 'warning',\n });\n },\n [confirmDiscard, strings],\n );\n\n const fallbackPath = useCallback(() => {\n const s = stateRef.current;\n return s.tabs.find((t) => t.pinned)?.path ?? '/';\n }, []);\n\n /**\n * Close `keys` now. If the tab on screen is among them it is not removed\n * here — it is marked closing and the address moves to `to` (or the\n * nearest surviving tab); `visit` drops it once the address has left.\n */\n const closeNow = useCallback(\n (keys: string[], to?: string, prefer?: string | null) => {\n const s = stateRef.current;\n const active = s.activeKey;\n if (!active || !keys.includes(active)) {\n dispatch({ type: 'close', keys, closing: null });\n return;\n }\n let target = to;\n if (!target) {\n const survivors = { ...s, tabs: s.tabs.filter((t) => t.key === active || !keys.includes(t.key)) };\n const preferred = prefer ? s.tabs.find((t) => t.key === prefer && !keys.includes(t.key)) : undefined;\n target = (preferred ?? neighbourOf(survivors, active))?.path ?? fallbackPath();\n }\n dispatch({ type: 'close', keys: keys.filter((k) => k !== active), closing: active });\n navigate(target);\n },\n [navigate, fallbackPath],\n );\n\n const close = useCallback(\n async (key: string, options: CloseTabOptions = {}) => {\n const tab = stateRef.current.tabs.find((t) => t.key === key);\n if (!tab || tab.pinned) {\n if (options.to) navigate(options.to);\n return false;\n }\n if (!options.force && stateRef.current.dirty.includes(key) && !(await askDiscard([tab]))) return false;\n // The dialog took time: the tab may have closed some other way.\n if (!stateRef.current.tabs.some((t) => t.key === key)) return false;\n closeNow([key], options.to);\n return true;\n },\n [askDiscard, closeNow, navigate],\n );\n\n const closeMany = useCallback(\n async (scope: 'others' | 'right' | 'all', key: string | null) => {\n let keys = keysToClose(stateRef.current, scope, key);\n const dirty = stateRef.current.tabs.filter(\n (t) => keys.includes(t.key) && stateRef.current.dirty.includes(t.key),\n );\n // One question for the lot. Declining keeps the unsaved ones and still\n // closes the rest — the user asked for fewer tabs, not for nothing.\n if (dirty.length && !(await askDiscard(dirty))) {\n keys = keys.filter((k) => !dirty.some((t) => t.key === k));\n }\n const open = new Set(stateRef.current.tabs.map((t) => t.key));\n keys = keys.filter((k) => open.has(k));\n if (keys.length) closeNow(keys, undefined, scope === 'all' ? null : key);\n },\n [askDiscard, closeNow],\n );\n\n const activate = useCallback(\n (key: string) => {\n const tab = stateRef.current.tabs.find((t) => t.key === key);\n if (!tab || key === stateRef.current.activeKey) return;\n navigate(tab.path);\n },\n [navigate],\n );\n\n const setTitle = useCallback((key: string, title: string) => dispatch({ type: 'title', key, title }), []);\n const setDirty = useCallback((key: string, dirty: boolean) => dispatch({ type: 'dirty', key, dirty }), []);\n const keyFor = useCallback((loc: TabLocation) => entryFor(loc)?.key ?? null, [entryFor]);\n const locationFor = useCallback((key: string) => locations.current.get(key), []);\n\n const visibleTabs = useMemo(\n () => (enabled ? state.tabs.filter((t) => t.key !== state.closing) : []),\n [enabled, state.tabs, state.closing],\n );\n\n const value = useMemo<PageTabsContextValue>(\n () => ({\n enabled,\n state,\n currentKey,\n resolve,\n keyFor,\n locationFor,\n idFor,\n activate,\n close,\n closeMany,\n setTitle,\n setDirty,\n visibleTabs,\n }),\n [enabled, state, currentKey, resolve, keyFor, locationFor, idFor, activate, close, closeMany, setTitle, setDirty, visibleTabs],\n );\n\n return <PageTabsContext.Provider value={value}>{children}</PageTabsContext.Provider>;\n}\n","/**\n * PageTabsOutlet — renders every open tab's page, and shows one.\n *\n * Each open tab gets a frame that renders the consumer's routes at THAT TAB'S\n * address (`children(location)`, typically `<Routes location={location}>`), so\n * a hidden tab keeps its page mounted — its query results, its filters, the\n * form half filled in — while a different page is on screen. The router's own\n * `<Routes location>` is what makes this possible: inside it, `useLocation`,\n * `useParams` and relative links all answer for the tab's address, not the\n * browser's.\n *\n * ## Hidden is not the same as paused\n *\n * On React 19.2+ each frame is wrapped in `<Activity>`. A hidden Activity keeps\n * its state but runs its effects' clean-ups, and runs them again when shown.\n * That is the behaviour a hidden page needs: a list stops polling, a form's\n * ⌘S handler stops listening (a keystroke in one tab must not submit another\n * tab's form), `document.title` is set again by whichever page comes forward.\n * React 18 has no Activity; there the frame is only hidden and its effects keep\n * running (`PAGE_TABS_PAUSE_HIDDEN` says which you got). The peer range is\n * still `react >=18`, so both are supported — but a portal whose pages bind\n * global listeners wants 19.2.\n *\n * ## Scroll\n *\n * A frame is the page's scroll container (give it `overflow-y-auto` through\n * `frameClassName`), and scrolling inside a hidden element is not something\n * browsers keep. The frame records scroll positions — its own and any\n * scrolling list inside it — while it is on screen, and puts them back when it\n * is shown again.\n */\nimport * as ReactModule from 'react';\nimport {\n memo,\n useContext,\n useEffect,\n useMemo,\n useRef,\n type ComponentType,\n type ReactNode,\n} from 'react';\nimport { useLocation, type Location } from 'react-router-dom';\n\nimport { useBrowserLayoutEffect } from '../shell/hydration';\nimport { PageTabFrameContext, PageTabsContext, type PageTabFrame } from './context';\n\n/** React 19.2's keep-alive primitive, or undefined on older Reacts. Read off\n * the namespace so this module still loads (and type-checks) on React 18. */\nconst Activity = (ReactModule as unknown as {\n Activity?: ComponentType<{ mode: 'visible' | 'hidden'; children?: ReactNode }>;\n}).Activity;\n\n/** True when hidden tabs are paused (`<Activity>`), false when they are only\n * hidden. Exported for the consumer that wants to know which it got. */\nexport const PAGE_TABS_PAUSE_HIDDEN = Boolean(Activity);\n\nfunction KeepAlive({ active, children }: { active: boolean; children: ReactNode }) {\n if (Activity) return <Activity mode={active ? 'visible' : 'hidden'}>{children}</Activity>;\n return <>{children}</>;\n}\n\ninterface FrameProps {\n tabKey: string;\n active: boolean;\n location: Location;\n render: (location: Location) => ReactNode;\n className?: string;\n panelId?: string;\n tabId?: string;\n}\n\n/**\n * A hidden frame re-renders only when its own address changes. Every\n * navigation re-renders the outlet, and without this each one would re-render\n * every open page behind the one in view — the outlet's `children` is a fresh\n * function each time, but it renders the same routes. Contexts the pages read\n * still reach them; this only stops the outlet's own re-render cascading.\n */\nconst sameFrame = (a: FrameProps, b: FrameProps) =>\n a.active === b.active &&\n a.location === b.location &&\n a.className === b.className &&\n a.panelId === b.panelId &&\n a.tabId === b.tabId &&\n (!b.active || a.render === b.render);\n\nconst TabFrame = memo(function TabFrame({ tabKey, active, location, render, className, panelId, tabId }: FrameProps) {\n const ref = useRef<HTMLDivElement>(null);\n const activeRef = useRef(active);\n const scrolls = useRef(new Map<Element, [number, number]>());\n\n // A layout effect, so `activeRef` is already false by the time a hiding\n // Activity runs its subtree's passive clean-ups (see PageTabFrame).\n useBrowserLayoutEffect(() => {\n const wasActive = activeRef.current;\n activeRef.current = active;\n if (!active || wasActive) return;\n for (const [el, [top, left]] of scrolls.current) {\n if (!el.isConnected) {\n scrolls.current.delete(el);\n continue;\n }\n el.scrollTop = top;\n el.scrollLeft = left;\n }\n }, [active]);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n // `scroll` does not bubble, but it can be captured: one listener here\n // hears the frame and every scrolling element inside it.\n const onScroll = (e: Event) => {\n if (!activeRef.current) return;\n const target = e.target as Element | null;\n if (!target || typeof target.scrollTop !== 'number') return;\n scrolls.current.set(target, [target.scrollTop, target.scrollLeft]);\n };\n el.addEventListener('scroll', onScroll, true);\n return () => el.removeEventListener('scroll', onScroll, true);\n }, []);\n\n const frame = useMemo<PageTabFrame>(() => ({ key: tabKey, activeRef }), [tabKey]);\n\n return (\n <div\n ref={ref}\n role={panelId ? 'tabpanel' : undefined}\n id={panelId}\n aria-labelledby={tabId}\n hidden={!active}\n // Inline, not the `hidden` attribute alone: a `flex` or `block` class on\n // the frame would out-rank the user-agent rule and show it anyway.\n style={active ? undefined : { display: 'none' }}\n className={className}\n data-page-tab={tabKey}\n >\n <PageTabFrameContext.Provider value={frame}>\n <KeepAlive active={active}>{render(location)}</KeepAlive>\n </PageTabFrameContext.Provider>\n </div>\n );\n}, sameFrame);\n\nexport interface PageTabsOutletProps {\n /** Render the page at an address — usually `<Routes location={location}>`\n * with the app's content routes. Called once per open tab. */\n children: (location: Location) => ReactNode;\n /** Classes for each tab's frame. The frame is the page's scroll container,\n * so it wants a height and `overflow-y-auto`. */\n frameClassName?: string;\n}\n\nconst TRANSIENT = '\\u0000transient';\n\nexport default function PageTabsOutlet({ children, frameClassName }: PageTabsOutletProps) {\n const ctx = useContext(PageTabsContext);\n const location = useLocation();\n\n if (!ctx || !ctx.enabled) {\n return (\n <div className={frameClassName} key={TRANSIENT}>\n {children(location)}\n </div>\n );\n }\n\n const { state, currentKey, locationFor, idFor } = ctx;\n // Every tab rendered so far, in strip order, plus the one the address is on\n // if the state has not caught up with it yet. Keyed by tab, so a frame keeps\n // its place in the tree — and its page — however the strip is reordered.\n const keys = state.tabs.filter((t) => state.mounted.includes(t.key)).map((t) => t.key);\n if (currentKey && !keys.includes(currentKey)) keys.push(currentKey);\n\n return (\n <>\n {keys.map((key) => {\n const active = key === currentKey;\n const loc = active ? location : locationFor(key);\n // A tab never shown this session has no location to render at; it\n // waits until it is opened.\n if (!loc) return null;\n return (\n <TabFrame\n key={key}\n tabKey={key}\n active={active}\n location={loc}\n render={children}\n className={frameClassName}\n panelId={idFor(key, 'panel')}\n tabId={idFor(key, 'tab')}\n />\n );\n })}\n {!currentKey && (\n <TabFrame key={TRANSIENT} tabKey={TRANSIENT} active location={location} render={children} className={frameClassName} />\n )}\n </>\n );\n}\n","/**\n * PageTabBar — the strip of open pages.\n *\n * One tab per open page, newest on the right, pinned ones first. Click a tab to\n * go back to it; its × (or a middle-click, or Delete while it has focus) closes\n * it; right-click offers Close, Close other tabs, Close tabs to the right and\n * Close all. A tab with unsaved changes shows a dot where its × would be and\n * asks before it goes.\n *\n * Many tabs shrink rather than scroll, as a browser's do (see TAB_BASE).\n *\n * Keyboard: the strip is one tab stop and the arrow keys move along it (Home /\n * End to the ends) — the roving-tabindex tablist pattern the kit's `Tabs` uses.\n * Moving focus does not switch pages; Enter or Space does, because switching\n * navigates and a user arrowing past five tabs should not visit five pages.\n *\n * The strip can be an app's whole top bar: `end` puts controls at its right\n * (a connection light, a theme switch) that stay put while the tabs scroll.\n * The active tab takes the page's background and covers the strip's bottom\n * rule, so it reads as the top of the page below it — give `activeClassName`\n * the page's background if it is not `bg-gray-50`.\n *\n * Renders nothing when tabs are off (`enabled={false}` on the provider).\n */\nimport { Fragment, useContext, useEffect, useRef, useState, type KeyboardEvent, type MouseEvent, type ReactNode } from 'react';\n\nimport { PopupMenu, PopupMenuDivider, PopupMenuItem } from '../shell/PopupMenu';\nimport { useShellStrings } from '../shell/strings';\nimport { PageTabsContext } from './context';\nimport { parseTabPath } from './PageTabsProvider';\nimport type { PageTab } from './tabsModel';\n\nexport interface PageTabBarProps {\n /** Replaces the strip's own classes (height, background, padding). */\n className?: string;\n /** Replaces the active tab's colours — its background should be the\n * page's, so the tab and the page read as one surface. */\n activeClassName?: string;\n /** Controls at the right end of the strip. They do not scroll with the\n * tabs. */\n end?: ReactNode;\n}\n\nconst STRIP = 'relative flex h-10 shrink-0 items-end gap-3 bg-gray-100 px-3';\n\n/** The tabs sit in this. It scrolls sideways only once every tab is at its\n * narrowest; the scrollbar is hidden (a wheel or a trackpad still scrolls it,\n * and the active tab is kept in view). */\nconst TABLIST =\n 'flex min-w-0 flex-1 items-end overflow-x-auto overflow-y-hidden [scrollbar-width:none] [&::-webkit-scrollbar]:hidden';\n\n/**\n * Sizing, the way a browser's tab strip does it. Every tab asks for 15rem and\n * all of them shrink evenly as more open (`flex: 0 1 15rem`), so a strip of\n * twelve tabs fits where it used to scroll. Each tab is a size container\n * (`@container`) and gives detail up as it narrows: its title fades at the\n * edge, then an idle tab's × steps aside until the pointer is over the tab,\n * then its title goes, until only the icon is left. The tab on screen keeps\n * its × however narrow it gets, and hovering any tab shows its × — closing a\n * page is always one click. Pinned tabs are icon-only.\n */\nconst TAB_BASE =\n 'group @container relative flex h-9 flex-[0_1_15rem] items-center rounded-t-md border border-b-0 text-[13px] transition-colors';\n/** Floors: an icon for an idle tab; an icon and its × for the active one. */\nconst TAB_FLOOR_IDLE = 'min-w-[2.75rem]';\nconst TAB_FLOOR_ACTIVE = 'min-w-[4.25rem]';\nconst TAB_PINNED =\n 'group relative flex h-9 flex-none items-center justify-center rounded-t-md border border-b-0 transition-colors';\nconst TAB_ACTIVE = 'z-[1] border-gray-200 bg-gray-50 text-gray-900';\nconst TAB_IDLE = 'border-transparent text-gray-600 hover:bg-gray-200/70 hover:text-gray-900';\n\n/** A title that runs out of room fades rather than ending in \"…\" — the\n * browser's treatment, and it reads more of the word. */\nconst TITLE =\n 'min-w-0 flex-1 overflow-hidden whitespace-nowrap [mask-image:linear-gradient(to_right,#000_calc(100%-1rem),transparent)]';\n\nconst ICON_WRAP = 'relative flex size-4 shrink-0 items-center justify-center text-gray-500 [&>svg]:size-4';\nconst FOCUS_RING = 'outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-blue-500';\n\nfunction CloseIcon() {\n return (\n <svg viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\" className=\"size-3.5\">\n <path d=\"M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z\" />\n </svg>\n );\n}\n\ninterface MenuState {\n x: number;\n y: number;\n key: string;\n}\n\nexport default function PageTabBar({ className, activeClassName, end }: PageTabBarProps) {\n const ctx = useContext(PageTabsContext);\n const strings = useShellStrings().pageTabs;\n const stripRef = useRef<HTMLDivElement>(null);\n const [menu, setMenu] = useState<MenuState | null>(null);\n const activeKey = ctx?.currentKey ?? null;\n\n // Keep the tab on screen in view — a page opened from the menu lands at the\n // far right of a strip that may be scrolled the other way.\n useEffect(() => {\n if (!ctx || !activeKey) return;\n const el = document.getElementById(ctx.idFor(activeKey, 'tab'));\n el?.scrollIntoView?.({ block: 'nearest', inline: 'nearest' });\n }, [ctx, activeKey]);\n\n if (!ctx || !ctx.enabled) return null;\n const { visibleTabs: tabs, state, idFor, activate, close, closeMany, resolve } = ctx;\n const focusKey = tabs.some((t) => t.key === activeKey) ? activeKey : tabs[0]?.key;\n\n const focusTab = (index: number) => {\n const tab = tabs[(index + tabs.length) % tabs.length];\n if (tab) document.getElementById(idFor(tab.key, 'tab'))?.focus();\n };\n\n const onKeyDown = (e: KeyboardEvent<HTMLDivElement>) => {\n const i = tabs.findIndex((t) => document.getElementById(idFor(t.key, 'tab')) === e.target);\n if (i < 0) return;\n if (e.key === 'ArrowRight') focusTab(i + 1);\n else if (e.key === 'ArrowLeft') focusTab(i - 1);\n else if (e.key === 'Home') focusTab(0);\n else if (e.key === 'End') focusTab(tabs.length - 1);\n else if (e.key === 'Delete' && !tabs[i].pinned) void close(tabs[i].key);\n else return;\n e.preventDefault();\n };\n\n const onContextMenu = (e: MouseEvent, tab: PageTab) => {\n // Claimed, so the shell's global menu stands down (see ShellContextMenu).\n e.preventDefault();\n setMenu({ x: e.clientX, y: e.clientY, key: tab.key });\n };\n\n const onAuxClick = (e: MouseEvent, tab: PageTab) => {\n if (e.button !== 1 || tab.pinned) return;\n e.preventDefault();\n void close(tab.key);\n };\n\n const menuTab = menu ? tabs.find((t) => t.key === menu.key) : undefined;\n const menuIndex = menuTab ? tabs.indexOf(menuTab) : -1;\n const closable = (list: PageTab[]) => list.some((t) => !t.pinned);\n const run = (fn: () => unknown) => () => {\n setMenu(null);\n void fn();\n };\n\n return (\n <div className={className ?? STRIP}>\n {/* The strip's bottom rule, drawn as its own layer so the active tab —\n stacked above it — can cover it. A border on the scrolling list\n could not be covered: the list clips anything that reaches past it. */}\n <div aria-hidden=\"true\" className=\"pointer-events-none absolute inset-x-0 bottom-0 h-px bg-gray-200\" />\n <div\n ref={stripRef}\n role=\"tablist\"\n aria-label={strings.label}\n onKeyDown={onKeyDown}\n className={TABLIST}\n >\n {tabs.map((tab, i) => {\n const active = tab.key === activeKey;\n const dirty = state.dirty.includes(tab.key);\n const icon = resolve(parseTabPath(tab.path))?.icon;\n const colours = active ? (activeClassName ?? TAB_ACTIVE) : TAB_IDLE;\n // A hairline between two idle tabs, as the browser draws; none\n // beside the active tab, whose own edges already separate it.\n const divider = i > 0 && !active && tabs[i - 1].key !== activeKey;\n const tabButton = {\n type: 'button' as const,\n role: 'tab',\n id: idFor(tab.key, 'tab'),\n 'aria-selected': active,\n 'aria-controls': state.mounted.includes(tab.key) ? idFor(tab.key, 'panel') : undefined,\n tabIndex: tab.key === focusKey ? 0 : -1,\n title: tab.title,\n onClick: () => activate(tab.key),\n };\n const handlers = {\n onContextMenu: (e: MouseEvent) => onContextMenu(e, tab),\n onAuxClick: (e: MouseEvent) => onAuxClick(e, tab),\n // A middle-press would otherwise start the browser's autoscroll.\n onMouseDown: (e: MouseEvent) => {\n if (e.button === 1) e.preventDefault();\n },\n };\n const iconEl = icon && (\n // The consumer's nav icons, whatever size they were drawn at for\n // the sidebar, sit at the strip's size here.\n <span className={ICON_WRAP} aria-hidden=\"true\">\n {icon}\n {dirty && !active && !tab.pinned && (\n // Unsaved work on a tab too narrow for its × slot: the dot\n // moves onto the icon.\n <span className=\"absolute -right-1 -top-1 hidden size-2 rounded-full bg-gray-500 ring-2 ring-gray-100 @max-[6.5rem]:block @max-[6.5rem]:group-hover:hidden\" />\n )}\n </span>\n );\n\n if (tab.pinned) {\n return (\n <div key={tab.key} role=\"presentation\" className={`${TAB_PINNED} ${icon ? 'w-10' : ''} ${colours}`} {...handlers}>\n <button {...tabButton} className={`flex h-full items-center justify-center rounded-t-md ${icon ? 'w-full' : 'px-3'} ${FOCUS_RING}`}>\n {iconEl}\n <span className={icon ? 'sr-only' : 'whitespace-nowrap text-[13px]'}>{tab.title}</span>\n </button>\n </div>\n );\n }\n\n return (\n <Fragment key={tab.key}>\n {/* A border, not a fill: `border-gray-300` is the line every\n theme remaps (dark to --line-strong), and `bg-gray-300` has\n no dark remap, so it drew a light bar on the dark strip. */}\n {divider && <span aria-hidden=\"true\" className=\"mb-2.5 h-4 w-px shrink-0 self-end border-l border-gray-300\" />}\n <div\n role=\"presentation\"\n className={`${TAB_BASE} ${active ? TAB_FLOOR_ACTIVE : TAB_FLOOR_IDLE} ${colours}`}\n {...handlers}\n >\n <button\n {...tabButton}\n className={`flex h-full min-w-0 flex-1 items-center gap-2 rounded-t-md pl-3 pr-1 text-left ${FOCUS_RING} ${\n active\n ? '@max-[5rem]:pl-2.5'\n : '@max-[6.5rem]:pr-3 @max-[6.5rem]:group-hover:pr-1 @max-[4.5rem]:justify-center @max-[4.5rem]:px-0 @max-[4.5rem]:group-hover:pl-1.5'\n }`}\n >\n {iconEl}\n <span className={`${TITLE} ${icon ? (active ? '@max-[5rem]:sr-only' : '@max-[4.5rem]:sr-only') : ''}`}>\n {tab.title}\n </span>\n </button>\n <span\n className={`mr-1.5 flex size-5 shrink-0 items-center justify-center ${\n active ? '' : '@max-[6.5rem]:hidden @max-[6.5rem]:group-hover:flex'\n }`}\n >\n {dirty && (\n <span\n role=\"img\"\n aria-label={strings.unsaved}\n className=\"size-2 rounded-full bg-gray-500 group-hover:hidden\"\n />\n )}\n <button\n type=\"button\"\n tabIndex={-1}\n aria-label={strings.closeTab?.(tab.title) ?? `${strings.close} ${tab.title}`}\n onClick={() => void close(tab.key)}\n className={`size-5 items-center justify-center rounded text-gray-400 hover:bg-gray-200 hover:text-gray-700 ${\n dirty ? 'hidden group-hover:flex' : active ? 'flex' : 'flex opacity-0 group-hover:opacity-100 focus-visible:opacity-100'\n }`}\n >\n <CloseIcon />\n </button>\n </span>\n </div>\n </Fragment>\n );\n })}\n </div>\n {end && <div className=\"flex shrink-0 items-center gap-2 self-center\">{end}</div>}\n\n {menu && menuTab && (\n <PopupMenu portal style={{ left: menu.x, top: menu.y }} onClose={() => setMenu(null)}>\n <PopupMenuItem disabled={menuTab.pinned} onClick={run(() => close(menuTab.key))}>\n {strings.close}\n </PopupMenuItem>\n <PopupMenuDivider />\n <PopupMenuItem\n disabled={!closable(tabs.filter((t) => t.key !== menuTab.key))}\n onClick={run(() => closeMany('others', menuTab.key))}\n >\n {strings.closeOthers}\n </PopupMenuItem>\n <PopupMenuItem\n disabled={!closable(tabs.slice(menuIndex + 1))}\n onClick={run(() => closeMany('right', menuTab.key))}\n >\n {strings.closeRight}\n </PopupMenuItem>\n <PopupMenuItem disabled={!closable(tabs)} onClick={run(() => closeMany('all', null))}>\n {strings.closeAll}\n </PopupMenuItem>\n </PopupMenu>\n )}\n </div>\n );\n}\n","/**\n * What a page asks of the tab it is in — and what the rest of the app asks of\n * the strip.\n *\n * Every hook here is safe outside a tab: with no provider, or with tabs turned\n * off (a phone), they do nothing, and `usePageTab().close` still honours `to`.\n * A page written for tabs therefore works unchanged without them.\n */\nimport { useCallback, useContext, useEffect } from 'react';\nimport { useNavigate } from 'react-router-dom';\n\nimport { PageTabFrameContext, PageTabsContext, type CloseTabOptions } from './context';\nimport type { PageTab } from './tabsModel';\n\nexport interface PageTabsApi {\n enabled: boolean;\n tabs: PageTab[];\n activeKey: string | null;\n activate: (key: string) => void;\n close: (key: string, options?: CloseTabOptions) => Promise<boolean>;\n closeOthers: (key: string) => Promise<void>;\n closeRight: (key: string) => Promise<void>;\n closeAll: () => Promise<void>;\n}\n\nconst NO_TABS: PageTabsApi = {\n enabled: false,\n tabs: [],\n activeKey: null,\n activate: () => {},\n close: async () => false,\n closeOthers: async () => {},\n closeRight: async () => {},\n closeAll: async () => {},\n};\n\n/** The strip, for code outside any one page (a keyboard shortcut, a menu). */\nexport function usePageTabs(): PageTabsApi {\n const ctx = useContext(PageTabsContext);\n if (!ctx || !ctx.enabled) return NO_TABS;\n return {\n enabled: true,\n tabs: ctx.visibleTabs,\n activeKey: ctx.currentKey,\n activate: ctx.activate,\n close: ctx.close,\n closeOthers: (key) => ctx.closeMany('others', key),\n closeRight: (key) => ctx.closeMany('right', key),\n closeAll: () => ctx.closeMany('all', null),\n };\n}\n\nexport interface PageTabHandle {\n /** This page's tab, or null outside one. */\n key: string | null;\n /**\n * Close this page's tab. Unsaved changes are asked about unless `force`.\n * Outside a tab it only navigates to `to`, if given. Resolves true when the\n * tab closed.\n */\n close: (options?: CloseTabOptions) => Promise<boolean>;\n}\n\n/** The tab this page is in. */\nexport function usePageTab(): PageTabHandle {\n const ctx = useContext(PageTabsContext);\n const frame = useContext(PageTabFrameContext);\n const navigate = useNavigate();\n const key = ctx?.enabled && frame ? frame.key : null;\n const close = useCallback(\n async (options: CloseTabOptions = {}) => {\n if (ctx?.enabled && frame && ctx.state.tabs.some((t) => t.key === frame.key)) {\n return ctx.close(frame.key, options);\n }\n if (options.to) navigate(options.to);\n return false;\n },\n [ctx, frame, navigate],\n );\n return { key, close };\n}\n\n/**\n * Name this page's tab — a record's number once it has loaded, say. Empty\n * strings are ignored, so a page can pass its title before the data arrives.\n * The name sticks: moving within the tab (a filter, a hash) keeps it.\n */\nexport function usePageTabTitle(title: string | null | undefined): void {\n const ctx = useContext(PageTabsContext);\n const frame = useContext(PageTabFrameContext);\n const setTitle = ctx?.enabled ? ctx.setTitle : undefined;\n const key = frame?.key;\n useEffect(() => {\n if (setTitle && key && title) setTitle(key, title);\n }, [setTitle, key, title]);\n}\n\n/**\n * Mark this page's tab as holding unsaved changes. Closing it then asks\n * first, and the browser tab asks before a reload.\n *\n * The flag survives the tab being hidden. A hidden tab's effects are cleaned\n * up (see PageTabsOutlet), and clearing the flag in that clean-up would let a\n * hidden form close silently; so the clean-up only clears it when the page\n * went away while its tab was on screen (the tab moved to another page). A\n * closed tab's flag goes with the tab.\n */\nexport function usePageTabDirty(dirty: boolean): void {\n const ctx = useContext(PageTabsContext);\n const frame = useContext(PageTabFrameContext);\n const setDirty = ctx?.enabled ? ctx.setDirty : undefined;\n useEffect(() => {\n if (!setDirty || !frame) return;\n setDirty(frame.key, dirty);\n return () => {\n if (frame.activeRef.current) setDirty(frame.key, false);\n };\n }, [setDirty, frame, dirty]);\n}\n"]}
@@ -1,3 +0,0 @@
1
- export { SOUND_PACKS, SOUND_PACK_KEYS, SOUND_TYPES, SOUND_TYPE_LABELS, getSoundConfig, playClick, playError, playLogout, playNotification, playStartup, playSuccess, playTimerDone, previewSound, setAllSounds, setSoundForType, soundsEnabled, toggleSounds } from './chunk-D7PYW2QS.js';
2
- //# sourceMappingURL=sounds-NT4DEZGD.js.map
3
- //# sourceMappingURL=sounds-NT4DEZGD.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"sounds-NT4DEZGD.js"}
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"names":[],"mappings":";","file":"index.js"}