efficient-shell 4.126.3 → 4.127.0
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.
- package/README.md +47 -0
- package/dist/{Browser-LM4RRZIL.js → Browser-F5TKMP4K.js} +7 -6
- package/dist/Browser-F5TKMP4K.js.map +1 -0
- package/dist/{Calculator-HCKFTSJR.js → Calculator-T3XG7S73.js} +6 -5
- package/dist/Calculator-T3XG7S73.js.map +1 -0
- package/dist/{CurrencyConverter-BKKORN6K.js → CurrencyConverter-CASQMKLO.js} +6 -5
- package/dist/CurrencyConverter-CASQMKLO.js.map +1 -0
- package/dist/{Documents-DD3QI5ER.js → Documents-YX67YTZS.js} +7 -6
- package/dist/Documents-YX67YTZS.js.map +1 -0
- package/dist/{Files-BOPCPMJE.js → Files-QB63GO6J.js} +7 -6
- package/dist/Files-QB63GO6J.js.map +1 -0
- package/dist/{Notepad-KJOLF63O.js → Notepad-3SUD5XUE.js} +7 -6
- package/dist/Notepad-3SUD5XUE.js.map +1 -0
- package/dist/{PomodoroTimer-SVJ62RXM.js → PomodoroTimer-4EHD2QC4.js} +7 -6
- package/dist/PomodoroTimer-4EHD2QC4.js.map +1 -0
- package/dist/{Preview-5UOC7ZRU.js → Preview-4ZHR4SKP.js} +7 -6
- package/dist/Preview-4ZHR4SKP.js.map +1 -0
- package/dist/{Spreadsheet-J7BH7UHC.js → Spreadsheet-G3SC7XJ5.js} +7 -6
- package/dist/Spreadsheet-G3SC7XJ5.js.map +1 -0
- package/dist/{Stock-CDUS4VCZ.js → Stock-TLT4VN2R.js} +6 -5
- package/dist/Stock-TLT4VN2R.js.map +1 -0
- package/dist/{Weather-EEDBWBPQ.js → Weather-FRKX3DAK.js} +6 -5
- package/dist/Weather-FRKX3DAK.js.map +1 -0
- package/dist/{WorldClock-FFFULVTJ.js → WorldClock-GKCITO3M.js} +6 -5
- package/dist/WorldClock-GKCITO3M.js.map +1 -0
- package/dist/apps/index.js +17 -16
- package/dist/apps/index.js.map +1 -1
- package/dist/brand.css +1 -1
- package/dist/chunk-CJZXR7MG.js +7 -0
- package/dist/{chunk-SZ4WMHUX.js.map → chunk-CJZXR7MG.js.map} +1 -1
- package/dist/{chunk-YNHLQ24V.js → chunk-G3HQLQYZ.js} +5 -114
- package/dist/{chunk-YNHLQ24V.js.map → chunk-G3HQLQYZ.js.map} +1 -1
- package/dist/{chunk-DMCFOWOJ.js → chunk-G4KA3SAX.js} +3 -3
- package/dist/{chunk-DMCFOWOJ.js.map → chunk-G4KA3SAX.js.map} +1 -1
- package/dist/{chunk-32W6VY2J.js → chunk-NPJCMM6V.js} +3 -3
- package/dist/{chunk-32W6VY2J.js.map → chunk-NPJCMM6V.js.map} +1 -1
- package/dist/chunk-OKEFO6TI.js +117 -0
- package/dist/chunk-OKEFO6TI.js.map +1 -0
- package/dist/{chunk-W6XTG23K.js → chunk-PPUZJFCI.js} +4 -3
- package/dist/chunk-PPUZJFCI.js.map +1 -0
- package/dist/{chunk-4TVV2BIU.js → chunk-WK7FV7VV.js} +5 -4
- package/dist/chunk-WK7FV7VV.js.map +1 -0
- package/dist/{chunk-QRGFTK2X.js → chunk-XUCIYMN7.js} +4 -4
- package/dist/{chunk-QRGFTK2X.js.map → chunk-XUCIYMN7.js.map} +1 -1
- package/dist/index.js +24 -15
- package/dist/index.js.map +1 -1
- package/dist/map/index.d.ts +329 -0
- package/dist/map/index.js +539 -0
- package/dist/map/index.js.map +1 -0
- package/dist/ui/index.js +5 -4
- package/package.json +11 -1
- package/dist/Browser-LM4RRZIL.js.map +0 -1
- package/dist/Calculator-HCKFTSJR.js.map +0 -1
- package/dist/CurrencyConverter-BKKORN6K.js.map +0 -1
- package/dist/Documents-DD3QI5ER.js.map +0 -1
- package/dist/Files-BOPCPMJE.js.map +0 -1
- package/dist/Notepad-KJOLF63O.js.map +0 -1
- package/dist/PomodoroTimer-SVJ62RXM.js.map +0 -1
- package/dist/Preview-5UOC7ZRU.js.map +0 -1
- package/dist/Spreadsheet-J7BH7UHC.js.map +0 -1
- package/dist/Stock-CDUS4VCZ.js.map +0 -1
- package/dist/Weather-EEDBWBPQ.js.map +0 -1
- package/dist/WorldClock-FFFULVTJ.js.map +0 -1
- package/dist/chunk-4TVV2BIU.js.map +0 -1
- package/dist/chunk-SZ4WMHUX.js +0 -7
- package/dist/chunk-W6XTG23K.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
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';\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 the `data-theme` attribute on `<html>` rather than calling `useTheme`:\n * every host stamps that attribute (the desktop shell through `useTheme`, the\n * routed portals through their own theme toggle), while `useTheme` is the\n * desktop's and owns side effects (it removes the attribute on unmount).\n */\nfunction subscribe(onChange: () => void): () => void {\n const watcher = new MutationObserver(onChange);\n watcher.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] });\n return () => watcher.disconnect();\n}\n\nconst isDark = () => document.documentElement.getAttribute('data-theme') === 'dark';\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"]}
|
package/dist/ui/index.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
export { APP_UPDATE_POLL_MS, Accordion, AppUpdateBanner, AttachButton, AttachmentDropZone, AttachmentList, AuthScreen, Avatar, AvatarGroup, Banner, BarChart, BoxPlotChart, BrandAssetEditor, BudgetBar, CHART_INK, Calendar, CandlestickChart, Card, CartesianPlot, ChartBrush, ChartDefs, ChartDot, ChartFrame, ChartHighlightProvider, ChartSkeleton, ChartTooltip, ChatTemplate, Checkbox_default as Checkbox, CheckoutTemplate, ChordChart, ColoredBadge, ColumnChart, ComposerAttachments, ContainerFillChart, CountBadge, DAY_MS, DashboardTemplate, DataTable, DataTablePage, DatePicker_default as DatePicker, DateRangePicker, DateTimePicker_default as DateTimePicker, DescriptionList, Divider, DonutChart, Drawer, DropdownMenu, EmailTemplate, EmptyState, ErrorBoundary, ErrorPage, FilePicker_default as FilePicker, FormErrorSummary, FormField, FormLayoutPage, FunnelChart, GROUP_COLORS, GROUP_COLORS_SOLID, GalleryTemplate, GlobalSearch, Grid, HeatmapChart, HelpCenter, HistogramChart, INPUT_BASE, INPUT_SIZES, IconButton_default as IconButton, Inline, Input_default as Input, InputNumber_default as InputNumber, Kanban, Label, LineChart, ListFooter, ListLoadError, MAX_FRACTION_DIGITS, MENU_MAX_HEIGHT, MOTION, Markdown, Markdown as MarkdownLite, MediaUploadField, MediaUploadGrid, Meter, MetricBar, MilestoneTimeline, NativeSelect, NumericKeypad, POPUP_MAX_WIDTH, POST_PRODUCTION_STATUSES, PageHeader, Pagination, PaperclipGlyph, Paragraph, PieChart, PortalBrandingProvider, ProductionTimeline, RADAR_SERIES_CAP, RadarChart, RadialBarChart, Radio_default as Radio, RadioGroup, RangeChart, RankedBars, SCATTER_SERIES_CAP, SERIES_SLOT_COUNT, SERIES_VARS, STAGES, STATUS_VARS, SankeyChart, ScatterChart, SearchableSelect, Segmented, Select_default as Select, SettingRow, SidebarActionButton, SidebarGroupLabel, SidebarNavItem, Skeleton, Snippet, Sparkline, Stack, StatCard, StatTile, Statistic, StatusBadge, StatusBadgeProvider, Stepper, SunburstChart, Switch_default as Switch, Tabs, TagInput, Text, Textarea_default as Textarea, TileButton_default as TileButton, TimePicker_default as TimePicker, TimeSeriesChart, TimelineTrack, Title, Tooltip, TopNav, TreemapChart, WaterfallChart, angleScale, appendKey, applyThemePrefs, arcPath, areaBetween, areaFrom, autoHighlightIndex, backspace, bandScale, binValues, budgetState, bumpPath, calcOverall, calcReportOverall, curvePath, dashFor, fillFor, fillId, fmtSliderDate, formatDate, fromKey as fromDateKey, glowId, groundId, groupColors, highlightOpacity, inputClasses, isSeverityTone, ladderScale, linearPath, linearScale, logScale, maskId, mediaFileName, monotonePath, niceMax, polygonPoints, popupBounds, radiusScale, resolvePortalBrandAsset, resolveSeriesColor, resolveTheme, seriesColor, severityOf, splinePath, squarify, stagger, stepPath, tabButtonId, tabPanelId, toKey as toDateKey, toDayMs, toISODate, useAppUpdate, useClickOutside, useDropdownPosition, useHighlight, usePlotWidth, usePortalBranding, useProductionTimeline, useTheme } from '../chunk-
|
|
1
|
+
export { APP_UPDATE_POLL_MS, Accordion, AppUpdateBanner, AttachButton, AttachmentDropZone, AttachmentList, AuthScreen, Avatar, AvatarGroup, Banner, BarChart, BoxPlotChart, BrandAssetEditor, BudgetBar, CHART_INK, Calendar, CandlestickChart, Card, CartesianPlot, ChartBrush, ChartDefs, ChartDot, ChartFrame, ChartHighlightProvider, ChartSkeleton, ChartTooltip, ChatTemplate, Checkbox_default as Checkbox, CheckoutTemplate, ChordChart, ColoredBadge, ColumnChart, ComposerAttachments, ContainerFillChart, CountBadge, DAY_MS, DashboardTemplate, DataTable, DataTablePage, DatePicker_default as DatePicker, DateRangePicker, DateTimePicker_default as DateTimePicker, DescriptionList, Divider, DonutChart, Drawer, DropdownMenu, EmailTemplate, EmptyState, ErrorBoundary, ErrorPage, FilePicker_default as FilePicker, FormErrorSummary, FormField, FormLayoutPage, FunnelChart, GROUP_COLORS, GROUP_COLORS_SOLID, GalleryTemplate, GlobalSearch, Grid, HeatmapChart, HelpCenter, HistogramChart, INPUT_BASE, INPUT_SIZES, IconButton_default as IconButton, Inline, Input_default as Input, InputNumber_default as InputNumber, Kanban, Label, LineChart, ListFooter, ListLoadError, MAX_FRACTION_DIGITS, MENU_MAX_HEIGHT, MOTION, Markdown, Markdown as MarkdownLite, MediaUploadField, MediaUploadGrid, Meter, MetricBar, MilestoneTimeline, NativeSelect, NumericKeypad, POPUP_MAX_WIDTH, POST_PRODUCTION_STATUSES, PageHeader, Pagination, PaperclipGlyph, Paragraph, PieChart, PortalBrandingProvider, ProductionTimeline, RADAR_SERIES_CAP, RadarChart, RadialBarChart, Radio_default as Radio, RadioGroup, RangeChart, RankedBars, SCATTER_SERIES_CAP, SERIES_SLOT_COUNT, SERIES_VARS, STAGES, STATUS_VARS, SankeyChart, ScatterChart, SearchableSelect, Segmented, Select_default as Select, SettingRow, SidebarActionButton, SidebarGroupLabel, SidebarNavItem, Skeleton, Snippet, Sparkline, Stack, StatCard, StatTile, Statistic, StatusBadge, StatusBadgeProvider, Stepper, SunburstChart, Switch_default as Switch, Tabs, TagInput, Text, Textarea_default as Textarea, TileButton_default as TileButton, TimePicker_default as TimePicker, TimeSeriesChart, TimelineTrack, Title, Tooltip, TopNav, TreemapChart, WaterfallChart, angleScale, appendKey, applyThemePrefs, arcPath, areaBetween, areaFrom, autoHighlightIndex, backspace, bandScale, binValues, budgetState, bumpPath, calcOverall, calcReportOverall, curvePath, dashFor, fillFor, fillId, fmtSliderDate, formatDate, fromKey as fromDateKey, glowId, groundId, groupColors, highlightOpacity, inputClasses, isSeverityTone, ladderScale, linearPath, linearScale, logScale, maskId, mediaFileName, monotonePath, niceMax, polygonPoints, popupBounds, radiusScale, resolvePortalBrandAsset, resolveSeriesColor, resolveTheme, seriesColor, severityOf, splinePath, squarify, stagger, stepPath, tabButtonId, tabPanelId, toKey as toDateKey, toDayMs, toISODate, useAppUpdate, useClickOutside, useDropdownPosition, useHighlight, usePlotWidth, usePortalBranding, useProductionTimeline, useTheme } from '../chunk-WK7FV7VV.js';
|
|
2
2
|
export { Breadcrumbs } from '../chunk-HNH7FOYB.js';
|
|
3
|
+
export { SidebarLayout } from '../chunk-YG45DAUT.js';
|
|
3
4
|
export { BrandMark, resolveBrandMarkTreatment } from '../chunk-6XQHH7D2.js';
|
|
4
5
|
export { EditableGrid } from '../chunk-JFQGPY7W.js';
|
|
5
6
|
import '../chunk-MGGY74RY.js';
|
|
6
|
-
export {
|
|
7
|
-
export { VERSION } from '../chunk-SZ4WMHUX.js';
|
|
7
|
+
export { VERSION } from '../chunk-CJZXR7MG.js';
|
|
8
8
|
export { toast_default as toast } from '../chunk-2VM2FRZJ.js';
|
|
9
|
-
export { ALT, ALT_SHIFT_D, ALT_SHIFT_E, ALT_SHIFT_N,
|
|
9
|
+
export { ALT, ALT_SHIFT_D, ALT_SHIFT_E, ALT_SHIFT_N, CMD_A, CMD_DOT, CMD_ENTER, CMD_K, CMD_S, ENTER, Kbd, LoadingSpinner, MOD, NEXT_CHUNK_PATH_PREFIX, Result, SHIFT, STALE_CHUNK_RELOAD_COOLDOWN_MS, STALE_CHUNK_RELOAD_KEY, ShellPrefsProvider, VITE_PRELOAD_ERROR_EVENT, installStaleChunkReload, isMac, isStaleChunkError, useIsMobile, useLocalStoragePrefs, useShellPrefs } from '../chunk-G3HQLQYZ.js';
|
|
10
10
|
export { FileIntakeAlert, acceptsFile, useFileIntake } from '../chunk-N3CG5HG5.js';
|
|
11
|
+
export { Button_default as Button } from '../chunk-OKEFO6TI.js';
|
|
11
12
|
export { DEFAULT_SHELL_STRINGS, PopupMenu, PopupMenuDivider, PopupMenuItem, PopupMenuLabel, PopupSubmenu, ShellStringsProvider, useShellStrings } from '../chunk-7KIOEKHS.js';
|
|
12
13
|
export { ConfirmProvider, Dialog, OVERLAY_OPEN_EVENT, Z_LAYERS, confirm, confirmDestructive, dismissPopups, prompt, useConfirm, useFocusTrap, useScrollLock } from '../chunk-HYZ5QMVO.js';
|
|
13
14
|
export { GLASS_DIVIDER, glassStyle } from '../chunk-3CUSBZWG.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "efficient-shell",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.127.0",
|
|
4
4
|
"description": "Desktop-style React UI shell — windows, taskbar, start menu, sticky notes, frosted glass theming, and bundled apps.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Victor Y. Mau",
|
|
@@ -44,6 +44,11 @@
|
|
|
44
44
|
"import": "./dist/page-tabs/index.js",
|
|
45
45
|
"default": "./dist/page-tabs/index.js"
|
|
46
46
|
},
|
|
47
|
+
"./map": {
|
|
48
|
+
"types": "./dist/map/index.d.ts",
|
|
49
|
+
"import": "./dist/map/index.js",
|
|
50
|
+
"default": "./dist/map/index.js"
|
|
51
|
+
},
|
|
47
52
|
"./markdown": {
|
|
48
53
|
"types": "./dist/markdown/index.d.ts",
|
|
49
54
|
"import": "./dist/markdown/index.js"
|
|
@@ -67,6 +72,7 @@
|
|
|
67
72
|
"axios": ">=1.6",
|
|
68
73
|
"dxf-viewer": "*",
|
|
69
74
|
"mammoth": "*",
|
|
75
|
+
"maplibre-gl": ">=6",
|
|
70
76
|
"online-3d-viewer": "*",
|
|
71
77
|
"pdfjs-dist": "^5.6.205 || ^6.0.0",
|
|
72
78
|
"react": ">=18",
|
|
@@ -93,6 +99,7 @@
|
|
|
93
99
|
"esbuild": "^0.27.0",
|
|
94
100
|
"jsdom": "^26.1.0",
|
|
95
101
|
"mammoth": "^1.8.0",
|
|
102
|
+
"maplibre-gl": "6.11.2",
|
|
96
103
|
"online-3d-viewer": "^0.18.0",
|
|
97
104
|
"pdfjs-dist": "^6.2.108",
|
|
98
105
|
"playwright": "^1.48.0",
|
|
@@ -149,6 +156,9 @@
|
|
|
149
156
|
"mammoth": {
|
|
150
157
|
"optional": true
|
|
151
158
|
},
|
|
159
|
+
"maplibre-gl": {
|
|
160
|
+
"optional": true
|
|
161
|
+
},
|
|
152
162
|
"online-3d-viewer": {
|
|
153
163
|
"optional": true
|
|
154
164
|
},
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/apps/Browser.tsx"],"names":[],"mappings":"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","file":"Browser-LM4RRZIL.js","sourcesContent":["/**\n * Browser — minimal iframe-backed web browser app.\n *\n * URL bar with back / forward / refresh, navigable bookmark bar\n * (persisted to localStorage), and a graceful \"open in new tab\"\n * escape hatch since most major sites refuse iframe embedding via\n * X-Frame-Options or Content-Security-Policy.\n */\nimport { useEffect, useRef, useState } from 'react';\nimport { WindowTitle } from '../shell/Modal';\nimport { confirm } from '../shell/ConfirmDialog';\nimport AboutApp from './_about';\nimport { peekBrowserStartUrl, claimBrowserStartUrl, type PendingStartUrl } from './_browserStage';\n\ninterface Bookmark {\n label: string;\n url: string;\n}\n\nconst BOOKMARKS_KEY = 'react-os-shell:browser-bookmarks';\nconst HOMEPAGE_KEY = 'react-os-shell:browser-homepage';\nconst DEFAULT_HOMEPAGE = 'https://en.wikipedia.org/wiki/Main_Page';\n// Search provider (DuckDuckGo, no tracking). Hoisted to a constant so the\n// provider is stated in exactly one place — note it is deliberately NOT\n// framable, hence its presence in BLOCKED_HOSTS below.\nconst SEARCH_URL = 'https://duckduckgo.com/?q=';\nconst DEFAULT_BOOKMARKS: Bookmark[] = [\n { label: 'Wikipedia', url: 'https://en.wikipedia.org/wiki/Main_Page' },\n // DevDocs rather than MDN: MDN sends `x-frame-options: DENY`, so shipping it\n // as a default bookmark shipped a guaranteed-blank pane (BG#00374). DevDocs\n // carries the same API reference content and permits embedding.\n { label: 'DevDocs', url: 'https://devdocs.io' },\n { label: 'Example', url: 'https://example.com' },\n];\n\n// A dotted single token is ambiguous: `example.com` is a host, but `node.js`,\n// `web.config` and `array.map` are search queries the user expects to search.\n// The only offline signal is the last label, so we treat a bare token as a host\n// ONLY when it ends in a real TLD; everything else searches. A host on an\n// exotic TLD missing from this list is still reachable by typing the scheme\n// (`https://foo.zuerich`), which short-circuits before this check.\nconst KNOWN_TLDS = new Set([\n // ISO 3166-1 alpha-2 ccTLDs. Mind the lookalikes: `md`, `py`, `rs` and `sh`\n // ARE countries (`foo.sh` is a real host), while `js` and `ts` are not.\n 'ac ad ae af ag ai al am ao aq ar as at au aw ax az ba bb bd be bf bg bh bi',\n 'bj bm bn bo br bs bt bw by bz ca cc cd cf cg ch ci ck cl cm cn co cr cu cv',\n 'cw cx cy cz de dj dk dm do dz ec ee eg er es et eu fi fj fk fm fo fr ga gd',\n 'ge gf gg gh gi gl gm gn gp gq gr gs gt gu gw gy hk hm hn hr ht hu id ie il',\n 'im in io iq ir is it je jm jo jp ke kg kh ki km kn kp kr kw ky kz la lb lc',\n 'li lk lr ls lt lu lv ly ma mc md me mg mh mk ml mm mn mo mp mq mr ms mt mu',\n 'mv mw mx my mz na nc ne nf ng ni nl no np nr nu nz om pa pe pf pg ph pk pl',\n 'pm pn pr ps pt pw py qa re ro rs ru rw sa sb sc sd se sg sh si sk sl sm sn',\n 'so sr ss st su sv sx sy sz tc td tf tg th tj tk tl tm tn to tr tt tv tw tz',\n 'ua ug uk us uy uz va vc ve vg vi vn vu wf ws ye yt za zm zw',\n // Common gTLDs, plus the ones this business actually uses (`design`).\n 'com net org edu gov mil int info biz name pro app dev xyz online site tech',\n 'store shop cloud design digital agency studio media news blog live life',\n 'world today email systems solutions services software page link space host',\n 'press wiki team group works careers finance capital energy academy',\n].join(' ').split(' '));\n\n// `example.com`, `sub.example.co.uk`, `example.com/path` → host; `node.js` → search.\nfunction isProbableHost(s: string): boolean {\n const m = /^([\\w-]+\\.)+([a-z]{2,})(\\/.*)?$/i.exec(s);\n return m !== null && KNOWN_TLDS.has(m[2].toLowerCase());\n}\n\nfunction normalizeUrl(input: string): string {\n let s = input.trim();\n if (!s) return '';\n // Already a URL? Otherwise treat as a search query.\n if (/^[a-z][a-z0-9+.-]*:\\/\\//i.test(s)) return s;\n if (isProbableHost(s)) return 'https://' + s;\n return SEARCH_URL + encodeURIComponent(s);\n}\n\n// Sites known to refuse iframe embedding via X-Frame-Options or CSP. There's\n// no workaround inside an iframe — the browser blocks the load. We keep a\n// list so we can short-circuit to a friendly \"open in new tab\" panel\n// instead of letting the browser's blank \"refused to connect\" error\n// through. Subdomain match: `mail.google.com` matches `google.com`.\nconst BLOCKED_HOSTS = [\n // The two hosts this app itself routes to or ships as a default bookmark.\n // Their absence here was BG#00374: an address-bar search went to a blank\n // pane rather than the panel below, reading as \"search does nothing\".\n // Verified live 2026-07-16 — duckduckgo.com: `x-frame-options: SAMEORIGIN` +\n // `frame-ancestors 'self' https://html.duckduckgo.com` (the html/ and lite/\n // mirrors are no escape hatch: `frame-ancestors 'self'`);\n // developer.mozilla.org: `x-frame-options: DENY` (kept even though the\n // default bookmark moved to DevDocs — existing users have MDN persisted in\n // localStorage, and it stays a thing people type).\n 'duckduckgo.com', 'developer.mozilla.org',\n 'google.com', 'gmail.com', 'youtube.com',\n 'facebook.com', 'instagram.com', 'whatsapp.com',\n 'twitter.com', 'x.com',\n 'github.com', 'gitlab.com',\n 'linkedin.com', 'reddit.com', 'pinterest.com',\n 'amazon.com', 'amazon.ca', 'amazon.co.uk',\n 'apple.com', 'icloud.com',\n 'microsoft.com', 'outlook.com', 'live.com', 'office.com',\n 'netflix.com', 'spotify.com',\n 'paypal.com', 'stripe.com',\n 'chat.openai.com', 'chatgpt.com', 'claude.ai',\n];\n\nfunction hostIsBlocked(href: string): boolean {\n try {\n const host = new URL(href).hostname.toLowerCase();\n return BLOCKED_HOSTS.some(b => host === b || host.endsWith('.' + b));\n } catch { return false; }\n}\n\nfunction loadBookmarks(): Bookmark[] {\n if (typeof window === 'undefined') return DEFAULT_BOOKMARKS;\n try {\n const raw = localStorage.getItem(BOOKMARKS_KEY);\n if (!raw) return DEFAULT_BOOKMARKS;\n const parsed = JSON.parse(raw);\n if (Array.isArray(parsed) && parsed.every(b => typeof b?.url === 'string')) return parsed;\n } catch {}\n return DEFAULT_BOOKMARKS;\n}\n\n// The consumer-facing `setBrowserStartUrl` lives in ./_browserStage so that\n// hosts importing it don't pull this module into their startup bundle. This\n// component drains the stage via the @internal peek/claim pair.\n\nfunction loadHomepage(): string {\n if (typeof window === 'undefined') return DEFAULT_HOMEPAGE;\n return localStorage.getItem(HOMEPAGE_KEY) || DEFAULT_HOMEPAGE;\n}\n\nexport default function Browser() {\n // One-shot drain of a staged start URL. The render phase only PEEKS — under\n // React 18 concurrent rendering the first render pass of this lazy component\n // can be discarded and replayed, so a destructive read here would lose the\n // payload (see Spreadsheet/Preview). The stage is cleared in the mount\n // effect below.\n const consumedRef = useRef<PendingStartUrl | null | undefined>(undefined);\n if (consumedRef.current === undefined) {\n consumedRef.current = peekBrowserStartUrl();\n }\n useEffect(() => {\n if (consumedRef.current != null) {\n claimBrowserStartUrl(consumedRef.current);\n }\n }, []);\n const startUrl = consumedRef.current ? normalizeUrl(consumedRef.current.url) : '';\n\n const [homepage, setHomepage] = useState(loadHomepage);\n const [url, setUrl] = useState(startUrl || homepage);\n const [inputUrl, setInputUrl] = useState(url);\n const [history, setHistory] = useState<string[]>([url]);\n const [historyIdx, setHistoryIdx] = useState(0);\n const [iframeKey, setIframeKey] = useState(0);\n const [bookmarks, setBookmarks] = useState<Bookmark[]>(loadBookmarks);\n const [showHelp, setShowHelp] = useState(false);\n // Inline \"name this bookmark\" popover state. Populated when the user\n // clicks the star icon to add — replaces the native window.prompt().\n const [bookmarkDraft, setBookmarkDraft] = useState<string | null>(null);\n const bookmarkInputRef = useRef<HTMLInputElement>(null);\n const iframeRef = useRef<HTMLIFrameElement>(null);\n\n // Persist bookmarks.\n useEffect(() => {\n try { localStorage.setItem(BOOKMARKS_KEY, JSON.stringify(bookmarks)); } catch {}\n }, [bookmarks]);\n\n const navigate = (next: string) => {\n const n = normalizeUrl(next);\n if (!n) return;\n setUrl(n);\n setInputUrl(n);\n // Truncate forward history when navigating from a back state.\n setHistory(h => {\n const trimmed = h.slice(0, historyIdx + 1);\n trimmed.push(n);\n return trimmed;\n });\n setHistoryIdx(i => i + 1);\n setShowHelp(false);\n };\n\n const back = () => {\n if (historyIdx > 0) {\n const next = historyIdx - 1;\n setHistoryIdx(next);\n setUrl(history[next]);\n setInputUrl(history[next]);\n setShowHelp(false);\n }\n };\n\n const forward = () => {\n if (historyIdx < history.length - 1) {\n const next = historyIdx + 1;\n setHistoryIdx(next);\n setUrl(history[next]);\n setInputUrl(history[next]);\n setShowHelp(false);\n }\n };\n\n const refresh = () => setIframeKey(k => k + 1);\n\n const goHome = () => navigate(homepage);\n\n const openExternal = () => {\n if (typeof window !== 'undefined') window.open(url, '_blank', 'noopener,noreferrer');\n };\n\n const openBookmarkPopover = () => {\n setBookmarkDraft(titleFromUrl(url));\n // Focus + select happens in a useEffect once the input is mounted.\n };\n\n const commitBookmark = () => {\n const label = bookmarkDraft?.trim();\n if (!label) { setBookmarkDraft(null); return; }\n setBookmarks(b => [...b, { label, url }]);\n setBookmarkDraft(null);\n };\n\n // Auto-focus + select the popover input when it opens, and dismiss on\n // outside click.\n useEffect(() => {\n if (bookmarkDraft === null) return;\n bookmarkInputRef.current?.focus();\n bookmarkInputRef.current?.select();\n const onDocClick = (e: MouseEvent) => {\n const target = e.target as HTMLElement | null;\n if (!target?.closest('[data-bookmark-popover]')) setBookmarkDraft(null);\n };\n // Defer one tick so the click that opened the popover doesn't close it.\n const t = setTimeout(() => document.addEventListener('mousedown', onDocClick), 0);\n return () => {\n clearTimeout(t);\n document.removeEventListener('mousedown', onDocClick);\n };\n }, [bookmarkDraft !== null]);\n\n const removeBookmark = (i: number) => {\n setBookmarks(b => b.filter((_, idx) => idx !== i));\n };\n\n const setAsHomepage = () => {\n setHomepage(url);\n try { localStorage.setItem(HOMEPAGE_KEY, url); } catch {}\n };\n\n const onSubmitUrl = (e: React.FormEvent) => {\n e.preventDefault();\n navigate(inputUrl);\n };\n\n const canBack = historyIdx > 0;\n const canForward = historyIdx < history.length - 1;\n const isBookmarked = bookmarks.some(b => b.url === url);\n const btn = 'p-1.5 rounded hover:bg-gray-200 transition-colors text-gray-600 disabled:opacity-30';\n\n return (\n <div className=\"relative flex flex-col h-full bg-white\">\n <WindowTitle title={`Browser - ${titleFromUrl(url)}`} />\n <AboutApp app=\"browser\" />\n\n {/* Top toolbar */}\n <div className=\"flex items-center gap-1 px-2 py-1.5 border-b border-gray-200 bg-gray-50 shrink-0\">\n <button onClick={back} disabled={!canBack} className={btn} title=\"Back\">\n <svg className=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2}><path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M15.75 19.5L8.25 12l7.5-7.5\" /></svg>\n </button>\n <button onClick={forward} disabled={!canForward} className={btn} title=\"Forward\">\n <svg className=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2}><path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M8.25 4.5l7.5 7.5-7.5 7.5\" /></svg>\n </button>\n <button onClick={refresh} className={btn} title=\"Refresh\">\n <svg className=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={1.8}><path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M16.023 9.348h4.992V4.356M2.985 19.644v-4.992h4.992M3.05 9.348a9 9 0 0114.85-3.36L21.015 9.348m0 5.304a9 9 0 01-14.85 3.36l-3.115-3.36\" /></svg>\n </button>\n <button onClick={goHome} className={btn} title=\"Home\">\n <svg className=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={1.8}><path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M2.25 12l8.954-8.955a1.5 1.5 0 012.122 0l8.954 8.955M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25\" /></svg>\n </button>\n\n <form onSubmit={onSubmitUrl} className=\"flex-1 flex items-center mx-1\">\n <div className=\"flex items-center w-full bg-white border border-gray-300 rounded focus-within:border-blue-400 focus-within:ring-1 focus-within:ring-blue-200\">\n <span className=\"px-2 text-gray-400 text-xs\">{url.startsWith('https://') ? '🔒' : '⚠️'}</span>\n <input\n type=\"text\"\n value={inputUrl}\n onChange={(e) => setInputUrl(e.target.value)}\n onFocus={(e) => e.target.select()}\n className=\"flex-1 px-1 py-1 text-sm bg-transparent outline-none font-mono\"\n spellCheck={false}\n placeholder=\"Enter URL or search…\"\n />\n {url !== inputUrl && (\n <button\n type=\"button\"\n onClick={() => setInputUrl(url)}\n className=\"px-1.5 text-[10px] text-gray-400 hover:text-gray-700\"\n title=\"Reset to current URL\"\n >×</button>\n )}\n </div>\n </form>\n\n <button\n data-bookmark-popover\n onClick={() => isBookmarked\n ? removeBookmark(bookmarks.findIndex(b => b.url === url))\n : openBookmarkPopover()}\n className={btn + ' ' + (isBookmarked ? 'text-yellow-500' : '')}\n title={isBookmarked ? 'Remove bookmark' : 'Add bookmark'}\n >\n <svg className=\"h-4 w-4\" fill={isBookmarked ? 'currentColor' : 'none'} viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={1.5}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M11.48 3.499a.562.562 0 011.04 0l2.125 5.111a.563.563 0 00.475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 00-.182.557l1.285 5.385a.562.562 0 01-.84.61l-4.725-2.885a.563.563 0 00-.586 0L6.982 20.54a.562.562 0 01-.84-.61l1.285-5.386a.562.562 0 00-.182-.557l-4.204-3.602a.563.563 0 01.321-.988l5.518-.442a.563.563 0 00.475-.345L11.48 3.5z\" />\n </svg>\n </button>\n <button onClick={openExternal} className={btn} title=\"Open in new tab\">\n <svg className=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={1.5}><path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M13.5 6H5.25A2.25 2.25 0 003 8.25v10.5A2.25 2.25 0 005.25 21h10.5A2.25 2.25 0 0018 18.75V10.5m-7.5-9L21 3m0 0v6m0-6L9.75 14.25\" /></svg>\n </button>\n <button onClick={() => setShowHelp(s => !s)} className={btn} title=\"Embedding help\">\n <svg className=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={1.5}><path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9 5.25h.008v.008H12v-.008z\" /></svg>\n </button>\n </div>\n\n {/* Inline \"name this bookmark\" popover. Anchored to the right of the\n toolbar (where the star icon lives) so the visual flow matches\n how the user just clicked. Renders inside the panel so it\n inherits the window's z-stacking. */}\n {bookmarkDraft !== null && (\n <div\n data-bookmark-popover\n className=\"absolute right-3 top-12 z-30 w-72 bg-white border border-gray-200 rounded-md shadow-lg p-3\"\n >\n <div className=\"text-[11px] font-semibold uppercase tracking-wide text-gray-500 mb-1\">Bookmark this page</div>\n <div className=\"text-[11px] text-gray-400 truncate mb-2 font-mono\" title={url}>{url}</div>\n <input\n ref={bookmarkInputRef}\n type=\"text\"\n value={bookmarkDraft}\n onChange={(e) => setBookmarkDraft(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') commitBookmark();\n else if (e.key === 'Escape') setBookmarkDraft(null);\n }}\n placeholder=\"Name\"\n className=\"w-full text-sm px-2 py-1.5 border border-gray-300 rounded focus:outline-none focus:border-blue-400 focus:ring-1 focus:ring-blue-200\"\n />\n <div className=\"flex justify-end gap-1 mt-2\">\n <button\n onClick={() => setBookmarkDraft(null)}\n className=\"px-2 py-1 text-xs text-gray-600 hover:bg-gray-100 rounded\"\n >Cancel</button>\n <button\n onClick={commitBookmark}\n disabled={!bookmarkDraft.trim()}\n className=\"px-2 py-1 text-xs bg-blue-500 text-white rounded hover:bg-blue-600 disabled:opacity-40\"\n >Save</button>\n </div>\n </div>\n )}\n\n {/* Bookmarks bar */}\n <div className=\"flex items-center gap-0.5 px-2 py-1 border-b border-gray-200 bg-white shrink-0 overflow-x-auto\">\n {bookmarks.length === 0 && (\n <span className=\"text-[11px] text-gray-400 italic px-2\">No bookmarks yet — star the address bar to add one.</span>\n )}\n {bookmarks.map((b, i) => (\n <button\n key={i}\n onClick={() => navigate(b.url)}\n onContextMenu={async (e) => {\n e.preventDefault();\n const ok = await confirm({\n title: 'Remove bookmark',\n message: `\"${b.label}\" will be removed from your bookmarks.`,\n confirmLabel: 'Remove',\n variant: 'danger',\n });\n if (ok) removeBookmark(i);\n }}\n className=\"flex items-center gap-1.5 px-2 py-0.5 rounded text-[12px] text-gray-700 hover:bg-gray-100 whitespace-nowrap\"\n title={`${b.url}\\n(right-click to remove)`}\n >\n <img\n src={`https://icons.duckduckgo.com/ip3/${new URL(b.url).hostname}.ico`}\n alt=\"\"\n className=\"h-3.5 w-3.5\"\n onError={(e) => { (e.target as HTMLImageElement).style.visibility = 'hidden'; }}\n />\n {b.label}\n </button>\n ))}\n <div className=\"flex-1\" />\n <button\n onClick={setAsHomepage}\n className=\"px-2 py-0.5 text-[11px] text-gray-500 hover:text-gray-800\"\n title=\"Set current page as homepage\"\n >Set as home</button>\n </div>\n\n {/* Iframe area */}\n <div className=\"flex-1 relative min-h-0 bg-gray-50\">\n <BrowserBody\n url={url}\n iframeKey={iframeKey}\n iframeRef={iframeRef}\n openExternal={openExternal}\n showHelp={showHelp}\n dismissHelp={() => setShowHelp(false)}\n />\n </div>\n </div>\n );\n}\n\nfunction titleFromUrl(u: string): string {\n try {\n const url = new URL(u);\n return url.hostname.replace(/^www\\./, '');\n } catch {\n return u;\n }\n}\n\nfunction BrowserBody({\n url,\n iframeKey,\n iframeRef,\n openExternal,\n showHelp,\n dismissHelp,\n}: {\n url: string;\n iframeKey: number;\n iframeRef: React.RefObject<HTMLIFrameElement | null>;\n openExternal: () => void;\n showHelp: boolean;\n dismissHelp: () => void;\n}) {\n // If the user dismisses the blocked-site panel, allow them to attempt\n // the iframe load anyway. Reset the override whenever URL changes.\n const [forceTry, setForceTry] = useState(false);\n useEffect(() => { setForceTry(false); }, [url]);\n\n const blocked = hostIsBlocked(url) && !forceTry;\n\n if (blocked) {\n return (\n <div className=\"absolute inset-0 flex items-center justify-center bg-white p-8\">\n <div className=\"max-w-md text-center\">\n <svg className=\"h-14 w-14 mx-auto text-gray-300 mb-3\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={1.2}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728L5.636 5.636m12.728 12.728L21 21M5.636 5.636L3 3m9 9a9 9 0 110-18 9 9 0 010 18z\" />\n </svg>\n <h3 className=\"text-base font-semibold text-gray-800 mb-1\">\n {titleFromUrl(url)} can't be embedded\n </h3>\n <p className=\"text-sm text-gray-500 mb-4\">\n This site sends an <span className=\"font-mono text-xs\">X-Frame-Options</span>{' '}\n or <span className=\"font-mono text-xs\">Content-Security-Policy</span> header\n that refuses iframe embedding. The browser blocks the load before our app\n can do anything about it.\n </p>\n <div className=\"flex flex-col items-center gap-2\">\n <button\n onClick={openExternal}\n className=\"px-4 py-2 bg-blue-500 text-white text-sm rounded hover:bg-blue-600 inline-flex items-center gap-2\"\n >\n <svg className=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={1.5}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M13.5 6H5.25A2.25 2.25 0 003 8.25v10.5A2.25 2.25 0 005.25 21h10.5A2.25 2.25 0 0018 18.75V10.5m-7.5-9L21 3m0 0v6m0-6L9.75 14.25\" />\n </svg>\n Open in a new tab\n </button>\n <button\n onClick={() => setForceTry(true)}\n className=\"text-xs text-gray-500 hover:text-gray-800 underline\"\n >\n Try loading it here anyway\n </button>\n </div>\n </div>\n </div>\n );\n }\n\n return (\n <>\n <iframe\n key={iframeKey + url}\n ref={iframeRef as React.RefObject<HTMLIFrameElement>}\n src={url}\n className=\"absolute inset-0 w-full h-full bg-white\"\n // Sandboxing keeps embedded pages from messing with the parent\n // window state. allow-same-origin lets sites that *do* allow\n // embedding actually behave normally; allow-scripts is needed\n // for any modern site.\n sandbox=\"allow-scripts allow-forms allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-modals\"\n referrerPolicy=\"no-referrer-when-downgrade\"\n />\n\n {showHelp && (\n <div className=\"absolute top-2 right-2 max-w-sm bg-white border border-gray-200 rounded-md shadow-lg p-3 text-xs text-gray-700 z-10\">\n <div className=\"font-medium text-gray-900 mb-1\">Why is this page blank?</div>\n <p className=\"mb-2\">\n Most major sites (Google, GitHub, banks, news) refuse to be embedded in\n an iframe via <span className=\"font-mono\">X-Frame-Options</span> or\n Content Security Policy. There's no workaround — the browser blocks the\n load before our app can do anything.\n </p>\n <p>\n Hit the <span className=\"font-medium\">↗</span> button to open the page in\n a real new tab. Sites that <em>do</em> allow embedding (Wikipedia, DevDocs,\n your own apps) work fine in here.\n </p>\n <button\n onClick={dismissHelp}\n className=\"mt-2 text-[11px] text-blue-600 hover:underline\"\n >Got it</button>\n </div>\n )}\n </>\n );\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/apps/Calculator.tsx"],"names":[],"mappings":"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","file":"Calculator-HCKFTSJR.js","sourcesContent":["import { useState, useCallback, useEffect } from 'react';\nimport { useWidgetSettings } from '../shell/Modal';\nimport WidgetSettingsModal, { loadAppearance, type WidgetAppearance } from '../shell/WidgetSettingsModal';\n\ntype Op = '+' | '-' | '×' | '÷' | null;\nconst CALC_SETTINGS_KEY = 'calc_appearance';\n\nexport default function Calculator() {\n const [display, setDisplay] = useState('0');\n const [prev, setPrev] = useState<number | null>(null);\n const [op, setOp] = useState<Op>(null);\n const [resetNext, setResetNext] = useState(false);\n const [lastExpr, setLastExpr] = useState('');\n const [history, setHistory] = useState<string[]>([]);\n const [copied, setCopied] = useState(false);\n const [appearance, setAppearance] = useState(() => loadAppearance(CALC_SETTINGS_KEY));\n const [settingsOpen, setSettingsOpen] = useState(false);\n const [configAppearance, setConfigAppearance] = useState<WidgetAppearance>(appearance);\n\n useWidgetSettings(useCallback(() => {\n setConfigAppearance({ ...appearance });\n setSettingsOpen(true);\n }, [appearance]));\n\n const append = useCallback((ch: string) => {\n setLastExpr('');\n setDisplay(d => {\n if (resetNext || d === '0') {\n setResetNext(false);\n return ch === '.' ? '0.' : ch;\n }\n if (ch === '.' && d.includes('.')) return d;\n return d + ch;\n });\n }, [resetNext]);\n\n const clear = useCallback(() => {\n setDisplay('0');\n setPrev(null);\n setOp(null);\n setResetNext(false);\n }, []);\n\n const compute = useCallback((a: number, b: number, operator: Op): number => {\n switch (operator) {\n case '+': return a + b;\n case '-': return a - b;\n case '×': return a * b;\n case '÷': return b === 0 ? 0 : a / b;\n default: return b;\n }\n }, []);\n\n const handleOp = useCallback((nextOp: Op) => {\n const current = parseFloat(display);\n if (prev !== null && op && !resetNext) {\n const result = compute(prev, current, op);\n setDisplay(String(result));\n setPrev(result);\n setHistory(h => [`${prev} ${op} ${current} = ${result}`, ...h].slice(0, 20));\n } else {\n setPrev(current);\n }\n setOp(nextOp);\n setResetNext(true);\n }, [display, prev, op, resetNext, compute]);\n\n const equals = useCallback(() => {\n if (prev === null || !op) return;\n const current = parseFloat(display);\n const result = compute(prev, current, op);\n const expr = `${prev} ${op} ${current} =`;\n setHistory(h => [`${expr} ${result}`, ...h].slice(0, 20));\n setLastExpr(expr);\n setDisplay(String(result));\n setPrev(null);\n setOp(null);\n setResetNext(true);\n }, [display, prev, op, compute]);\n\n const percent = useCallback(() => {\n setDisplay(d => String(parseFloat(d) / 100));\n }, []);\n\n const negate = useCallback(() => {\n setDisplay(d => d.startsWith('-') ? d.slice(1) : d === '0' ? d : '-' + d);\n }, []);\n\n // Keyboard support\n useEffect(() => {\n const handler = (e: KeyboardEvent) => {\n const tag = (e.target as HTMLElement).tagName;\n if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return;\n if (e.key >= '0' && e.key <= '9') append(e.key);\n else if (e.key === '.') append('.');\n else if (e.key === '+') handleOp('+');\n else if (e.key === '-') handleOp('-');\n else if (e.key === '*') handleOp('×');\n else if (e.key === '/') { e.preventDefault(); handleOp('÷'); }\n else if (e.key === 'Enter' || e.key === '=') equals();\n else if (e.key === 'Escape') clear();\n else if (e.key === 'Backspace') setDisplay(d => d.length > 1 ? d.slice(0, -1) : '0');\n else if (e.key === '%') percent();\n };\n window.addEventListener('keydown', handler);\n return () => window.removeEventListener('keydown', handler);\n }, [append, handleOp, equals, clear, percent]);\n\n const btn = 'flex items-center justify-center rounded-lg text-sm font-medium transition-colors';\n const numBtn = `${btn} bg-white border border-gray-200 text-gray-900 hover:bg-gray-50 active:bg-gray-100`;\n const opBtn = `${btn} bg-blue-50 border border-blue-200 text-blue-700 hover:bg-blue-100 active:bg-blue-200`;\n const fnBtn = `${btn} bg-gray-100 border border-gray-200 text-gray-600 hover:bg-gray-200 active:bg-gray-300`;\n const eqBtn = `${btn} bg-blue-600 text-white hover:bg-blue-700 active:bg-blue-800`;\n\n // Format display for readability\n const formatDisplay = (val: string) => {\n const num = parseFloat(val);\n if (isNaN(num)) return val;\n if (val.endsWith('.') || val.endsWith('.0')) return val;\n if (Math.abs(num) < 1e15 && val.length < 16) return val;\n return num.toExponential(6);\n };\n\n return (\n <>\n <div className=\"flex flex-col h-full\" style={{\n opacity: appearance.activeOpacity / 100,\n backdropFilter: appearance.activeBlur > 0 ? `blur(${appearance.activeBlur}px)` : undefined,\n }}>\n {/* Display — 5-line area with history + current value */}\n <div className=\"bg-slate-200 rounded-t-lg px-4 py-2 group/display relative flex flex-col justify-end min-h-[120px] border-b border-slate-300\">\n {/* History lines (most recent at bottom, above current) */}\n <div className=\"flex flex-col justify-end flex-1 overflow-hidden\">\n {history.slice(0, 3).reverse().map((h, i) => (\n <div key={i} className=\"text-[11px] text-slate-400 text-right font-mono truncate\">{h}</div>\n ))}\n </div>\n {/* Current expression */}\n {(lastExpr || (op && prev !== null)) && (\n <div className=\"text-base text-slate-600 text-right font-mono\">\n {lastExpr || `${prev} ${op} ${!resetNext ? display : ''}`}\n </div>\n )}\n {/* Current value */}\n <div className=\"flex items-center gap-2\">\n <button onClick={() => { navigator.clipboard.writeText(display); setCopied(true); setTimeout(() => setCopied(false), 1200); }}\n title=\"Copy result\"\n className=\"shrink-0 text-slate-300 hover:text-slate-500 transition-colors opacity-0 group-hover/display:opacity-100\">\n {copied\n ? <svg className=\"h-5 w-5 text-green-500\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2}><path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M5 13l4 4L19 7\" /></svg>\n : <svg className=\"h-5 w-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2}><path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\" /></svg>\n }\n </button>\n <div className=\"text-right text-4xl font-mono font-bold text-slate-800 truncate flex-1\">\n {formatDisplay(display)}\n </div>\n </div>\n </div>\n\n {/* Buttons */}\n <div className=\"grid grid-cols-4 grid-rows-5 gap-1.5 flex-1 min-h-0 p-2\">\n <button onClick={clear} className={fnBtn}>AC</button>\n <button onClick={negate} className={fnBtn}>+/−</button>\n <button onClick={percent} className={fnBtn}>%</button>\n <button onClick={() => handleOp('÷')} className={`${opBtn} ${op === '÷' ? 'ring-2 ring-blue-400' : ''}`}>÷</button>\n\n <button onClick={() => append('7')} className={numBtn}>7</button>\n <button onClick={() => append('8')} className={numBtn}>8</button>\n <button onClick={() => append('9')} className={numBtn}>9</button>\n <button onClick={() => handleOp('×')} className={`${opBtn} ${op === '×' ? 'ring-2 ring-blue-400' : ''}`}>×</button>\n\n <button onClick={() => append('4')} className={numBtn}>4</button>\n <button onClick={() => append('5')} className={numBtn}>5</button>\n <button onClick={() => append('6')} className={numBtn}>6</button>\n <button onClick={() => handleOp('-')} className={`${opBtn} ${op === '-' ? 'ring-2 ring-blue-400' : ''}`}>−</button>\n\n <button onClick={() => append('1')} className={numBtn}>1</button>\n <button onClick={() => append('2')} className={numBtn}>2</button>\n <button onClick={() => append('3')} className={numBtn}>3</button>\n <button onClick={() => handleOp('+')} className={`${opBtn} ${op === '+' ? 'ring-2 ring-blue-400' : ''}`}>+</button>\n\n <button onClick={() => append('0')} className={`${numBtn} col-span-2`}>0</button>\n <button onClick={() => append('.')} className={numBtn}>.</button>\n <button onClick={equals} className={eqBtn}>=</button>\n </div>\n\n </div>\n <WidgetSettingsModal open={settingsOpen} onClose={() => setSettingsOpen(false)} title=\"Calculator Settings\"\n appearance={configAppearance} onAppearanceChange={setConfigAppearance}\n onSave={() => { setAppearance(configAppearance); localStorage.setItem(CALC_SETTINGS_KEY, JSON.stringify(configAppearance)); setSettingsOpen(false); }} />\n </>\n );\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/apps/CurrencyConverter.tsx"],"names":[],"mappings":"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","file":"CurrencyConverter-BKKORN6K.js","sourcesContent":["import { useState, useEffect, useCallback } from 'react';\nimport { useWidgetSettings } from '../shell/Modal';\nimport WidgetSettingsModal, { loadAppearance, type WidgetAppearance } from '../shell/WidgetSettingsModal';\n\nconst ALL_CURRENCIES = ['USD', 'CNY', 'AUD', 'GBP', 'EUR', 'JPY', 'CAD', 'THB', 'NZD', 'SGD', 'HKD', 'CHF', 'KRW', 'INR', 'MXN', 'BRL'];\n\n/** Cap the rate list at 4 — keeps the widget compact and avoids\n * overwhelming the user with currencies they don't actually track. */\nconst MAX_PAIRS = 4;\n\nconst DEFAULT_PAIRS: [string, string][] = [\n ['USD', 'CNY'], ['USD', 'AUD'], ['GBP', 'USD'], ['USD', 'JPY'],\n];\n\nconst PAIRS_KEY = 'currency_pairs';\nconst SETTINGS_KEY = 'currency_appearance';\nconst CACHE_KEY = 'currency_rates_cache';\nconst CACHE_TTL = 3600000;\n\ntype CacheEntry = { rates: Record<string, number>; timestamp: number };\n\nfunction getCached(base: string): Record<string, number> | null {\n try { const c: Record<string, CacheEntry> = JSON.parse(localStorage.getItem(CACHE_KEY) || '{}'); const e = c[base]; if (e && Date.now() - e.timestamp < CACHE_TTL) return e.rates; } catch {} return null;\n}\nfunction setCache(base: string, rates: Record<string, number>) {\n try { const c = JSON.parse(localStorage.getItem(CACHE_KEY) || '{}'); c[base] = { rates, timestamp: Date.now() }; localStorage.setItem(CACHE_KEY, JSON.stringify(c)); } catch {}\n}\nfunction loadPairs(): [string, string][] {\n try {\n const s = JSON.parse(localStorage.getItem(PAIRS_KEY) || '');\n if (Array.isArray(s) && s.length) return s.slice(0, MAX_PAIRS);\n } catch {}\n return DEFAULT_PAIRS;\n}\n\nconst FLAG: Record<string, string> = {\n USD: '\\u{1F1FA}\\u{1F1F8}', CNY: '\\u{1F1E8}\\u{1F1F3}', AUD: '\\u{1F1E6}\\u{1F1FA}',\n GBP: '\\u{1F1EC}\\u{1F1E7}', JPY: '\\u{1F1EF}\\u{1F1F5}', CAD: '\\u{1F1E8}\\u{1F1E6}',\n EUR: '\\u{1F1EA}\\u{1F1FA}', THB: '\\u{1F1F9}\\u{1F1ED}', NZD: '\\u{1F1F3}\\u{1F1FF}',\n SGD: '\\u{1F1F8}\\u{1F1EC}', HKD: '\\u{1F1ED}\\u{1F1F0}', CHF: '\\u{1F1E8}\\u{1F1ED}',\n KRW: '\\u{1F1F0}\\u{1F1F7}', INR: '\\u{1F1EE}\\u{1F1F3}', MXN: '\\u{1F1F2}\\u{1F1FD}',\n BRL: '\\u{1F1E7}\\u{1F1F7}',\n};\n\nexport default function CurrencyConverter() {\n const [pairs, setPairs] = useState(loadPairs);\n const [appearance, setAppearance] = useState(() => loadAppearance(SETTINGS_KEY));\n const [allRates, setAllRates] = useState<Record<string, Record<string, number>>>({});\n const [loading, setLoading] = useState(false);\n const [settingsOpen, setSettingsOpen] = useState(false);\n const [configPairs, setConfigPairs] = useState<[string, string][]>([]);\n const [configAppearance, setConfigAppearance] = useState<WidgetAppearance>(appearance);\n const [newFrom, setNewFrom] = useState('USD');\n const [newTo, setNewTo] = useState('CNY');\n\n useWidgetSettings(useCallback(() => {\n setConfigPairs([...pairs]);\n setConfigAppearance({ ...appearance });\n setSettingsOpen(true);\n }, [pairs, appearance]));\n\n useEffect(() => {\n const bases = [...new Set(pairs.map(([from]) => from))];\n let mounted = true;\n async function fetchAll() {\n setLoading(true);\n const result: Record<string, Record<string, number>> = {};\n for (const base of bases) {\n const cached = getCached(base);\n if (cached) { result[base] = cached; continue; }\n try { const res = await fetch(`https://open.er-api.com/v6/latest/${base}`); const data = await res.json(); if (data.rates) { result[base] = data.rates; setCache(base, data.rates); } } catch {}\n }\n if (mounted) { setAllRates(result); setLoading(false); }\n }\n fetchAll();\n return () => { mounted = false; };\n }, [pairs]);\n\n const saveSettings = () => {\n if (configPairs.length === 0) return;\n const capped = configPairs.slice(0, MAX_PAIRS);\n setPairs(capped); setAppearance(configAppearance);\n localStorage.setItem(PAIRS_KEY, JSON.stringify(capped));\n localStorage.setItem(SETTINGS_KEY, JSON.stringify(configAppearance));\n setSettingsOpen(false);\n };\n\n return (\n <>\n {/* Theme-aware panel — pulls the same `--taskbar-bg-rgb` colour the\n * taskbar uses so the widget matches it across all themes\n * (gray-100-ish on light, Catppuccin mantle in dark). Tailwind\n * gray-* text/border classes auto-invert in dark via the existing\n * `[data-theme=\"dark\"]` overrides in styles.css.\n *\n * A plain naturally-flowing div — deliberately no `h-full`/`flex-1`, so\n * the window's `autoHeight` measurement hugs these rows instead of\n * reading the root as fill-height and pinning the window to its full\n * `dimensions` height (320×480) with empty space below the rows. */}\n <div\n style={{ backgroundColor: `rgb(var(--taskbar-bg-rgb, 243 244 246) / ${appearance.activeOpacity / 100})`, backdropFilter: appearance.activeBlur > 0 ? `blur(${appearance.activeBlur}px)` : undefined }}>\n <div className=\"px-4 py-3 space-y-1\">\n {loading && <div className=\"text-xs text-gray-400 text-center py-4\">Loading rates...</div>}\n {pairs.map(([from, to], idx) => {\n const rate = allRates[from]?.[to];\n return (\n <button key={`${from}-${to}-${idx}`} onClick={() => {\n const swapped: [string, string][] = pairs.map((p, i) => i === idx ? [p[1], p[0]] : p);\n setPairs(swapped);\n localStorage.setItem(PAIRS_KEY, JSON.stringify(swapped));\n }}\n className=\"flex items-center justify-between px-2 py-2 border-b border-gray-200 last:border-0 w-full hover:bg-gray-100 rounded transition-colors cursor-pointer\">\n <div className=\"flex items-center gap-1.5\">\n <span className=\"text-base\">{FLAG[from] || ''}</span>\n <span className=\"text-sm font-semibold text-gray-700\">{from}</span>\n <svg className=\"h-3 w-3 text-gray-400\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2}><path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M7 16V4m0 0L3 8m4-4l4 4m6 0v12m0 0l4-4m-4 4l-4-4\" /></svg>\n <span className=\"text-base\">{FLAG[to] || ''}</span>\n <span className=\"text-sm font-semibold text-gray-700\">{to}</span>\n </div>\n <div className=\"text-base font-mono font-semibold text-gray-900 tabular-nums\">\n {rate != null ? rate.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 4 }) : '—'}\n </div>\n </button>\n );\n })}\n </div>\n </div>\n\n <WidgetSettingsModal open={settingsOpen} onClose={() => setSettingsOpen(false)} title=\"Currency Settings\"\n appearance={configAppearance} onAppearanceChange={setConfigAppearance} onSave={saveSettings}>\n <div>\n <h3 className=\"text-sm font-semibold text-gray-700 mb-2\">Currency Pairs <span className=\"text-[11px] font-normal text-gray-400\">({configPairs.length}/{MAX_PAIRS})</span></h3>\n <div className=\"space-y-1 mb-2\">\n {configPairs.map(([f, t], i) => (\n <div key={i} className=\"flex items-center justify-between py-1 px-2 bg-gray-50 rounded\">\n <span className=\"text-sm\">{FLAG[f] || ''} {f} → {FLAG[t] || ''} {t}</span>\n <button onClick={() => setConfigPairs(p => p.filter((_, idx) => idx !== i))} className=\"text-red-400 hover:text-red-600\">×</button>\n </div>\n ))}\n </div>\n {configPairs.length < MAX_PAIRS ? (\n <div className=\"flex items-center gap-2\">\n <select value={newFrom} onChange={e => setNewFrom(e.target.value)}\n className=\"bg-gray-50 border border-gray-200 rounded px-2 py-1 text-sm focus:outline-none focus:ring-1 focus:ring-blue-500\">\n {ALL_CURRENCIES.map(c => <option key={c}>{c}</option>)}\n </select>\n <span className=\"text-gray-400\">→</span>\n <select value={newTo} onChange={e => setNewTo(e.target.value)}\n className=\"bg-gray-50 border border-gray-200 rounded px-2 py-1 text-sm focus:outline-none focus:ring-1 focus:ring-blue-500\">\n {ALL_CURRENCIES.map(c => <option key={c}>{c}</option>)}\n </select>\n <button onClick={() => { if (newFrom !== newTo && configPairs.length < MAX_PAIRS) setConfigPairs(p => [...p, [newFrom, newTo]]); }}\n className=\"text-sm font-medium text-blue-600 hover:text-blue-800 px-2\">+ Add</button>\n </div>\n ) : (\n <p className=\"text-[11px] text-gray-400 italic\">Max {MAX_PAIRS} pairs — remove one to add another.</p>\n )}\n </div>\n </WidgetSettingsModal>\n </>\n );\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/apps/Documents.tsx"],"names":[],"mappings":"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","file":"Documents-DD3QI5ER.js","sourcesContent":["/**\n * Documents — windowed viewer / editor for plain-text and Word documents.\n * TXT-family files (.txt, .md, .csv, .log, .json, .xml, .html, .css, .ts,\n * .tsx, .js, .jsx, .py, .yml, .yaml, .toml, .sh) open in a full-width\n * monospace editor. Word .docx files (and new blank documents) open on a\n * letter-size page (8.5 × 11 in) with rich formatting: bold/italic/…,\n * font size & color, bulleted / numbered lists, text alignment, and\n * images (toolbar button, paste, or drag-drop; click an image to resize\n * or remove it — images embed as data URLs so saved files stay\n * self-contained). .docx conversion uses mammoth (an optional peer dep).\n *\n * Open files via the toolbar's Open button or by dragging them onto\n * the window.\n */\nimport { useState, useRef, useEffect } from 'react';\nimport { WindowTitle } from '../shell/Modal';\nimport { PopupMenu, PopupMenuItem, PopupMenuDivider, PopupMenuLabel } from '../shell/PopupMenu';\nimport toast from '../shell/toast';\nimport AboutApp from './_about';\nimport { escapeHtml } from '../utils/escapeHtml';\n\nconst TITLE_DISPLAY_MAX = 24;\nfunction truncateForTitle(s: string) {\n return s.length > TITLE_DISPLAY_MAX ? `${s.slice(0, TITLE_DISPLAY_MAX - 1)}…` : s;\n}\n\nconst TEXT_EXTS = new Set([\n 'txt', 'md', 'markdown', 'csv', 'tsv', 'log', 'json', 'xml',\n 'html', 'htm', 'css', 'js', 'jsx', 'ts', 'tsx', 'py', 'rb',\n 'go', 'rs', 'java', 'c', 'h', 'cpp', 'hpp', 'sh', 'bash',\n 'yml', 'yaml', 'toml', 'ini', 'conf', 'env', 'sql',\n]);\nconst DOCX_EXTS = new Set(['docx']);\n\ninterface DocData {\n filename: string;\n kind: 'text' | 'docx';\n content: string;\n /** Original mime / extension hint for the download button. */\n ext: string;\n}\n\nconst BLANK_DOC: DocData = { filename: 'Untitled', kind: 'docx', content: '', ext: 'docx' };\n\nexport default function Documents() {\n // Documents is for editing — open with a blank paper-style canvas so the\n // user can start typing immediately. Loading a file replaces this state.\n const [data, setData] = useState<DocData>(BLANK_DOC);\n const [busy, setBusy] = useState(false);\n const [isDragging, setIsDragging] = useState(false);\n const [edited, setEdited] = useState(false);\n // Floating menu shown when an image in the document is clicked.\n const [imgMenu, setImgMenu] = useState<{ x: number; y: number; el: HTMLImageElement } | null>(null);\n const fileRef = useRef<HTMLInputElement>(null);\n const imgInputRef = useRef<HTMLInputElement>(null);\n const editorRef = useRef<HTMLDivElement>(null);\n\n // Apply a formatting command to the current selection in the editor.\n const exec = (cmd: string, value?: string) => {\n editorRef.current?.focus();\n document.execCommand(cmd, false, value);\n setEdited(true);\n };\n\n // ── Images ──\n // Inserted at the caret as data URLs, so the document needs no server-side\n // storage and a saved .html round-trips with its images embedded.\n const insertImageFiles = (files: File[]) => {\n for (const file of files) {\n if (!file.type.startsWith('image/')) continue;\n const reader = new FileReader();\n reader.onload = () => {\n editorRef.current?.focus();\n document.execCommand('insertImage', false, String(reader.result));\n setEdited(true);\n };\n reader.readAsDataURL(file);\n }\n };\n\n const handlePaste = (e: React.ClipboardEvent) => {\n const images = Array.from(e.clipboardData?.items ?? [])\n .filter(i => i.kind === 'file' && i.type.startsWith('image/'))\n .map(i => i.getAsFile())\n .filter((f): f is File => !!f);\n if (images.length) {\n e.preventDefault();\n insertImageFiles(images);\n }\n };\n\n // Click an image → floating resize / remove menu.\n const handleEditorClick = (e: React.MouseEvent) => {\n const target = e.target as HTMLElement;\n if (target.tagName === 'IMG') {\n e.preventDefault();\n setImgMenu({ x: e.clientX, y: e.clientY, el: target as HTMLImageElement });\n }\n };\n\n const setImageWidth = (width: string | null) => {\n if (!imgMenu) return;\n // Pixel width/height attributes (e.g. from pasted HTML) would fight the\n // percentage style — drop them and let height follow the aspect ratio.\n imgMenu.el.removeAttribute('width');\n imgMenu.el.removeAttribute('height');\n imgMenu.el.style.width = width ?? '';\n imgMenu.el.style.height = width ? 'auto' : '';\n setEdited(true);\n setImgMenu(null);\n };\n\n const removeImage = () => {\n imgMenu?.el.remove();\n setEdited(true);\n setImgMenu(null);\n };\n\n // Pull current HTML out of the editor (for save).\n const getEditorHtml = () => editorRef.current?.innerHTML ?? '';\n const getEditorText = () => editorRef.current?.innerText ?? '';\n\n const ingestFile = async (file: File) => {\n const ext = (file.name.split('.').pop() || '').toLowerCase();\n setBusy(true);\n try {\n if (TEXT_EXTS.has(ext) || (!DOCX_EXTS.has(ext) && file.type.startsWith('text/'))) {\n const text = await file.text();\n // Convert plain text → HTML so the contenteditable shows\n // line breaks correctly. Escape HTML special chars first.\n const html = escapeHtml(text).replace(/\\n/g, '<br>');\n setData({ filename: file.name, kind: 'text', content: html, ext });\n setEdited(false);\n } else if (DOCX_EXTS.has(ext)) {\n try {\n // mammoth is an optional peer dep — convert .docx → HTML.\n const mammoth = await import(/* @vite-ignore */ 'mammoth' as any);\n const buf = await file.arrayBuffer();\n const result = await mammoth.convertToHtml({ arrayBuffer: buf });\n setData({ filename: file.name, kind: 'docx', content: result.value || '<p><em>(empty document)</em></p>', ext });\n setEdited(false);\n } catch (err) {\n toast.error('Install the optional \"mammoth\" peer dep to read .docx files.');\n }\n } else if (ext === 'doc') {\n toast.error('.doc is the legacy Word binary format — convert to .docx first.');\n } else {\n toast.error(`Unsupported file type: .${ext || 'unknown'}`);\n }\n } finally {\n setBusy(false);\n }\n };\n\n const handlePick = () => fileRef.current?.click();\n const handleFile = (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n if (file) ingestFile(file);\n if (fileRef.current) fileRef.current.value = '';\n };\n const handleDrop = (e: React.DragEvent) => {\n e.preventDefault();\n setIsDragging(false);\n const files = Array.from(e.dataTransfer.files ?? []);\n if (files.length === 0) return;\n // Dropped images go INTO the document; anything else opens as a document.\n if (files.every(f => f.type.startsWith('image/'))) {\n insertImageFiles(files);\n return;\n }\n ingestFile(files[0]);\n };\n\n const downloadCurrent = () => {\n if (!data) return;\n const html = getEditorHtml();\n if (data.kind === 'text') {\n // If the user added formatting, save as .html alongside; otherwise\n // strip back to plain text so the original .txt round-trips cleanly.\n // (`style=\"…text-align` only matches a real attribute — literal text\n // mentioning text-align gets its quotes escaped to " on load.)\n const hasFormatting = /<(b|strong|i|em|u|font|span style|ul|ol|img)|style=\"[^\"]*text-align/i.test(html);\n if (hasFormatting) {\n const blob = new Blob([`<!doctype html><meta charset=\"utf-8\"><title>${escapeHtml(data.filename)}</title><body style=\"font-family:ui-monospace,monospace;white-space:pre-wrap\">${html}`], { type: 'text/html' });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a'); a.href = url; a.download = `${data.filename.replace(/\\.[^.]+$/, '')}.html`; a.click();\n URL.revokeObjectURL(url);\n } else {\n const text = getEditorText();\n const blob = new Blob([text], { type: 'text/plain' });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a'); a.href = url; a.download = data.filename; a.click();\n URL.revokeObjectURL(url);\n }\n } else {\n const blob = new Blob([`<!doctype html><meta charset=\"utf-8\"><title>${escapeHtml(data.filename)}</title>${html}`], { type: 'text/html' });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url; a.download = `${data.filename.replace(/\\.docx$/i, '')}.html`; a.click();\n URL.revokeObjectURL(url);\n }\n };\n\n const titleName = data?.filename ? truncateForTitle(data.filename) : 'Untitled';\n\n return (\n <div\n className=\"relative flex flex-col h-full\"\n onDragOver={(e) => { e.preventDefault(); if (!isDragging) setIsDragging(true); }}\n onDragLeave={(e) => { if (e.currentTarget === e.target) setIsDragging(false); }}\n onDrop={handleDrop}\n >\n <WindowTitle title={`${titleName}${edited ? ' •' : ''} - Documents`} />\n <AboutApp app=\"documents\" />\n\n {/* Toolbar */}\n <div className=\"flex items-center flex-wrap gap-x-2 gap-y-1 px-3 py-2 border-b border-gray-200 bg-gray-50 shrink-0\">\n <input\n ref={fileRef}\n type=\"file\"\n accept=\".txt,.md,.csv,.tsv,.log,.json,.xml,.html,.htm,.css,.js,.jsx,.ts,.tsx,.py,.rb,.go,.rs,.java,.c,.h,.cpp,.hpp,.sh,.bash,.yml,.yaml,.toml,.ini,.conf,.env,.sql,.docx,text/*\"\n onChange={handleFile}\n className=\"hidden\"\n />\n <button onClick={handlePick} className=\"text-xs text-gray-700 hover:text-gray-900 px-2 py-1 rounded hover:bg-gray-200 transition-colors flex items-center gap-1\">\n <svg className=\"h-3.5 w-3.5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={1.5}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M3.75 9.776c.112-.017.227-.026.344-.026h15.812c.117 0 .232.009.344.026m-16.5 0a2.25 2.25 0 00-1.883 2.542l.857 6a2.25 2.25 0 002.227 1.932H19.05a2.25 2.25 0 002.227-1.932l.857-6a2.25 2.25 0 00-1.883-2.542m-16.5 0V6A2.25 2.25 0 016 3.75h3.879a1.5 1.5 0 011.06.44l2.122 2.12a1.5 1.5 0 001.06.44H18A2.25 2.25 0 0120.25 9v.776\" />\n </svg>\n Open\n </button>\n <button onClick={downloadCurrent} className=\"text-xs text-gray-700 hover:text-gray-900 px-2 py-1 rounded hover:bg-gray-200 transition-colors\">\n Save\n </button>\n <span className=\"text-[10px] text-gray-400 ml-1\">TXT · DOCX · Code</span>\n <div className=\"h-4 w-px bg-gray-300 mx-1\" />\n <span className=\"text-xs font-medium text-gray-700 truncate max-w-[200px]\" title={data.filename}>\n {data.filename}{edited ? ' •' : ''}\n </span>\n\n {/* Formatting toolbar */}\n <div className=\"h-4 w-px bg-gray-300 mx-1\" />\n <div className=\"flex items-center gap-0.5\">\n <button onMouseDown={e => e.preventDefault()} onClick={() => exec('bold')}\n className=\"px-2 py-1 text-xs rounded font-bold text-gray-700 hover:bg-gray-200 transition-colors\" title=\"Bold\">B</button>\n <button onMouseDown={e => e.preventDefault()} onClick={() => exec('italic')}\n className=\"px-2 py-1 text-xs rounded italic text-gray-700 hover:bg-gray-200 transition-colors\" title=\"Italic\">I</button>\n <button onMouseDown={e => e.preventDefault()} onClick={() => exec('underline')}\n className=\"px-2 py-1 text-xs rounded underline text-gray-700 hover:bg-gray-200 transition-colors\" title=\"Underline\">U</button>\n <button onMouseDown={e => e.preventDefault()} onClick={() => exec('strikeThrough')}\n className=\"px-2 py-1 text-xs rounded line-through text-gray-700 hover:bg-gray-200 transition-colors\" title=\"Strikethrough\">S</button>\n </div>\n <div className=\"h-4 w-px bg-gray-300 mx-1\" />\n <select onMouseDown={e => e.preventDefault()} onChange={e => { exec('fontSize', e.target.value); e.currentTarget.value = ''; }}\n defaultValue=\"\" className=\"text-xs border border-gray-300 rounded px-1 py-0.5 bg-white\">\n <option value=\"\" disabled>Size</option>\n <option value=\"1\">XS</option>\n <option value=\"2\">S</option>\n <option value=\"3\">M</option>\n <option value=\"4\">L</option>\n <option value=\"5\">XL</option>\n <option value=\"6\">2XL</option>\n </select>\n <input type=\"color\" defaultValue=\"#000000\" onChange={e => exec('foreColor', e.target.value)}\n className=\"w-7 h-6 border border-gray-300 rounded cursor-pointer\" title=\"Text color\" />\n <div className=\"h-4 w-px bg-gray-300 mx-1\" />\n <button onMouseDown={e => e.preventDefault()} onClick={() => exec('insertUnorderedList')}\n className=\"px-2 py-1 text-xs rounded text-gray-700 hover:bg-gray-200 transition-colors\" title=\"Bulleted list\">• List</button>\n <button onMouseDown={e => e.preventDefault()} onClick={() => exec('insertOrderedList')}\n className=\"px-2 py-1 text-xs rounded text-gray-700 hover:bg-gray-200 transition-colors\" title=\"Numbered list\">1.</button>\n\n {/* Alignment */}\n <div className=\"h-4 w-px bg-gray-300 mx-1\" />\n <div className=\"flex items-center gap-0.5\">\n <button onMouseDown={e => e.preventDefault()} onClick={() => exec('justifyLeft')}\n className=\"px-2 py-1 rounded text-gray-700 hover:bg-gray-200 transition-colors\" title=\"Align left\">\n <svg className=\"h-3.5 w-3.5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={1.8}>\n <path strokeLinecap=\"round\" d=\"M3.75 6h16.5M3.75 12h9.75M3.75 18h14.25\" />\n </svg>\n </button>\n <button onMouseDown={e => e.preventDefault()} onClick={() => exec('justifyCenter')}\n className=\"px-2 py-1 rounded text-gray-700 hover:bg-gray-200 transition-colors\" title=\"Align center\">\n <svg className=\"h-3.5 w-3.5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={1.8}>\n <path strokeLinecap=\"round\" d=\"M3.75 6h16.5M7.125 12h9.75M4.875 18h14.25\" />\n </svg>\n </button>\n <button onMouseDown={e => e.preventDefault()} onClick={() => exec('justifyRight')}\n className=\"px-2 py-1 rounded text-gray-700 hover:bg-gray-200 transition-colors\" title=\"Align right\">\n <svg className=\"h-3.5 w-3.5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={1.8}>\n <path strokeLinecap=\"round\" d=\"M3.75 6h16.5M10.5 12h9.75M6 18h14.25\" />\n </svg>\n </button>\n <button onMouseDown={e => e.preventDefault()} onClick={() => exec('justifyFull')}\n className=\"px-2 py-1 rounded text-gray-700 hover:bg-gray-200 transition-colors\" title=\"Justify\">\n <svg className=\"h-3.5 w-3.5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={1.8}>\n <path strokeLinecap=\"round\" d=\"M3.75 6h16.5M3.75 12h16.5M3.75 18h16.5\" />\n </svg>\n </button>\n </div>\n\n {/* Image insert */}\n <div className=\"h-4 w-px bg-gray-300 mx-1\" />\n <input\n ref={imgInputRef}\n type=\"file\"\n accept=\"image/*\"\n multiple\n className=\"hidden\"\n onChange={e => {\n if (e.target.files?.length) insertImageFiles(Array.from(e.target.files));\n if (imgInputRef.current) imgInputRef.current.value = '';\n }}\n />\n <button onClick={() => imgInputRef.current?.click()}\n className=\"px-2 py-1 text-xs rounded text-gray-700 hover:bg-gray-200 transition-colors flex items-center gap-1\" title=\"Insert image (or paste / drop one)\">\n <svg className=\"h-3.5 w-3.5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={1.5}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 001.5-1.5V6a1.5 1.5 0 00-1.5-1.5H3.75A1.5 1.5 0 002.25 6v12a1.5 1.5 0 001.5 1.5zm10.5-11.25h.008v.008h-.008V8.25zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0z\" />\n </svg>\n Image\n </button>\n </div>\n\n {/* Body */}\n <div className=\"flex-1 min-h-0 overflow-hidden\">\n {busy ? (\n <div className=\"flex items-center justify-center h-full text-sm text-gray-400\">Loading…</div>\n ) : (\n <div className={`h-full overflow-auto ${data.kind === 'docx' ? 'bg-gray-100 px-6 py-8' : 'bg-white'}`}>\n {/* Word-style documents render on a US-letter page (8.5 × 11 in,\n 1 in margins) that grows past 11 in as content does; the gray\n \"desk\" scrolls when the window is narrower than the page. */}\n <div\n ref={editorRef}\n contentEditable\n suppressContentEditableWarning\n spellCheck={false}\n onInput={() => setEdited(true)}\n onPaste={handlePaste}\n onClick={handleEditorClick}\n dangerouslySetInnerHTML={{ __html: data.content }}\n className={\n data.kind === 'docx'\n ? 'docs-editor mx-auto w-[8.5in] min-h-[11in] bg-white p-[1in] shadow text-[14px] leading-relaxed text-gray-800 outline-none focus:ring-2 focus:ring-blue-400/40'\n : 'docs-editor block w-full min-h-full p-4 font-mono text-[13px] leading-relaxed text-gray-800 bg-white outline-none whitespace-pre-wrap'\n }\n />\n </div>\n )}\n </div>\n\n {isDragging && (\n <div className=\"absolute inset-0 bg-blue-500/15 border-4 border-dashed border-blue-500 pointer-events-none flex items-center justify-center z-20\">\n <div className=\"px-4 py-2 rounded-md bg-blue-600 text-white text-sm font-medium shadow-lg\">\n Drop a document to open · images insert into the page\n </div>\n </div>\n )}\n\n {/* Image menu — resize / remove the clicked image */}\n {imgMenu && (\n <PopupMenu\n minWidth={150}\n style={{ left: imgMenu.x, top: imgMenu.y }}\n onClose={() => setImgMenu(null)}\n >\n <PopupMenuLabel>Image width</PopupMenuLabel>\n <PopupMenuItem onClick={() => setImageWidth('25%')}>25%</PopupMenuItem>\n <PopupMenuItem onClick={() => setImageWidth('50%')}>50%</PopupMenuItem>\n <PopupMenuItem onClick={() => setImageWidth('75%')}>75%</PopupMenuItem>\n <PopupMenuItem onClick={() => setImageWidth('100%')}>100%</PopupMenuItem>\n <PopupMenuItem onClick={() => setImageWidth(null)}>Original size</PopupMenuItem>\n <PopupMenuDivider />\n <PopupMenuItem danger onClick={removeImage}>Remove image</PopupMenuItem>\n </PopupMenu>\n )}\n </div>\n );\n}\n\n"]}
|