@xemahq/ui-kernel 0.9.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (238) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +1 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/lib/biome-host/biome-registry.d.ts.map +1 -1
  6. package/dist/lib/biome-host/biome-registry.js +14 -9
  7. package/dist/lib/biome-host/biome-registry.js.map +1 -1
  8. package/dist/lib/contribution-registry/contribution-registry.d.ts +24 -0
  9. package/dist/lib/contribution-registry/contribution-registry.d.ts.map +1 -0
  10. package/dist/lib/contribution-registry/contribution-registry.js +67 -0
  11. package/dist/lib/contribution-registry/contribution-registry.js.map +1 -0
  12. package/dist/lib/contribution-registry/index.d.ts +3 -0
  13. package/dist/lib/contribution-registry/index.d.ts.map +1 -0
  14. package/dist/lib/contribution-registry/index.js +3 -0
  15. package/dist/lib/contribution-registry/index.js.map +1 -0
  16. package/dist/lib/contribution-registry/renderable-registry.d.ts +33 -0
  17. package/dist/lib/contribution-registry/renderable-registry.d.ts.map +1 -0
  18. package/dist/lib/contribution-registry/renderable-registry.js +51 -0
  19. package/dist/lib/contribution-registry/renderable-registry.js.map +1 -0
  20. package/dist/ui/capture/field-spec-to-zod.d.ts +1 -0
  21. package/dist/ui/capture/field-spec-to-zod.d.ts.map +1 -1
  22. package/dist/ui/capture/field-spec-to-zod.js +19 -0
  23. package/dist/ui/capture/field-spec-to-zod.js.map +1 -1
  24. package/dist/ui/capture/field-spec-validate.d.ts +4 -0
  25. package/dist/ui/capture/field-spec-validate.d.ts.map +1 -0
  26. package/dist/ui/capture/field-spec-validate.js +129 -0
  27. package/dist/ui/capture/field-spec-validate.js.map +1 -0
  28. package/dist/ui/capture/field-spec.d.ts +13 -1
  29. package/dist/ui/capture/field-spec.d.ts.map +1 -1
  30. package/dist/ui/capture/field-spec.js +25 -0
  31. package/dist/ui/capture/field-spec.js.map +1 -1
  32. package/dist/ui/capture/index.d.ts +4 -2
  33. package/dist/ui/capture/index.d.ts.map +1 -1
  34. package/dist/ui/capture/index.js +4 -2
  35. package/dist/ui/capture/index.js.map +1 -1
  36. package/dist/ui/capture/schema-field-input.d.ts.map +1 -1
  37. package/dist/ui/capture/schema-field-input.js +39 -12
  38. package/dist/ui/capture/schema-field-input.js.map +1 -1
  39. package/dist/ui/capture/submit-values.d.ts +6 -0
  40. package/dist/ui/capture/submit-values.d.ts.map +1 -0
  41. package/dist/ui/capture/submit-values.js +28 -0
  42. package/dist/ui/capture/submit-values.js.map +1 -0
  43. package/dist/ui/index.d.ts +1 -0
  44. package/dist/ui/index.d.ts.map +1 -1
  45. package/dist/ui/index.js +1 -0
  46. package/dist/ui/index.js.map +1 -1
  47. package/dist/ui/primitives/overflow-tabs.d.ts +5 -0
  48. package/dist/ui/primitives/overflow-tabs.d.ts.map +1 -1
  49. package/dist/ui/primitives/overflow-tabs.js +4 -4
  50. package/dist/ui/primitives/overflow-tabs.js.map +1 -1
  51. package/dist/ui/primitives/url-preview-chrome.d.ts +8 -0
  52. package/dist/ui/primitives/url-preview-chrome.d.ts.map +1 -0
  53. package/dist/ui/primitives/url-preview-chrome.js +40 -0
  54. package/dist/ui/primitives/url-preview-chrome.js.map +1 -0
  55. package/dist/vista/GenericCatalogueSurface.d.ts +10 -0
  56. package/dist/vista/GenericCatalogueSurface.d.ts.map +1 -0
  57. package/dist/vista/GenericCatalogueSurface.js +80 -0
  58. package/dist/vista/GenericCatalogueSurface.js.map +1 -0
  59. package/dist/vista/SurfaceFallbackCard.d.ts +9 -0
  60. package/dist/vista/SurfaceFallbackCard.d.ts.map +1 -0
  61. package/dist/vista/SurfaceFallbackCard.js +44 -0
  62. package/dist/vista/SurfaceFallbackCard.js.map +1 -0
  63. package/dist/vista/VistaPanel.d.ts +21 -0
  64. package/dist/vista/VistaPanel.d.ts.map +1 -0
  65. package/dist/vista/VistaPanel.js +31 -0
  66. package/dist/vista/VistaPanel.js.map +1 -0
  67. package/dist/vista/html/CustomUiFrame.d.ts +20 -0
  68. package/dist/vista/html/CustomUiFrame.d.ts.map +1 -0
  69. package/dist/vista/html/CustomUiFrame.js +69 -0
  70. package/dist/vista/html/CustomUiFrame.js.map +1 -0
  71. package/dist/vista/html/custom-ui-frame-bridge.d.ts +13 -0
  72. package/dist/vista/html/custom-ui-frame-bridge.d.ts.map +1 -0
  73. package/dist/vista/html/custom-ui-frame-bridge.js +30 -0
  74. package/dist/vista/html/custom-ui-frame-bridge.js.map +1 -0
  75. package/dist/vista/html/custom-ui-frame-contract.d.ts +29 -0
  76. package/dist/vista/html/custom-ui-frame-contract.d.ts.map +1 -0
  77. package/dist/vista/html/custom-ui-frame-contract.js +16 -0
  78. package/dist/vista/html/custom-ui-frame-contract.js.map +1 -0
  79. package/dist/vista/html/custom-ui-intents.d.ts +20 -0
  80. package/dist/vista/html/custom-ui-intents.d.ts.map +1 -0
  81. package/dist/vista/html/custom-ui-intents.js +57 -0
  82. package/dist/vista/html/custom-ui-intents.js.map +1 -0
  83. package/dist/vista/html/custom-ui-srcdoc.d.ts +17 -0
  84. package/dist/vista/html/custom-ui-srcdoc.d.ts.map +1 -0
  85. package/dist/vista/html/custom-ui-srcdoc.js +70 -0
  86. package/dist/vista/html/custom-ui-srcdoc.js.map +1 -0
  87. package/dist/vista/html/index.d.ts +6 -0
  88. package/dist/vista/html/index.d.ts.map +1 -0
  89. package/dist/vista/html/index.js +6 -0
  90. package/dist/vista/html/index.js.map +1 -0
  91. package/dist/vista/index.d.ts +18 -0
  92. package/dist/vista/index.d.ts.map +1 -0
  93. package/dist/vista/index.js +18 -0
  94. package/dist/vista/index.js.map +1 -0
  95. package/dist/vista/surface-catalogue.d.ts +5 -0
  96. package/dist/vista/surface-catalogue.d.ts.map +1 -0
  97. package/dist/vista/surface-catalogue.js +30 -0
  98. package/dist/vista/surface-catalogue.js.map +1 -0
  99. package/dist/vista/surface-icons.d.ts +7 -0
  100. package/dist/vista/surface-icons.d.ts.map +1 -0
  101. package/dist/vista/surface-icons.js +26 -0
  102. package/dist/vista/surface-icons.js.map +1 -0
  103. package/dist/vista/surface-registry.d.ts +7 -0
  104. package/dist/vista/surface-registry.d.ts.map +1 -0
  105. package/dist/vista/surface-registry.js +7 -0
  106. package/dist/vista/surface-registry.js.map +1 -0
  107. package/dist/vista/surfaces/ChartSurface.d.ts +4 -0
  108. package/dist/vista/surfaces/ChartSurface.d.ts.map +1 -0
  109. package/dist/vista/surfaces/ChartSurface.js +17 -0
  110. package/dist/vista/surfaces/ChartSurface.js.map +1 -0
  111. package/dist/vista/surfaces/FilesSurface.d.ts +15 -0
  112. package/dist/vista/surfaces/FilesSurface.d.ts.map +1 -0
  113. package/dist/vista/surfaces/FilesSurface.js +121 -0
  114. package/dist/vista/surfaces/FilesSurface.js.map +1 -0
  115. package/dist/vista/surfaces/FormSurface.d.ts +4 -0
  116. package/dist/vista/surfaces/FormSurface.d.ts.map +1 -0
  117. package/dist/vista/surfaces/FormSurface.js +97 -0
  118. package/dist/vista/surfaces/FormSurface.js.map +1 -0
  119. package/dist/vista/surfaces/HtmlSurface.d.ts +4 -0
  120. package/dist/vista/surfaces/HtmlSurface.d.ts.map +1 -0
  121. package/dist/vista/surfaces/HtmlSurface.js +11 -0
  122. package/dist/vista/surfaces/HtmlSurface.js.map +1 -0
  123. package/dist/vista/surfaces/TableSurface.d.ts +4 -0
  124. package/dist/vista/surfaces/TableSurface.d.ts.map +1 -0
  125. package/dist/vista/surfaces/TableSurface.js +42 -0
  126. package/dist/vista/surfaces/TableSurface.js.map +1 -0
  127. package/dist/vista/surfaces/UrlSurface.d.ts +4 -0
  128. package/dist/vista/surfaces/UrlSurface.d.ts.map +1 -0
  129. package/dist/vista/surfaces/UrlSurface.js +7 -0
  130. package/dist/vista/surfaces/UrlSurface.js.map +1 -0
  131. package/dist/vista/surfaces/blob-surfaces.d.ts +6 -0
  132. package/dist/vista/surfaces/blob-surfaces.d.ts.map +1 -0
  133. package/dist/vista/surfaces/blob-surfaces.js +38 -0
  134. package/dist/vista/surfaces/blob-surfaces.js.map +1 -0
  135. package/dist/vista/surfaces/elicitation-transport.d.ts +18 -0
  136. package/dist/vista/surfaces/elicitation-transport.d.ts.map +1 -0
  137. package/dist/vista/surfaces/elicitation-transport.js +100 -0
  138. package/dist/vista/surfaces/elicitation-transport.js.map +1 -0
  139. package/dist/vista/surfaces/index.d.ts +11 -0
  140. package/dist/vista/surfaces/index.d.ts.map +1 -0
  141. package/dist/vista/surfaces/index.js +89 -0
  142. package/dist/vista/surfaces/index.js.map +1 -0
  143. package/dist/vista/surfaces/text-surfaces.d.ts +8 -0
  144. package/dist/vista/surfaces/text-surfaces.d.ts.map +1 -0
  145. package/dist/vista/surfaces/text-surfaces.js +54 -0
  146. package/dist/vista/surfaces/text-surfaces.js.map +1 -0
  147. package/dist/vista/use-vista-tabs.d.ts +30 -0
  148. package/dist/vista/use-vista-tabs.d.ts.map +1 -0
  149. package/dist/vista/use-vista-tabs.js +77 -0
  150. package/dist/vista/use-vista-tabs.js.map +1 -0
  151. package/dist/vista/vista-code-renderer.d.ts +12 -0
  152. package/dist/vista/vista-code-renderer.d.ts.map +1 -0
  153. package/dist/vista/vista-code-renderer.js +10 -0
  154. package/dist/vista/vista-code-renderer.js.map +1 -0
  155. package/dist/vista/vista-file-source.d.ts +20 -0
  156. package/dist/vista/vista-file-source.d.ts.map +1 -0
  157. package/dist/vista/vista-file-source.js +69 -0
  158. package/dist/vista/vista-file-source.js.map +1 -0
  159. package/dist/vista/vista-form-host.d.ts +16 -0
  160. package/dist/vista/vista-form-host.d.ts.map +1 -0
  161. package/dist/vista/vista-form-host.js +10 -0
  162. package/dist/vista/vista-form-host.js.map +1 -0
  163. package/dist/vista/vista-model.d.ts +12 -0
  164. package/dist/vista/vista-model.d.ts.map +1 -0
  165. package/dist/vista/vista-model.js +72 -0
  166. package/dist/vista/vista-model.js.map +1 -0
  167. package/dist/vista/vista-open-context.d.ts +9 -0
  168. package/dist/vista/vista-open-context.d.ts.map +1 -0
  169. package/dist/vista/vista-open-context.js +10 -0
  170. package/dist/vista/vista-open-context.js.map +1 -0
  171. package/dist/vista/vista-payloads.d.ts +107 -0
  172. package/dist/vista/vista-payloads.d.ts.map +1 -0
  173. package/dist/vista/vista-payloads.js +305 -0
  174. package/dist/vista/vista-payloads.js.map +1 -0
  175. package/dist/vista/vista-tabs-manifest.d.ts +17 -0
  176. package/dist/vista/vista-tabs-manifest.d.ts.map +1 -0
  177. package/dist/vista/vista-tabs-manifest.js +60 -0
  178. package/dist/vista/vista-tabs-manifest.js.map +1 -0
  179. package/dist/vista/vista-tools.d.ts +41 -0
  180. package/dist/vista/vista-tools.d.ts.map +1 -0
  181. package/dist/vista/vista-tools.js +113 -0
  182. package/dist/vista/vista-tools.js.map +1 -0
  183. package/package.json +3 -3
  184. package/src/index.ts +5 -0
  185. package/src/lib/biome-host/biome-registry.ts +31 -11
  186. package/src/lib/contribution-registry/contribution-registry.spec.ts +141 -0
  187. package/src/lib/contribution-registry/contribution-registry.ts +202 -0
  188. package/src/lib/contribution-registry/index.ts +9 -0
  189. package/src/lib/contribution-registry/renderable-registry.ts +142 -0
  190. package/src/ui/capture/field-spec-to-zod.ts +22 -0
  191. package/src/ui/capture/field-spec-validate.ts +147 -0
  192. package/src/ui/capture/field-spec.ts +62 -0
  193. package/src/ui/capture/index.ts +9 -1
  194. package/src/ui/capture/schema-field-input.tsx +134 -14
  195. package/src/ui/capture/submit-values.test.ts +70 -0
  196. package/src/ui/capture/submit-values.ts +63 -0
  197. package/src/ui/index.ts +4 -0
  198. package/src/ui/primitives/overflow-tabs.tsx +24 -4
  199. package/src/ui/primitives/url-preview-chrome.tsx +102 -0
  200. package/src/vista/GenericCatalogueSurface.tsx +177 -0
  201. package/src/vista/SurfaceFallbackCard.tsx +109 -0
  202. package/src/vista/VistaPanel.tsx +155 -0
  203. package/src/vista/form-payload.spec.ts +93 -0
  204. package/src/vista/html/CustomUiFrame.spec.tsx +125 -0
  205. package/src/vista/html/CustomUiFrame.tsx +204 -0
  206. package/src/vista/html/custom-ui-frame-bridge.spec.ts +191 -0
  207. package/src/vista/html/custom-ui-frame-bridge.ts +92 -0
  208. package/src/vista/html/custom-ui-frame-contract.ts +88 -0
  209. package/src/vista/html/custom-ui-intents.spec.ts +133 -0
  210. package/src/vista/html/custom-ui-intents.ts +151 -0
  211. package/src/vista/html/custom-ui-srcdoc.spec.ts +151 -0
  212. package/src/vista/html/custom-ui-srcdoc.ts +193 -0
  213. package/src/vista/html/index.ts +13 -0
  214. package/src/vista/index.ts +48 -0
  215. package/src/vista/surface-catalogue.spec.ts +140 -0
  216. package/src/vista/surface-catalogue.ts +84 -0
  217. package/src/vista/surface-icons.ts +57 -0
  218. package/src/vista/surface-registry.ts +56 -0
  219. package/src/vista/surfaces/ChartSurface.tsx +34 -0
  220. package/src/vista/surfaces/FilesSurface.tsx +268 -0
  221. package/src/vista/surfaces/FormSurface.tsx +264 -0
  222. package/src/vista/surfaces/HtmlSurface.tsx +43 -0
  223. package/src/vista/surfaces/TableSurface.tsx +99 -0
  224. package/src/vista/surfaces/UrlSurface.tsx +40 -0
  225. package/src/vista/surfaces/blob-surfaces.tsx +129 -0
  226. package/src/vista/surfaces/elicitation-transport.spec.ts +187 -0
  227. package/src/vista/surfaces/elicitation-transport.ts +175 -0
  228. package/src/vista/surfaces/index.tsx +157 -0
  229. package/src/vista/surfaces/text-surfaces.tsx +119 -0
  230. package/src/vista/use-vista-tabs.ts +188 -0
  231. package/src/vista/vista-code-renderer.tsx +46 -0
  232. package/src/vista/vista-file-source.tsx +163 -0
  233. package/src/vista/vista-form-host.tsx +78 -0
  234. package/src/vista/vista-model.ts +130 -0
  235. package/src/vista/vista-open-context.tsx +42 -0
  236. package/src/vista/vista-payloads.ts +514 -0
  237. package/src/vista/vista-tabs-manifest.ts +123 -0
  238. package/src/vista/vista-tools.ts +220 -0
@@ -0,0 +1,78 @@
1
+ /**
2
+ * `VistaFormHost` — the ONE seam through which a `SurfaceKind.Form` surface
3
+ * gets the SUBJECT's bearer token and reports completion.
4
+ *
5
+ * ── Why this exists, and why the shapes are what they are ───────────────────
6
+ * The `form` surface is the secure-elicitation surface: an agent asks for a
7
+ * credential it must never see. The value therefore travels
8
+ * BROWSER → OWNING SERVICE, signed with the user's own JWT, and the agent
9
+ * learns only that the elicitation settled.
10
+ *
11
+ * That is enforced STRUCTURALLY, not by convention:
12
+ *
13
+ * 1. {@link VistaFormOutcome} is the ONLY thing the surface can report. It is
14
+ * a closed interface with three scalar members and NO `unknown`, no index
15
+ * signature, and no payload/values member. There is no shape a field value
16
+ * could be assigned to. Widening it is the one change that would break the
17
+ * guarantee — do not add a member that can hold arbitrary data.
18
+ * 2. `getAccessToken` returns the SUBJECT's token. The host binds it to the
19
+ * logged-in user's session; it is never a service token and never the
20
+ * agent's token, so the service authorizes the human, not the model.
21
+ * 3. The surface's transport posts to an ABSOLUTE service URL taken from the
22
+ * validated payload (see `validateFormPayload`, which rejects a relative
23
+ * URL). There is no host-relative path, so it cannot be pointed at the
24
+ * app's own BFF/chat transport.
25
+ *
26
+ * There is deliberately NO default host: a `form` surface mounted without a
27
+ * provider renders a visible refusal and cannot submit. Fail-closed — a form
28
+ * that silently posts nowhere would be worse than one that says it cannot.
29
+ */
30
+ import { createContext, useContext, type ReactElement, type ReactNode } from 'react';
31
+
32
+ /**
33
+ * Everything a settled elicitation reports. Closed, scalar-only, and
34
+ * value-identical to the service's `{ submitted, ref, connectionId }` — the
35
+ * persistence layer behind it has no column able to hold a field value, and
36
+ * neither does this type.
37
+ */
38
+ export interface VistaFormOutcome {
39
+ /** Always `true`: an unsettled elicitation produces no outcome at all. */
40
+ readonly submitted: true;
41
+ /** The elicitation reference the agent polls. Opaque, non-secret. */
42
+ readonly ref: string;
43
+ /** The connection the submission provisioned, when the service reported one. */
44
+ readonly connectionId: string | null;
45
+ }
46
+
47
+ /** Host-supplied capabilities for the `form` surface. */
48
+ export interface VistaFormHost {
49
+ /**
50
+ * The SUBJECT's own access token. Called once per request so a refreshed
51
+ * token is picked up without remounting the surface.
52
+ */
53
+ getAccessToken(): Promise<string>;
54
+ /**
55
+ * Called exactly once when the elicitation settles. The host relays this to
56
+ * whatever needs to know (cache invalidation, the agent session). By type it
57
+ * can carry nothing but the outcome.
58
+ */
59
+ onOutcome(outcome: VistaFormOutcome): void;
60
+ }
61
+
62
+ const VistaFormHostContext = createContext<VistaFormHost | null>(null);
63
+
64
+ export function VistaFormHostProvider({
65
+ host,
66
+ children,
67
+ }: Readonly<{ host: VistaFormHost; children: ReactNode }>): ReactElement {
68
+ return (
69
+ <VistaFormHostContext.Provider value={host}>
70
+ {children}
71
+ </VistaFormHostContext.Provider>
72
+ );
73
+ }
74
+
75
+ /** The active form host, or `null` when no provider is mounted. */
76
+ export function useVistaFormHost(): VistaFormHost | null {
77
+ return useContext(VistaFormHostContext);
78
+ }
@@ -0,0 +1,130 @@
1
+ /**
2
+ * The Vista tab model — ONE tab vocabulary for every preview plane.
3
+ *
4
+ * Before this module the frontend carried THREE overlapping tab models with no
5
+ * shared code:
6
+ * • `WorkspaceTabKind` (`files|code|md|diff|tests|url`) in the thin session
7
+ * workspace;
8
+ * • `PreviewTabKind` (`files|html|markdown|pdf|image|plain`) buried inside the
9
+ * Design-System-Builder page;
10
+ * • the App-Forge preview.
11
+ * All three collapse onto the kernel {@link SurfaceKind} (Canopy §8.5). A tab is
12
+ * just a {@link VistaSurface} envelope; the Vista registry resolves its `kind`
13
+ * to a renderer.
14
+ *
15
+ * Collapse notes (deliberate, documented decisions):
16
+ * • `md` → {@link SurfaceKind.Markdown}.
17
+ * • `plain` → {@link SurfaceKind.Code}. `Code` is "a source file", and the
18
+ * code renderer is a mono pane; a separate "plain text" kind would be the
19
+ * same renderer under a second name.
20
+ * • `tests` had no kernel equivalent and was never a *render* kind — a test
21
+ * report is a biome-owned surface, so it becomes a biome-contributed
22
+ * `<biomeId>:tests` kind rather than a first-party one. Nothing in the tree
23
+ * rendered it (the thin workspace's `renderTests` slot was never supplied).
24
+ */
25
+ import { SurfaceKind, type VistaSurface } from '@xemahq/kernel-contracts/surface';
26
+
27
+ /**
28
+ * Id of the pinned, UI-owned file-browser tab. It is owned by the panel, never
29
+ * declared by an agent, and never closeable.
30
+ */
31
+ export const VISTA_FILES_TAB_ID = 'files';
32
+
33
+ /** Current payload schema version of every first-party surface kind. */
34
+ export const VISTA_SURFACE_VERSION = 1;
35
+
36
+ /**
37
+ * A Vista tab. Structurally a {@link VistaSurface} — the tab model and the
38
+ * agent-facing envelope are the SAME shape on purpose, so `vista_open` needs no
39
+ * translation layer.
40
+ */
41
+ export type VistaTab = VistaSurface;
42
+
43
+ /** Extension → first-party surface kind. Closed, deterministic mapping. */
44
+ const EXTENSION_KIND: Readonly<Record<string, SurfaceKind>> = {
45
+ html: SurfaceKind.Html,
46
+ htm: SurfaceKind.Html,
47
+ md: SurfaceKind.Markdown,
48
+ mdx: SurfaceKind.Markdown,
49
+ markdown: SurfaceKind.Markdown,
50
+ pdf: SurfaceKind.Pdf,
51
+ png: SurfaceKind.Image,
52
+ jpg: SurfaceKind.Image,
53
+ jpeg: SurfaceKind.Image,
54
+ gif: SurfaceKind.Image,
55
+ webp: SurfaceKind.Image,
56
+ svg: SurfaceKind.Image,
57
+ avif: SurfaceKind.Image,
58
+ mp4: SurfaceKind.Media,
59
+ webm: SurfaceKind.Media,
60
+ mov: SurfaceKind.Media,
61
+ mp3: SurfaceKind.Media,
62
+ wav: SurfaceKind.Media,
63
+ ogg: SurfaceKind.Media,
64
+ m4a: SurfaceKind.Media,
65
+ json: SurfaceKind.Json,
66
+ diff: SurfaceKind.Diff,
67
+ patch: SurfaceKind.Diff,
68
+ };
69
+
70
+ /**
71
+ * Resolve the surface kind a file path renders as. Deterministic and total:
72
+ * anything unrecognised is {@link SurfaceKind.Code} (a mono text pane), which is
73
+ * always a correct — if plain — rendering of a file's bytes.
74
+ */
75
+ export function surfaceKindForPath(path: string): SurfaceKind {
76
+ const dot = path.lastIndexOf('.');
77
+ if (dot === -1) return SurfaceKind.Code;
78
+ const ext = path.slice(dot + 1).toLowerCase();
79
+ return EXTENSION_KIND[ext] ?? SurfaceKind.Code;
80
+ }
81
+
82
+ /** The `content-type` a media/blob surface reports for a path, when derivable. */
83
+ const EXTENSION_CONTENT_TYPE: Readonly<Record<string, string>> = {
84
+ png: 'image/png',
85
+ jpg: 'image/jpeg',
86
+ jpeg: 'image/jpeg',
87
+ gif: 'image/gif',
88
+ webp: 'image/webp',
89
+ svg: 'image/svg+xml',
90
+ avif: 'image/avif',
91
+ pdf: 'application/pdf',
92
+ mp4: 'video/mp4',
93
+ webm: 'video/webm',
94
+ mov: 'video/quicktime',
95
+ mp3: 'audio/mpeg',
96
+ wav: 'audio/wav',
97
+ ogg: 'audio/ogg',
98
+ m4a: 'audio/mp4',
99
+ };
100
+
101
+ /** Content type for a path, or `null` when the extension is unknown. */
102
+ export function contentTypeForPath(path: string): string | null {
103
+ const dot = path.lastIndexOf('.');
104
+ if (dot === -1) return null;
105
+ return EXTENSION_CONTENT_TYPE[path.slice(dot + 1).toLowerCase()] ?? null;
106
+ }
107
+
108
+ /** The last path segment — the default tab label for a file-backed surface. */
109
+ export function labelForPath(path: string): string {
110
+ const segments = path.split('/');
111
+ return segments[segments.length - 1] || path;
112
+ }
113
+
114
+ /**
115
+ * Build a file-backed surface envelope for a path. One seam so every caller
116
+ * (the `.tabs.json` parser, the file browser, `vista_open`) derives kind + label
117
+ * + tab id identically.
118
+ */
119
+ export function fileSurface(
120
+ path: string,
121
+ overrides?: Readonly<{ title?: string; kind?: SurfaceKind }>,
122
+ ): VistaTab {
123
+ return {
124
+ tabId: `file:${path}`,
125
+ kind: overrides?.kind ?? surfaceKindForPath(path),
126
+ version: VISTA_SURFACE_VERSION,
127
+ title: overrides?.title ?? labelForPath(path),
128
+ payload: { path },
129
+ };
130
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * `VistaOpenContext` — how a surface asks the panel to open ANOTHER surface.
3
+ *
4
+ * The registry's `render(payload)` is a pure function, so a renderer cannot
5
+ * receive an `onOpen` prop. The panel publishes its `open` through this context
6
+ * instead: the file browser opens a file tab, an object view opens a related
7
+ * object, and so on — all through the SAME `vista_open` semantics the agent
8
+ * uses, never a second code path.
9
+ *
10
+ * A surface mounted without a provider simply renders non-interactive rows
11
+ * (`useVistaOpen()` returns `null`) — an inert list, never a dead click that
12
+ * silently does nothing.
13
+ */
14
+ import {
15
+ createContext,
16
+ useContext,
17
+ type ReactElement,
18
+ type ReactNode,
19
+ } from 'react';
20
+
21
+ import type { VistaTab } from './vista-model';
22
+
23
+ /** Open (or replace) a tab and focus it. Mirrors `VistaTabsController.open`. */
24
+ export type VistaOpenFn = (surface: VistaTab) => string;
25
+
26
+ const VistaOpenContext = createContext<VistaOpenFn | null>(null);
27
+
28
+ export function VistaOpenProvider({
29
+ open,
30
+ children,
31
+ }: Readonly<{ open: VistaOpenFn; children: ReactNode }>): ReactElement {
32
+ return (
33
+ <VistaOpenContext.Provider value={open}>
34
+ {children}
35
+ </VistaOpenContext.Provider>
36
+ );
37
+ }
38
+
39
+ /** The panel's `open`, or `null` when no panel is mounted above. */
40
+ export function useVistaOpen(): VistaOpenFn | null {
41
+ return useContext(VistaOpenContext);
42
+ }