@xemahq/ui-kernel 0.11.1 → 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 (234) hide show
  1. package/package.json +1 -2
  2. package/src/index.ts +0 -17
  3. package/src/lib/biome-host/agent-validation.ts +0 -239
  4. package/src/lib/biome-host/biome-builders.ts +0 -112
  5. package/src/lib/biome-host/biome-mode.ts +0 -6
  6. package/src/lib/biome-host/biome-navigation.ts +0 -37
  7. package/src/lib/biome-host/biome-registry.ts +0 -299
  8. package/src/lib/biome-host/biome-scope-hooks.ts +0 -41
  9. package/src/lib/biome-host/biome-scope.tsx +0 -52
  10. package/src/lib/biome-host/biome-scoped-query.ts +0 -134
  11. package/src/lib/biome-host/create-biome-orval-config.ts +0 -82
  12. package/src/lib/biome-host/define-web-biome.spec.ts +0 -221
  13. package/src/lib/biome-host/define-web-biome.ts +0 -222
  14. package/src/lib/biome-host/errors.ts +0 -220
  15. package/src/lib/biome-host/frontend-biome.ts +0 -256
  16. package/src/lib/biome-host/host-bridge.ts +0 -281
  17. package/src/lib/biome-host/host-sources.ts +0 -54
  18. package/src/lib/biome-host/i18n-hooks.ts +0 -51
  19. package/src/lib/biome-host/i18n-port.test.ts +0 -50
  20. package/src/lib/biome-host/i18n-port.ts +0 -96
  21. package/src/lib/biome-host/index.ts +0 -35
  22. package/src/lib/biome-host/realtime-hooks.ts +0 -75
  23. package/src/lib/biome-host/realtime-port.ts +0 -115
  24. package/src/lib/biome-host/session-contributions.ts +0 -296
  25. package/src/lib/biome-host/use-mutation-with-error-toast.ts +0 -90
  26. package/src/lib/biome-host/use-page-state.ts +0 -248
  27. package/src/lib/capabilities/capability-context.ts +0 -25
  28. package/src/lib/capabilities/capability-provider.tsx +0 -73
  29. package/src/lib/capabilities/index.ts +0 -17
  30. package/src/lib/capabilities/types.ts +0 -69
  31. package/src/lib/capabilities/use-capability.ts +0 -79
  32. package/src/lib/contribution-registry/contribution-registry.spec.ts +0 -141
  33. package/src/lib/contribution-registry/contribution-registry.ts +0 -204
  34. package/src/lib/contribution-registry/index.ts +0 -9
  35. package/src/lib/contribution-registry/renderable-registry.ts +0 -142
  36. package/src/lib/system-bus/capability-invoke.ts +0 -92
  37. package/src/lib/system-bus/deeplink.ts +0 -210
  38. package/src/lib/system-bus/enums.ts +0 -86
  39. package/src/lib/system-bus/host-ports.ts +0 -93
  40. package/src/lib/system-bus/index.ts +0 -16
  41. package/src/lib/system-bus/intent-registry.ts +0 -109
  42. package/src/lib/system-bus/intents.ts +0 -113
  43. package/src/lib/system-bus/palette.ts +0 -78
  44. package/src/lib/system-bus/system-bus-builder.ts +0 -159
  45. package/src/lib/system-bus/system-bus.ts +0 -37
  46. package/src/lib/system-bus/windows.ts +0 -51
  47. package/src/offline/id.ts +0 -16
  48. package/src/offline/index.ts +0 -54
  49. package/src/offline/mutation.ts +0 -75
  50. package/src/offline/offline-status-badge.tsx +0 -51
  51. package/src/offline/online-controller.ts +0 -105
  52. package/src/offline/outbox.test.ts +0 -172
  53. package/src/offline/outbox.ts +0 -348
  54. package/src/offline/react-hooks.ts +0 -40
  55. package/src/offline/react.tsx +0 -32
  56. package/src/offline/storage.ts +0 -112
  57. package/src/offline/types.ts +0 -77
  58. package/src/org-db/components/DbResultTable.tsx +0 -176
  59. package/src/org-db/index.ts +0 -1
  60. package/src/pdf/index.ts +0 -11
  61. package/src/registry/index.ts +0 -9
  62. package/src/registry/lib/agent-validation-host.ts +0 -36
  63. package/src/registry/lib/biome-slot.tsx +0 -50
  64. package/src/registry/lib/biomes-enabled-context.ts +0 -20
  65. package/src/registry/lib/extension-points.ts +0 -134
  66. package/src/registry/lib/primitives/SessionEventCard.tsx +0 -164
  67. package/src/registry/lib/primitives/SessionEventTimeline.tsx +0 -95
  68. package/src/registry/lib/primitives/SessionMutationBar.tsx +0 -76
  69. package/src/registry/lib/primitives/SessionTabbedDrawer.tsx +0 -165
  70. package/src/registry/lib/primitives/index.ts +0 -18
  71. package/src/registry/lib/session-context-builder.ts +0 -84
  72. package/src/registry/lib/session-context-provider.tsx +0 -33
  73. package/src/registry/lib/session-context.ts +0 -21
  74. package/src/registry/lib/session-selectors.ts +0 -272
  75. package/src/session/comments/CommentRail.tsx +0 -164
  76. package/src/session/index.ts +0 -3
  77. package/src/session/lib/cn.ts +0 -11
  78. package/src/session/shell/SessionWorkspaceShell.tsx +0 -147
  79. package/src/session-kit/approvals/ApprovalButton.tsx +0 -89
  80. package/src/session-kit/approvals/ApprovalCard.tsx +0 -341
  81. package/src/session-kit/approvals/ApprovalsCenter.tsx +0 -336
  82. package/src/session-kit/approvals/CapabilityApprovalStickyBar.tsx +0 -118
  83. package/src/session-kit/approvals/Obligation.tsx +0 -111
  84. package/src/session-kit/approvals/ScopedBody.tsx +0 -392
  85. package/src/session-kit/approvals/approval-icons.ts +0 -31
  86. package/src/session-kit/approvals/approval-model.ts +0 -204
  87. package/src/session-kit/approvals/index.ts +0 -22
  88. package/src/session-kit/approvals/obligation-display.ts +0 -100
  89. package/src/session-kit/approvals/risk-accent.ts +0 -47
  90. package/src/session-kit/approvals/scope-icons.ts +0 -19
  91. package/src/session-kit/combobox/Combobox.tsx +0 -358
  92. package/src/session-kit/combobox/use-click-outside.ts +0 -21
  93. package/src/session-kit/display/ContextHeader.tsx +0 -148
  94. package/src/session-kit/display/FileDiffCard.tsx +0 -140
  95. package/src/session-kit/display/MD.tsx +0 -162
  96. package/src/session-kit/display/MessageTurn.tsx +0 -157
  97. package/src/session-kit/display/ThinkingPanel.tsx +0 -81
  98. package/src/session-kit/display/TodoChecklist.tsx +0 -120
  99. package/src/session-kit/display/TokenMeter.tsx +0 -89
  100. package/src/session-kit/display/ToolStrip.tsx +0 -278
  101. package/src/session-kit/display/TypingDots.tsx +0 -24
  102. package/src/session-kit/index.ts +0 -44
  103. package/src/session-kit/lib/enums.ts +0 -66
  104. package/src/session-kit/lib/portal-accent.ts +0 -30
  105. package/src/session-kit/lib/status-dot.ts +0 -68
  106. package/src/session-kit/primitives/Avatar.tsx +0 -44
  107. package/src/session-kit/primitives/PortalGlyph.tsx +0 -51
  108. package/src/session-kit/primitives/RiskPill.tsx +0 -95
  109. package/src/session-kit/primitives/ScopeDot.tsx +0 -47
  110. package/src/session-kit/primitives/Segmented.tsx +0 -71
  111. package/src/ui/branding/app-branding-context.ts +0 -20
  112. package/src/ui/branding/app-branding-provider.tsx +0 -123
  113. package/src/ui/branding/sanitize.ts +0 -89
  114. package/src/ui/breakpoints.ts +0 -14
  115. package/src/ui/capture/document-preview.tsx +0 -166
  116. package/src/ui/capture/field-spec-to-zod.test.ts +0 -81
  117. package/src/ui/capture/field-spec-to-zod.ts +0 -174
  118. package/src/ui/capture/field-spec-validate.ts +0 -236
  119. package/src/ui/capture/field-spec.test.ts +0 -49
  120. package/src/ui/capture/field-spec.ts +0 -274
  121. package/src/ui/capture/index.ts +0 -51
  122. package/src/ui/capture/schema-field-input.tsx +0 -620
  123. package/src/ui/capture/schema-form.tsx +0 -149
  124. package/src/ui/capture/submit-values.test.ts +0 -70
  125. package/src/ui/capture/submit-values.ts +0 -63
  126. package/src/ui/capture/use-prefill.ts +0 -186
  127. package/src/ui/capture/use-schema-validation.ts +0 -145
  128. package/src/ui/capture/use-wizard.ts +0 -180
  129. package/src/ui/capture/value-format.ts +0 -32
  130. package/src/ui/capture/wizard.tsx +0 -105
  131. package/src/ui/charts/bar-chart.tsx +0 -139
  132. package/src/ui/charts/chart-frame.tsx +0 -125
  133. package/src/ui/charts/chart-types.ts +0 -52
  134. package/src/ui/charts/chart-utils.ts +0 -190
  135. package/src/ui/charts/charts.test.tsx +0 -204
  136. package/src/ui/charts/donut-chart.tsx +0 -129
  137. package/src/ui/charts/index.ts +0 -27
  138. package/src/ui/charts/line-chart.tsx +0 -202
  139. package/src/ui/charts/sparkline.tsx +0 -109
  140. package/src/ui/chrome/AsyncBoundary.tsx +0 -74
  141. package/src/ui/chrome/EmptyState.tsx +0 -257
  142. package/src/ui/chrome/ErrorCard.tsx +0 -63
  143. package/src/ui/chrome/LoadingState.tsx +0 -61
  144. package/src/ui/chrome/PageHeader.tsx +0 -137
  145. package/src/ui/chrome/RecordCardList.tsx +0 -98
  146. package/src/ui/chrome/StateCard.tsx +0 -150
  147. package/src/ui/chrome/confirm-delete-dialog.tsx +0 -59
  148. package/src/ui/chrome/page-shell.spec.tsx +0 -117
  149. package/src/ui/chrome/page-shell.tsx +0 -221
  150. package/src/ui/chrome/scope-badge.tsx +0 -40
  151. package/src/ui/chrome/status-badge.tsx +0 -79
  152. package/src/ui/cn.ts +0 -32
  153. package/src/ui/design-tokens.ts +0 -372
  154. package/src/ui/hooks/use-debounced-value.ts +0 -16
  155. package/src/ui/hooks/use-is-mobile.ts +0 -28
  156. package/src/ui/index.ts +0 -110
  157. package/src/ui/pdf/pdf-preview.tsx +0 -122
  158. package/src/ui/primitives/alert-dialog.tsx +0 -104
  159. package/src/ui/primitives/async-combobox.tsx +0 -182
  160. package/src/ui/primitives/badge-variants.ts +0 -21
  161. package/src/ui/primitives/badge.tsx +0 -25
  162. package/src/ui/primitives/button-variants.ts +0 -31
  163. package/src/ui/primitives/button.tsx +0 -29
  164. package/src/ui/primitives/card.tsx +0 -43
  165. package/src/ui/primitives/checkbox.tsx +0 -26
  166. package/src/ui/primitives/collapsible.tsx +0 -9
  167. package/src/ui/primitives/dialog.tsx +0 -103
  168. package/src/ui/primitives/dropdown-menu.tsx +0 -179
  169. package/src/ui/primitives/form-stepper.tsx +0 -94
  170. package/src/ui/primitives/form-stepper.types.ts +0 -15
  171. package/src/ui/primitives/input.tsx +0 -22
  172. package/src/ui/primitives/label.tsx +0 -17
  173. package/src/ui/primitives/overflow-tabs.tsx +0 -331
  174. package/src/ui/primitives/popover.tsx +0 -33
  175. package/src/ui/primitives/preview-device-frame.tsx +0 -213
  176. package/src/ui/primitives/radio-group.tsx +0 -36
  177. package/src/ui/primitives/resizable.tsx +0 -67
  178. package/src/ui/primitives/scroll-area.tsx +0 -38
  179. package/src/ui/primitives/select.tsx +0 -143
  180. package/src/ui/primitives/separator.tsx +0 -20
  181. package/src/ui/primitives/sheet.tsx +0 -107
  182. package/src/ui/primitives/skeleton.tsx +0 -99
  183. package/src/ui/primitives/switch.tsx +0 -27
  184. package/src/ui/primitives/table.tsx +0 -72
  185. package/src/ui/primitives/tabs.tsx +0 -53
  186. package/src/ui/primitives/tag-multi-select.tsx +0 -261
  187. package/src/ui/primitives/textarea.tsx +0 -21
  188. package/src/ui/primitives/tooltip.tsx +0 -30
  189. package/src/ui/primitives/url-preview-chrome.tsx +0 -103
  190. package/src/vista/GenericCatalogueSurface.tsx +0 -179
  191. package/src/vista/SurfaceFallbackCard.tsx +0 -109
  192. package/src/vista/VistaPanel.tsx +0 -163
  193. package/src/vista/form-payload.spec.ts +0 -139
  194. package/src/vista/html/CustomUiFrame.spec.tsx +0 -127
  195. package/src/vista/html/CustomUiFrame.tsx +0 -206
  196. package/src/vista/html/custom-ui-frame-bridge.spec.ts +0 -191
  197. package/src/vista/html/custom-ui-frame-bridge.ts +0 -98
  198. package/src/vista/html/custom-ui-frame-contract.ts +0 -88
  199. package/src/vista/html/custom-ui-intents.spec.ts +0 -135
  200. package/src/vista/html/custom-ui-intents.ts +0 -155
  201. package/src/vista/html/custom-ui-srcdoc.spec.ts +0 -151
  202. package/src/vista/html/custom-ui-srcdoc.ts +0 -193
  203. package/src/vista/html/index.ts +0 -13
  204. package/src/vista/index.ts +0 -52
  205. package/src/vista/surface-catalogue.spec.ts +0 -140
  206. package/src/vista/surface-catalogue.ts +0 -90
  207. package/src/vista/surface-icons.ts +0 -57
  208. package/src/vista/surface-registry.ts +0 -56
  209. package/src/vista/surfaces/ChartSurface.tsx +0 -34
  210. package/src/vista/surfaces/FilesSurface.tsx +0 -202
  211. package/src/vista/surfaces/FormSurface.tsx +0 -380
  212. package/src/vista/surfaces/HtmlSurface.tsx +0 -43
  213. package/src/vista/surfaces/TableSurface.tsx +0 -100
  214. package/src/vista/surfaces/UrlSurface.tsx +0 -40
  215. package/src/vista/surfaces/blob-surfaces.tsx +0 -139
  216. package/src/vista/surfaces/elicitation-transport.spec.ts +0 -369
  217. package/src/vista/surfaces/elicitation-transport.ts +0 -367
  218. package/src/vista/surfaces/file-surface-utils.ts +0 -69
  219. package/src/vista/surfaces/index.ts +0 -24
  220. package/src/vista/surfaces/register-surfaces.tsx +0 -139
  221. package/src/vista/surfaces/text-surfaces.tsx +0 -132
  222. package/src/vista/use-vista-tabs.ts +0 -196
  223. package/src/vista/vista-code-renderer-context.ts +0 -16
  224. package/src/vista/vista-code-renderer.tsx +0 -35
  225. package/src/vista/vista-file-context.ts +0 -14
  226. package/src/vista/vista-file-source.tsx +0 -141
  227. package/src/vista/vista-form-context.ts +0 -20
  228. package/src/vista/vista-form-host.tsx +0 -48
  229. package/src/vista/vista-model.ts +0 -137
  230. package/src/vista/vista-open-context.tsx +0 -27
  231. package/src/vista/vista-open-state.ts +0 -11
  232. package/src/vista/vista-payloads.ts +0 -635
  233. package/src/vista/vista-tabs-manifest.ts +0 -138
  234. package/src/vista/vista-tools.ts +0 -232
@@ -1,90 +0,0 @@
1
- /**
2
- * Unified mutation hook with automatic success/error toast notifications.
3
- *
4
- * Rebuilt on the {@link HostBridge}: toasts route through `bridge.toast` and
5
- * error copy is decoded by the bridge's error decoder (falling back to the
6
- * kernel {@link getUserFacingErrorMessage}). Biome code stays decoupled from
7
- * any particular toast library or error-envelope shape.
8
- *
9
- * Requires `@tanstack/react-query` (an optional peer of the kernel) and a
10
- * `QueryClientProvider` mounted above.
11
- *
12
- * Usage:
13
- * const mutation = useMutationWithErrorToast(
14
- * { mutationFn: (vars) => apiFn(vars), onSuccess: () => invalidate() },
15
- * { successTitle: 'Saved', errorTitle: 'Save failed' },
16
- * );
17
- */
18
- import { useMutation, type UseMutationOptions } from '@tanstack/react-query';
19
-
20
- import { getUserFacingErrorMessage } from './errors';
21
- import { useHostBridge } from './host-bridge';
22
-
23
- export type MutationCoreOptions<
24
- TData = unknown,
25
- TError = Error,
26
- TVariables = unknown,
27
- TContext = unknown,
28
- > = Omit<UseMutationOptions<TData, TError, TVariables, TContext>, 'onError'>;
29
-
30
- export interface MutationToastOptions<TError = Error> {
31
- /** Title for the success toast. Omit or pass null to suppress the success toast. */
32
- successTitle?: string | null;
33
- /** Optional description for the success toast. */
34
- successDescription?: string;
35
- /** Title for the error toast. Omit to suppress the error toast. */
36
- errorTitle?: string;
37
- /**
38
- * Custom error side-effect. Called before the auto-error-toast.
39
- * Return `true` to suppress the automatic toast.
40
- */
41
- onError?: (error: TError) => boolean | void;
42
- }
43
-
44
- export function useMutationWithErrorToast<
45
- TData = unknown,
46
- TError extends Error = Error,
47
- TVariables = unknown,
48
- TContext = unknown,
49
- >(
50
- mutationOptions: MutationCoreOptions<TData, TError, TVariables, TContext>,
51
- toastOptions?: MutationToastOptions<TError>,
52
- ) {
53
- const bridge = useHostBridge();
54
- const decodeError =
55
- bridge.errors?.getUserFacingErrorMessage ?? getUserFacingErrorMessage;
56
-
57
- const { onSuccess: userOnSuccess, ...coreMutationOptions } = mutationOptions;
58
- const {
59
- successTitle,
60
- successDescription,
61
- errorTitle,
62
- onError: customOnError,
63
- } = toastOptions ?? {};
64
-
65
- return useMutation({
66
- ...coreMutationOptions,
67
- onSuccess: (data, variables, context, mutation) => {
68
- if (successTitle !== null && successTitle !== undefined) {
69
- bridge.toast.success(successTitle, successDescription);
70
- }
71
- if (userOnSuccess) {
72
- userOnSuccess(data, variables, context, mutation);
73
- }
74
- },
75
- onError: (error: TError) => {
76
- if (errorTitle !== undefined) {
77
- const shouldSuppressToast = customOnError?.(error) === true;
78
- if (!shouldSuppressToast) {
79
- const errorMessage = decodeError(
80
- error,
81
- 'Unable to complete this action.',
82
- );
83
- bridge.toast.error(errorTitle, errorMessage);
84
- }
85
- } else {
86
- customOnError?.(error);
87
- }
88
- },
89
- });
90
- }
@@ -1,248 +0,0 @@
1
- /**
2
- * usePageState — page-level UI state synced to the URL query string.
3
- *
4
- * State survives refresh, back/forward, and is shareable via URL. Rebuilt on
5
- * the {@link HostBridge} navigation port (`useSearchParams` to read,
6
- * `useLocation` for the current path, `push`/`replace` to write) so biome code
7
- * never imports a router primitive (`next/navigation`, `react-router-dom`)
8
- * directly — the host owns history semantics.
9
- *
10
- * Usage:
11
- * const [activeTab, setActiveTab] = usePageState<string>('tab', 'runs');
12
- * const [selectedId, setSelectedId] = usePageState<string | null>('run', null);
13
- */
14
-
15
- import { useCallback, useEffect, useRef, useState } from 'react';
16
-
17
- import { useHostBridge } from './host-bridge';
18
-
19
- /** Default debounce for the URL mirror of a free-text input, in ms. */
20
- const INPUT_URL_MIRROR_DEBOUNCE_MS = 300;
21
-
22
- function buildPath(pathname: string, params: URLSearchParams): string {
23
- const queryString = params.toString();
24
- return queryString ? `${pathname}?${queryString}` : pathname;
25
- }
26
-
27
- function serializeValue<T>(value: T): string {
28
- if (typeof value === 'string') {
29
- return value;
30
- }
31
- if (typeof value === 'number' || typeof value === 'boolean') {
32
- return String(value);
33
- }
34
- return JSON.stringify(value);
35
- }
36
-
37
- function tryParseJson(rawValue: string): unknown {
38
- try {
39
- return JSON.parse(rawValue);
40
- } catch {
41
- return undefined;
42
- }
43
- }
44
-
45
- function deserializePrimitive<T>(
46
- rawValue: string,
47
- defaultValue: T,
48
- ): T | undefined {
49
- if (typeof defaultValue === 'string') {
50
- return rawValue as T;
51
- }
52
-
53
- if (typeof defaultValue === 'number') {
54
- const numericValue = Number(rawValue);
55
- return (Number.isNaN(numericValue) ? defaultValue : numericValue) as T;
56
- }
57
-
58
- if (typeof defaultValue === 'boolean') {
59
- if (rawValue === 'true') {
60
- return true as T;
61
- }
62
- if (rawValue === 'false') {
63
- return false as T;
64
- }
65
- return defaultValue;
66
- }
67
-
68
- return undefined;
69
- }
70
-
71
- function deserializeValue<T>(rawValue: string | null, defaultValue: T): T {
72
- if (rawValue === null) {
73
- return defaultValue;
74
- }
75
-
76
- const primitive = deserializePrimitive(rawValue, defaultValue);
77
- if (primitive !== undefined) {
78
- return primitive;
79
- }
80
-
81
- // Default is null/undefined or a complex type (array/object). The value was
82
- // written via JSON.stringify in serializeValue, so attempt to parse it back.
83
- // If parsing yields a structured value (array/object/null), return it;
84
- // otherwise fall back to the raw string (covers `T = string | null`).
85
- const parsedValue = tryParseJson(rawValue);
86
- if (parsedValue !== undefined && typeof parsedValue === 'object') {
87
- return parsedValue as T;
88
- }
89
-
90
- if (defaultValue === null || defaultValue === undefined) {
91
- return rawValue as T;
92
- }
93
-
94
- if (parsedValue !== undefined) {
95
- return parsedValue as T;
96
- }
97
-
98
- if (Array.isArray(defaultValue)) {
99
- return rawValue ? ([rawValue] as unknown as T) : defaultValue;
100
- }
101
-
102
- return defaultValue;
103
- }
104
-
105
- export function usePageState<T>(
106
- stateKey: string,
107
- defaultValue: T,
108
- ): [T, (value: T) => void] {
109
- const { navigation } = useHostBridge();
110
- const searchParams = navigation.useSearchParams();
111
- const { pathname } = navigation.useLocation();
112
-
113
- const rawValue = searchParams.get(stateKey);
114
- const value = deserializeValue(rawValue, defaultValue);
115
-
116
- const setValue = useCallback(
117
- (newValue: T) => {
118
- const next = new URLSearchParams(searchParams);
119
- if (
120
- newValue === null ||
121
- newValue === undefined ||
122
- serializeValue(newValue) === serializeValue(defaultValue)
123
- ) {
124
- next.delete(stateKey);
125
- } else {
126
- next.set(stateKey, serializeValue(newValue));
127
- }
128
- navigation.push(buildPath(pathname, next));
129
- },
130
- [defaultValue, stateKey, searchParams, pathname, navigation],
131
- );
132
-
133
- return [value, setValue];
134
- }
135
-
136
- /**
137
- * usePageInputState — like {@link usePageState}, but tuned for FREE-TEXT
138
- * `<Input>` values that are mirrored to the URL on every keystroke.
139
- *
140
- * The returned value lives in LOCAL React state, so typing is instant. The URL
141
- * is mirrored — debounced — via the host's `replace` (an in-place history entry
142
- * update), so the value stays shareable / refresh-survivable WITHOUT pushing a
143
- * new history entry per keystroke.
144
- *
145
- * The local value re-syncs whenever the underlying search param changes from
146
- * the outside (a real navigation, a "Clear" button writing the param, or
147
- * back/forward), so external resets still flow through.
148
- *
149
- * Use this ONLY for free-text inputs. Tabs, selects, and row selection are real
150
- * navigations — keep them on {@link usePageState}.
151
- *
152
- * Usage:
153
- * const [filter, setFilter] = usePageInputState('runFilter', '');
154
- * <Input value={filter} onChange={(e) => setFilter(e.target.value)} />
155
- */
156
- export function usePageInputState(
157
- stateKey: string,
158
- defaultValue: string,
159
- debounceMs: number = INPUT_URL_MIRROR_DEBOUNCE_MS,
160
- ): [string, (value: string) => void] {
161
- const { navigation } = useHostBridge();
162
- const searchParams = navigation.useSearchParams();
163
- const { pathname } = navigation.useLocation();
164
-
165
- const urlValue = searchParams.get(stateKey) ?? defaultValue;
166
-
167
- const [localValue, setLocalValue] = useState(urlValue);
168
-
169
- // Resync local state when the URL param changes from the outside (real
170
- // navigation, Clear button, back/forward). We compare against the value we
171
- // last mirrored so our own debounced writes don't bounce back and clobber
172
- // freshly-typed characters.
173
- const lastMirroredRef = useRef(urlValue);
174
- useEffect(() => {
175
- if (urlValue !== lastMirroredRef.current) {
176
- lastMirroredRef.current = urlValue;
177
- setLocalValue(urlValue);
178
- }
179
- }, [urlValue]);
180
-
181
- const debounceTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
182
- useEffect(
183
- () => () => {
184
- if (debounceTimerRef.current !== null) {
185
- clearTimeout(debounceTimerRef.current);
186
- }
187
- },
188
- [],
189
- );
190
-
191
- const setValue = useCallback(
192
- (newValue: string) => {
193
- // Instant: drive the input from local state synchronously.
194
- setLocalValue(newValue);
195
-
196
- if (debounceTimerRef.current !== null) {
197
- clearTimeout(debounceTimerRef.current);
198
- }
199
- debounceTimerRef.current = setTimeout(() => {
200
- debounceTimerRef.current = null;
201
- lastMirroredRef.current =
202
- newValue === defaultValue ? defaultValue : newValue;
203
- const next = new URLSearchParams(searchParams);
204
- if (newValue === defaultValue || newValue.length === 0) {
205
- next.delete(stateKey);
206
- } else {
207
- next.set(stateKey, newValue);
208
- }
209
- navigation.replace(buildPath(pathname, next));
210
- }, debounceMs);
211
- },
212
- [debounceMs, defaultValue, stateKey, searchParams, pathname, navigation],
213
- );
214
-
215
- return [localValue, setValue];
216
- }
217
-
218
- /**
219
- * Batch-update multiple page-state keys in a single navigation.
220
- *
221
- * Pass `null` for a value to delete the key (reset to default).
222
- *
223
- * Usage:
224
- * const batch = useBatchPageState();
225
- * batch({ selectedId: 'abc', mainTab: null, contextTab: null });
226
- */
227
- export function useBatchPageState(): (
228
- updates: Record<string, string | null>,
229
- ) => void {
230
- const { navigation } = useHostBridge();
231
- const searchParams = navigation.useSearchParams();
232
- const { pathname } = navigation.useLocation();
233
-
234
- return useCallback(
235
- (updates: Record<string, string | null>) => {
236
- const next = new URLSearchParams(searchParams);
237
- for (const [key, value] of Object.entries(updates)) {
238
- if (value === null) {
239
- next.delete(key);
240
- } else {
241
- next.set(key, value);
242
- }
243
- }
244
- navigation.push(buildPath(pathname, next));
245
- },
246
- [searchParams, pathname, navigation],
247
- );
248
- }
@@ -1,25 +0,0 @@
1
- import { createContext, useContext } from 'react';
2
-
3
- import type { MeCapabilitiesSnapshot } from './types';
4
-
5
- export interface CapabilityContextValue {
6
- readonly snapshot: MeCapabilitiesSnapshot | undefined;
7
- readonly isLoading: boolean;
8
- readonly error: unknown;
9
- }
10
-
11
- export const CapabilityContext =
12
- createContext<CapabilityContextValue | null>(null);
13
- CapabilityContext.displayName = 'CapabilityContext';
14
-
15
- export function useCapabilityContext(): CapabilityContextValue {
16
- const ctx = useContext(CapabilityContext);
17
- if (!ctx) {
18
- throw new Error(
19
- '[xema-ui-kernel] useCapability()/useCapabilities() called outside ' +
20
- '<CapabilityProvider>. The host (or biome shell) must mount the ' +
21
- 'capability provider above any capability-gated subtree.',
22
- );
23
- }
24
- return ctx;
25
- }
@@ -1,73 +0,0 @@
1
- import { useQuery } from '@tanstack/react-query';
2
-
3
- import {
4
- CapabilityContext,
5
- type CapabilityContextValue,
6
- } from './capability-context';
7
- import { useHostBridge } from '../biome-host/host-bridge';
8
-
9
- import type { CapabilityPort, MeCapabilitiesSnapshot } from './types';
10
- import type { ReactNode } from 'react';
11
-
12
- /**
13
- * Stable query-key root for the capability snapshot. Folded with the active
14
- * org so each org caches independently and a re-fetch fires when the org
15
- * changes — same tenant-isolation discipline as `useScopedQuery`.
16
- */
17
- const CAPABILITIES_QUERY_ROOT = ['xema', 'capabilities', 'me'] as const;
18
-
19
- export interface CapabilityProviderProps {
20
- readonly children: ReactNode;
21
- /**
22
- * Override the host-provided port. Defaults to `bridge.capabilities`. Exists
23
- * for tests and for hosts that compose the provider outside a bridge subtree;
24
- * production wiring reads it OFF the bridge so biomes need no extra prop.
25
- */
26
- readonly port?: CapabilityPort;
27
- /**
28
- * Override the active org. Defaults to `null` (the host's
29
- * currently-selected org). When provided, the port is asked for that org and
30
- * the cache key includes it.
31
- */
32
- readonly orgId?: string | null;
33
- }
34
-
35
- /**
36
- * Fetches the caller's server-authoritative capability set once per active org
37
- * and exposes it (plus loading/error) to descendants via React context.
38
- *
39
- * Wiring: the {@link CapabilityPort} is read OFF `HostBridge.capabilities`
40
- * (not prop-drilled) — the same decoupling the rest of the kernel uses to
41
- * reach the host. The active org is read from the bridge's auth source. The
42
- * bridge's `QueryClient` (TanStack Query) owns caching/refetch; the kernel
43
- * rolls no bespoke state. The query re-runs when the org changes because the
44
- * org is part of the query key.
45
- *
46
- * Rendering is fail-soft (callers can show a loading state), but failures are
47
- * SURFACED via `error` rather than masquerading as an empty capability set.
48
- */
49
- export function CapabilityProvider(
50
- props: CapabilityProviderProps,
51
- ): JSX.Element {
52
- const bridge = useHostBridge();
53
- const port = props.port ?? bridge.capabilities;
54
- const orgId =
55
- props.orgId !== undefined ? props.orgId : bridge.auth.getOrgId();
56
-
57
- const query = useQuery<MeCapabilitiesSnapshot>({
58
- queryKey: [...CAPABILITIES_QUERY_ROOT, orgId],
59
- queryFn: () => port.list(orgId ? { orgId } : undefined),
60
- });
61
-
62
- const value: CapabilityContextValue = {
63
- snapshot: query.data,
64
- isLoading: query.isLoading,
65
- error: query.error,
66
- };
67
-
68
- return (
69
- <CapabilityContext.Provider value={value}>
70
- {props.children}
71
- </CapabilityContext.Provider>
72
- );
73
- }
@@ -1,17 +0,0 @@
1
- /**
2
- * Host-agnostic capabilities SDK for `@xemahq/ui-kernel`.
3
- *
4
- * Lets a biome UI gate on SERVER-AUTHORITATIVE capabilities (the caller's
5
- * effective set from `GET /bff/me/capabilities`) instead of cosmetic role
6
- * booleans. The kernel defines the contract and the React surface; it never
7
- * fetches — the HOST implements {@link CapabilityPort} (hung off
8
- * `HostBridge.capabilities`) and maps its Orval client response onto the
9
- * local {@link MeCapabilitiesSnapshot} shape.
10
- *
11
- * Biome usage:
12
- * import { CapabilityProvider, useCapability } from '@xemahq/ui-kernel';
13
- */
14
- export * from './types';
15
- export * from './capability-context';
16
- export * from './capability-provider';
17
- export * from './use-capability';
@@ -1,69 +0,0 @@
1
- import type { ExecutionEnvironmentKind } from '@xemahq/kernel-contracts/execution-environment';
2
-
3
- /**
4
- * One server-authoritative capability the caller holds, mirroring the
5
- * FROZEN `GET /bff/me/capabilities` wire entry.
6
- *
7
- * These types are defined LOCALLY in the kernel — deliberately NOT imported
8
- * from a backend DTO or an Orval client — so `@xemahq/ui-kernel` stays
9
- * host-agnostic (no fetch lib, no generated client). The concrete host
10
- * (`submodules/xema-host-web`) maps its client response onto this shape when
11
- * it implements {@link CapabilityPort}.
12
- */
13
- export interface CapabilityEntry {
14
- /**
15
- * The capability reference (e.g. `capability:session.launch`). Matched
16
- * verbatim by {@link useCapability}; the kernel does not parse it.
17
- */
18
- readonly ref: string;
19
- /**
20
- * The execution environments in which the caller holds this capability.
21
- * A gate that names an `environment` is allowed only if it appears here.
22
- */
23
- readonly environments: ExecutionEnvironmentKind[];
24
- /**
25
- * When `true`, holding the capability still requires a human approval step
26
- * before the side-effect runs — so a UI affordance should route through the
27
- * approval flow rather than acting directly.
28
- */
29
- readonly approvalRequired: boolean;
30
- }
31
-
32
- /**
33
- * The caller's effective capability set for one active org, mirroring the
34
- * FROZEN `MeCapabilitiesResponse` wire shape.
35
- */
36
- export interface MeCapabilitiesSnapshot {
37
- /** The subject (user) the snapshot was resolved for. */
38
- readonly subjectId: string;
39
- /** The org the snapshot is scoped to. */
40
- readonly orgId: string;
41
- /** Every capability the subject effectively holds in this org. */
42
- readonly capabilities: CapabilityEntry[];
43
- }
44
-
45
- /**
46
- * Host-implemented port that fetches the caller's effective capability set.
47
- *
48
- * The kernel never calls an API directly — the HOST implements this against
49
- * its Orval client (`GET /bff/me/capabilities`) and maps the response onto
50
- * {@link MeCapabilitiesSnapshot}. The implementation is hung off
51
- * {@link HostBridge.capabilities}; {@link CapabilityProvider} reads it from
52
- * the bridge and uses the bridge's `QueryClient` for caching/refetch.
53
- */
54
- export interface CapabilityPort {
55
- /**
56
- * Return the caller's effective capability set for the active org.
57
- *
58
- * @param options.orgId The org to resolve for. When omitted the host
59
- * resolves against its currently-selected org.
60
- * @param options.environment Optional environment hint the host MAY pass to
61
- * the backend to narrow the set. Callers gate by environment client-side
62
- * via {@link useCapability}, so a host MAY ignore this and return the full
63
- * set; it exists so a host can request a pre-filtered snapshot.
64
- */
65
- list(options?: {
66
- orgId?: string;
67
- environment?: ExecutionEnvironmentKind;
68
- }): Promise<MeCapabilitiesSnapshot>;
69
- }
@@ -1,79 +0,0 @@
1
- import {
2
- useCapabilityContext,
3
- type CapabilityContextValue,
4
- } from './capability-context';
5
-
6
- import type { MeCapabilitiesSnapshot } from './types';
7
- import type { ExecutionEnvironmentKind } from '@xemahq/kernel-contracts/execution-environment';
8
-
9
- export interface UseCapabilityOptions {
10
- /**
11
- * Gate against a specific execution environment. When set, the capability
12
- * counts only if the snapshot lists it for this environment.
13
- */
14
- readonly environment?: ExecutionEnvironmentKind;
15
- }
16
-
17
- export interface CapabilityDecision {
18
- /**
19
- * `true` only when the caller holds the capability, holds it in the named
20
- * environment (if one was given), AND it does NOT require approval. A gate
21
- * that should let the user act directly checks this.
22
- */
23
- readonly allowed: boolean;
24
- /**
25
- * `true` when the caller holds the capability (in the named environment, if
26
- * given) but it requires a human approval step first — render the
27
- * approval-routed affordance instead of acting directly.
28
- */
29
- readonly approvalRequired: boolean;
30
- /** The snapshot is still being fetched; treat as "unknown", not "denied". */
31
- readonly isLoading: boolean;
32
- /** The fetch failed; surfaced so callers never conflate failure with denial. */
33
- readonly error: unknown;
34
- }
35
-
36
- /**
37
- * Server-authoritative capability gate.
38
- *
39
- * Resolves `ref` against the cached {@link CapabilityProvider} snapshot:
40
- * - `allowed` = entry exists AND (no environment asked OR listed for it) AND NOT approvalRequired.
41
- * - `approvalRequired` = entry exists AND (no environment asked OR listed for it) AND approvalRequired.
42
- *
43
- * While the snapshot is loading, `allowed` is `false` and `isLoading` is
44
- * `true` — callers gate on `isLoading` to distinguish "unknown" from "denied".
45
- * On error, `allowed` is `false` and `error` is set; the hook never silently
46
- * degrades a failed fetch into a confident denial.
47
- */
48
- export function useCapability(
49
- ref: string,
50
- opts?: UseCapabilityOptions,
51
- ): CapabilityDecision {
52
- const { snapshot, isLoading, error } = useCapabilityContext();
53
-
54
- const entry = snapshot?.capabilities.find((c) => c.ref === ref);
55
- const inEnvironment =
56
- !!entry &&
57
- (!opts?.environment || entry.environments.includes(opts.environment));
58
-
59
- return {
60
- allowed: inEnvironment && !entry.approvalRequired,
61
- approvalRequired: inEnvironment && entry.approvalRequired,
62
- isLoading,
63
- error,
64
- };
65
- }
66
-
67
- /**
68
- * Accessor for the raw capability snapshot plus its loading/error state.
69
- * Use when a component needs to enumerate capabilities rather than gate on a
70
- * single `ref`.
71
- */
72
- export function useCapabilities(): {
73
- snapshot: MeCapabilitiesSnapshot | undefined;
74
- isLoading: boolean;
75
- error: unknown;
76
- } {
77
- const ctx: CapabilityContextValue = useCapabilityContext();
78
- return { snapshot: ctx.snapshot, isLoading: ctx.isLoading, error: ctx.error };
79
- }