@zephytiju/prism-geovision-embed 0.1.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.
Files changed (54) hide show
  1. package/README.md +101 -0
  2. package/dist/EmbedBody.d.ts +17 -0
  3. package/dist/EmbedBody.d.ts.map +1 -0
  4. package/dist/EmbedBody.js +81 -0
  5. package/dist/GeovisionEmbed.d.ts +20 -0
  6. package/dist/GeovisionEmbed.d.ts.map +1 -0
  7. package/dist/GeovisionEmbed.js +44 -0
  8. package/dist/describeViews.d.ts +26 -0
  9. package/dist/describeViews.d.ts.map +1 -0
  10. package/dist/describeViews.js +59 -0
  11. package/dist/index.d.ts +13 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +8 -0
  14. package/dist/locales/en.json +29 -0
  15. package/dist/locales/index.d.ts +79 -0
  16. package/dist/locales/index.d.ts.map +1 -0
  17. package/dist/locales/index.js +18 -0
  18. package/dist/locales/zh-CN.json +29 -0
  19. package/dist/map/MiniMap.d.ts +21 -0
  20. package/dist/map/MiniMap.d.ts.map +1 -0
  21. package/dist/map/MiniMap.js +75 -0
  22. package/dist/map/ReplayControl.d.ts +20 -0
  23. package/dist/map/ReplayControl.d.ts.map +1 -0
  24. package/dist/map/ReplayControl.js +37 -0
  25. package/dist/map/heat.d.ts +41 -0
  26. package/dist/map/heat.d.ts.map +1 -0
  27. package/dist/map/heat.js +54 -0
  28. package/dist/map/projection.d.ts +30 -0
  29. package/dist/map/projection.d.ts.map +1 -0
  30. package/dist/map/projection.js +54 -0
  31. package/dist/map/replay.d.ts +52 -0
  32. package/dist/map/replay.d.ts.map +1 -0
  33. package/dist/map/replay.js +91 -0
  34. package/dist/map/sideColoring.d.ts +39 -0
  35. package/dist/map/sideColoring.d.ts.map +1 -0
  36. package/dist/map/sideColoring.js +69 -0
  37. package/dist/map/tokens.d.ts +10 -0
  38. package/dist/map/tokens.d.ts.map +1 -0
  39. package/dist/map/tokens.js +25 -0
  40. package/dist/rail/LayerRailCompact.d.ts +19 -0
  41. package/dist/rail/LayerRailCompact.d.ts.map +1 -0
  42. package/dist/rail/LayerRailCompact.js +85 -0
  43. package/dist/resolveView.d.ts +18 -0
  44. package/dist/resolveView.d.ts.map +1 -0
  45. package/dist/resolveView.js +58 -0
  46. package/dist/seed.d.ts +22 -0
  47. package/dist/seed.d.ts.map +1 -0
  48. package/dist/seed.js +45 -0
  49. package/dist/types.d.ts +63 -0
  50. package/dist/types.d.ts.map +1 -0
  51. package/dist/types.js +9 -0
  52. package/locales/en.json +29 -0
  53. package/locales/zh-CN.json +29 -0
  54. package/package.json +68 -0
package/README.md ADDED
@@ -0,0 +1,101 @@
1
+ # @zephytiju/prism-geovision-embed
2
+
3
+ Platform Prism **geovision-embed** micro-UI (component id `"geovision-embed"`) — the render-slot
4
+ component of the CLOSED dossier-embed contract (GeoVision ↔ Dossier Editor coordination,
5
+ 2026-09-22; the agreed channel summary lives in the GeoVision wiki docs below). The editor's chrome
6
+ (title / SRC FILE / VIEW ▾ / ↗) is the EDITOR's; this package owns ONLY the slot, and every internal
7
+ control (compact layer rail, video-like replay, …) stays inside it — nothing an embed does
8
+ navigates away.
9
+
10
+ ## The contract (implemented exactly)
11
+
12
+ ```tsx
13
+ <GeovisionEmbed sourceFileRef viewId mode? interactionMode? locale? width height />
14
+ ```
15
+
16
+ | Prop | Type | Default | Contract |
17
+ | --- | --- | --- | --- |
18
+ | `sourceFileRef` | `string` | — | stable ref addressing the saved geovision-view file (`IFileEntry.read`) |
19
+ | `viewId` | `string` | — | the saved view to render; a file carries exactly one view, so the viewId is the file ref itself — a mismatch renders the localized UNKNOWN VIEW frame |
20
+ | `mode` | `"static" \| "live"` | `"static"` | v1: `live` is reserved and behaves as `static` (no live fetches) |
21
+ | `interactionMode` | `"rich" \| "static"` | `"rich"` | `rich` captures pointer events inside the slot for inline interactions; `static` renders a static frame (also forced below 240 px height) |
22
+ | `locale` | `"en" \| "zh-CN"` | `"en"` | the embed renders in the editor's active locale (unknown locales fall back to en) |
23
+ | `width` / `height` | `number \| string` | `"100%"` / `"100%"` | slot size; below 240 px height the embed degrades to a static frame with no pointer capture |
24
+
25
+ `GeovisionEmbed.describeViews(sourceFileRef)` feeds the editor's VIEW ▾ dropdown: it reads +
26
+ validates the file and returns `{ viewId, name, previewKind }[]` (one saved view per file; the name
27
+ is derived from the file's recorded provenance — `savedBy · save time` — because the format carries
28
+ no name field; `previewKind` summarizes the painted map variant: `normal` / `heat` /
29
+ `side-color`). Fails closed to an empty list for unreadable or invalid files.
30
+
31
+ Agreed coordination record: task list
32
+ https://applink.feishu.cn/client/todo/task_list?guid=33ec590f-7c59-4ace-b069-53c649fc55ab and the
33
+ authoritative designs:
34
+ - https://qcnwge0wy4s0.feishu.cn/wiki/IZ55wbfHPiqag5kT5eGckDlJnXf
35
+ - https://qcnwge0wy4s0.feishu.cn/wiki/DZ78w8NneizBNhkhuE2cCyp5nRb
36
+ - https://qcnwge0wy4s0.feishu.cn/wiki/DOljwdYuRib4uIkbRaGcu1pNnSc
37
+
38
+ ## How it renders
39
+
40
+ 1. **Resolve** `{ sourceFileRef, viewId }` via `createFileEntryClient(useLatticeTransport()).read`
41
+ — the HOST supplies the transport; the embed installs NO executor of its own, so in static mode
42
+ no live fetches occur beyond the host's own routes.
43
+ 2. **Validate** fail-closed through the authoritative format
44
+ (`@zephytiju/lattice-common-bundle/geovisionView`): structure AND digest. Failures render
45
+ localized frames (`not-found` / `invalid` / `unknown-view`).
46
+ 3. **Seed** an isolated `<ChannelScope>` — `query-box.entity-results` carries the snapshot
47
+ projection, `search-results.selected-entity` the workspace's selected entity — so every embed
48
+ renders its own file and nothing leaks across embeds on the same page.
49
+ 4. **Mount the REAL packages inside the scope**: `@zephytiju/prism-relation-fluxboard`,
50
+ `@zephytiju/prism-timeline-board` and `@zephytiju/prism-stats-panel` (their internal fetches
51
+ flow through the host transport; in the demo the mock executor answers from the file's
52
+ snapshot), plus the NEW simplified map:
53
+ - a static equirectangular SVG mini-map over the snapshot's located entities;
54
+ - layers from the file — normal markers (overlay), kernel-density blobs per enabled heat layer
55
+ (simplified variant of the globe's heat kernel), side-color mapping applied to marker
56
+ fills/outlines (`solid` / `stripe` / `accent`). Tag matchers apply to the item's stream
57
+ `type` (the kinds≡tags convention the recorded behavior uses for heat selections) because
58
+ the format's `EntityResultItem` declares only `id/type/label/lat/lon`;
59
+ - the compact layer rail — the same click/long-press contract as the globe's rail at compact
60
+ density (click toggles, long-press/right-click opens the anchored opacity options; changes
61
+ are local to the surface, persisted only via the editor's block config);
62
+ - the VIDEO-LIKE replay control — play/pause + progress scrubbing entity positions across the
63
+ snapshot's time-windowed timeline events (tracks interpolate linearly by time between
64
+ deterministic waypoints derived from the events; display-only).
65
+
66
+ ## Peer dependencies / registry status
67
+
68
+ The three mounted component packages are REGISTRY PEERS (`>=0.1.0`). They are not yet on npm —
69
+ their tag-triggered trusted-publishing runs returned 404 (the npm-side trusted-publisher
70
+ registration for the new package names appears pending; `v0.1.0` tags are already pushed), and
71
+ the ambient npm auth is expired, so they could not be published from this work. Until the first
72
+ release the dev surface installs them from **vendored tarballs** produced by `npm pack` in the
73
+ sibling repos (`vendor/*.tgz`, referenced as `file:` devDependencies):
74
+
75
+ - `.npmrc` sets `legacy-peer-deps=true` so npm never auto-installs the peers;
76
+ - typecheck/tests/demo run against the REAL packaged output (dist) of the siblings, exactly what
77
+ the registry will serve once published — the jsdom run inlines them and stubs the
78
+ `@xyflow/react` stylesheet (`vitest.config.ts`);
79
+ - the EMITTED package keeps the registry specifiers (`import … from
80
+ "@zephytiju/prism-relation-fluxboard"`), so hosts resolve them normally once published;
81
+ - when the siblings land on npm, drop `vendor/`, pin registry versions in devDependencies and
82
+ remove `legacy-peer-deps`.
83
+
84
+ Per the owner gate this package is NOT published or tagged from this work.
85
+
86
+ ## Development
87
+
88
+ ```bash
89
+ npm install # legacy-peer-deps (see .npmrc); peers come from vendor/*.tgz
90
+ npm run typecheck # tsc
91
+ npm test # vitest (jsdom) — 26 tests
92
+ npm run dev # vite demo (two embeds, two files, EN/中文, dark × light)
93
+ npm run build # emits dist/ with registry peer imports
94
+ ```
95
+
96
+ Source layout (each module ≤250 lines): `src/GeovisionEmbed.tsx` (contract + failure frames),
97
+ `src/resolveView.ts` (IFileEntry.read + fail-closed validation), `src/seed.ts` (ChannelScope
98
+ seeding), `src/EmbedBody.tsx` (scoped composition of the real packages + map + rail + replay),
99
+ `src/describeViews.ts`, `src/map/` (projection, replay math, heat blobs, side coloring, tokens,
100
+ MiniMap, ReplayControl), `src/rail/LayerRailCompact.tsx`, `src/locales/` (en + zh-CN with exact
101
+ key parity; `stringsForLocale` falls back to en).
@@ -0,0 +1,17 @@
1
+ import type { GeovisionViewFile } from "@zephytiju/lattice-common-bundle/geovisionView";
2
+ import type { GeovisionEmbedStrings } from "./locales/index.js";
3
+ /**
4
+ * The seeded render surface: an ISOLATED ChannelScope carrying the file's
5
+ * snapshot channels wraps the REAL packages (relation-fluxboard,
6
+ * timeline-board, stats-panel) and the simplified mini-map. Every internal
7
+ * control — the compact layer rail and the video-like replay — stays inside
8
+ * the slot; nothing an embed does navigates away. Static frames wrap the
9
+ * whole body with pointerEvents none (no pointer capture at all).
10
+ */
11
+ export declare function EmbedBody({ file, locale, staticFrame, strings, }: {
12
+ readonly file: GeovisionViewFile;
13
+ readonly locale: "en" | "zh-CN";
14
+ readonly staticFrame: boolean;
15
+ readonly strings: GeovisionEmbedStrings;
16
+ }): React.ReactElement;
17
+ //# sourceMappingURL=EmbedBody.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmbedBody.d.ts","sourceRoot":"","sources":["../src/EmbedBody.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gDAAgD,CAAC;AAUxF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAEhE;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,EACxB,IAAI,EACJ,MAAM,EACN,WAAW,EACX,OAAO,GACR,EAAE;IACD,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,IAAI,GAAG,OAAO,CAAC;IAChC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,qBAAqB,CAAC;CACzC,GAAG,KAAK,CAAC,YAAY,CAiJrB"}
@@ -0,0 +1,81 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useMemo, useRef, useState } from "react";
3
+ import { ChannelScope } from "@zephytiju/prism-react";
4
+ import { RelationFluxboard } from "@zephytiju/prism-relation-fluxboard";
5
+ import { TimelineBoard } from "@zephytiju/prism-timeline-board";
6
+ import { StatsPanel } from "@zephytiju/prism-stats-panel";
7
+ import { seedFromFile } from "./seed.js";
8
+ import { MiniMap } from "./map/MiniMap.js";
9
+ import { ReplayControl } from "./map/ReplayControl.js";
10
+ import { hasMotion, buildTracks } from "./map/replay.js";
11
+ import { LayerRailCompact } from "./rail/LayerRailCompact.js";
12
+ /**
13
+ * The seeded render surface: an ISOLATED ChannelScope carrying the file's
14
+ * snapshot channels wraps the REAL packages (relation-fluxboard,
15
+ * timeline-board, stats-panel) and the simplified mini-map. Every internal
16
+ * control — the compact layer rail and the video-like replay — stays inside
17
+ * the slot; nothing an embed does navigates away. Static frames wrap the
18
+ * whole body with pointerEvents none (no pointer capture at all).
19
+ */
20
+ export function EmbedBody({ file, locale, staticFrame, strings, }) {
21
+ const seeded = useMemo(() => seedFromFile(file), [file]);
22
+ const containerRef = useRef(null);
23
+ // The mounted packages accept only en | "zh-CN" — unknown locales resolve
24
+ // onto the en fallback before crossing the seam.
25
+ const innerLocale = locale === "zh-CN" ? "zh-CN" : "en";
26
+ const initialRail = useMemo(() => file.view.layers.map((layer, index) => {
27
+ if (layer.kind === "overlay") {
28
+ return { id: `overlay-${String(index)}`, kind: "overlay", name: "markers", enabled: true, opacity: layer.opacity };
29
+ }
30
+ if (layer.kind === "heat") {
31
+ return { id: `heat-${String(index)}`, kind: "heat", name: layer.selection.name, enabled: true, opacity: layer.intensity };
32
+ }
33
+ return { id: `side-color-${String(index)}`, kind: "side-color", name: "sides", enabled: true, opacity: 1 };
34
+ }), [file]);
35
+ const [rail, setRail] = useState(initialRail);
36
+ const railForFile = useMemo(() => (rail.length === initialRail.length ? rail : initialRail), [rail, initialRail]);
37
+ const replayWindow = useMemo(() => {
38
+ const start = Date.parse(seeded.window.start);
39
+ const end = Date.parse(seeded.window.end);
40
+ return Number.isFinite(start) && Number.isFinite(end) && end > start
41
+ ? { startMs: start, endMs: end }
42
+ : { startMs: 0, endMs: 3600_000 };
43
+ }, [seeded]);
44
+ const [fraction, setFraction] = useState(0);
45
+ const [playing, setPlaying] = useState(false);
46
+ const onFraction = useCallback((next) => {
47
+ const clamped = Math.min(1, Math.max(0, next));
48
+ setFraction(clamped);
49
+ if (clamped >= 1)
50
+ setPlaying(false);
51
+ }, []);
52
+ const located = useMemo(() => (file.snapshot.entityResults?.items ?? []).filter((item) => item.lat !== undefined && item.lon !== undefined), [file]);
53
+ const motion = useMemo(() => hasMotion(buildTracks(located, file.snapshot.timelineEvents, replayWindow)), [located, file, replayWindow]);
54
+ const toggleLayer = useCallback((id) => {
55
+ setRail((previous) => previous.map((layer) => (layer.id === id ? { ...layer, enabled: !layer.enabled } : layer)));
56
+ }, []);
57
+ const setOpacity = useCallback((id, opacity) => {
58
+ setRail((previous) => previous.map((layer) => (layer.id === id ? { ...layer, opacity } : layer)));
59
+ }, []);
60
+ return (_jsx(ChannelScope, { seed: seeded.seed, children: _jsxs("div", { ref: containerRef, "data-testid": "geovision-embed-body", style: {
61
+ width: "100%",
62
+ height: "100%",
63
+ display: "flex",
64
+ flexDirection: "column",
65
+ overflow: "hidden",
66
+ ...(staticFrame ? { pointerEvents: "none" } : {}),
67
+ }, children: [_jsxs("div", { style: { position: "relative", flex: "1 1 46%", minHeight: 120 }, children: [_jsx(MiniMap, { file: file, container: containerRef.current, rail: railForFile, replayFraction: fraction, replayWindow: replayWindow, strings: strings }), _jsx(LayerRailCompact, { layers: railForFile, disabled: staticFrame, strings: strings, onToggle: toggleLayer, onOpacity: setOpacity }), staticFrame ? (_jsx("div", { "data-testid": "geovision-embed-static-chip", style: {
68
+ position: "absolute",
69
+ right: 6,
70
+ top: 6,
71
+ fontSize: 8,
72
+ padding: "1px 6px",
73
+ borderRadius: 3,
74
+ fontFamily: "var(--mantine-font-family-monospace)",
75
+ background: "var(--mantine-color-input-filled)",
76
+ color: "var(--mantine-color-muted-filled)",
77
+ border: "1px solid var(--mantine-color-border-filled)",
78
+ }, children: strings.staticFrame })) : null] }), _jsx(ReplayControl, { fraction: fraction, playing: playing, disabled: staticFrame || !motion, timeWindow: replayWindow, strings: strings, onFraction: onFraction, onTogglePlay: () => {
79
+ setPlaying((value) => !value);
80
+ } }), _jsxs("div", { style: { display: "flex", gap: 4, flex: "1 1 30%", minHeight: 96, padding: 4 }, children: [_jsx("div", { style: { flex: 1, minWidth: 0, overflow: "hidden" }, children: _jsx(RelationFluxboard, { locale: innerLocale, title: strings.fluxTitle, subtitle: strings.fluxSubtitle }) }), _jsx("div", { style: { width: 210, flexShrink: 0, overflow: "hidden" }, children: _jsx(StatsPanel, { locale: innerLocale, window: seeded.window, title: strings.statsTitle, subtitle: strings.statsSubtitle }) })] }), _jsx("div", { style: { height: 108, flexShrink: 0, padding: 4, overflow: "hidden" }, children: _jsx(TimelineBoard, { locale: innerLocale, defaultWindow: seeded.window, live: false, title: strings.timelineTitle, subtitle: strings.timelineSubtitle }) })] }) }));
81
+ }
@@ -0,0 +1,20 @@
1
+ import type { GeovisionEmbedProps } from "./types.js";
2
+ /** Contract minimum slot height (px) — below it the embed degrades to a static frame. */
3
+ export declare const MIN_RICH_HEIGHT = 240;
4
+ /**
5
+ * Platform Prism geovision-embed micro-UI (component id "geovision-embed")
6
+ * — the agreed dossier-embed render component:
7
+ * `<GeovisionEmbed sourceFileRef viewId mode? interactionMode? locale? width height />`.
8
+ *
9
+ * The component resolves { sourceFileRef, viewId } through the HOST-supplied
10
+ * Lattice transport (IFileEntry.read — the embed installs NO executor of
11
+ * its own, so in static mode no live fetches occur beyond the host's own
12
+ * routes), validates the file against the authoritative geovision-view
13
+ * format (fail-closed: structure + digest), then seeds an isolated
14
+ * ChannelScope and mounts the real packages + the simplified SVG map.
15
+ * "live" mode is reserved and behaves as static in v1. Below 240px height
16
+ * or with interactionMode="static" the slot degrades to a static frame
17
+ * (pointer-events captured nowhere).
18
+ */
19
+ export declare function GeovisionEmbed({ sourceFileRef, viewId, mode, interactionMode, locale, width, height, }: GeovisionEmbedProps): React.ReactElement;
20
+ //# sourceMappingURL=GeovisionEmbed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GeovisionEmbed.d.ts","sourceRoot":"","sources":["../src/GeovisionEmbed.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAItD,yFAAyF;AACzF,eAAO,MAAM,eAAe,MAAM,CAAC;AAEnC;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,EAC7B,aAAa,EACb,MAAM,EACN,IAAe,EACf,eAAwB,EACxB,MAAa,EACb,KAAc,EACd,MAAM,GACP,EAAE,mBAAmB,GAAG,KAAK,CAAC,YAAY,CAwD1C"}
@@ -0,0 +1,44 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useRef } from "react";
3
+ import { useLatticeTransport } from "@zephytiju/prism-react";
4
+ import { stringsForLocale } from "./locales/index.js";
5
+ import { useResolvedView } from "./resolveView.js";
6
+ import { EmbedBody } from "./EmbedBody.js";
7
+ /** Contract minimum slot height (px) — below it the embed degrades to a static frame. */
8
+ export const MIN_RICH_HEIGHT = 240;
9
+ /**
10
+ * Platform Prism geovision-embed micro-UI (component id "geovision-embed")
11
+ * — the agreed dossier-embed render component:
12
+ * `<GeovisionEmbed sourceFileRef viewId mode? interactionMode? locale? width height />`.
13
+ *
14
+ * The component resolves { sourceFileRef, viewId } through the HOST-supplied
15
+ * Lattice transport (IFileEntry.read — the embed installs NO executor of
16
+ * its own, so in static mode no live fetches occur beyond the host's own
17
+ * routes), validates the file against the authoritative geovision-view
18
+ * format (fail-closed: structure + digest), then seeds an isolated
19
+ * ChannelScope and mounts the real packages + the simplified SVG map.
20
+ * "live" mode is reserved and behaves as static in v1. Below 240px height
21
+ * or with interactionMode="static" the slot degrades to a static frame
22
+ * (pointer-events captured nowhere).
23
+ */
24
+ export function GeovisionEmbed({ sourceFileRef, viewId, mode = "static", interactionMode = "rich", locale = "en", width = "100%", height, }) {
25
+ const strings = stringsForLocale(locale);
26
+ const transport = useLatticeTransport();
27
+ const resolved = useResolvedView(transport, sourceFileRef, viewId);
28
+ const containerRef = useRef(null);
29
+ const numericHeight = typeof height === "number" ? height : undefined;
30
+ const richHeight = numericHeight === undefined || numericHeight >= MIN_RICH_HEIGHT;
31
+ const staticFrame = interactionMode === "static" || !richHeight;
32
+ return (_jsxs("div", { ref: containerRef, "data-testid": "geovision-embed", "data-embed-mode": mode, "data-interaction-mode": staticFrame ? "static" : "rich", "data-file-ref": sourceFileRef, "aria-label": strings.slotAria, style: {
33
+ width,
34
+ height: height ?? "100%",
35
+ position: "relative",
36
+ overflow: "hidden",
37
+ background: "var(--mantine-color-panel-filled)",
38
+ border: "1px solid var(--mantine-color-border-filled)",
39
+ }, children: [resolved.phase.phase === "loading" ? (_jsx("div", { "data-testid": "geovision-embed-loading", style: { padding: 12, fontSize: 10, fontFamily: "var(--mantine-font-family-monospace)", color: "var(--mantine-color-muted-filled)" }, children: strings.loading })) : null, resolved.phase.phase === "error" ? (_jsxs("div", { "data-testid": "geovision-embed-error", "data-error-code": resolved.phase.code, style: { padding: 12, display: "flex", flexDirection: "column", gap: 6 }, children: [_jsx("span", { style: { fontSize: 10, fontFamily: "var(--mantine-font-family-monospace)", color: "var(--mantine-color-error-filled)" }, children: resolved.phase.code === "not-found"
40
+ ? strings.errNotFound
41
+ : resolved.phase.code === "invalid"
42
+ ? strings.errInvalid
43
+ : strings.errUnknownView }), _jsx("span", { style: { fontSize: 9, color: "var(--mantine-color-muted-filled)" }, children: resolved.phase.message })] })) : null, resolved.phase.phase === "ready" && resolved.file !== null ? (_jsx(EmbedBody, { file: resolved.file, locale: locale, staticFrame: staticFrame, strings: strings })) : null] }));
44
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * describeViews(sourceFileRef) — the editor's VIEW ▾ dropdown source: reads
3
+ * the file through the host Lattice transport, validates it against the
4
+ * authoritative format, and lists what it contains. A geovision-view file
5
+ * carries exactly ONE saved view, so the descriptor's viewId IS the file
6
+ * ref; the display name is derived from the file's recorded provenance
7
+ * (savedBy + save time — the format carries no name field), and previewKind
8
+ * summarizes the painted map variant.
9
+ */
10
+ import type { LatticeTransport } from "@zephytiju/prism-react";
11
+ import type { GeovisionViewDescriptor } from "./types.js";
12
+ /** Summarizes the painted map variant from the file's layer set. */
13
+ export declare function previewKindFor(layers: readonly {
14
+ readonly kind: string;
15
+ }[]): GeovisionViewDescriptor["previewKind"];
16
+ /** Descriptor for a validated file (one saved view per file). */
17
+ export declare function describeFile(sourceFileRef: string, savedBy: string, savedAt: string, layers: readonly {
18
+ readonly kind: string;
19
+ }[]): GeovisionViewDescriptor;
20
+ /**
21
+ * Lists the file's saved views for the editor dropdown. Fails closed: an
22
+ * unreadable or invalid file yields an empty list (the dropdown simply has
23
+ * nothing to offer) — the EMBED surface renders the structured failure.
24
+ */
25
+ export declare function describeViews(sourceFileRef: string, transport?: LatticeTransport): Promise<readonly GeovisionViewDescriptor[]>;
26
+ //# sourceMappingURL=describeViews.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"describeViews.d.ts","sourceRoot":"","sources":["../src/describeViews.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG/D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAe1D,oEAAoE;AACpE,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG,uBAAuB,CAAC,aAAa,CAAC,CAInH;AAED,iEAAiE;AACjE,wBAAgB,YAAY,CAAC,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG,uBAAuB,CAM3J;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CACjC,aAAa,EAAE,MAAM,EACrB,SAAS,GAAE,gBAA2C,GACrD,OAAO,CAAC,SAAS,uBAAuB,EAAE,CAAC,CAU7C"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * describeViews(sourceFileRef) — the editor's VIEW ▾ dropdown source: reads
3
+ * the file through the host Lattice transport, validates it against the
4
+ * authoritative format, and lists what it contains. A geovision-view file
5
+ * carries exactly ONE saved view, so the descriptor's viewId IS the file
6
+ * ref; the display name is derived from the file's recorded provenance
7
+ * (savedBy + save time — the format carries no name field), and previewKind
8
+ * summarizes the painted map variant.
9
+ */
10
+ import { createFileEntryClient } from "@zephytiju/lattice-common-interfaces";
11
+ import { createLatticeTransport } from "@zephytiju/prism-react";
12
+ import { readGeovisionView } from "@zephytiju/lattice-common-bundle/geovisionView";
13
+ /** UTC "YYYY-MM-DD HH:MM" label for the descriptor name. */
14
+ function savedAtLabel(savedAt) {
15
+ const ms = Date.parse(savedAt);
16
+ if (!Number.isFinite(ms))
17
+ return savedAt;
18
+ const date = new Date(ms);
19
+ const yyyy = date.getUTCFullYear();
20
+ const mm = String(date.getUTCMonth() + 1).padStart(2, "0");
21
+ const dd = String(date.getUTCDate()).padStart(2, "0");
22
+ const hh = String(date.getUTCHours()).padStart(2, "0");
23
+ const mi = String(date.getUTCMinutes()).padStart(2, "0");
24
+ return `${yyyy}-${mm}-${dd} ${hh}:${mi} UTC`;
25
+ }
26
+ /** Summarizes the painted map variant from the file's layer set. */
27
+ export function previewKindFor(layers) {
28
+ if (layers.some((layer) => layer.kind === "side-color"))
29
+ return "side-color";
30
+ if (layers.some((layer) => layer.kind === "heat"))
31
+ return "heat";
32
+ return "normal";
33
+ }
34
+ /** Descriptor for a validated file (one saved view per file). */
35
+ export function describeFile(sourceFileRef, savedBy, savedAt, layers) {
36
+ return {
37
+ viewId: sourceFileRef,
38
+ name: `${savedBy} · ${savedAtLabel(savedAt)}`,
39
+ previewKind: previewKindFor(layers),
40
+ };
41
+ }
42
+ /**
43
+ * Lists the file's saved views for the editor dropdown. Fails closed: an
44
+ * unreadable or invalid file yields an empty list (the dropdown simply has
45
+ * nothing to offer) — the EMBED surface renders the structured failure.
46
+ */
47
+ export async function describeViews(sourceFileRef, transport = createLatticeTransport()) {
48
+ try {
49
+ const result = await createFileEntryClient(transport).read({ ref: sourceFileRef });
50
+ const check = readGeovisionView(result.content);
51
+ if (!check.ok)
52
+ return [];
53
+ const file = check.file;
54
+ return [describeFile(sourceFileRef, file.savedBy, file.savedAt, file.view.layers)];
55
+ }
56
+ catch {
57
+ return [];
58
+ }
59
+ }
@@ -0,0 +1,13 @@
1
+ export { GeovisionEmbed, MIN_RICH_HEIGHT } from "./GeovisionEmbed.js";
2
+ export { describeViews, describeFile, previewKindFor } from "./describeViews.js";
3
+ export type { GeovisionViewDescriptor } from "./types.js";
4
+ export type { GeovisionEmbedInteractionMode, GeovisionEmbedMode, GeovisionEmbedProps, RailLayerState, EmbedFilePhase, } from "./types.js";
5
+ export { ENTITY_RESULTS_CHANNEL, SELECTED_ENTITY_CHANNEL, seedFromFile } from "./seed.js";
6
+ export { buildTracks, positionAt, replayTimeLabel, hasMotion } from "./map/replay.js";
7
+ export type { ReplayTrack, ReplayWaypoint, ReplayWindow } from "./map/replay.js";
8
+ export { boundsFor, project } from "./map/projection.js";
9
+ export { heatBlobs, matchesSelection, kernelWeight } from "./map/heat.js";
10
+ export { paintFor, withinSideIndices } from "./map/sideColoring.js";
11
+ export { en, zhCN, locales, stringsForLocale } from "./locales/index.js";
12
+ export type { GeovisionEmbedLocale, GeovisionEmbedStrings } from "./locales/index.js";
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACjF,YAAY,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAC1D,YAAY,EACV,6BAA6B,EAC7B,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,cAAc,GACf,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC1F,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACtF,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACjF,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC1E,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AACpE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACzE,YAAY,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ export { GeovisionEmbed, MIN_RICH_HEIGHT } from "./GeovisionEmbed.js";
2
+ export { describeViews, describeFile, previewKindFor } from "./describeViews.js";
3
+ export { ENTITY_RESULTS_CHANNEL, SELECTED_ENTITY_CHANNEL, seedFromFile } from "./seed.js";
4
+ export { buildTracks, positionAt, replayTimeLabel, hasMotion } from "./map/replay.js";
5
+ export { boundsFor, project } from "./map/projection.js";
6
+ export { heatBlobs, matchesSelection, kernelWeight } from "./map/heat.js";
7
+ export { paintFor, withinSideIndices } from "./map/sideColoring.js";
8
+ export { en, zhCN, locales, stringsForLocale } from "./locales/index.js";
@@ -0,0 +1,29 @@
1
+ {
2
+ "geovision-embed": {
3
+ "loading": "LOADING VIEW…",
4
+ "errNotFound": "FILE NOT FOUND",
5
+ "errInvalid": "FILE FAILED VALIDATION",
6
+ "errUnknownView": "UNKNOWN VIEW",
7
+ "retry": "RETRY",
8
+ "slotAria": "Geovision embed",
9
+ "mapAria": "Situation mini-map",
10
+ "railTitle": "LAYERS",
11
+ "railOverlay": "MARKERS",
12
+ "railSideColor": "SIDE COLORING",
13
+ "railHeat": "HEAT",
14
+ "railOpacity": "OPACITY",
15
+ "railOptions": "LAYER OPTIONS",
16
+ "replayPlay": "PLAY",
17
+ "replayPause": "PAUSE",
18
+ "replayScrub": "Replay progress",
19
+ "replayTime": "POSITION",
20
+ "replayOff": "NO TIMELINE EVENTS",
21
+ "staticFrame": "STATIC FRAME",
22
+ "fluxTitle": "RELATIONS",
23
+ "fluxSubtitle": "SNAPSHOT GRAPH",
24
+ "timelineTitle": "TIMELINE",
25
+ "timelineSubtitle": "SNAPSHOT EVENTS",
26
+ "statsTitle": "STATISTICS",
27
+ "statsSubtitle": "SNAPSHOT DERIVED"
28
+ }
29
+ }
@@ -0,0 +1,79 @@
1
+ import enBundle from "./en.json";
2
+ /**
3
+ * Locales the geovision-embed micro-UI bundles. Every component-authored UI
4
+ * string is resolved from these bundles (statically imported,
5
+ * resolveJsonModule); the component never hardcodes copy. Hosts can deep-
6
+ * merge their own tables over `locales` for composition-level overrides.
7
+ */
8
+ export type GeovisionEmbedLocale = "en" | "zh-CN";
9
+ /** Per-locale string table — the keys of the en bundle, widened to string. */
10
+ export type GeovisionEmbedStrings = {
11
+ readonly [K in keyof typeof enBundle["geovision-embed"]]: string;
12
+ };
13
+ /** Parsed en locale bundle (namespaced under the component id "geovision-embed"). */
14
+ export declare const en: {
15
+ "geovision-embed": {
16
+ loading: string;
17
+ errNotFound: string;
18
+ errInvalid: string;
19
+ errUnknownView: string;
20
+ retry: string;
21
+ slotAria: string;
22
+ mapAria: string;
23
+ railTitle: string;
24
+ railOverlay: string;
25
+ railSideColor: string;
26
+ railHeat: string;
27
+ railOpacity: string;
28
+ railOptions: string;
29
+ replayPlay: string;
30
+ replayPause: string;
31
+ replayScrub: string;
32
+ replayTime: string;
33
+ replayOff: string;
34
+ staticFrame: string;
35
+ fluxTitle: string;
36
+ fluxSubtitle: string;
37
+ timelineTitle: string;
38
+ timelineSubtitle: string;
39
+ statsTitle: string;
40
+ statsSubtitle: string;
41
+ };
42
+ };
43
+ /** Parsed zh-CN locale bundle. */
44
+ export declare const zhCN: {
45
+ "geovision-embed": {
46
+ loading: string;
47
+ errNotFound: string;
48
+ errInvalid: string;
49
+ errUnknownView: string;
50
+ retry: string;
51
+ slotAria: string;
52
+ mapAria: string;
53
+ railTitle: string;
54
+ railOverlay: string;
55
+ railSideColor: string;
56
+ railHeat: string;
57
+ railOpacity: string;
58
+ railOptions: string;
59
+ replayPlay: string;
60
+ replayPause: string;
61
+ replayScrub: string;
62
+ replayTime: string;
63
+ replayOff: string;
64
+ staticFrame: string;
65
+ fluxTitle: string;
66
+ fluxSubtitle: string;
67
+ timelineTitle: string;
68
+ timelineSubtitle: string;
69
+ statsTitle: string;
70
+ statsSubtitle: string;
71
+ };
72
+ };
73
+ /** All bundled locale bundles keyed by locale id — for downstream deep-merge. */
74
+ export declare const locales: Record<GeovisionEmbedLocale, {
75
+ readonly "geovision-embed": GeovisionEmbedStrings;
76
+ }>;
77
+ /** Resolves the string table for a locale, falling back to en. */
78
+ export declare function stringsForLocale(locale: GeovisionEmbedLocale | string | undefined): GeovisionEmbedStrings;
79
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/locales/index.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,WAAW,CAAC;AAGjC;;;;;GAKG;AACH,MAAM,MAAM,oBAAoB,GAAG,IAAI,GAAG,OAAO,CAAC;AAElD,8EAA8E;AAC9E,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,EAAE,CAAC,IAAI,MAAM,OAAO,QAAQ,CAAC,iBAAiB,CAAC,GAAG,MAAM;CACjE,CAAC;AAOF,qFAAqF;AACrF,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAAW,CAAC;AAE3B,kCAAkC;AAClC,eAAO,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAAa,CAAC;AAE/B,iFAAiF;AACjF,eAAO,MAAM,OAAO;gCAZsD,qBAAqB;EAYjE,CAAC;AAE/B,kEAAkE;AAClE,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,oBAAoB,GAAG,MAAM,GAAG,SAAS,GAChD,qBAAqB,CAGvB"}
@@ -0,0 +1,18 @@
1
+ import enBundle from "./en.json";
2
+ import zhCNBundle from "./zh-CN.json";
3
+ const bundles = {
4
+ en: enBundle,
5
+ "zh-CN": zhCNBundle,
6
+ };
7
+ /** Parsed en locale bundle (namespaced under the component id "geovision-embed"). */
8
+ export const en = enBundle;
9
+ /** Parsed zh-CN locale bundle. */
10
+ export const zhCN = zhCNBundle;
11
+ /** All bundled locale bundles keyed by locale id — for downstream deep-merge. */
12
+ export const locales = bundles;
13
+ /** Resolves the string table for a locale, falling back to en. */
14
+ export function stringsForLocale(locale) {
15
+ if (locale === "zh-CN")
16
+ return bundles["zh-CN"]["geovision-embed"];
17
+ return bundles.en["geovision-embed"];
18
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "geovision-embed": {
3
+ "loading": "正在加载视图…",
4
+ "errNotFound": "文件不存在",
5
+ "errInvalid": "文件校验失败",
6
+ "errUnknownView": "未知视图",
7
+ "retry": "重试",
8
+ "slotAria": "地理视野嵌入",
9
+ "mapAria": "态势小地图",
10
+ "railTitle": "图层",
11
+ "railOverlay": "标记",
12
+ "railSideColor": "阵营配色",
13
+ "railHeat": "热力",
14
+ "railOpacity": "不透明度",
15
+ "railOptions": "图层选项",
16
+ "replayPlay": "播放",
17
+ "replayPause": "暂停",
18
+ "replayScrub": "回放进度",
19
+ "replayTime": "位置",
20
+ "replayOff": "无时间线事件",
21
+ "staticFrame": "静态帧",
22
+ "fluxTitle": "关系图",
23
+ "fluxSubtitle": "快照关系",
24
+ "timelineTitle": "时间线",
25
+ "timelineSubtitle": "快照事件",
26
+ "statsTitle": "统计分析",
27
+ "statsSubtitle": "快照派生"
28
+ }
29
+ }
@@ -0,0 +1,21 @@
1
+ import type { GeovisionViewFile } from "@zephytiju/lattice-common-bundle/geovisionView";
2
+ import type { RailLayerState } from "../types.js";
3
+ import { type ReplayWindow } from "./replay.js";
4
+ import type { GeovisionEmbedStrings } from "../locales/index.js";
5
+ /**
6
+ * The simplified STATIC EQUIRECTANGULAR mini-map: an SVG surface over the
7
+ * snapshot's located entities — normal markers from the overlay layer,
8
+ * kernel-density blobs from each enabled heat layer, side-color mapping
9
+ * applied to marker fills/outlines, and (when the replay is scrubbed) the
10
+ * entities at their interpolated track positions with the static positions
11
+ * faded underneath. Display-only; pointer capture stays on the slot.
12
+ */
13
+ export declare function MiniMap({ file, container, rail, replayFraction, replayWindow, strings, }: {
14
+ readonly file: GeovisionViewFile;
15
+ readonly container: HTMLElement | null;
16
+ readonly rail: readonly RailLayerState[];
17
+ readonly replayFraction: number;
18
+ readonly replayWindow: ReplayWindow;
19
+ readonly strings: GeovisionEmbedStrings;
20
+ }): React.ReactElement;
21
+ //# sourceMappingURL=MiniMap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MiniMap.d.ts","sourceRoot":"","sources":["../../src/map/MiniMap.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gDAAgD,CAAC;AACxF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAIlD,OAAO,EAAsC,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAEpF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAEjE;;;;;;;GAOG;AACH,wBAAgB,OAAO,CAAC,EACtB,IAAI,EACJ,SAAS,EACT,IAAI,EACJ,cAAc,EACd,YAAY,EACZ,OAAO,GACR,EAAE;IACD,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IACjC,QAAQ,CAAC,SAAS,EAAE,WAAW,GAAG,IAAI,CAAC;IACvC,QAAQ,CAAC,IAAI,EAAE,SAAS,cAAc,EAAE,CAAC;IACzC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;IACpC,QAAQ,CAAC,OAAO,EAAE,qBAAqB,CAAC;CACzC,GAAG,KAAK,CAAC,YAAY,CAyHrB"}