@ngis/plugin-ui 0.4.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 (79) hide show
  1. package/LICENSE +13 -0
  2. package/README.md +25 -0
  3. package/dist/index.cjs +71 -0
  4. package/dist/index.d.ts +114 -0
  5. package/dist/index.mjs +69 -0
  6. package/dist/src/api/analysis.d.ts +140 -0
  7. package/dist/src/api/resources.d.ts +530 -0
  8. package/dist/src/api/scenes.d.ts +251 -0
  9. package/dist/src/api/users.d.ts +22 -0
  10. package/dist/src/api/workflows.d.ts +191 -0
  11. package/dist/src/components/plugin-ui/AttributeTable.d.ts +26 -0
  12. package/dist/src/components/plugin-ui/BatchTable.d.ts +47 -0
  13. package/dist/src/components/plugin-ui/ChartCanvas.d.ts +14 -0
  14. package/dist/src/components/plugin-ui/ExtentPicker.d.ts +22 -0
  15. package/dist/src/components/plugin-ui/FieldSelect.d.ts +23 -0
  16. package/dist/src/components/plugin-ui/Flow.d.ts +256 -0
  17. package/dist/src/components/plugin-ui/ModelSelect.d.ts +36 -0
  18. package/dist/src/components/plugin-ui/ParamForm.d.ts +63 -0
  19. package/dist/src/components/plugin-ui/PickerField.d.ts +52 -0
  20. package/dist/src/components/plugin-ui/ResourceSelect.d.ts +37 -0
  21. package/dist/src/components/plugin-ui/ResultActions.d.ts +48 -0
  22. package/dist/src/components/plugin-ui/RunButton.d.ts +49 -0
  23. package/dist/src/components/plugin-ui/StatChart.d.ts +66 -0
  24. package/dist/src/components/plugin-ui/TaskChrome.d.ts +35 -0
  25. package/dist/src/components/plugin-ui/TaskList.d.ts +29 -0
  26. package/dist/src/components/plugin-ui/TaskStatus.d.ts +55 -0
  27. package/dist/src/components/plugin-ui/batch-model.d.ts +442 -0
  28. package/dist/src/components/plugin-ui/flow-inputs.d.ts +61 -0
  29. package/dist/src/components/plugin-ui/flow-model.d.ts +303 -0
  30. package/dist/src/components/plugin-ui/flow-ports.d.ts +155 -0
  31. package/dist/src/components/plugin-ui/param-form-data.d.ts +122 -0
  32. package/dist/src/components/plugin-ui/param-form-model.d.ts +187 -0
  33. package/dist/src/components/plugin-ui/param-widgets.d.ts +88 -0
  34. package/dist/src/components/plugin-ui/picker-data.d.ts +141 -0
  35. package/dist/src/components/plugin-ui/picker-model.d.ts +150 -0
  36. package/dist/src/components/plugin-ui/recipe-model.d.ts +320 -0
  37. package/dist/src/components/plugin-ui/result-data.d.ts +112 -0
  38. package/dist/src/components/plugin-ui/result-model.d.ts +96 -0
  39. package/dist/src/components/plugin-ui/run-data.d.ts +118 -0
  40. package/dist/src/components/plugin-ui/run-model.d.ts +179 -0
  41. package/dist/src/components/plugin-ui/stat-chart-model.d.ts +168 -0
  42. package/dist/src/components/plugin-ui/validation-messages.d.ts +18 -0
  43. package/dist/src/components/plugin-ui/w2-contracts.d.ts +151 -0
  44. package/dist/src/components/plugin-ui/w3-contracts.d.ts +328 -0
  45. package/dist/src/components/plugin-ui/w4-contracts.d.ts +510 -0
  46. package/dist/src/components/ui/button.d.ts +12 -0
  47. package/dist/src/components/ui/checkbox.d.ts +4 -0
  48. package/dist/src/components/ui/column-resize-handle.d.ts +23 -0
  49. package/dist/src/components/ui/dropdown-menu.d.ts +25 -0
  50. package/dist/src/components/ui/input.d.ts +3 -0
  51. package/dist/src/components/ui/label.d.ts +4 -0
  52. package/dist/src/components/ui/popover.d.ts +10 -0
  53. package/dist/src/components/ui/select.d.ts +15 -0
  54. package/dist/src/components/ui/switch.d.ts +6 -0
  55. package/dist/src/domain/map/types.d.ts +128 -0
  56. package/dist/src/features/gis/components/GisAttributeTable.d.ts +56 -0
  57. package/dist/src/features/gis/hooks/useDatasetFeatureWindow.d.ts +51 -0
  58. package/dist/src/lib/api-error.d.ts +81 -0
  59. package/dist/src/lib/api.d.ts +82 -0
  60. package/dist/src/lib/auth-session.d.ts +22 -0
  61. package/dist/src/lib/hooks/useColumnResize.d.ts +57 -0
  62. package/dist/src/lib/raster-color-layer/RasterColorLayer.d.ts +48 -0
  63. package/dist/src/lib/raster-color-layer/TileGrid.d.ts +31 -0
  64. package/dist/src/lib/raster-color-layer/colorRamp.d.ts +18 -0
  65. package/dist/src/lib/raster-color-layer/shaders/raster-color.frag.d.ts +2 -0
  66. package/dist/src/lib/raster-color-layer/shaders/raster-color.vert.d.ts +2 -0
  67. package/dist/src/lib/raster-color-layer/zoomInterpolate.d.ts +2 -0
  68. package/dist/src/lib/utils.d.ts +2 -0
  69. package/dist/src/sdk/facets/types.d.ts +370 -0
  70. package/dist/src/sdk/host-api.d.ts +256 -0
  71. package/dist/src/sdk/map-api.d.ts +288 -0
  72. package/dist/src/sdk/panels.d.ts +130 -0
  73. package/dist/src/types/analysis.d.ts +205 -0
  74. package/dist/src/types/common.d.ts +17 -0
  75. package/dist/src/types/extensions.d.ts +560 -0
  76. package/dist/src/types/gis.d.ts +242 -0
  77. package/dist/src/types/market-social.d.ts +378 -0
  78. package/dist/src/types/workflow.d.ts +83 -0
  79. package/package.json +59 -0
@@ -0,0 +1,288 @@
1
+ /**
2
+ * `NgisMapAPI` — the typed facade a plugin programs against (L3 SDK contract).
3
+ *
4
+ * This is the "GeoLibre `GeoLibreAppAPI`" equivalent (see
5
+ * `../../GEOLIBRE_VS_NGIS.md` §3, one level up from the repo). It is a **binding
6
+ * layer, not a fourth subsystem**: every method delegates to something that
7
+ * already exists —
8
+ *
9
+ * - the scene controllers' imperative surface (draw / measure / camera),
10
+ * reached today through the composed `GisMapHandle` the shell exposes
11
+ * (which itself reads the *live* scene-map-context method registry
12
+ * `SceneMapHandleRegistry` — see `components/scenes/map/scene-map-context.ts`,
13
+ * C2/C4/C5 — so the facade never captures a stale controller instance);
14
+ * - the GIS scene store (`store/gis-scene.ts`) — the `layers` facet;
15
+ * - the `WorkspacePanelRegistry` (`sdk/panels.ts`, C7) — the `panels` facet;
16
+ * - workspace-owned highlight + view state — the `selection.highlight` and
17
+ * `camera` facets.
18
+ *
19
+ * The facade introduces **zero new direct `maplibregl.Map` access**. The one
20
+ * raw touch it exposes — `getMap()` — is a deliberately-unstable escape hatch
21
+ * that stays `null` until the host wires a raw-map getter into it (the one-line
22
+ * `SceneMapCanvas.tsx` provider hookup applied after C5 lands; see
23
+ * `docs/componentization_stage/session-C8-map-api-facade.md`).
24
+ *
25
+ * GeoLibre's proven decisions, adopted verbatim in shape:
26
+ * 1. **Every subscription returns an unsubscribe function** (`onX(cb) => () => void`).
27
+ * 2. **Newer / not-yet-proven methods are optional-typed** (`api.x?.()`) so
28
+ * plugin code written against v0 keeps compiling as the surface grows.
29
+ * As of 0.2.0 (session P3, plugin-runtime stage) every method is either
30
+ * **real and required** or was deleted; the lone remaining optional member
31
+ * is `draw.snapshot` (see its doc for the stated reason).
32
+ * 3. **A raw-MapLibre escape hatch** (`getMap()`), documented as unstable.
33
+ *
34
+ * Framework-agnostic on purpose: this module imports no React and only
35
+ * type-imports its domain types, so the pure binding/bookkeeping logic is
36
+ * unit-tested directly with `node:test` (`map-api.test.ts`).
37
+ */
38
+ import type { Map as MapLibreMap } from "maplibre-gl";
39
+ import type { SceneLayerHighlight, SceneMapView } from "../domain/map/types";
40
+ import type { GisDrawMode, GisMeasureKind } from "../types/gis";
41
+ import type { WorkspacePanelRegistry } from "./panels";
42
+ /** Current facade contract version — bumped when the frozen surface changes.
43
+ * A plugin's `minNgisVersion` (`docs/frontend-map-sdk.md` §3) is
44
+ * checked against this. Pre-1.0: the surface may still grow (additively).
45
+ *
46
+ * 0.2.0 (plugin-runtime stage, P3): promoted `camera.flyTo`/`fitBounds`,
47
+ * `layers.onChange` and `draw.onDrawChange` to required; deleted the never-
48
+ * implemented `selection.get`/`clear`/`onChange` (+ the `"selectionchange"`
49
+ * event) per decision D10; bound `draw.snapshot` through the imperative
50
+ * handle (kept optional — see its doc). See `docs/frontend-map-sdk.md` §2.
51
+ *
52
+ * 0.3.0 (Stage 11 E7, design doc §19, ED19): added the `commands` capability
53
+ * (`src/sdk/commands.ts`) — a plugin-declared, externally-invocable seam the
54
+ * codeenv SDK bridge's plugin-invocation executor calls by id. Contract-safe
55
+ * in both directions (the runtime's unknown-capability refusal covers an old
56
+ * runtime seeing a `commands`-declaring plugin; `minNgisVersion` covers a new
57
+ * plugin requiring a runtime this old). The bump lands atomically with this
58
+ * capability's only consumer, per ED19 — never staged ahead of it.
59
+ *
60
+ * 0.4.0 (Stage 13, X4b; F13.13 + ED19): the **`NgisHostAPI` capability
61
+ * contract**. `NgisPluginHost` widens from `{ api }` to the full nine-facet
62
+ * facade (`src/sdk/host-api.ts`), the runtime's first-wins `register` gains an
63
+ * `unregister`/`replace` seam (F13.8), and `contributes` v1 + derived
64
+ * activation events land beside `capabilities`. **`host.api` — this whole
65
+ * module — is unchanged and stays ungated**, which is the back-compat rule: a
66
+ * 0.3-era plugin declares `minNgisVersion: "0.3.0"`, negotiates identically,
67
+ * and keeps working byte-for-byte.
68
+ *
69
+ * Bumped atomically with its consumers per ED19: `plugin.ts`'s alias,
70
+ * `plugin-runtime.ts`, `GisWorkspace.tsx`'s host construction and the loader
71
+ * path all land in the same commit. Note the reverse leg of F13.13 —
72
+ * a 0.4-requiring manifest on a 0.3 host is refused by the `minNgisVersion`
73
+ * semver compare, **not** by the unknown-capability check: `contributes` and
74
+ * `permissions` are top-level manifest siblings, and a 0.3 runtime inspects
75
+ * only `manifest.capabilities`. */
76
+ export declare const NGIS_MAP_API_VERSION = "0.4.0";
77
+ /** A scene layer as the SDK exposes it — a stable projection of the store's
78
+ * `NgisSceneLayer`, so plugins never depend on the store's internal shape. */
79
+ export interface NgisLayerSummary {
80
+ id: string;
81
+ name: string;
82
+ /** Scene layer type id (e.g. `"MAPLIBRE_FILL"`); `null` when unset. */
83
+ type: string | null;
84
+ visible: boolean;
85
+ }
86
+ /** Camera state — identical shape to the scene's persisted `SceneMapView`. */
87
+ export type NgisMapView = SceneMapView;
88
+ /** Geographic bounds `[west, south, east, north]`. */
89
+ export type NgisBounds = [number, number, number, number];
90
+ /** An unsubscribe function — the return value of every `onX` subscription. */
91
+ export type NgisUnsubscribe = () => void;
92
+ /** `layers` facet — list / inspect / focus / toggle scene layers. */
93
+ export interface NgisLayersFacet {
94
+ list(): NgisLayerSummary[];
95
+ get(id: string): NgisLayerSummary | null;
96
+ /** Fit/ease the camera to a layer's extent (the layer-list "focus" action). */
97
+ focus(id: string): void;
98
+ /** Show/hide a layer (persists via the scene store, like the layer list). */
99
+ setVisibility(id: string, visible: boolean): void;
100
+ /** Notified when the effective layer set changes (add/remove/reorder/
101
+ * visibility *and* live style-draft edits — anything that replaces the
102
+ * store's `effectiveLayers` array). The host's binding equality-guards the
103
+ * subscription so listeners only fire on real changes, never on unrelated
104
+ * store updates. Required since 0.2.0. */
105
+ onChange(listener: () => void): NgisUnsubscribe;
106
+ }
107
+ /** `camera` facet — read/move the viewport + observe view changes. */
108
+ export interface NgisCameraFacet {
109
+ /** Current view, or `null` when the raw map isn't bound yet (`getMap()`). */
110
+ getView(): NgisMapView | null;
111
+ /** Subscribe to camera moves (drives view persistence). Returns unsubscribe. */
112
+ onViewChange(listener: (view: NgisMapView) => void): NgisUnsubscribe;
113
+ zoomIn(): void;
114
+ zoomOut(): void;
115
+ /** Ease back to the scene's default/initial view. */
116
+ zoomHome(): void;
117
+ /** Fly to an explicit view. Escape-hatch-backed: delegates to the raw map,
118
+ * so it is a documented **no-op while `getMap()` returns `null`** (i.e.
119
+ * until the host binds a raw-map getter — the first-party canvas does via
120
+ * `useBindNgisRawMap`). Required since 0.2.0. */
121
+ flyTo(view: NgisMapView): void;
122
+ /** Fit the camera to bounds. Escape-hatch-backed — same getMap-null no-op
123
+ * behavior as `flyTo`. Required since 0.2.0. */
124
+ fitBounds(bounds: NgisBounds): void;
125
+ }
126
+ /** Enumerated feature highlight — identical to the scene's `SceneLayerHighlight`. */
127
+ export type NgisHighlight = SceneLayerHighlight;
128
+ /** The highlight sub-facet under `selection` — set/clear the feature-state
129
+ * highlight the attribute table and layer list drive. */
130
+ export interface NgisHighlightFacet {
131
+ get(): NgisHighlight | null;
132
+ set(highlight: NgisHighlight | null): void;
133
+ clear(): void;
134
+ onChange(listener: (highlight: NgisHighlight | null) => void): NgisUnsubscribe;
135
+ }
136
+ /** `selection` facet — feature highlight only.
137
+ *
138
+ * 0.2.0 deleted the declared-but-dead drawn-feature-selection surface
139
+ * (`get?`/`clear?`/`onChange?` + the `"selectionchange"` event): nothing ever
140
+ * implemented or emitted it (decision D10 — contract-safe pre-1.0), and the
141
+ * one cheap first-party emit source (`SelectionController`'s feature-select
142
+ * enable/disable) signals an interaction-mode toggle, not a selection-set
143
+ * change. If a real Terra Draw selection source lands, the members return
144
+ * additively. */
145
+ export interface NgisSelectionFacet {
146
+ highlight: NgisHighlightFacet;
147
+ }
148
+ /** `draw` facet — Terra Draw mode + one-shot geometry actions. */
149
+ export interface NgisDrawFacet {
150
+ setMode(mode: GisDrawMode): void;
151
+ /** Leave the active mode, back to idle. */
152
+ cancelMode(): void;
153
+ getActiveMode(): string | null;
154
+ /** Remove every drawn feature. */
155
+ clear(): void;
156
+ /** Delete the currently selected drawn feature(s). */
157
+ deleteSelection(): void;
158
+ undo(): void;
159
+ redo(): void;
160
+ /** Download the drawn features as GeoJSON. */
161
+ export(): void;
162
+ /** Snapshot the raw Terra Draw features. Bound (0.2.0) through the
163
+ * imperative handle's optional `getDrawSnapshot` — returns `[]` when the
164
+ * handle is absent or doesn't expose the service.
165
+ *
166
+ * **Live since Stage 14 (F14.18).** The first-party shell's composed
167
+ * `GisMapHandle` now surfaces `getDrawSnapshot`, so in the shipping host this
168
+ * returns the real feature list rather than always `[]`. The member **stays
169
+ * optional-typed** because the 0.3 surface is frozen and a host is still
170
+ * allowed to bind a handle without the service; callers keep writing
171
+ * `snapshot?.() ?? []`. */
172
+ snapshot?(): unknown[];
173
+ /** Observe draw changes (drawn-feature/query-result changes and draw-mode
174
+ * transitions). Wired since 0.2.0: the host feeds its draw-result/draw-mode
175
+ * handlers into `NgisMapAPIHandle.emitDrawChange`. Required. */
176
+ onDrawChange(listener: () => void): NgisUnsubscribe;
177
+ }
178
+ /** `measure` facet — the ephemeral distance/area overlay. (Not in GeoLibre's
179
+ * original facet list; added because the rail's measure tools are a real v0
180
+ * consumer — measuring is a sibling Terra Draw control, never a drawn feature.) */
181
+ export interface NgisMeasureFacet {
182
+ start(kind: GisMeasureKind): void;
183
+ clear(): void;
184
+ }
185
+ /** Named lifecycle events a plugin can observe generically.
186
+ * (`"selectionchange"` was deleted in 0.2.0 alongside the dead
187
+ * drawn-feature-selection surface — see `NgisSelectionFacet`.) */
188
+ export type NgisMapEventName = "viewchange" | "highlightchange" | "drawchange";
189
+ /** `events` facet — a generic named-event bus over the same emitters the typed
190
+ * facet subscriptions use (a forward-compat convenience for plugins). */
191
+ export interface NgisEventsFacet {
192
+ on(event: NgisMapEventName, listener: () => void): NgisUnsubscribe;
193
+ }
194
+ /**
195
+ * The typed facade. v0 — the surface a plugin (and, today, the three first-party
196
+ * consumers) program against. Concrete methods are frozen; optional-typed
197
+ * methods may light up as the map subsystems expose more.
198
+ */
199
+ export interface NgisMapAPI {
200
+ readonly version: string;
201
+ layers: NgisLayersFacet;
202
+ camera: NgisCameraFacet;
203
+ selection: NgisSelectionFacet;
204
+ draw: NgisDrawFacet;
205
+ measure: NgisMeasureFacet;
206
+ /** The C7 workspace panel registry, re-exposed so a plugin registers UI slots
207
+ * through the same SDK object. */
208
+ panels: WorkspacePanelRegistry;
209
+ events: NgisEventsFacet;
210
+ /** Raw MapLibre instance — the **unstable escape hatch**. `null` until the
211
+ * host binds a getter (post-C5 `SceneMapCanvas` hookup). Anything a plugin
212
+ * does with this is outside the stability contract. */
213
+ getMap(): MapLibreMap | null;
214
+ }
215
+ /** The imperative surface the facade delegates draw/measure/camera to — a
216
+ * structural subset of the shell's `GisMapHandle`, kept structural so this
217
+ * module doesn't import the (large) `SceneMapCanvas` component. */
218
+ export interface NgisMapImperativeHandle {
219
+ setDrawMode(mode: GisDrawMode): void;
220
+ cancelMode(): void;
221
+ getActiveMode(): string | null;
222
+ clearDraw(): void;
223
+ deleteSelection(): void;
224
+ undo(): void;
225
+ redo(): void;
226
+ exportDraw(): void;
227
+ startMeasure(kind: GisMeasureKind): void;
228
+ clearMeasure(): void;
229
+ zoomIn(): void;
230
+ zoomOut(): void;
231
+ zoomHome(): void;
232
+ /** Raw Terra Draw feature snapshot (the scene-map-context `getDrawSnapshot`
233
+ * service). **The first-party shell supplies it since Stage 14 (F14.18)**, so
234
+ * `draw.snapshot` returns real data there; it stays optional so a host may
235
+ * still bind a handle without the service. */
236
+ getDrawSnapshot?(): unknown[];
237
+ }
238
+ /** The layer operations the `layers` facet needs (a thin adapter over the
239
+ * scene store, supplied by the host so the SDK stays store-agnostic). */
240
+ export interface NgisLayersBinding {
241
+ list(): NgisLayerSummary[];
242
+ focus(id: string): void;
243
+ setVisibility(id: string, visible: boolean): void;
244
+ /** Store-change subscription backing `layers.onChange`. The host must
245
+ * equality-guard it (fire only when the effective layer set really changed).
246
+ * Required since 0.2.0 — `onChange` is part of the frozen surface. */
247
+ subscribe(listener: () => void): NgisUnsubscribe;
248
+ }
249
+ /** The highlight sink — the workspace-owned highlight state (the exact data
250
+ * path the attribute table uses today: this ultimately calls `setHighlight`). */
251
+ export interface NgisHighlightBinding {
252
+ set(highlight: NgisHighlight | null): void;
253
+ /** Initial value (defaults to `null`). */
254
+ initial?: NgisHighlight | null;
255
+ }
256
+ export interface NgisMapAPIBindings {
257
+ /** Live composed imperative handle (`mapRef.current`), or `null` before ready. */
258
+ getHandle(): NgisMapImperativeHandle | null;
259
+ layers: NgisLayersBinding;
260
+ panels: WorkspacePanelRegistry;
261
+ highlight: NgisHighlightBinding;
262
+ /** Raw-map getter for `getMap()`. Usually omitted at construction and wired
263
+ * later via `NgisMapAPIHandle.bindRawMap` (the post-C5 hookup). */
264
+ getRawMap?(): MapLibreMap | null;
265
+ }
266
+ /**
267
+ * The factory's return value: the public `api` plus the host-side "driver"
268
+ * controls (feed-in points the host wires that are *not* part of the public
269
+ * plugin surface).
270
+ */
271
+ export interface NgisMapAPIHandle {
272
+ api: NgisMapAPI;
273
+ /** Feed a camera-view change into the facade (called from the canvas's
274
+ * `onViewChange` prop today; from `CameraController` post-hookup). */
275
+ emitViewChange(view: NgisMapView): void;
276
+ /** Feed a draw change into the facade — drives `draw.onDrawChange` and the
277
+ * `"drawchange"` event. The host calls this from its draw-result and
278
+ * draw-mode handlers (0.2.0's first-party emit source). */
279
+ emitDrawChange(): void;
280
+ /** Bind (or replace) the raw-map getter behind `getMap()` — the one-line
281
+ * `SceneMapCanvas` provider hookup applied after rebasing onto merged C5. */
282
+ bindRawMap(getter: () => MapLibreMap | null): void;
283
+ }
284
+ /**
285
+ * Build a facade over the supplied bindings. Pure and framework-agnostic —
286
+ * create it once per workspace (the host memoizes it) and keep it stable.
287
+ */
288
+ export declare function createNgisMapAPI(bindings: NgisMapAPIBindings): NgisMapAPIHandle;
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Workspace panel registry — the plugin-readiness seam for GIS workspace UI
3
+ * "slots" (today: the right dock and the bottom docks). Formalizes what used
4
+ * to be a hand-wired `GisRightMode` enum plus three independent booleans
5
+ * (`attributeOpen`/`workflowOpen`/`codeOpen`) into a generic registration API:
6
+ *
7
+ * const unregister = registerPanel({ id, slot, icon, labelKey, render });
8
+ *
9
+ * Design notes (see `docs/componentization_stage/session-C7-*.md`):
10
+ * - `render(container, ctx)` is a **DOM-based, framework-agnostic** contract —
11
+ * modeled on GeoLibre's lesson that forcing every panel to hand back a React
12
+ * element makes a registry impossible for a future non-React plugin loader
13
+ * to target. A real plugin only needs to mutate a `container: HTMLElement`
14
+ * and (optionally) return a cleanup function.
15
+ * - `reactPanel()` below is the React *adapter* the design doc calls for.
16
+ * First-party panels stay ordinary React components; the adapter's returned
17
+ * `render` function still matches the generic signature (so a naive host
18
+ * invoking it imperatively still works, via an internal `createRoot`
19
+ * fallback), but it also carries a `reactNode` escape hatch that a
20
+ * React-aware host (`GisPanelHost`) uses to mount the panel as a normal
21
+ * child in the app's own React tree — preserving context (next-intl, theme)
22
+ * and ordinary prop-driven reactivity, which an isolated `createRoot` tree
23
+ * cannot (separate roots don't share React context).
24
+ * - A slot still shows only one active panel at a time, but it also tracks the
25
+ * panels opened during the current workspace session. Both GIS docks present
26
+ * that list as tabs and keep inactive opened bodies mounted so local UI state
27
+ * survives tab switches.
28
+ */
29
+ import { type ReactNode } from "react";
30
+ /** Slots this stage. Order matters only for future additions, not behavior. */
31
+ export type PanelSlot = "right" | "bottom";
32
+ export interface PanelRenderContext {
33
+ /** Close this panel in its slot (and its tab, when the host presents tabs). */
34
+ close: () => void;
35
+ }
36
+ export type PanelDisposer = () => void;
37
+ /**
38
+ * The framework-agnostic render contract. A real (future) plugin panel would
39
+ * implement this directly: mutate `container`, return a cleanup function.
40
+ */
41
+ export type PanelRenderFn = (container: HTMLElement, ctx: PanelRenderContext) => PanelDisposer | void;
42
+ /** `reactPanel()`'s return type — same call signature, plus the fast path. */
43
+ export interface ReactPanelRenderFn extends PanelRenderFn {
44
+ /** Build the React node for this panel "live" — called on every render of
45
+ * the React-aware host, so ordinary prop/state reactivity works exactly
46
+ * like a normal child component. */
47
+ reactNode: (ctx: PanelRenderContext) => ReactNode;
48
+ }
49
+ export interface PanelRegistration {
50
+ id: string;
51
+ slot: PanelSlot;
52
+ /** Plain string identifier (e.g. a lucide icon name) — kept string-typed,
53
+ * not a React node, so the registration shape never requires React. The
54
+ * consuming UI (e.g. `GisCommandRail`'s right-panel entries) resolves it. */
55
+ icon?: string;
56
+ /** i18n key (e.g. `"GIS.panels.ai"`) resolved by the consumer's `t()`. */
57
+ labelKey?: string;
58
+ /** Literal label, for callers without an i18n key (e.g. a future plugin). */
59
+ label?: string;
60
+ /** Lower sorts first. Ties fall back to registration order. */
61
+ order?: number;
62
+ render: PanelRenderFn;
63
+ }
64
+ type Listener = () => void;
65
+ /**
66
+ * A registry instance. `panels.ts` exports a process-wide singleton
67
+ * (`workspacePanelRegistry`) for the app to use, but the class is exported too
68
+ * so tests (or a future second workspace) can build an isolated instance.
69
+ */
70
+ export declare class WorkspacePanelRegistry {
71
+ private readonly panelsBySlot;
72
+ private readonly orderedCache;
73
+ private readonly activeBySlot;
74
+ private readonly openBySlot;
75
+ private readonly listeners;
76
+ private seq;
77
+ private readonly seqById;
78
+ constructor(slots?: readonly PanelSlot[]);
79
+ /** Register a panel into its slot. Returns an unregister function — the
80
+ * same "every registration returns an unregister function" convention the
81
+ * `NgisMapAPI` facade (C8) uses. */
82
+ registerPanel(def: PanelRegistration): PanelDisposer;
83
+ /** Registered panels in a slot, sorted by `order` then registration order.
84
+ * Cached so repeated calls between mutations return the same array
85
+ * reference (required for `useSyncExternalStore`'s snapshot contract). */
86
+ getPanels(slot: PanelSlot): PanelRegistration[];
87
+ getPanel(slot: PanelSlot, id: string): PanelRegistration | null;
88
+ getActive(slot: PanelSlot): string | null;
89
+ /** Panel ids opened in this slot, in first-opened order. The returned array
90
+ * is referentially stable until the open set changes. */
91
+ getOpenPanelIds(slot: PanelSlot): readonly string[];
92
+ /** Open a registered panel and make it active. Reopening an existing panel
93
+ * only activates it; it does not duplicate or reorder its tab. */
94
+ open(slot: PanelSlot, id: string): void;
95
+ setActive(slot: PanelSlot, id: string | null): void;
96
+ /** Close one opened panel. When it was active, prefer the nearest tab on
97
+ * its left, then the nearest tab on its right. */
98
+ close(slot: PanelSlot, id: string): void;
99
+ /** Invoking the already-active panel closes it — the "toggle-off" parity
100
+ * every rail tool and dock icon relies on. */
101
+ toggle(slot: PanelSlot, id: string): void;
102
+ subscribe(listener: Listener): PanelDisposer;
103
+ private slotMap;
104
+ private invalidate;
105
+ private removeOpenPanel;
106
+ private notify;
107
+ }
108
+ /** Process-wide registry for the `/gis` workspace. */
109
+ export declare const workspacePanelRegistry: WorkspacePanelRegistry;
110
+ /**
111
+ * React adapter: wraps a `(ctx) => ReactNode` factory as a `PanelRenderFn`.
112
+ * First-party panels use this. See the module doc comment for why the
113
+ * returned function carries both an imperative `createRoot` fallback (the
114
+ * literal, framework-agnostic contract) and a `reactNode` fast path
115
+ * (`GisPanelHost` uses this one, to stay inside the app's own React tree).
116
+ */
117
+ export declare function reactPanel(factory: (ctx: PanelRenderContext) => ReactNode): ReactPanelRenderFn;
118
+ export declare function isReactPanelRenderFn(fn: PanelRenderFn): fn is ReactPanelRenderFn;
119
+ /** Convenience: build a `reactPanel` from a fixed element (no live `ctx`
120
+ * dependency needed) — most first-party panels want the factory form instead
121
+ * so they can pass `ctx.close`, but this keeps trivial cases terse. */
122
+ export declare function staticReactPanel(node: ReactNode): ReactPanelRenderFn;
123
+ /** Reactive list of a slot's registered panels, sorted — for building things
124
+ * like `GisCommandRail`'s right-panel entry group generically. */
125
+ export declare function usePanels(registry: WorkspacePanelRegistry, slot: PanelSlot): readonly PanelRegistration[];
126
+ /** Reactive active-panel id for a slot (`null` when the slot is closed). */
127
+ export declare function usePanelActive(registry: WorkspacePanelRegistry, slot: PanelSlot): string | null;
128
+ /** Reactive first-opened list for hosts that present a slot as tabs. */
129
+ export declare function useOpenPanelIds(registry: WorkspacePanelRegistry, slot: PanelSlot): readonly string[];
130
+ export {};
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Analysis types.
3
+ *
4
+ * Two halves, and they are different in kind:
5
+ *
6
+ * 1. **UI-surface types** (`WorkSurface`, `RightDockMode`, …) — pre-existing,
7
+ * local to the analysis workspace.
8
+ * 2. **The Stage 14 UI-schema + validation wire DTOs** — **F14.2 / F14.3
9
+ * verbatim**, authored at the W1 kickoff and **read-only to every W1
10
+ * session** (`docs/components_flow_stage/README.md` → F14.24). YB1 serves
11
+ * these shapes, YB2 serves the validation/search ones, Y3/Y4 consume them.
12
+ * A session that needs a change here **stops and raises it** — YB1's Python
13
+ * tests and Y3's TS tests must round-trip the *same* fixtures
14
+ * (`src/sdk/__fixtures__/ui-schema/` ≡ `ngis-geoanalysis/tests/fixtures/ui_schema/`,
15
+ * byte-identical, divergence is a wave-close blocker).
16
+ *
17
+ * **The wire is snake_case and so is the type.** Any camelCase remap would be a
18
+ * second normalization — the exact thing this stage exists to kill — and would
19
+ * break the byte-identical fixture rule. One spelling, one serializer, two
20
+ * transports (HTTP + the `GetToolUiSchema` gRPC leg).
21
+ *
22
+ * The `import type` of `NgisDataType` from `@/sdk/facets/types` is deliberate
23
+ * and type-only in both directions: that module imports `NgisToolUiSchema` back
24
+ * from here for `NgisModelsFacet.uiSchema`. Both edges are erased at compile
25
+ * time, so there is no runtime cycle. F14.1 fixes the vocabulary's home in
26
+ * `src/sdk/facets/types.ts`; F14.2 fixes the schema family's home here.
27
+ */
28
+ import type { NgisDataType } from "../sdk/facets/types";
29
+ export type WorkSurface = "workflow" | "insight" | "code";
30
+ export type RightDockMode = "none" | "ai";
31
+ export type MapFocusMode = "split" | "map" | "work";
32
+ export type LayerSourcePanelMode = "closed" | "resources" | "style";
33
+ export type NgisUiSchemaValueType = "string" | "number" | "integer" | "boolean";
34
+ export type NgisParamRole = "input" | "option" | "output";
35
+ /**
36
+ * Total, frozen. **There is NO fallback widget:** an unknown key is a
37
+ * render-time error surfaced in place, never a silently-degraded text input.
38
+ * A Y3 widget registry missing any of the twelve is a defect.
39
+ *
40
+ * `field-select`, `extent` and `textarea` are **never emitted by YB1 in v1** —
41
+ * no builtin schema expresses a field, a bbox or a long-text parameter. They
42
+ * are in the union because the registry must be total over it, and because an
43
+ * author's `ParamForm` `widgets` override and a recipe's promoted inputs can
44
+ * select them.
45
+ */
46
+ export type NgisWidgetKey = "resource-select" | "resource-multi" | "select" | "multi-select" | "switch" | "number" | "integer" | "text" | "textarea" | "output-name" | "field-select" | "extent";
47
+ export interface NgisUiSchemaChoice {
48
+ value: string;
49
+ label: string;
50
+ }
51
+ /** Derived from `x-rely-on`. Empty array when the parameter has no declared dependency. */
52
+ export interface NgisUiSchemaDependency {
53
+ /** The `name` of the parameter whose value gates this one. */
54
+ param: string;
55
+ /** Shown only when the parent's value, stringified, is one of these. */
56
+ equals: readonly string[];
57
+ }
58
+ export interface NgisToolUiParam {
59
+ /** The wire name — exactly the key `POST /api/analysis/tasks` takes. */
60
+ name: string;
61
+ /** `x-display-name` -> `title` -> `name`. The generated-reason text source (F14.10). */
62
+ label: string;
63
+ description: string | null;
64
+ role: NgisParamRole;
65
+ value_type: NgisUiSchemaValueType;
66
+ /** Canonical (F14.1). `null` for role "option". */
67
+ data_type: NgisDataType | null;
68
+ widget: NgisWidgetKey;
69
+ /** `name in required[]` OR `x-optional === false`. Both halves, always. */
70
+ required: boolean;
71
+ /** `x-multiple === true`. Multi-valued inputs are comma-joined on submit. */
72
+ multiple: boolean;
73
+ default_value: string | number | boolean | null;
74
+ /** `enum` first, else `x-constraints.choices[].value` with `.label`. `[]` when none. */
75
+ choices: readonly NgisUiSchemaChoice[];
76
+ /** `minimum` first, else `x-constraints.min`. Same for max. */
77
+ minimum: number | null;
78
+ maximum: number | null;
79
+ depends_on: readonly NgisUiSchemaDependency[];
80
+ /** Advisory. `x-vector-geom` canonicalized to "point" | "line" | "polygon", else null. */
81
+ geometry: string | null;
82
+ /** `x-output-ext`. `null` unless role === "output". */
83
+ output_extension: string | null;
84
+ /** Every `x-*` key of the source property, verbatim and unmodified — the escape
85
+ * hatch, and the reason no consumer ever needs the raw endpoint. Includes keys
86
+ * this contract does not name (`x-flags`, `x-grass-element`, `x-required-srid`,
87
+ * `x-required-fields`, `x-primary`, `x-vector-layer`, `x-entry-file`, `x-runtime`). */
88
+ extensions: Readonly<Record<string, unknown>>;
89
+ }
90
+ export interface NgisToolUiSchema {
91
+ /** Literal 1 in this stage. A consumer that reads a version it does not know
92
+ * refuses; it never best-efforts a newer shape. */
93
+ ui_schema_version: 1;
94
+ tool_id: string;
95
+ /** `ngis_model_tool.version`. The value to pass as `expected_tool_version`. */
96
+ tool_version: number;
97
+ /** `owner_id IS NULL`. Never stored; always derived. */
98
+ is_builtin: boolean;
99
+ provider: string;
100
+ name: string;
101
+ description: string | null;
102
+ /** Diagnostic only. Clients MUST NOT branch on it. */
103
+ source_shape: "properties" | "inputs";
104
+ params: readonly NgisToolUiParam[];
105
+ }
106
+ /**
107
+ * The `ui_schema_version` this build understands. A reader that sees anything
108
+ * else **refuses** — it never best-efforts a newer shape (F14.2, and the same
109
+ * rule `recipeVersion` carries in F14.6).
110
+ */
111
+ export declare const NGIS_UI_SCHEMA_VERSION: 1;
112
+ /** `PASS | ADAPTABLE | UNKNOWN | FAIL`, passed through verbatim. */
113
+ export type NgisBindingVerdict = "PASS" | "ADAPTABLE" | "UNKNOWN" | "FAIL";
114
+ /** `READY | NEEDS_CONFIGURATION | NOT_EXECUTABLE`, passed through verbatim. */
115
+ export type NgisExecutionVerdict = "READY" | "NEEDS_CONFIGURATION" | "NOT_EXECUTABLE";
116
+ export type NgisExecutionMode = "DIRECT" | "WORKFLOW";
117
+ export type NgisOutputMode = "ADD_LAYER" | "UPDATE_RESOURCE";
118
+ /** `DatasetBinding` — the wire form. */
119
+ export interface NgisDatasetBindingWire {
120
+ port_name: string;
121
+ file_ids: readonly string[];
122
+ }
123
+ /** `BindingValidation`, field-for-field. */
124
+ export interface NgisBindingValidationEntry {
125
+ port_name: string;
126
+ file_id: string;
127
+ expected_data_type: string;
128
+ actual_dataset_type: string;
129
+ dataset_status: string;
130
+ verdict: NgisBindingVerdict;
131
+ reason: string;
132
+ geometry_type: string;
133
+ srid: number;
134
+ native_srid: number;
135
+ working_srid: number;
136
+ access_mode: string;
137
+ executable: boolean;
138
+ }
139
+ /** `ValidateToolBindingsResponse`, field-for-field. */
140
+ export interface NgisBindingValidation {
141
+ tool_id: string;
142
+ tool_version: number;
143
+ verdict: NgisBindingVerdict;
144
+ bindings: readonly NgisBindingValidationEntry[];
145
+ unresolved_ports: readonly string[];
146
+ errors: readonly string[];
147
+ warnings: readonly string[];
148
+ }
149
+ /** `ValidateToolExecutionResponse`, field-for-field, with `normalized_params_json`
150
+ * replaced by the decoded `normalized_params` object (the one divergence). */
151
+ export interface NgisExecutionValidation {
152
+ tool_id: string;
153
+ tool_version: number;
154
+ verdict: NgisExecutionVerdict;
155
+ binding_validation: NgisBindingValidation | null;
156
+ missing_parameters: readonly string[];
157
+ parameter_errors: readonly string[];
158
+ output_errors: readonly string[];
159
+ warnings: readonly string[];
160
+ direct_executable: boolean;
161
+ execution_mode: NgisExecutionMode;
162
+ /** Decoded. The proto's `normalized_params_json` string is never sent to a browser. */
163
+ normalized_params: Readonly<Record<string, string>>;
164
+ }
165
+ /** `POST /api/analysis/tools/{tool_id}/validate-execution` body. */
166
+ export interface NgisValidateExecutionBody {
167
+ expected_tool_version?: number;
168
+ bindings?: readonly NgisDatasetBindingWire[];
169
+ /** Decoded, not `params_json`. */
170
+ params?: Readonly<Record<string, string>>;
171
+ scene_id?: string;
172
+ expected_scene_generation?: number;
173
+ output_mode?: NgisOutputMode;
174
+ target_resource_id?: string | null;
175
+ target_srid?: number;
176
+ }
177
+ /** `POST /api/analysis/tools/{tool_id}/validate-bindings` body. */
178
+ export interface NgisValidateBindingsBody {
179
+ expected_tool_version?: number;
180
+ bindings?: readonly NgisDatasetBindingWire[];
181
+ }
182
+ /** `ToolSearchResult`, field-for-field. */
183
+ export interface NgisToolSearchResult {
184
+ tool_id: string;
185
+ version: number;
186
+ kind: string;
187
+ name: string;
188
+ provider: string;
189
+ summary: string;
190
+ domains: readonly string[];
191
+ operation_types: readonly string[];
192
+ input_types: readonly string[];
193
+ output_types: readonly string[];
194
+ modelbox_path: string;
195
+ score: number;
196
+ semantic_match: boolean;
197
+ lexical_match: boolean;
198
+ }
199
+ /** `SearchToolsResponse`, field-for-field. `GET /api/analysis/tools/search`.
200
+ * `limit` default 10, server maximum 50. */
201
+ export interface NgisToolSearchResponse {
202
+ tools: readonly NgisToolSearchResult[];
203
+ search_degraded: boolean;
204
+ degradation_reason: string;
205
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Cross-domain API shapes shared by more than one client module.
3
+ *
4
+ * `PageResult<T>` moved here verbatim from `src/api/scenes.ts` (F1 / W1.2) so the
5
+ * extension market does not fork a second pagination interface. `scenes.ts`
6
+ * re-exports it, so every existing `import { PageResult } from "@/api/scenes"`
7
+ * keeps working.
8
+ */
9
+ export interface PageResult<T> {
10
+ content: T[];
11
+ totalElements: number;
12
+ totalPages: number;
13
+ size: number;
14
+ number: number;
15
+ first?: boolean;
16
+ last?: boolean;
17
+ }