@xemahq/ui-kernel 0.11.0 → 0.11.2

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 (237) hide show
  1. package/dist/ui/chrome/page-shell.d.ts +1 -1
  2. package/dist/ui/chrome/page-shell.d.ts.map +1 -1
  3. package/dist/ui/chrome/page-shell.js +9 -4
  4. package/dist/ui/chrome/page-shell.js.map +1 -1
  5. package/package.json +6 -4
  6. package/src/index.ts +0 -17
  7. package/src/lib/biome-host/agent-validation.ts +0 -239
  8. package/src/lib/biome-host/biome-builders.ts +0 -112
  9. package/src/lib/biome-host/biome-mode.ts +0 -6
  10. package/src/lib/biome-host/biome-navigation.ts +0 -37
  11. package/src/lib/biome-host/biome-registry.ts +0 -299
  12. package/src/lib/biome-host/biome-scope-hooks.ts +0 -41
  13. package/src/lib/biome-host/biome-scope.tsx +0 -52
  14. package/src/lib/biome-host/biome-scoped-query.ts +0 -134
  15. package/src/lib/biome-host/create-biome-orval-config.ts +0 -82
  16. package/src/lib/biome-host/define-web-biome.spec.ts +0 -221
  17. package/src/lib/biome-host/define-web-biome.ts +0 -222
  18. package/src/lib/biome-host/errors.ts +0 -220
  19. package/src/lib/biome-host/frontend-biome.ts +0 -256
  20. package/src/lib/biome-host/host-bridge.ts +0 -281
  21. package/src/lib/biome-host/host-sources.ts +0 -54
  22. package/src/lib/biome-host/i18n-hooks.ts +0 -51
  23. package/src/lib/biome-host/i18n-port.test.ts +0 -50
  24. package/src/lib/biome-host/i18n-port.ts +0 -96
  25. package/src/lib/biome-host/index.ts +0 -35
  26. package/src/lib/biome-host/realtime-hooks.ts +0 -75
  27. package/src/lib/biome-host/realtime-port.ts +0 -115
  28. package/src/lib/biome-host/session-contributions.ts +0 -296
  29. package/src/lib/biome-host/use-mutation-with-error-toast.ts +0 -90
  30. package/src/lib/biome-host/use-page-state.ts +0 -248
  31. package/src/lib/capabilities/capability-context.ts +0 -25
  32. package/src/lib/capabilities/capability-provider.tsx +0 -73
  33. package/src/lib/capabilities/index.ts +0 -17
  34. package/src/lib/capabilities/types.ts +0 -69
  35. package/src/lib/capabilities/use-capability.ts +0 -79
  36. package/src/lib/contribution-registry/contribution-registry.spec.ts +0 -141
  37. package/src/lib/contribution-registry/contribution-registry.ts +0 -204
  38. package/src/lib/contribution-registry/index.ts +0 -9
  39. package/src/lib/contribution-registry/renderable-registry.ts +0 -142
  40. package/src/lib/system-bus/capability-invoke.ts +0 -92
  41. package/src/lib/system-bus/deeplink.ts +0 -210
  42. package/src/lib/system-bus/enums.ts +0 -86
  43. package/src/lib/system-bus/host-ports.ts +0 -93
  44. package/src/lib/system-bus/index.ts +0 -16
  45. package/src/lib/system-bus/intent-registry.ts +0 -109
  46. package/src/lib/system-bus/intents.ts +0 -113
  47. package/src/lib/system-bus/palette.ts +0 -78
  48. package/src/lib/system-bus/system-bus-builder.ts +0 -159
  49. package/src/lib/system-bus/system-bus.ts +0 -37
  50. package/src/lib/system-bus/windows.ts +0 -51
  51. package/src/offline/id.ts +0 -16
  52. package/src/offline/index.ts +0 -54
  53. package/src/offline/mutation.ts +0 -75
  54. package/src/offline/offline-status-badge.tsx +0 -51
  55. package/src/offline/online-controller.ts +0 -105
  56. package/src/offline/outbox.test.ts +0 -172
  57. package/src/offline/outbox.ts +0 -348
  58. package/src/offline/react-hooks.ts +0 -40
  59. package/src/offline/react.tsx +0 -32
  60. package/src/offline/storage.ts +0 -112
  61. package/src/offline/types.ts +0 -77
  62. package/src/org-db/components/DbResultTable.tsx +0 -176
  63. package/src/org-db/index.ts +0 -1
  64. package/src/pdf/index.ts +0 -11
  65. package/src/registry/index.ts +0 -9
  66. package/src/registry/lib/agent-validation-host.ts +0 -36
  67. package/src/registry/lib/biome-slot.tsx +0 -50
  68. package/src/registry/lib/biomes-enabled-context.ts +0 -20
  69. package/src/registry/lib/extension-points.ts +0 -134
  70. package/src/registry/lib/primitives/SessionEventCard.tsx +0 -164
  71. package/src/registry/lib/primitives/SessionEventTimeline.tsx +0 -95
  72. package/src/registry/lib/primitives/SessionMutationBar.tsx +0 -76
  73. package/src/registry/lib/primitives/SessionTabbedDrawer.tsx +0 -165
  74. package/src/registry/lib/primitives/index.ts +0 -18
  75. package/src/registry/lib/session-context-builder.ts +0 -84
  76. package/src/registry/lib/session-context-provider.tsx +0 -33
  77. package/src/registry/lib/session-context.ts +0 -21
  78. package/src/registry/lib/session-selectors.ts +0 -272
  79. package/src/session/comments/CommentRail.tsx +0 -164
  80. package/src/session/index.ts +0 -3
  81. package/src/session/lib/cn.ts +0 -11
  82. package/src/session/shell/SessionWorkspaceShell.tsx +0 -147
  83. package/src/session-kit/approvals/ApprovalButton.tsx +0 -89
  84. package/src/session-kit/approvals/ApprovalCard.tsx +0 -341
  85. package/src/session-kit/approvals/ApprovalsCenter.tsx +0 -336
  86. package/src/session-kit/approvals/CapabilityApprovalStickyBar.tsx +0 -118
  87. package/src/session-kit/approvals/Obligation.tsx +0 -111
  88. package/src/session-kit/approvals/ScopedBody.tsx +0 -392
  89. package/src/session-kit/approvals/approval-icons.ts +0 -31
  90. package/src/session-kit/approvals/approval-model.ts +0 -204
  91. package/src/session-kit/approvals/index.ts +0 -22
  92. package/src/session-kit/approvals/obligation-display.ts +0 -100
  93. package/src/session-kit/approvals/risk-accent.ts +0 -47
  94. package/src/session-kit/approvals/scope-icons.ts +0 -19
  95. package/src/session-kit/combobox/Combobox.tsx +0 -358
  96. package/src/session-kit/combobox/use-click-outside.ts +0 -21
  97. package/src/session-kit/display/ContextHeader.tsx +0 -148
  98. package/src/session-kit/display/FileDiffCard.tsx +0 -140
  99. package/src/session-kit/display/MD.tsx +0 -162
  100. package/src/session-kit/display/MessageTurn.tsx +0 -157
  101. package/src/session-kit/display/ThinkingPanel.tsx +0 -81
  102. package/src/session-kit/display/TodoChecklist.tsx +0 -120
  103. package/src/session-kit/display/TokenMeter.tsx +0 -89
  104. package/src/session-kit/display/ToolStrip.tsx +0 -278
  105. package/src/session-kit/display/TypingDots.tsx +0 -24
  106. package/src/session-kit/index.ts +0 -44
  107. package/src/session-kit/lib/enums.ts +0 -66
  108. package/src/session-kit/lib/portal-accent.ts +0 -30
  109. package/src/session-kit/lib/status-dot.ts +0 -68
  110. package/src/session-kit/primitives/Avatar.tsx +0 -44
  111. package/src/session-kit/primitives/PortalGlyph.tsx +0 -51
  112. package/src/session-kit/primitives/RiskPill.tsx +0 -95
  113. package/src/session-kit/primitives/ScopeDot.tsx +0 -47
  114. package/src/session-kit/primitives/Segmented.tsx +0 -71
  115. package/src/ui/branding/app-branding-context.ts +0 -20
  116. package/src/ui/branding/app-branding-provider.tsx +0 -123
  117. package/src/ui/branding/sanitize.ts +0 -89
  118. package/src/ui/breakpoints.ts +0 -14
  119. package/src/ui/capture/document-preview.tsx +0 -166
  120. package/src/ui/capture/field-spec-to-zod.test.ts +0 -81
  121. package/src/ui/capture/field-spec-to-zod.ts +0 -174
  122. package/src/ui/capture/field-spec-validate.ts +0 -236
  123. package/src/ui/capture/field-spec.test.ts +0 -49
  124. package/src/ui/capture/field-spec.ts +0 -274
  125. package/src/ui/capture/index.ts +0 -51
  126. package/src/ui/capture/schema-field-input.tsx +0 -620
  127. package/src/ui/capture/schema-form.tsx +0 -149
  128. package/src/ui/capture/submit-values.test.ts +0 -70
  129. package/src/ui/capture/submit-values.ts +0 -63
  130. package/src/ui/capture/use-prefill.ts +0 -186
  131. package/src/ui/capture/use-schema-validation.ts +0 -145
  132. package/src/ui/capture/use-wizard.ts +0 -180
  133. package/src/ui/capture/value-format.ts +0 -32
  134. package/src/ui/capture/wizard.tsx +0 -105
  135. package/src/ui/charts/bar-chart.tsx +0 -139
  136. package/src/ui/charts/chart-frame.tsx +0 -125
  137. package/src/ui/charts/chart-types.ts +0 -52
  138. package/src/ui/charts/chart-utils.ts +0 -190
  139. package/src/ui/charts/charts.test.tsx +0 -204
  140. package/src/ui/charts/donut-chart.tsx +0 -129
  141. package/src/ui/charts/index.ts +0 -27
  142. package/src/ui/charts/line-chart.tsx +0 -202
  143. package/src/ui/charts/sparkline.tsx +0 -109
  144. package/src/ui/chrome/AsyncBoundary.tsx +0 -74
  145. package/src/ui/chrome/EmptyState.tsx +0 -257
  146. package/src/ui/chrome/ErrorCard.tsx +0 -63
  147. package/src/ui/chrome/LoadingState.tsx +0 -61
  148. package/src/ui/chrome/PageHeader.tsx +0 -137
  149. package/src/ui/chrome/RecordCardList.tsx +0 -98
  150. package/src/ui/chrome/StateCard.tsx +0 -150
  151. package/src/ui/chrome/confirm-delete-dialog.tsx +0 -59
  152. package/src/ui/chrome/page-shell.tsx +0 -189
  153. package/src/ui/chrome/scope-badge.tsx +0 -40
  154. package/src/ui/chrome/status-badge.tsx +0 -79
  155. package/src/ui/cn.ts +0 -32
  156. package/src/ui/design-tokens.ts +0 -372
  157. package/src/ui/hooks/use-debounced-value.ts +0 -16
  158. package/src/ui/hooks/use-is-mobile.ts +0 -28
  159. package/src/ui/index.ts +0 -110
  160. package/src/ui/pdf/pdf-preview.tsx +0 -122
  161. package/src/ui/primitives/alert-dialog.tsx +0 -104
  162. package/src/ui/primitives/async-combobox.tsx +0 -182
  163. package/src/ui/primitives/badge-variants.ts +0 -21
  164. package/src/ui/primitives/badge.tsx +0 -25
  165. package/src/ui/primitives/button-variants.ts +0 -31
  166. package/src/ui/primitives/button.tsx +0 -29
  167. package/src/ui/primitives/card.tsx +0 -43
  168. package/src/ui/primitives/checkbox.tsx +0 -26
  169. package/src/ui/primitives/collapsible.tsx +0 -9
  170. package/src/ui/primitives/dialog.tsx +0 -103
  171. package/src/ui/primitives/dropdown-menu.tsx +0 -179
  172. package/src/ui/primitives/form-stepper.tsx +0 -94
  173. package/src/ui/primitives/form-stepper.types.ts +0 -15
  174. package/src/ui/primitives/input.tsx +0 -22
  175. package/src/ui/primitives/label.tsx +0 -17
  176. package/src/ui/primitives/overflow-tabs.tsx +0 -331
  177. package/src/ui/primitives/popover.tsx +0 -33
  178. package/src/ui/primitives/preview-device-frame.tsx +0 -213
  179. package/src/ui/primitives/radio-group.tsx +0 -36
  180. package/src/ui/primitives/resizable.tsx +0 -67
  181. package/src/ui/primitives/scroll-area.tsx +0 -38
  182. package/src/ui/primitives/select.tsx +0 -143
  183. package/src/ui/primitives/separator.tsx +0 -20
  184. package/src/ui/primitives/sheet.tsx +0 -107
  185. package/src/ui/primitives/skeleton.tsx +0 -99
  186. package/src/ui/primitives/switch.tsx +0 -27
  187. package/src/ui/primitives/table.tsx +0 -72
  188. package/src/ui/primitives/tabs.tsx +0 -53
  189. package/src/ui/primitives/tag-multi-select.tsx +0 -261
  190. package/src/ui/primitives/textarea.tsx +0 -21
  191. package/src/ui/primitives/tooltip.tsx +0 -30
  192. package/src/ui/primitives/url-preview-chrome.tsx +0 -103
  193. package/src/vista/GenericCatalogueSurface.tsx +0 -179
  194. package/src/vista/SurfaceFallbackCard.tsx +0 -109
  195. package/src/vista/VistaPanel.tsx +0 -163
  196. package/src/vista/form-payload.spec.ts +0 -139
  197. package/src/vista/html/CustomUiFrame.spec.tsx +0 -127
  198. package/src/vista/html/CustomUiFrame.tsx +0 -206
  199. package/src/vista/html/custom-ui-frame-bridge.spec.ts +0 -191
  200. package/src/vista/html/custom-ui-frame-bridge.ts +0 -98
  201. package/src/vista/html/custom-ui-frame-contract.ts +0 -88
  202. package/src/vista/html/custom-ui-intents.spec.ts +0 -135
  203. package/src/vista/html/custom-ui-intents.ts +0 -155
  204. package/src/vista/html/custom-ui-srcdoc.spec.ts +0 -151
  205. package/src/vista/html/custom-ui-srcdoc.ts +0 -193
  206. package/src/vista/html/index.ts +0 -13
  207. package/src/vista/index.ts +0 -52
  208. package/src/vista/surface-catalogue.spec.ts +0 -140
  209. package/src/vista/surface-catalogue.ts +0 -90
  210. package/src/vista/surface-icons.ts +0 -57
  211. package/src/vista/surface-registry.ts +0 -56
  212. package/src/vista/surfaces/ChartSurface.tsx +0 -34
  213. package/src/vista/surfaces/FilesSurface.tsx +0 -202
  214. package/src/vista/surfaces/FormSurface.tsx +0 -380
  215. package/src/vista/surfaces/HtmlSurface.tsx +0 -43
  216. package/src/vista/surfaces/TableSurface.tsx +0 -100
  217. package/src/vista/surfaces/UrlSurface.tsx +0 -40
  218. package/src/vista/surfaces/blob-surfaces.tsx +0 -139
  219. package/src/vista/surfaces/elicitation-transport.spec.ts +0 -369
  220. package/src/vista/surfaces/elicitation-transport.ts +0 -367
  221. package/src/vista/surfaces/file-surface-utils.ts +0 -69
  222. package/src/vista/surfaces/index.ts +0 -24
  223. package/src/vista/surfaces/register-surfaces.tsx +0 -139
  224. package/src/vista/surfaces/text-surfaces.tsx +0 -132
  225. package/src/vista/use-vista-tabs.ts +0 -196
  226. package/src/vista/vista-code-renderer-context.ts +0 -16
  227. package/src/vista/vista-code-renderer.tsx +0 -35
  228. package/src/vista/vista-file-context.ts +0 -14
  229. package/src/vista/vista-file-source.tsx +0 -141
  230. package/src/vista/vista-form-context.ts +0 -20
  231. package/src/vista/vista-form-host.tsx +0 -48
  232. package/src/vista/vista-model.ts +0 -137
  233. package/src/vista/vista-open-context.tsx +0 -27
  234. package/src/vista/vista-open-state.ts +0 -11
  235. package/src/vista/vista-payloads.ts +0 -635
  236. package/src/vista/vista-tabs-manifest.ts +0 -138
  237. package/src/vista/vista-tools.ts +0 -232
@@ -1,78 +0,0 @@
1
- import { type PaletteEntrySource } from './enums';
2
-
3
- import type { CapabilityRef } from '@xemahq/kernel-contracts/capability';
4
-
5
- /**
6
- * Command-palette plane of the SystemBus.
7
- *
8
- * The palette is the UNION of two sources (tagged by
9
- * {@link PaletteEntrySource}):
10
- * 1. every capability the actor may invoke — sourced from
11
- * `capability-registry-api` (the host's capability-registry client);
12
- * 2. every registered cross-biome intent action — sourced from the
13
- * SystemBus `intents` registry.
14
- *
15
- * The palette performs NO authorization. The capability source is already
16
- * actor-scoped server-side (the registry returns only capabilities the
17
- * subject can see); selecting an entry routes through `capability.invoke`,
18
- * where the backend router makes the final allow/deny decision. The
19
- * palette is pure presentation orchestration.
20
- */
21
-
22
- /** A palette entry backed by a capability descriptor. */
23
- export interface CapabilityPaletteEntry {
24
- readonly source: PaletteEntrySource.Capability;
25
- /** Stable id (the capability ref doubles as the id). */
26
- readonly id: string;
27
- /** Human label rendered in the palette row. */
28
- readonly label: string;
29
- /** Optional one-line description from the capability descriptor. */
30
- readonly summary?: string;
31
- /** The capability the entry invokes when selected. */
32
- readonly capabilityRef: CapabilityRef;
33
- }
34
-
35
- /** A palette entry backed by a registered cross-biome intent. */
36
- export interface IntentPaletteEntry {
37
- readonly source: PaletteEntrySource.Intent;
38
- /** Stable id, unique across registered intents. */
39
- readonly id: string;
40
- /** Human label rendered in the palette row. */
41
- readonly label: string;
42
- /** Biome that provides the intent action. */
43
- readonly biomeId: string;
44
- }
45
-
46
- /** Discriminated union of palette entries. */
47
- export type PaletteEntry = CapabilityPaletteEntry | IntentPaletteEntry;
48
-
49
- /**
50
- * Query against the palette. `text` is matched by the host's own ranker
51
- * (the contract does not prescribe a fuzzy algorithm). `sources` narrows
52
- * to a subset of {@link PaletteEntrySource}; omitted means "all sources".
53
- */
54
- export interface PaletteQuery {
55
- readonly text: string;
56
- readonly sources?: readonly PaletteEntrySource[];
57
- }
58
-
59
- /**
60
- * The `palette` surface of the SystemBus. Framework-agnostic — no React.
61
- * The host wires a capability-registry client + the SystemBus intent
62
- * registry behind `query`. `subscribe` lets a palette UI re-render when
63
- * either source changes (a biome registers an intent, the registry
64
- * refreshes).
65
- */
66
- export interface PaletteBus {
67
- /**
68
- * Resolve palette entries matching `query`, ranked by the host. Async
69
- * because the capability source may hit `capability-registry-api`.
70
- */
71
- query(query: PaletteQuery): Promise<readonly PaletteEntry[]>;
72
- /**
73
- * Subscribe to palette-source changes. Returns an unsubscribe fn. Fires
74
- * when a biome registers/unregisters an intent or the capability source
75
- * is refreshed — so a palette overlay can re-query without polling.
76
- */
77
- subscribe(listener: () => void): () => void;
78
- }
@@ -1,159 +0,0 @@
1
- import {
2
- parseDeeplink,
3
- type DeeplinkBus,
4
- type ParsedDeeplink,
5
- } from './deeplink';
6
- import { PaletteEntrySource } from './enums';
7
- import {
8
- type CapabilityInvoker,
9
- type DeeplinkResolver,
10
- type PaletteSource,
11
- type WindowManager,
12
- } from './host-ports';
13
- import { IntentRegistry } from './intent-registry';
14
- import {
15
- type IntentPaletteEntry,
16
- type PaletteBus,
17
- type PaletteEntry,
18
- type PaletteQuery,
19
- } from './palette';
20
-
21
- import type { SystemBus } from './system-bus';
22
-
23
- /**
24
- * Host-supplied ports needed to build a real {@link SystemBus}. The kernel
25
- * ships the framework-agnostic pieces (`IntentRegistry`, `parseDeeplink`);
26
- * the host supplies the concrete backend + shell behind these ports. NONE
27
- * are optional — a missing port is a wiring bug and `buildSystemBus` fails
28
- * fast.
29
- */
30
- export interface SystemBusPorts {
31
- /** Relays `capability.invoke` to the backend capability-router. */
32
- readonly capability: CapabilityInvoker;
33
- /** Sources the capability half of the palette from the registry. */
34
- readonly palette: PaletteSource;
35
- /** Realises the window plane (open / focus / close / arrange). */
36
- readonly windows: WindowManager;
37
- /** Navigates the running shell to a parsed deeplink target. */
38
- readonly deeplink: DeeplinkResolver;
39
- }
40
-
41
- function assertPort<T>(value: T | undefined, name: string): T {
42
- if (value === undefined || value === null) {
43
- throw new Error(
44
- `[xema-ui-kernel] SystemBus port "${name}" was not supplied. ` +
45
- 'The host MUST provide every SystemBus port (capability / palette / ' +
46
- 'windows / deeplink) — there is no silent no-op fallback.',
47
- );
48
- }
49
- return value;
50
- }
51
-
52
- /**
53
- * Build the palette bus by unioning the host's capability source with the
54
- * SystemBus intent registry. The capability half is already actor-scoped
55
- * server-side; the intent half is flattened from every registration's
56
- * provided actions. The host's `PaletteSource.queryCapabilities` applies
57
- * its own ranking to the capability rows; intent rows are filtered here by
58
- * a case-insensitive label/biome substring match (the contract leaves the
59
- * ranker to the host — substring is the deterministic floor).
60
- *
61
- * Pure orchestration: no authorization. Selecting a capability entry
62
- * routes through `capability.invoke` where the backend decides allow/deny.
63
- */
64
- function buildPaletteBus(
65
- capabilitySource: PaletteSource,
66
- intents: IntentRegistry,
67
- ): PaletteBus {
68
- function intentEntries(query: PaletteQuery): IntentPaletteEntry[] {
69
- if (query.sources && !query.sources.includes(PaletteEntrySource.Intent)) {
70
- return [];
71
- }
72
- const needle = query.text.trim().toLowerCase();
73
- const out: IntentPaletteEntry[] = [];
74
- // The IntentRegistry exposes registrations only through `resolve(kind)`;
75
- // for a palette we want every provided action regardless of kind, so we
76
- // read the public snapshot the registry exposes via `snapshot()`.
77
- for (const registration of intents.snapshot()) {
78
- for (const action of registration.provides) {
79
- if (
80
- needle.length > 0 &&
81
- !action.label.toLowerCase().includes(needle) &&
82
- !registration.biomeId.toLowerCase().includes(needle)
83
- ) {
84
- continue;
85
- }
86
- out.push({
87
- source: PaletteEntrySource.Intent,
88
- id: `${registration.biomeId}:${action.id}`,
89
- label: action.label,
90
- biomeId: registration.biomeId,
91
- });
92
- }
93
- }
94
- return out;
95
- }
96
-
97
- return {
98
- async query(query: PaletteQuery): Promise<readonly PaletteEntry[]> {
99
- const wantsCapabilities =
100
- !query.sources || query.sources.includes(PaletteEntrySource.Capability);
101
- const capabilityEntries = wantsCapabilities
102
- ? await capabilitySource.queryCapabilities(query)
103
- : [];
104
- return [...capabilityEntries, ...intentEntries(query)];
105
- },
106
- subscribe(listener: () => void): () => void {
107
- const offCapabilities = capabilitySource.subscribe(listener);
108
- const offIntents = intents.subscribe(listener);
109
- return () => {
110
- offCapabilities();
111
- offIntents();
112
- };
113
- },
114
- };
115
- }
116
-
117
- function buildDeeplinkBus(resolver: DeeplinkResolver): DeeplinkBus {
118
- return {
119
- parse(raw: string): ParsedDeeplink {
120
- return parseDeeplink(raw);
121
- },
122
- async resolve(target: string | ParsedDeeplink): Promise<void> {
123
- // Parse-first so a malformed deeplink fails fast in the kernel parser
124
- // before the host navigation ever runs.
125
- const parsed =
126
- typeof target === 'string' ? parseDeeplink(target) : target;
127
- await resolver.resolve(parsed);
128
- },
129
- };
130
- }
131
-
132
- /**
133
- * Construct the real {@link SystemBus} from the host's ports plus the
134
- * kernel's framework-agnostic runtime (`IntentRegistry` + `parseDeeplink`).
135
- * The `intents` registry is owned by the bus instance so biome
136
- * registrations and palette intent rows share one source of truth.
137
- *
138
- * Fail-fast: every port is required. No port may be silently stubbed. This
139
- * is the framework-agnostic builder — any host adapter (Next.js, React-
140
- * Router, …) wires its concrete ports and calls this.
141
- */
142
- export function buildSystemBus(ports: SystemBusPorts): SystemBus {
143
- const capability = assertPort(ports.capability, 'capability');
144
- const paletteSource = assertPort(ports.palette, 'palette');
145
- const windows = assertPort(ports.windows, 'windows');
146
- const deeplinkResolver = assertPort(ports.deeplink, 'deeplink');
147
-
148
- // Concrete type (not the `IntentBus` interface): the palette builder
149
- // reads `intents.snapshot()`, which is an `IntentRegistry` affordance.
150
- const intents: IntentRegistry = new IntentRegistry();
151
-
152
- return {
153
- capability,
154
- intents,
155
- palette: buildPaletteBus(paletteSource, intents),
156
- deeplink: buildDeeplinkBus(deeplinkResolver),
157
- windows,
158
- };
159
- }
@@ -1,37 +0,0 @@
1
- import type { CapabilityBus } from './capability-invoke';
2
- import type { DeeplinkBus } from './deeplink';
3
- import type { IntentBus } from './intents';
4
- import type { PaletteBus } from './palette';
5
- import type { WindowBus } from './windows';
6
-
7
- /**
8
- * `SystemBus` — the framework-agnostic OS-orchestration plane every
9
- * frontend biome reaches through its {@link HostBridge}.
10
- *
11
- * **Pure orchestration. Zero policy.** The SystemBus routes intent,
12
- * navigation, capability invocation, palette, and windowing. It NEVER
13
- * authorizes, denies, or duplicates policy. Every `capability.invoke`
14
- * routes through the backend capability-router, which is the single
15
- * authority on auth / tenancy / policy / audit. The SystemBus's job is to
16
- * carry orchestration metadata faithfully and relay the router's outcome —
17
- * a denial surfaces as an observable result (`output === undefined` +
18
- * `auditId`), never a silent swallow.
19
- *
20
- * The contract is framework-agnostic on purpose: it imports no Vite, no
21
- * Next.js, no React-Router, and no generated transport client. A concrete
22
- * host (Vite SPA today, Next.js tomorrow) implements each sub-bus against
23
- * its router, its capability-router client, and its window manager. Biome
24
- * code is identical across hosts.
25
- */
26
- export interface SystemBus {
27
- /** Capability invocation → backend router (backend enforces; bus adds none). */
28
- readonly capability: CapabilityBus;
29
- /** Cross-biome "Open with… / Send to…" intent registry. */
30
- readonly intents: IntentBus;
31
- /** Command palette = capabilities ∪ registered intents. */
32
- readonly palette: PaletteBus;
33
- /** Typed `xema://` deeplink parse + resolve. */
34
- readonly deeplink: DeeplinkBus;
35
- /** Host window manager (open / focus / close / arrange). */
36
- readonly windows: WindowBus;
37
- }
@@ -1,51 +0,0 @@
1
- import { type WindowArrangement, WindowOp } from './enums';
2
-
3
- /**
4
- * Window-management plane of the SystemBus.
5
- *
6
- * A "window" is a host-managed surface hosting a biome route or object.
7
- * The contract is framework-agnostic: the host (desktop shell, tiling WM,
8
- * tabbed SPA) decides how a window is physically realised, but every host
9
- * exposes the same closed {@link WindowOp} verb set. The SystemBus carries
10
- * no authorization here — windowing is pure shell orchestration.
11
- */
12
-
13
- /** Identifier of an open window, minted by the host on `open`. */
14
- export type WindowId = string;
15
-
16
- /** A request to open a new window onto a deeplink target. */
17
- export interface OpenWindowRequest {
18
- /** The `xema://` deeplink the new window should display. */
19
- readonly target: string;
20
- /** Optional human title; the host falls back to the target's own title. */
21
- readonly title?: string;
22
- }
23
-
24
- /** Snapshot of an open window. */
25
- export interface WindowState {
26
- readonly id: WindowId;
27
- readonly target: string;
28
- readonly title: string;
29
- readonly focused: boolean;
30
- }
31
-
32
- /**
33
- * The `windows` surface of the SystemBus. Each method maps to a closed
34
- * {@link WindowOp}. Operations a host cannot honour reject fail-fast
35
- * rather than silently no-op, so callers never assume a window exists
36
- * when it does not.
37
- */
38
- export interface WindowBus {
39
- /** {@link WindowOp.Open} — open a new window; resolves with its id. */
40
- open(request: OpenWindowRequest): Promise<WindowId>;
41
- /** {@link WindowOp.Focus} — bring a window to the foreground. Rejects on unknown id. */
42
- focus(id: WindowId): Promise<void>;
43
- /** {@link WindowOp.Close} — close a window. Rejects on unknown id. */
44
- close(id: WindowId): Promise<void>;
45
- /** {@link WindowOp.Arrange} — apply a layout to all open windows. */
46
- arrange(arrangement: WindowArrangement): Promise<void>;
47
- /** Current snapshot of open windows (for a window-list UI). */
48
- list(): readonly WindowState[];
49
- /** Subscribe to window-set changes; returns an unsubscribe fn. */
50
- subscribe(listener: () => void): () => void;
51
- }
package/src/offline/id.ts DELETED
@@ -1,16 +0,0 @@
1
- /** RFC-4122 id, using the platform crypto when available (browser + Node 18+),
2
- * with a non-cryptographic fallback for exotic runtimes. */
3
- export function newId(): string {
4
- const c = (globalThis as { crypto?: Crypto }).crypto;
5
- if (c && typeof c.randomUUID === 'function') {
6
- return c.randomUUID();
7
- }
8
- // Fallback — good enough for an outbox entry id (not a security token).
9
- return (
10
- 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (ch) => {
11
- const r = (Math.floor(Math.random() * 16) + Date.now()) % 16;
12
- const v = ch === 'x' ? r : (r & 0x3) | 0x8;
13
- return v.toString(16);
14
- })
15
- );
16
- }
@@ -1,54 +0,0 @@
1
- // ═══════════════════════════════════════════════════════════════════════════
2
- // @xemahq/ui-kernel/offline — generic offline field-capture kit
3
- //
4
- // A durable, provider-agnostic mutation outbox: queue mutations while
5
- // disconnected (IndexedDB) and drain them on reconnect with retry, backoff, and
6
- // idempotency. Pairs with the existing guided-capture kit (`../ui/capture`) and
7
- // the existing mobile off-canvas shell so a form captured in the field (trade
8
- // fair, no signal) is never lost. The immediate-online path is unchanged; only
9
- // a failed/offline send is queued.
10
- // ═══════════════════════════════════════════════════════════════════════════
11
-
12
- export { newId } from './id';
13
- export {
14
- OutboxEntryStatus,
15
- type OutboxEntry,
16
- type OutboxStorage,
17
- type OutboxExecutor,
18
- type OutboxExecuteContext,
19
- type OutboxSnapshot,
20
- } from './types';
21
- export {
22
- InMemoryOutboxStorage,
23
- IndexedDbOutboxStorage,
24
- createDefaultOutboxStorage,
25
- } from './storage';
26
- export {
27
- OnlineController,
28
- isNetworkUnavailableError,
29
- type OnlineListener,
30
- } from './online-controller';
31
- export {
32
- Outbox,
33
- type OutboxOptions,
34
- type OutboxListener,
35
- type EnqueueOptions,
36
- } from './outbox';
37
- export {
38
- createOfflineMutationFn,
39
- type OfflineMutationResult,
40
- type OfflineMutationOptions,
41
- } from './mutation';
42
- export {
43
- OutboxProvider,
44
- type OutboxProviderProps,
45
- } from './react';
46
- export {
47
- useOutbox,
48
- useOutboxSnapshot,
49
- useOnlineStatus,
50
- } from './react-hooks';
51
- export {
52
- OfflineStatusBadge,
53
- type OfflineStatusBadgeProps,
54
- } from './offline-status-badge';
@@ -1,75 +0,0 @@
1
- import { newId } from './id';
2
- import { isNetworkUnavailableError } from './online-controller';
3
-
4
- import type { Outbox } from './outbox';
5
- import type { OutboxExecuteContext } from './types';
6
-
7
- /** Outcome of an offline-aware mutation. */
8
- export interface OfflineMutationResult<TResult> {
9
- /** True when the mutation was queued for later delivery (offline / network
10
- * failure) instead of applied immediately. */
11
- queued: boolean;
12
- /** The outbox entry id when queued (for optimistic UI correlation). */
13
- entryId?: string;
14
- /** The server result when applied immediately (`queued === false`). */
15
- result?: TResult;
16
- }
17
-
18
- export interface OfflineMutationOptions<TPayload, TResult> {
19
- outbox: Outbox;
20
- /** Stable executor key for this mutation type (e.g. "app-forge:create-record"). */
21
- kind: string;
22
- /** Sends the mutation to the backend. Used BOTH for the immediate online path
23
- * and for draining the queue. Pass `ctx.idempotencyKey` to the backend so a
24
- * redelivered mutation is not double-applied. */
25
- send: (payload: TPayload, ctx: OutboxExecuteContext) => Promise<TResult>;
26
- /** Optional label for the pending-items UI. */
27
- label?: (payload: TPayload) => string | undefined;
28
- }
29
-
30
- /**
31
- * Build an offline-aware `mutationFn` (drop-in for `@tanstack/react-query`'s
32
- * `useMutation`). When online it sends immediately; if the send fails because
33
- * the network is unavailable — or the app is already offline — the payload is
34
- * durably queued and the promise RESOLVES (queued: true) so the UI can update
35
- * optimistically. The outbox drains on reconnect. A real server rejection (4xx)
36
- * still throws so the form shows the validation error.
37
- *
38
- * Registers the outbox executor for `kind` once (idempotent).
39
- */
40
- export function createOfflineMutationFn<TPayload, TResult>(
41
- opts: OfflineMutationOptions<TPayload, TResult>,
42
- ): (payload: TPayload) => Promise<OfflineMutationResult<TResult>> {
43
- const { outbox, kind, send, label } = opts;
44
-
45
- if (!outbox.hasExecutor(kind)) {
46
- outbox.registerExecutor<TPayload>(kind, async (payload, ctx) => {
47
- await send(payload, ctx);
48
- });
49
- }
50
-
51
- return async (payload: TPayload): Promise<OfflineMutationResult<TResult>> => {
52
- const idempotencyKey = newId();
53
-
54
- const queue = async (): Promise<OfflineMutationResult<TResult>> => {
55
- const entry = await outbox.enqueue(kind, payload, {
56
- idempotencyKey,
57
- ...(label ? { label: label(payload) } : {}),
58
- });
59
- return { queued: true, entryId: entry.id };
60
- };
61
-
62
- if (!outbox.isOnline()) {
63
- return queue();
64
- }
65
- try {
66
- const result = await send(payload, { idempotencyKey, attempt: 1, entryId: 'direct' });
67
- return { queued: false, result };
68
- } catch (err) {
69
- if (isNetworkUnavailableError(err)) {
70
- return queue();
71
- }
72
- throw err; // genuine server rejection — surface it to the caller
73
- }
74
- };
75
- }
@@ -1,51 +0,0 @@
1
- import { useOutboxSnapshot } from './react-hooks';
2
-
3
- import type { Outbox } from './outbox';
4
-
5
- export interface OfflineStatusBadgeProps {
6
- /** Explicit outbox; falls back to the nearest OutboxProvider. */
7
- outbox?: Outbox;
8
- className?: string;
9
- }
10
-
11
- const BASE =
12
- 'inline-flex items-center gap-1.5 rounded-full px-2.5 py-0.5 text-body-2 font-medium';
13
-
14
- /**
15
- * A compact connectivity + queue indicator for the offline field-capture kit.
16
- * Renders nothing when online with an empty queue; otherwise surfaces the
17
- * offline / syncing / dead-letter state so the user knows captures are safe and
18
- * will sync. Self-contained (no UI-primitive deps) so it can live in the
19
- * framework-level offline module.
20
- */
21
- export function OfflineStatusBadge({ outbox, className }: OfflineStatusBadgeProps) {
22
- const { online, pendingCount, failedCount, draining } = useOutboxSnapshot(outbox);
23
-
24
- if (online && pendingCount === 0 && failedCount === 0) {
25
- return null;
26
- }
27
-
28
- const cls = (tone: string): string =>
29
- `${BASE} ${tone}${className ? ` ${className}` : ''}`;
30
-
31
- if (failedCount > 0) {
32
- return (
33
- <span className={cls('bg-destructive/10 text-destructive')} role="status">
34
- ⚠ {failedCount} failed to sync
35
- </span>
36
- );
37
- }
38
- if (!online) {
39
- return (
40
- <span className={cls('bg-amber-500/10 text-amber-600')} role="status">
41
- ● Offline{pendingCount > 0 ? ` — ${pendingCount} queued` : ''}
42
- </span>
43
- );
44
- }
45
- // Online with a non-empty queue → actively draining.
46
- return (
47
- <span className={cls('bg-secondary text-secondary-foreground')} role="status">
48
- {draining ? '⟳' : '●'} Syncing {pendingCount}…
49
- </span>
50
- );
51
- }
@@ -1,105 +0,0 @@
1
- export type OnlineListener = (online: boolean) => void;
2
-
3
- /**
4
- * Tracks connectivity via `navigator.onLine` + the window `online`/`offline`
5
- * events. SSR-safe: when there is no `window`/`navigator` it reports online (so
6
- * server rendering never queues) and is inert. `navigator.onLine` only knows
7
- * about the local link, so callers may also mark the controller offline when a
8
- * request fails as network-unavailable (`reportOffline`) and online again after
9
- * a successful send (`reportOnline`).
10
- */
11
- export class OnlineController {
12
- private online: boolean;
13
- private readonly listeners = new Set<OnlineListener>();
14
- private started = false;
15
-
16
- private readonly onOnline = () => this.set(true);
17
- private readonly onOffline = () => this.set(false);
18
-
19
- constructor() {
20
- this.online =
21
- typeof navigator === 'undefined' || typeof navigator.onLine !== 'boolean'
22
- ? true
23
- : navigator.onLine;
24
- }
25
-
26
- isOnline(): boolean {
27
- return this.online;
28
- }
29
-
30
- start(): void {
31
- if (this.started || typeof window === 'undefined') {
32
- return;
33
- }
34
- this.started = true;
35
- window.addEventListener('online', this.onOnline);
36
- window.addEventListener('offline', this.onOffline);
37
- }
38
-
39
- stop(): void {
40
- if (!this.started || typeof window === 'undefined') {
41
- return;
42
- }
43
- this.started = false;
44
- window.removeEventListener('online', this.onOnline);
45
- window.removeEventListener('offline', this.onOffline);
46
- }
47
-
48
- /** Force offline after a network-unavailable request failure. */
49
- reportOffline(): void {
50
- this.set(false);
51
- }
52
-
53
- /** Force online after a successful send (recovers from a stale offline flag). */
54
- reportOnline(): void {
55
- this.set(true);
56
- }
57
-
58
- subscribe(listener: OnlineListener): () => void {
59
- this.listeners.add(listener);
60
- return () => this.listeners.delete(listener);
61
- }
62
-
63
- private set(online: boolean): void {
64
- if (this.online === online) {
65
- return;
66
- }
67
- this.online = online;
68
- for (const l of this.listeners) {
69
- l(online);
70
- }
71
- }
72
- }
73
-
74
- /**
75
- * Best-effort classifier for "the request failed because the network was
76
- * unavailable" (offline, DNS, connection refused) vs a real server rejection.
77
- * A fetch that never reaches the server rejects with a TypeError ("Failed to
78
- * fetch"); an aborted request has name AbortError. Anything with an HTTP status
79
- * is a server response, NOT a network failure.
80
- */
81
- export function isNetworkUnavailableError(err: unknown): boolean {
82
- if (typeof navigator !== 'undefined' && navigator.onLine === false) {
83
- return true;
84
- }
85
- if (!err || typeof err !== 'object') {
86
- return false;
87
- }
88
- const e = err as { name?: string; message?: string; status?: number; response?: unknown };
89
- // A response with a status is a server answer, not a transport failure.
90
- if (typeof e.status === 'number' || e.response !== undefined) {
91
- return false;
92
- }
93
- if (e.name === 'AbortError') {
94
- return false;
95
- }
96
- const msg = (e.message ?? '').toLowerCase();
97
- return (
98
- e.name === 'TypeError' ||
99
- msg.includes('failed to fetch') ||
100
- msg.includes('network') ||
101
- msg.includes('load failed') ||
102
- msg.includes('econnrefused') ||
103
- msg.includes('enotfound')
104
- );
105
- }