@xemahq/ui-kernel 0.5.0 → 0.6.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 (143) hide show
  1. package/dist/lib/biome-host/define-web-biome.d.ts +3 -3
  2. package/dist/lib/biome-host/define-web-biome.d.ts.map +1 -1
  3. package/dist/lib/biome-host/define-web-biome.js +23 -2
  4. package/dist/lib/biome-host/define-web-biome.js.map +1 -1
  5. package/dist/lib/biome-host/frontend-biome.d.ts +4 -3
  6. package/dist/lib/biome-host/frontend-biome.d.ts.map +1 -1
  7. package/dist/lib/biome-host/host-bridge.d.ts +2 -0
  8. package/dist/lib/biome-host/host-bridge.d.ts.map +1 -1
  9. package/dist/lib/biome-host/host-bridge.js.map +1 -1
  10. package/dist/lib/biome-host/i18n-hooks.d.ts +4 -0
  11. package/dist/lib/biome-host/i18n-hooks.d.ts.map +1 -0
  12. package/dist/lib/biome-host/i18n-hooks.js +22 -0
  13. package/dist/lib/biome-host/i18n-hooks.js.map +1 -0
  14. package/dist/lib/biome-host/i18n-port.d.ts +15 -0
  15. package/dist/lib/biome-host/i18n-port.d.ts.map +1 -0
  16. package/dist/lib/biome-host/i18n-port.js +29 -0
  17. package/dist/lib/biome-host/i18n-port.js.map +1 -0
  18. package/dist/lib/biome-host/index.d.ts +2 -1
  19. package/dist/lib/biome-host/index.d.ts.map +1 -1
  20. package/dist/lib/biome-host/index.js +2 -1
  21. package/dist/lib/biome-host/index.js.map +1 -1
  22. package/dist/lib/biome-host/realtime-port.d.ts +1 -1
  23. package/dist/lib/biome-host/realtime-port.d.ts.map +1 -1
  24. package/dist/session-kit/approvals/approval-model.d.ts.map +1 -1
  25. package/dist/session-kit/approvals/approval-model.js.map +1 -1
  26. package/dist/ui/branding/app-branding-provider.d.ts +17 -0
  27. package/dist/ui/branding/app-branding-provider.d.ts.map +1 -0
  28. package/dist/ui/branding/app-branding-provider.js +81 -0
  29. package/dist/ui/branding/app-branding-provider.js.map +1 -0
  30. package/dist/ui/branding/sanitize.d.ts +4 -0
  31. package/dist/ui/branding/sanitize.d.ts.map +1 -0
  32. package/dist/ui/branding/sanitize.js +47 -0
  33. package/dist/ui/branding/sanitize.js.map +1 -0
  34. package/dist/ui/breakpoints.d.ts +2 -0
  35. package/dist/ui/breakpoints.d.ts.map +1 -0
  36. package/dist/ui/breakpoints.js +5 -0
  37. package/dist/ui/breakpoints.js.map +1 -0
  38. package/dist/ui/charts/bar-chart.d.ts +7 -0
  39. package/dist/ui/charts/bar-chart.d.ts.map +1 -0
  40. package/dist/ui/charts/bar-chart.js +47 -0
  41. package/dist/ui/charts/bar-chart.js.map +1 -0
  42. package/dist/ui/charts/chart-frame.d.ts +25 -0
  43. package/dist/ui/charts/chart-frame.d.ts.map +1 -0
  44. package/dist/ui/charts/chart-frame.js +23 -0
  45. package/dist/ui/charts/chart-frame.js.map +1 -0
  46. package/dist/ui/charts/chart-types.d.ts +19 -0
  47. package/dist/ui/charts/chart-types.d.ts.map +1 -0
  48. package/dist/ui/charts/chart-types.js +3 -0
  49. package/dist/ui/charts/chart-types.js.map +1 -0
  50. package/dist/ui/charts/chart-utils.d.ts +22 -0
  51. package/dist/ui/charts/chart-utils.d.ts.map +1 -0
  52. package/dist/ui/charts/chart-utils.js +97 -0
  53. package/dist/ui/charts/chart-utils.js.map +1 -0
  54. package/dist/ui/charts/donut-chart.d.ts +7 -0
  55. package/dist/ui/charts/donut-chart.d.ts.map +1 -0
  56. package/dist/ui/charts/donut-chart.js +55 -0
  57. package/dist/ui/charts/donut-chart.js.map +1 -0
  58. package/dist/ui/charts/index.d.ts +8 -0
  59. package/dist/ui/charts/index.d.ts.map +1 -0
  60. package/dist/ui/charts/index.js +21 -0
  61. package/dist/ui/charts/index.js.map +1 -0
  62. package/dist/ui/charts/line-chart.d.ts +7 -0
  63. package/dist/ui/charts/line-chart.d.ts.map +1 -0
  64. package/dist/ui/charts/line-chart.js +70 -0
  65. package/dist/ui/charts/line-chart.js.map +1 -0
  66. package/dist/ui/charts/sparkline.d.ts +13 -0
  67. package/dist/ui/charts/sparkline.d.ts.map +1 -0
  68. package/dist/ui/charts/sparkline.js +39 -0
  69. package/dist/ui/charts/sparkline.js.map +1 -0
  70. package/dist/ui/hooks/use-is-mobile.d.ts +2 -0
  71. package/dist/ui/hooks/use-is-mobile.d.ts.map +1 -0
  72. package/dist/ui/hooks/use-is-mobile.js +19 -0
  73. package/dist/ui/hooks/use-is-mobile.js.map +1 -0
  74. package/dist/ui/index.d.ts +6 -0
  75. package/dist/ui/index.d.ts.map +1 -1
  76. package/dist/ui/index.js +14 -1
  77. package/dist/ui/index.js.map +1 -1
  78. package/dist/ui/pdf/pdf-preview.d.ts +8 -0
  79. package/dist/ui/pdf/pdf-preview.d.ts.map +1 -0
  80. package/dist/ui/pdf/pdf-preview.js +34 -0
  81. package/dist/ui/pdf/pdf-preview.js.map +1 -0
  82. package/dist/ui/primitives/preview-device-frame.d.ts +9 -0
  83. package/dist/ui/primitives/preview-device-frame.d.ts.map +1 -0
  84. package/dist/ui/primitives/preview-device-frame.js +90 -0
  85. package/dist/ui/primitives/preview-device-frame.js.map +1 -0
  86. package/package.json +8 -7
  87. package/src/lib/biome-host/define-web-biome.test.js +93 -0
  88. package/src/lib/biome-host/define-web-biome.ts +76 -15
  89. package/src/lib/biome-host/frontend-biome.ts +47 -18
  90. package/src/lib/biome-host/host-bridge.ts +13 -0
  91. package/src/lib/biome-host/i18n-hooks.ts +50 -0
  92. package/src/lib/biome-host/i18n-port.test.ts +50 -0
  93. package/src/lib/biome-host/i18n-port.ts +96 -0
  94. package/src/lib/biome-host/index.ts +2 -1
  95. package/src/lib/biome-host/realtime-port.ts +9 -3
  96. package/src/lib/system-bus/enums.ts +1 -1
  97. package/src/lib/system-bus/index.ts +1 -1
  98. package/src/lib/system-bus/intents.ts +1 -1
  99. package/src/session-kit/approvals/ApprovalCard.tsx +1 -1
  100. package/src/session-kit/approvals/ApprovalsCenter.tsx +2 -2
  101. package/src/session-kit/approvals/CapabilityApprovalStickyBar.tsx +1 -1
  102. package/src/session-kit/approvals/Obligation.tsx +1 -1
  103. package/src/session-kit/approvals/ScopedBody.tsx +1 -1
  104. package/src/session-kit/approvals/approval-model.ts +7 -8
  105. package/src/session-kit/approvals/index.ts +1 -1
  106. package/src/session-kit/approvals/obligation-display.ts +1 -1
  107. package/src/session-kit/approvals/risk-accent.ts +1 -1
  108. package/src/session-kit/combobox/Combobox.tsx +1 -1
  109. package/src/session-kit/display/MessageTurn.tsx +1 -1
  110. package/src/session-kit/display/TokenMeter.tsx +1 -1
  111. package/src/session-kit/index.ts +3 -3
  112. package/src/session-kit/lib/enums.ts +1 -1
  113. package/src/session-kit/lib/status-dot.ts +1 -1
  114. package/src/session-kit/primitives/RiskPill.tsx +1 -1
  115. package/src/ui/branding/app-branding-provider.tsx +146 -0
  116. package/src/ui/branding/sanitize.ts +77 -0
  117. package/src/ui/breakpoints.ts +14 -0
  118. package/src/ui/charts/bar-chart.tsx +135 -0
  119. package/src/ui/charts/chart-frame.tsx +112 -0
  120. package/src/ui/charts/chart-types.ts +52 -0
  121. package/src/ui/charts/chart-utils.ts +158 -0
  122. package/src/ui/charts/charts.test.tsx +204 -0
  123. package/src/ui/charts/donut-chart.tsx +111 -0
  124. package/src/ui/charts/index.ts +27 -0
  125. package/src/ui/charts/line-chart.tsx +180 -0
  126. package/src/ui/charts/sparkline.tsx +101 -0
  127. package/src/ui/hooks/use-is-mobile.ts +28 -0
  128. package/src/ui/index.ts +22 -0
  129. package/src/ui/pdf/pdf-preview.tsx +122 -0
  130. package/src/ui/primitives/preview-device-frame.tsx +205 -0
  131. package/dist/lib/biome-host/composition-validation.d.ts +0 -22
  132. package/dist/lib/biome-host/composition-validation.d.ts.map +0 -1
  133. package/dist/lib/biome-host/composition-validation.js +0 -127
  134. package/dist/lib/biome-host/composition-validation.js.map +0 -1
  135. package/dist/lib/biome-host/nav.d.ts +0 -17
  136. package/dist/lib/biome-host/nav.d.ts.map +0 -1
  137. package/dist/lib/biome-host/nav.js +0 -52
  138. package/dist/lib/biome-host/nav.js.map +0 -1
  139. package/dist/registry/lib/composition-validation-host.d.ts +0 -3
  140. package/dist/registry/lib/composition-validation-host.d.ts.map +0 -1
  141. package/dist/registry/lib/composition-validation-host.js +0 -10
  142. package/dist/registry/lib/composition-validation-host.js.map +0 -1
  143. package/src/lib/biome-host/nav.ts +0 -83
package/dist/ui/index.js CHANGED
@@ -17,12 +17,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
17
17
  return (mod && mod.__esModule) ? mod : { "default": mod };
18
18
  };
19
19
  Object.defineProperty(exports, "__esModule", { value: true });
20
- exports.AsyncBoundary = exports.LoadingState = exports.PageToolbar = exports.PageHeader = exports.StateCard = exports.EmptyState = exports.PageShell = exports.ConfirmDeleteDialog = exports.ScopeBadge = exports.StatusBadge = exports.ErrorCard = exports.useDebouncedValue = exports.cn = void 0;
20
+ exports.PdfPreview = exports.AsyncBoundary = exports.LoadingState = exports.PageToolbar = exports.PageHeader = exports.StateCard = exports.EmptyState = exports.PageShell = exports.ConfirmDeleteDialog = exports.ScopeBadge = exports.StatusBadge = exports.ErrorCard = exports.APP_PRIMARY_COLOR_VAR = exports.useAppBranding = exports.AppBrandingProvider = exports.PreviewDeviceFrame = exports.useIsMobile = exports.useDebouncedValue = exports.MOBILE_BREAKPOINT = exports.cn = void 0;
21
21
  var cn_1 = require("./cn");
22
22
  Object.defineProperty(exports, "cn", { enumerable: true, get: function () { return cn_1.cn; } });
23
23
  __exportStar(require("./design-tokens"), exports);
24
+ var breakpoints_1 = require("./breakpoints");
25
+ Object.defineProperty(exports, "MOBILE_BREAKPOINT", { enumerable: true, get: function () { return breakpoints_1.MOBILE_BREAKPOINT; } });
24
26
  var use_debounced_value_1 = require("./hooks/use-debounced-value");
25
27
  Object.defineProperty(exports, "useDebouncedValue", { enumerable: true, get: function () { return use_debounced_value_1.useDebouncedValue; } });
28
+ var use_is_mobile_1 = require("./hooks/use-is-mobile");
29
+ Object.defineProperty(exports, "useIsMobile", { enumerable: true, get: function () { return use_is_mobile_1.useIsMobile; } });
26
30
  __exportStar(require("./primitives/button"), exports);
27
31
  __exportStar(require("./primitives/badge"), exports);
28
32
  __exportStar(require("./primitives/input"), exports);
@@ -50,6 +54,13 @@ __exportStar(require("./primitives/tag-multi-select"), exports);
50
54
  __exportStar(require("./primitives/sheet"), exports);
51
55
  __exportStar(require("./primitives/form-stepper"), exports);
52
56
  __exportStar(require("./primitives/async-combobox"), exports);
57
+ var preview_device_frame_1 = require("./primitives/preview-device-frame");
58
+ Object.defineProperty(exports, "PreviewDeviceFrame", { enumerable: true, get: function () { return preview_device_frame_1.PreviewDeviceFrame; } });
59
+ __exportStar(require("./charts"), exports);
60
+ var app_branding_provider_1 = require("./branding/app-branding-provider");
61
+ Object.defineProperty(exports, "AppBrandingProvider", { enumerable: true, get: function () { return app_branding_provider_1.AppBrandingProvider; } });
62
+ Object.defineProperty(exports, "useAppBranding", { enumerable: true, get: function () { return app_branding_provider_1.useAppBranding; } });
63
+ Object.defineProperty(exports, "APP_PRIMARY_COLOR_VAR", { enumerable: true, get: function () { return app_branding_provider_1.APP_PRIMARY_COLOR_VAR; } });
53
64
  var ErrorCard_1 = require("./chrome/ErrorCard");
54
65
  Object.defineProperty(exports, "ErrorCard", { enumerable: true, get: function () { return __importDefault(ErrorCard_1).default; } });
55
66
  var status_badge_1 = require("./chrome/status-badge");
@@ -71,4 +82,6 @@ var LoadingState_1 = require("./chrome/LoadingState");
71
82
  Object.defineProperty(exports, "LoadingState", { enumerable: true, get: function () { return __importDefault(LoadingState_1).default; } });
72
83
  var AsyncBoundary_1 = require("./chrome/AsyncBoundary");
73
84
  Object.defineProperty(exports, "AsyncBoundary", { enumerable: true, get: function () { return __importDefault(AsyncBoundary_1).default; } });
85
+ var pdf_preview_1 = require("./pdf/pdf-preview");
86
+ Object.defineProperty(exports, "PdfPreview", { enumerable: true, get: function () { return pdf_preview_1.PdfPreview; } });
74
87
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ui/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAYA,2BAA0B;AAAjB,wFAAA,EAAE,OAAA;AAGX,kDAAgC;AAGhC,mEAAgE;AAAvD,wHAAA,iBAAiB,OAAA;AAG1B,sDAAoC;AACpC,qDAAmC;AACnC,qDAAmC;AACnC,qDAAmC;AACnC,sDAAoC;AACpC,sDAAoC;AACpC,oDAAkC;AAClC,wDAAsC;AACtC,oDAAkC;AAClC,6DAA2C;AAC3C,wDAAsC;AACtC,wDAAsC;AACtC,qDAAmC;AACnC,uDAAqC;AACrC,sDAAoC;AACpC,yDAAuC;AACvC,2DAAyC;AACzC,4DAA0C;AAC1C,uDAAqC;AACrC,2DAAyC;AACzC,yDAAuC;AACvC,2DAAyC;AACzC,6DAA2C;AAC3C,gEAA8C;AAC9C,qDAAmC;AACnC,4DAA0C;AAC1C,8DAA4C;AAG5C,gDAAsF;AAA7E,uHAAA,OAAO,OAAa;AAC7B,sDAA4G;AAAnG,4HAAA,OAAO,OAAe;AAC/B,oDAAmF;AAA1E,0HAAA,OAAO,OAAc;AAC9B,wEAGwC;AAFtC,6IAAA,OAAO,OAAuB;AAGhC,kDAA2D;AAAlD,wHAAA,OAAO,OAAa;AAC7B,kDAK6B;AAJ3B,yHAAA,OAAO,OAAc;AAKvB,gDAA+C;AAAtC,sGAAA,SAAS,OAAA;AAClB,kDAAyE;AAAhE,yHAAA,OAAO,OAAc;AAAE,yGAAA,WAAW,OAAA;AAC3C,sDAA0F;AAAjF,6HAAA,OAAO,OAAgB;AAChC,wDAAkE;AAAzD,+HAAA,OAAO,OAAiB"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ui/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAYA,2BAA0B;AAAjB,wFAAA,EAAE,OAAA;AAGX,kDAAgC;AAGhC,6CAAkD;AAAzC,gHAAA,iBAAiB,OAAA;AAG1B,mEAAgE;AAAvD,wHAAA,iBAAiB,OAAA;AAC1B,uDAAoD;AAA3C,4GAAA,WAAW,OAAA;AAGpB,sDAAoC;AACpC,qDAAmC;AACnC,qDAAmC;AACnC,qDAAmC;AACnC,sDAAoC;AACpC,sDAAoC;AACpC,oDAAkC;AAClC,wDAAsC;AACtC,oDAAkC;AAClC,6DAA2C;AAC3C,wDAAsC;AACtC,wDAAsC;AACtC,qDAAmC;AACnC,uDAAqC;AACrC,sDAAoC;AACpC,yDAAuC;AACvC,2DAAyC;AACzC,4DAA0C;AAC1C,uDAAqC;AACrC,2DAAyC;AACzC,yDAAuC;AACvC,2DAAyC;AACzC,6DAA2C;AAC3C,gEAA8C;AAC9C,qDAAmC;AACnC,4DAA0C;AAC1C,8DAA4C;AAC5C,0EAG2C;AAFzC,0HAAA,kBAAkB,OAAA;AAKpB,2CAAyB;AAGzB,0EAM0C;AALxC,4HAAA,mBAAmB,OAAA;AACnB,uHAAA,cAAc,OAAA;AACd,8HAAA,qBAAqB,OAAA;AAMvB,gDAAsF;AAA7E,uHAAA,OAAO,OAAa;AAC7B,sDAA4G;AAAnG,4HAAA,OAAO,OAAe;AAC/B,oDAAmF;AAA1E,0HAAA,OAAO,OAAc;AAC9B,wEAGwC;AAFtC,6IAAA,OAAO,OAAuB;AAGhC,kDAA2D;AAAlD,wHAAA,OAAO,OAAa;AAC7B,kDAK6B;AAJ3B,yHAAA,OAAO,OAAc;AAKvB,gDAA+C;AAAtC,sGAAA,SAAS,OAAA;AAClB,kDAAyE;AAAhE,yHAAA,OAAO,OAAc;AAAE,yGAAA,WAAW,OAAA;AAC3C,sDAA0F;AAAjF,6HAAA,OAAO,OAAgB;AAChC,wDAAkE;AAAzD,+HAAA,OAAO,OAAiB;AAEjC,iDAAqE;AAA5D,yGAAA,UAAU,OAAA"}
@@ -0,0 +1,8 @@
1
+ import { type JSX } from 'react';
2
+ export interface PdfPreviewProps {
3
+ readonly url: string;
4
+ readonly title?: string;
5
+ readonly className?: string;
6
+ }
7
+ export declare function PdfPreview({ url, title, className }: PdfPreviewProps): JSX.Element;
8
+ //# sourceMappingURL=pdf-preview.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pdf-preview.d.ts","sourceRoot":"","sources":["../../../src/ui/pdf/pdf-preview.tsx"],"names":[],"mappings":"AAgBA,OAAO,EAAkC,KAAK,GAAG,EAAE,MAAM,OAAO,CAAC;AAsBjE,MAAM,WAAW,eAAe;IAE9B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAErB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,wBAAgB,UAAU,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,eAAe,GAAG,GAAG,CAAC,OAAO,CA2ElF"}
@@ -0,0 +1,34 @@
1
+ 'use client';
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.PdfPreview = PdfPreview;
5
+ const jsx_runtime_1 = require("react/jsx-runtime");
6
+ const lucide_react_1 = require("lucide-react");
7
+ const react_1 = require("react");
8
+ const react_pdf_1 = require("react-pdf");
9
+ const cn_1 = require("../cn");
10
+ if (!react_pdf_1.pdfjs.GlobalWorkerOptions.workerSrc) {
11
+ react_pdf_1.pdfjs.GlobalWorkerOptions.workerSrc = '/pdf.worker.min.mjs';
12
+ }
13
+ const MIN_SCALE = 0.5;
14
+ const MAX_SCALE = 3;
15
+ const STEP = 0.25;
16
+ const DEFAULT_SCALE = 1;
17
+ const clampScale = (s) => Math.min(MAX_SCALE, Math.max(MIN_SCALE, Math.round(s * 100) / 100));
18
+ function PdfPreview({ url, title, className }) {
19
+ const [numPages, setNumPages] = (0, react_1.useState)(0);
20
+ const [scale, setScale] = (0, react_1.useState)(DEFAULT_SCALE);
21
+ const [error, setError] = (0, react_1.useState)(null);
22
+ const file = (0, react_1.useMemo)(() => ({ url }), [url]);
23
+ const zoomOut = (0, react_1.useCallback)(() => setScale((s) => clampScale(s - STEP)), []);
24
+ const zoomIn = (0, react_1.useCallback)(() => setScale((s) => clampScale(s + STEP)), []);
25
+ const reset = (0, react_1.useCallback)(() => setScale(DEFAULT_SCALE), []);
26
+ const btn = 'inline-flex h-7 w-7 items-center justify-center rounded text-neutral-600 ' +
27
+ 'hover:bg-neutral-200/70 disabled:pointer-events-none disabled:opacity-40 ' +
28
+ 'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-1';
29
+ return ((0, jsx_runtime_1.jsxs)("div", { className: (0, cn_1.cn)('flex h-[520px] flex-col overflow-hidden rounded border border-neutral-200 bg-white', className), children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-1 border-b border-neutral-200 bg-neutral-50 px-2 py-1", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", onClick: zoomOut, disabled: scale <= MIN_SCALE, "aria-label": "Zoom out", className: btn, children: (0, jsx_runtime_1.jsx)(lucide_react_1.ZoomOut, { className: "h-4 w-4" }) }), (0, jsx_runtime_1.jsxs)("span", { className: "w-12 text-center text-xs tabular-nums text-neutral-500", children: [Math.round(scale * 100), "%"] }), (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: zoomIn, disabled: scale >= MAX_SCALE, "aria-label": "Zoom in", className: btn, children: (0, jsx_runtime_1.jsx)(lucide_react_1.ZoomIn, { className: "h-4 w-4" }) }), (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: reset, disabled: scale === DEFAULT_SCALE, "aria-label": "Reset zoom", className: btn, children: (0, jsx_runtime_1.jsx)(lucide_react_1.RotateCcw, { className: "h-4 w-4" }) }), title ? ((0, jsx_runtime_1.jsx)("span", { className: "ml-2 min-w-0 flex-1 truncate text-xs text-neutral-400", title: title, children: title })) : null] }), (0, jsx_runtime_1.jsx)("div", { className: "flex-1 overflow-auto bg-neutral-100 p-3", children: error ? ((0, jsx_runtime_1.jsxs)("div", { className: "p-4 text-xs text-neutral-500", children: ["Couldn't render this PDF (", error, "). Use the download affordance."] })) : ((0, jsx_runtime_1.jsx)(react_pdf_1.Document, { file: file, onLoadSuccess: ({ numPages: n }) => {
30
+ setNumPages(n);
31
+ setError(null);
32
+ }, onLoadError: (e) => setError(e.message), loading: (0, jsx_runtime_1.jsx)("div", { className: "p-4 text-xs text-neutral-500", children: "Loading PDF\u2026" }), className: "flex flex-col items-center gap-3", children: Array.from({ length: numPages }, (_, i) => ((0, jsx_runtime_1.jsx)(react_pdf_1.Page, { pageNumber: i + 1, scale: scale, renderTextLayer: false, renderAnnotationLayer: false, className: "shadow-sm" }, i))) })) })] }));
33
+ }
34
+ //# sourceMappingURL=pdf-preview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pdf-preview.js","sourceRoot":"","sources":["../../../src/ui/pdf/pdf-preview.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import { type ReactNode } from 'react';
2
+ export type PreviewDevice = 'desktop' | 'tablet' | 'mobile' | 'presentation' | 'a4';
3
+ interface PreviewDeviceFrameProps {
4
+ readonly device: PreviewDevice;
5
+ readonly children: ReactNode;
6
+ }
7
+ export declare function PreviewDeviceFrame({ device, children, }: PreviewDeviceFrameProps): import("react").JSX.Element;
8
+ export {};
9
+ //# sourceMappingURL=preview-device-frame.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-device-frame.d.ts","sourceRoot":"","sources":["../../../src/ui/primitives/preview-device-frame.tsx"],"names":[],"mappings":"AAmBA,OAAO,EAAqC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAS1E,MAAM,MAAM,aAAa,GACrB,SAAS,GACT,QAAQ,GACR,QAAQ,GACR,cAAc,GACd,IAAI,CAAC;AAET,UAAU,uBAAuB;IAC/B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;CAC9B;AAoBD,wBAAgB,kBAAkB,CAAC,EACjC,MAAM,EACN,QAAQ,GACT,EAAE,uBAAuB,+BAmBzB"}
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PreviewDeviceFrame = PreviewDeviceFrame;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_1 = require("react");
6
+ const cn_1 = require("../cn");
7
+ const BEZELS = {
8
+ tablet: { width: 768, height: 1024, radius: 18 },
9
+ mobile: { width: 390, height: 844, radius: 36 },
10
+ };
11
+ const PRESENTATION_VIEWPORT = { width: 1280, height: 720, radius: 20 };
12
+ const A4_VIEWPORT = { width: 794, height: 1123, radius: 4 };
13
+ function PreviewDeviceFrame({ device, children, }) {
14
+ if (device === 'desktop') {
15
+ return (0, jsx_runtime_1.jsx)("div", { className: "h-full w-full", children: children });
16
+ }
17
+ if (device === 'presentation') {
18
+ return ((0, jsx_runtime_1.jsx)(ResponsiveViewport, { bezel: PRESENTATION_VIEWPORT, aspectRatio: 16 / 9, children: children }));
19
+ }
20
+ if (device === 'a4') {
21
+ return ((0, jsx_runtime_1.jsx)(ResponsiveViewport, { bezel: A4_VIEWPORT, aspectRatio: 794 / 1123, children: children }));
22
+ }
23
+ return (0, jsx_runtime_1.jsx)(ScaledBezel, { bezel: BEZELS[device], children: children });
24
+ }
25
+ function ScaledBezel({ bezel, children }) {
26
+ const containerRef = (0, react_1.useRef)(null);
27
+ const [scale, setScale] = (0, react_1.useState)(1);
28
+ (0, react_1.useLayoutEffect)(() => {
29
+ const el = containerRef.current;
30
+ if (!el)
31
+ return;
32
+ const compute = () => {
33
+ const { clientWidth, clientHeight } = el;
34
+ if (clientWidth === 0 || clientHeight === 0)
35
+ return;
36
+ const margin = 24;
37
+ const availW = Math.max(0, clientWidth - margin);
38
+ const availH = Math.max(0, clientHeight - margin);
39
+ const next = Math.min(1, availW / bezel.width, availH / bezel.height);
40
+ setScale(next);
41
+ };
42
+ compute();
43
+ const observer = new ResizeObserver(compute);
44
+ observer.observe(el);
45
+ return () => observer.disconnect();
46
+ }, [bezel.width, bezel.height]);
47
+ return ((0, jsx_runtime_1.jsx)("div", { ref: containerRef, className: "flex h-full w-full items-center justify-center overflow-hidden bg-paper-elev/60 p-2", children: (0, jsx_runtime_1.jsx)("div", { className: (0, cn_1.cn)('relative shrink-0 overflow-hidden bg-paper shadow-2xl ring-1 ring-border/75'), style: {
48
+ width: bezel.width,
49
+ height: bezel.height,
50
+ borderRadius: bezel.radius,
51
+ transform: `scale(${scale})`,
52
+ transformOrigin: 'center center',
53
+ }, children: children }) }));
54
+ }
55
+ function ResponsiveViewport({ bezel, aspectRatio, children, }) {
56
+ const containerRef = (0, react_1.useRef)(null);
57
+ const [size, setSize] = (0, react_1.useState)(() => ({
58
+ width: bezel.width,
59
+ height: bezel.height,
60
+ }));
61
+ (0, react_1.useLayoutEffect)(() => {
62
+ const el = containerRef.current;
63
+ if (!el)
64
+ return;
65
+ const compute = () => {
66
+ const { clientWidth, clientHeight } = el;
67
+ if (clientWidth === 0 || clientHeight === 0)
68
+ return;
69
+ const margin = 24;
70
+ const availW = Math.max(0, clientWidth - margin);
71
+ const availH = Math.max(0, clientHeight - margin);
72
+ const nextWidth = Math.min(availW, availH * aspectRatio);
73
+ const nextHeight = nextWidth / aspectRatio;
74
+ setSize({
75
+ width: Math.max(1, Math.round(nextWidth)),
76
+ height: Math.max(1, Math.round(nextHeight)),
77
+ });
78
+ };
79
+ compute();
80
+ const observer = new ResizeObserver(compute);
81
+ observer.observe(el);
82
+ return () => observer.disconnect();
83
+ }, [bezel.width, bezel.height]);
84
+ return ((0, jsx_runtime_1.jsx)("div", { ref: containerRef, className: "flex h-full w-full items-center justify-center overflow-hidden bg-paper-elev/60 p-2", children: (0, jsx_runtime_1.jsx)("div", { className: (0, cn_1.cn)('relative shrink-0 overflow-hidden bg-paper shadow-2xl ring-1 ring-border/75'), style: {
85
+ width: size.width,
86
+ height: size.height,
87
+ borderRadius: bezel.radius,
88
+ }, children: children }) }));
89
+ }
90
+ //# sourceMappingURL=preview-device-frame.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-device-frame.js","sourceRoot":"","sources":["../../../src/ui/primitives/preview-device-frame.tsx"],"names":[],"mappings":";;AA0DA,gDAsBC;;AA7DD,iCAA0E;AAE1E,8BAA2B;AAyB3B,MAAM,MAAM,GAGR;IACF,MAAM,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE;IAChD,MAAM,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE;CAChD,CAAC;AAEF,MAAM,qBAAqB,GAAU,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;AAE9E,MAAM,WAAW,GAAU,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;AAEnE,SAAgB,kBAAkB,CAAC,EACjC,MAAM,EACN,QAAQ,GACgB;IACxB,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,gCAAK,SAAS,EAAC,eAAe,YAAE,QAAQ,GAAO,CAAC;IACzD,CAAC;IACD,IAAI,MAAM,KAAK,cAAc,EAAE,CAAC;QAC9B,OAAO,CACL,uBAAC,kBAAkB,IAAC,KAAK,EAAE,qBAAqB,EAAE,WAAW,EAAE,EAAE,GAAG,CAAC,YAClE,QAAQ,GACU,CACtB,CAAC;IACJ,CAAC;IACD,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,OAAO,CACL,uBAAC,kBAAkB,IAAC,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,GAAG,GAAG,IAAI,YAC5D,QAAQ,GACU,CACtB,CAAC;IACJ,CAAC;IACD,OAAO,uBAAC,WAAW,IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,YAAG,QAAQ,GAAe,CAAC;AACtE,CAAC;AAYD,SAAS,WAAW,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAoB;IACxD,MAAM,YAAY,GAAG,IAAA,cAAM,EAAwB,IAAI,CAAC,CAAC;IACzD,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,IAAA,gBAAQ,EAAC,CAAC,CAAC,CAAC;IAEtC,IAAA,uBAAe,EAAC,GAAG,EAAE;QACnB,MAAM,EAAE,GAAG,YAAY,CAAC,OAAO,CAAC;QAChC,IAAI,CAAC,EAAE;YAAE,OAAO;QAChB,MAAM,OAAO,GAAG,GAAS,EAAE;YACzB,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,GAAG,EAAE,CAAC;YACzC,IAAI,WAAW,KAAK,CAAC,IAAI,YAAY,KAAK,CAAC;gBAAE,OAAO;YAEpD,MAAM,MAAM,GAAG,EAAE,CAAC;YAClB,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC,CAAC;YACjD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,YAAY,GAAG,MAAM,CAAC,CAAC;YAClD,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;YACtE,QAAQ,CAAC,IAAI,CAAC,CAAC;QACjB,CAAC,CAAC;QACF,OAAO,EAAE,CAAC;QACV,MAAM,QAAQ,GAAG,IAAI,cAAc,CAAC,OAAO,CAAC,CAAC;QAC7C,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACrB,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;IACrC,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IAEhC,OAAO,CACL,gCACE,GAAG,EAAE,YAAY,EACjB,SAAS,EAAC,qFAAqF,YAE/F,gCACE,SAAS,EAAE,IAAA,OAAE,EACX,6EAA6E,CAC9E,EACD,KAAK,EAAE;gBACL,KAAK,EAAE,KAAK,CAAC,KAAK;gBAClB,MAAM,EAAE,KAAK,CAAC,MAAM;gBACpB,YAAY,EAAE,KAAK,CAAC,MAAM;gBAK1B,SAAS,EAAE,SAAS,KAAK,GAAG;gBAC5B,eAAe,EAAE,eAAe;aACjC,YAEA,QAAQ,GACL,GACF,CACP,CAAC;AACJ,CAAC;AAYD,SAAS,kBAAkB,CAAC,EAC1B,KAAK,EACL,WAAW,EACX,QAAQ,GACgB;IACxB,MAAM,YAAY,GAAG,IAAA,cAAM,EAAwB,IAAI,CAAC,CAAC;IACzD,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,IAAA,gBAAQ,EAAC,GAAG,EAAE,CAAC,CAAC;QACtC,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,MAAM,EAAE,KAAK,CAAC,MAAM;KACrB,CAAC,CAAC,CAAC;IAEJ,IAAA,uBAAe,EAAC,GAAG,EAAE;QACnB,MAAM,EAAE,GAAG,YAAY,CAAC,OAAO,CAAC;QAChC,IAAI,CAAC,EAAE;YAAE,OAAO;QAChB,MAAM,OAAO,GAAG,GAAS,EAAE;YACzB,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,GAAG,EAAE,CAAC;YACzC,IAAI,WAAW,KAAK,CAAC,IAAI,YAAY,KAAK,CAAC;gBAAE,OAAO;YACpD,MAAM,MAAM,GAAG,EAAE,CAAC;YAClB,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC,CAAC;YACjD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,YAAY,GAAG,MAAM,CAAC,CAAC;YAClD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC;YACzD,MAAM,UAAU,GAAG,SAAS,GAAG,WAAW,CAAC;YAC3C,OAAO,CAAC;gBACN,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;gBACzC,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;aAC5C,CAAC,CAAC;QACL,CAAC,CAAC;QACF,OAAO,EAAE,CAAC;QACV,MAAM,QAAQ,GAAG,IAAI,cAAc,CAAC,OAAO,CAAC,CAAC;QAC7C,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACrB,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;IACrC,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IAEhC,OAAO,CACL,gCACE,GAAG,EAAE,YAAY,EACjB,SAAS,EAAC,qFAAqF,YAE/F,gCACE,SAAS,EAAE,IAAA,OAAE,EACX,6EAA6E,CAC9E,EACD,KAAK,EAAE;gBACL,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,YAAY,EAAE,KAAK,CAAC,MAAM;aAC3B,YAEA,QAAQ,GACL,GACF,CACP,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xemahq/ui-kernel",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
4
4
  "description": "Host-framework-agnostic UI kernel for the Xema OS. Defines the SystemBus orchestration contract (capability.invoke, cross-biome intents, command palette, xema:// deeplinks, window manager) AND the biome-host contract surface (FrontendBiome/FrontendBiomeFactory, HostBridge, the singleton biomeRegistry, session contributions) that every frontend biome composes against. No Vite, Next.js, or React-Router — React itself IS allowed as the shared component model (the contracts traffic in ReactNode/ComponentType and a React context). Concrete host adapters (router/auth/toast wiring) live in separate packages that consume this kernel. The SystemBus is pure orchestration: it never authorizes, the backend capability-router enforces all policy.",
5
5
  "keywords": [
6
6
  "xema",
@@ -12,16 +12,15 @@
12
12
  "deeplink",
13
13
  "intents"
14
14
  ],
15
+ "license": "Apache-2.0",
16
+ "author": "Neuralchowder Inc. <developer@xema.dev> (https://xema.dev)",
15
17
  "homepage": "https://docs.xema.dev/biomes",
16
- "bugs": {
17
- "url": "https://github.com/xema-dev/xema-monorepo/issues"
18
- },
19
18
  "repository": {
20
19
  "type": "git",
21
- "url": "git+https://github.com/xema-dev/xema-monorepo.git",
20
+ "url": "git+https://github.com/xema-dev/xema-kernel-sdk.git",
22
21
  "directory": "packages/ui/xema-ui-kernel"
23
22
  },
24
- "license": "Apache-2.0",
23
+ "bugs": "https://github.com/xema-dev/xema-kernel-sdk/issues",
25
24
  "publishConfig": {
26
25
  "access": "public",
27
26
  "registry": "https://registry.npmjs.org/"
@@ -54,6 +53,7 @@
54
53
  "class-variance-authority": "^0.7.1",
55
54
  "clsx": "^2.1.1",
56
55
  "lucide-react": "^0.462.0",
56
+ "react-pdf": "^9.1.1",
57
57
  "react-resizable-panels": "^4.9.0",
58
58
  "tailwind-merge": "^2.5.4"
59
59
  },
@@ -74,7 +74,7 @@
74
74
  "prettier": "3.6.2",
75
75
  "react": "^18.3.1",
76
76
  "typescript": "5.9.3",
77
- "@xemahq/kernel-contracts": "^0.14.0"
77
+ "@xemahq/kernel-contracts": "^0.36.0"
78
78
  },
79
79
  "exports": {
80
80
  ".": {
@@ -101,6 +101,7 @@
101
101
  "scripts": {
102
102
  "clean": "rm -rf dist",
103
103
  "build": "tsc -p tsconfig.json",
104
+ "test": "echo '[ui-kernel] *.{test,spec} files run under host-web vitest (this package carries no vitest dep by design — see tsconfig exclude). Write tests with vitest; the frontend-bridge sync fails on a node:test file.'",
104
105
  "format": "prettier --write \"src/**/*.{ts,tsx}\"",
105
106
  "lint": "tsc -p tsconfig.lint.json --noEmit",
106
107
  "typecheck": "tsc -p tsconfig.lint.json --noEmit"
@@ -0,0 +1,93 @@
1
+ // Test suite for defineWebBiome's define-time scope/access validation and the
2
+ // chrome default. Runs on Node's native test runner against the COMPILED dist
3
+ // (CommonJS), so the package needs no jest/vitest/ts-jest toolchain — the
4
+ // `test` script builds first, then runs this file with `node --test`.
5
+ //
6
+ // Covered:
7
+ // 1. A `public` page with access `'member'` (or omitted) throws at define time.
8
+ // 2. A `public` + access `'public'` page is accepted; its route carries no
9
+ // nav item and mounts with access `'public'`.
10
+ // 3. A non-public page declaring access `'public'` throws at define time.
11
+ // 4. `chrome` defaults to `'host'` when a page omits it; `'none'` passes through.
12
+
13
+ 'use strict';
14
+
15
+ const test = require('node:test');
16
+ const assert = require('node:assert/strict');
17
+
18
+ const { defineWebBiome } = require('../../../dist/lib/biome-host/define-web-biome.js');
19
+
20
+ const load = () => Promise.resolve({ default: () => null });
21
+ const bridge = /** @type {any} */ ({});
22
+
23
+ test('rejects a public page whose access is not public (member default)', () => {
24
+ assert.throws(
25
+ () =>
26
+ defineWebBiome({
27
+ id: 'test-biome',
28
+ displayName: 'Test',
29
+ pages: [{ slug: 'ext/:id', label: 'Ext', scope: 'public', load }],
30
+ }),
31
+ /a 'public' scope page MUST declare access: 'public'/,
32
+ );
33
+ });
34
+
35
+ test('rejects a public page with an explicit non-public access tier', () => {
36
+ assert.throws(
37
+ () =>
38
+ defineWebBiome({
39
+ id: 'test-biome',
40
+ displayName: 'Test',
41
+ pages: [
42
+ { slug: 'ext/:id', label: 'Ext', scope: 'public', access: 'org-admin', load },
43
+ ],
44
+ }),
45
+ /a 'public' scope page MUST declare access: 'public'/,
46
+ );
47
+ });
48
+
49
+ test('rejects a non-public page that declares access public', () => {
50
+ assert.throws(
51
+ () =>
52
+ defineWebBiome({
53
+ id: 'test-biome',
54
+ displayName: 'Test',
55
+ pages: [{ slug: 'system/x', label: 'X', access: 'public', load }],
56
+ }),
57
+ /access: 'public' is only valid on a 'public' scope page/,
58
+ );
59
+ });
60
+
61
+ test('accepts a public + access:public page and emits no nav item', () => {
62
+ const factory = defineWebBiome({
63
+ id: 'test-biome',
64
+ displayName: 'Test',
65
+ pages: [
66
+ { slug: 'ext/:id', label: 'Ext', scope: 'public', access: 'public', load },
67
+ ],
68
+ });
69
+ const biome = factory(bridge);
70
+ assert.equal(biome.routes.length, 1);
71
+ const route = biome.routes[0];
72
+ assert.equal(route.path, 'ext/:id');
73
+ assert.equal(route.access, 'public');
74
+ assert.equal(route.projectScoped, false);
75
+ // Public routes never enter the authenticated app rail.
76
+ assert.equal(biome.navItems.length, 0);
77
+ });
78
+
79
+ test('chrome defaults to host and none passes through', () => {
80
+ const factory = defineWebBiome({
81
+ id: 'test-biome',
82
+ displayName: 'Test',
83
+ pages: [
84
+ { slug: 'system/a', label: 'A', load },
85
+ { slug: 'system/b', label: 'B', chrome: 'none', load },
86
+ ],
87
+ });
88
+ const biome = factory(bridge);
89
+ const a = biome.routes.find((r) => r.path === 'system/a');
90
+ const b = biome.routes.find((r) => r.path === 'system/b');
91
+ assert.equal(a.chrome, 'host');
92
+ assert.equal(b.chrome, 'none');
93
+ });
@@ -14,10 +14,10 @@
14
14
  * `<Suspense fallback={null}>` boundary via {@link lazyRoute}.
15
15
  *
16
16
  * Authors declare WHAT each surface is (`category`) and let the HOST own WHERE
17
- * it renders in the global menu — preferring `category` over the legacy
18
- * `section`/`weight`. `init`/`dispose`/`panels`/`session`/`outputRenderers`
19
- * pass through to the `FrontendBiome` untouched, so bespoke biomes (one-shot
20
- * registrations, slot fillers, session contributions) still use this helper.
17
+ * it renders in the global menu. `init`/`dispose`/`panels`/`session`/
18
+ * `outputRenderers` pass through to the `FrontendBiome` untouched, so bespoke
19
+ * biomes (one-shot registrations, slot fillers, session contributions) still
20
+ * use this helper.
21
21
  *
22
22
  * Host-framework-agnostic: pure React (`lazy`/`Suspense`/`createElement` via
23
23
  * `lazyRoute`), no router and no Next.js.
@@ -44,8 +44,10 @@ import type {
44
44
  FrontendBiome,
45
45
  FrontendBiomeFactory,
46
46
  NavItemContribution,
47
+ PageChrome,
47
48
  RouteAccess,
48
49
  RouteContribution,
50
+ RouteScope,
49
51
  } from './frontend-biome';
50
52
  import type { ComponentType } from 'react';
51
53
 
@@ -70,23 +72,31 @@ export interface WebBiomePage {
70
72
  /**
71
73
  * Host menu category — the biome's INTENT (`primary` | `build` | `operate` |
72
74
  * `knowledge` | `admin` | `account`). The host maps it to a concrete rail
73
- * group + order. Prefer this over `weight`.
75
+ * group + order.
74
76
  */
75
77
  readonly category?: string;
76
78
  /**
77
79
  * Route scope. `'org'` (default) mounts at the org root; `'project'` mounts
78
- * under `/projects/:projectId`. Maps to {@link RouteContribution.projectScoped}.
80
+ * under the canonical project-scoped route prefix; `'public'` mounts OUTSIDE
81
+ * the host's authenticated group (no Keycloak session, no login redirect, no
82
+ * org switcher). Maps to {@link RouteContribution.projectScoped} for the
83
+ * authenticated scopes; a `'public'` page MUST also declare
84
+ * `access: 'public'` (enforced at define time).
85
+ */
86
+ readonly scope?: RouteScope;
87
+ /**
88
+ * Access tier required to render the route. Defaults to `'member'`. A
89
+ * `'public'` page (scope `'public'`) MUST set this to `'public'`; a non-public
90
+ * page MUST NOT — both contradictions are rejected at define time.
79
91
  */
80
- readonly scope?: 'org' | 'project';
81
- /** Access tier required to render the route. Defaults to `'member'`. */
82
92
  readonly access?: RouteAccess;
83
- /** Dynamic import of the page module (`() => import('./pages/X')`). */
84
- readonly load: () => Promise<{ default: ComponentType<unknown> }>;
85
93
  /**
86
- * Optional legacy intra-category sort weight. Prefer host-owned `category`
87
- * ordering; retained only as a tiebreak for un-migrated taxonomies.
94
+ * Whether the host wraps the page in its application shell. Defaults to
95
+ * `'host'`; `'none'` renders with no app rail, topbar, or org switcher.
88
96
  */
89
- readonly weight?: number;
97
+ readonly chrome?: PageChrome;
98
+ /** Dynamic import of the page module (`() => import('./pages/X')`). */
99
+ readonly load: () => Promise<{ default: ComponentType<unknown> }>;
90
100
  /**
91
101
  * When true the route is registered but NO nav item is emitted — for hidden
92
102
  * surfaces such as a `:param` detail route reached only by deeplink.
@@ -113,6 +123,47 @@ export interface DefineWebBiomeOptions {
113
123
  readonly outputRenderers?: FrontendBiome['outputRenderers'];
114
124
  }
115
125
 
126
+ /** Default host-shell mode a page renders in when it omits `chrome`. */
127
+ const DEFAULT_PAGE_CHROME: PageChrome = 'host';
128
+
129
+ /**
130
+ * Reject the two `scope`/`access` contradictions at define time (module load,
131
+ * when the biome calls `defineWebBiome(...)` at its top level) — fail fast, do
132
+ * NOT silently coerce:
133
+ *
134
+ * 1. A `public` page with any `access` other than `'public'`: a public route
135
+ * is un-authenticated, so a privilege tier is meaningless. The author must
136
+ * set `access: 'public'` explicitly (an omitted `access` defaults to
137
+ * `'member'`, which is exactly this contradiction).
138
+ * 2. A non-`public` page declaring `access: 'public'`: `'public'` is the
139
+ * no-auth marker; an org/project route is always authenticated.
140
+ *
141
+ * `scope` defaults to `'org'` and `access` defaults to `'member'` for the
142
+ * purpose of this check, matching the emit defaults below.
143
+ */
144
+ function assertPageScopeAccessLocked(biomeId: string, page: WebBiomePage): void {
145
+ const scope: RouteScope = page.scope ?? 'org';
146
+ const access: RouteAccess = page.access ?? 'member';
147
+ const where = `web biome '${biomeId}' page '${page.slug}'`;
148
+
149
+ if (scope === 'public' && access !== 'public') {
150
+ throw new Error(
151
+ `${where}: a 'public' scope page MUST declare access: 'public' ` +
152
+ `(got access: '${access}'). A public route is un-authenticated, so an ` +
153
+ `authenticated privilege tier is a contradiction. Set access: 'public' ` +
154
+ `or change the scope.`,
155
+ );
156
+ }
157
+ if (scope !== 'public' && access === 'public') {
158
+ throw new Error(
159
+ `${where}: access: 'public' is only valid on a 'public' scope page ` +
160
+ `(got scope: '${scope}'). '${access}' is the no-auth marker; an ` +
161
+ `org/project route is always authenticated. Set scope: 'public' or ` +
162
+ `change the access tier.`,
163
+ );
164
+ }
165
+ }
166
+
116
167
  /**
117
168
  * Build the `FrontendBiomeFactory` a web biome default-exports. See the
118
169
  * module doc-comment for the rationale and an example.
@@ -120,6 +171,13 @@ export interface DefineWebBiomeOptions {
120
171
  export function defineWebBiome(
121
172
  options: DefineWebBiomeOptions,
122
173
  ): FrontendBiomeFactory {
174
+ // Validate every page's scope/access pairing NOW, at module load, so a
175
+ // contradiction surfaces the instant the biome module is imported — not
176
+ // lazily when the host first calls the factory.
177
+ for (const page of options.pages) {
178
+ assertPageScopeAccessLocked(options.id, page);
179
+ }
180
+
123
181
  return () => {
124
182
  const navItems: NavItemContribution[] = [];
125
183
  const routes: RouteContribution[] = [];
@@ -129,17 +187,20 @@ export function defineWebBiome(
129
187
  path: page.slug,
130
188
  projectScoped: page.scope === 'project',
131
189
  element: lazyRoute(page.load),
190
+ chrome: page.chrome ?? DEFAULT_PAGE_CHROME,
132
191
  ...(page.access ? { access: page.access } : {}),
133
192
  });
134
193
 
135
- if (!page.navHidden) {
194
+ // Public routes mount OUTSIDE the authenticated group and never belong in
195
+ // the authenticated app rail — so they contribute no nav item regardless
196
+ // of `navHidden`. Authenticated pages emit a nav item unless hidden.
197
+ if (!page.navHidden && page.scope !== 'public') {
136
198
  navItems.push({
137
199
  id: page.id ?? page.slug,
138
200
  label: page.label,
139
201
  route: page.slug,
140
202
  ...(page.icon ? { icon: page.icon } : {}),
141
203
  ...(page.category !== undefined ? { category: page.category } : {}),
142
- ...(page.weight !== undefined ? { weight: page.weight } : {}),
143
204
  });
144
205
  }
145
206
  }
@@ -33,22 +33,10 @@ export interface NavItemContribution {
33
33
  * information-architecture: it maps a category to a concrete rail group,
34
34
  * heading, and order. Authors declare WHAT a surface is, not WHERE in the
35
35
  * global menu it renders — so the platform can re-organise the menu without
36
- * editing every biome. Prefer this over `section`/`weight`. When omitted,
37
- * the host falls back to its own taxonomy (keyed on the nav-item id) and,
38
- * last, to the legacy `section`.
36
+ * editing every biome. When omitted, the host falls back to its own
37
+ * taxonomy (keyed on the nav-item id).
39
38
  */
40
39
  readonly category?: string;
41
- /**
42
- * @deprecated Legacy direct section-heading placement. The host now owns
43
- * grouping via `category` + its taxonomy; retained only until every biome
44
- * declares a `category`. Do NOT use in new biomes.
45
- */
46
- readonly section?: string;
47
- /**
48
- * @deprecated Legacy intra-section sort weight. Ordering is host-owned per
49
- * category now. Retained only for un-migrated biomes.
50
- */
51
- readonly weight?: number;
52
40
  }
53
41
 
54
42
  /**
@@ -56,8 +44,42 @@ export interface NavItemContribution {
56
44
  * declaratively — nav-hide is NOT a security boundary, a deeplink bypasses it.
57
45
  * Precedence (least → most privileged): `member` < `org-admin` <
58
46
  * `platform-admin`. Defaults to `member` when a route omits the field.
47
+ *
48
+ * `public` is NOT a privilege tier — it is the *no-auth* marker: a `public`
49
+ * route requires no Keycloak session at all and mounts OUTSIDE the host's
50
+ * authenticated route group. It is value-locked to `RouteScope` `'public'`
51
+ * (`defineWebBiome` rejects any other pairing at define time), so a route is
52
+ * public in BOTH senses or in neither. Closed set — a new member is a
53
+ * one-line change here and one `case` in the host resolver's exhaustive
54
+ * `hasRouteAccess` switch.
55
+ */
56
+ export type RouteAccess = 'member' | 'org-admin' | 'platform-admin' | 'public';
57
+
58
+ /**
59
+ * Where a route mounts in the host's URL/auth topology. Closed set — adding a
60
+ * member is a one-line change here (and the single mapping in `defineWebBiome`
61
+ * plus one resolver partition in the host).
62
+ *
63
+ * - `org` — mounts at the org root, inside the authenticated group.
64
+ * - `project` — mounts under the project-scoped route prefix, inside the
65
+ * authenticated group.
66
+ * - `public` — mounts OUTSIDE the authenticated group; no Keycloak session,
67
+ * no login redirect, no org switcher. Locked to `RouteAccess` `'public'`.
68
+ */
69
+ export type RouteScope = 'org' | 'project' | 'public';
70
+
71
+ /**
72
+ * Whether a page renders wrapped in the host application shell (app rail,
73
+ * topbar, org switcher) or standalone. Closed set — a new member is a one-line
74
+ * change here. Defaults to `'host'` when a page omits it.
75
+ *
76
+ * - `host` — the default; the host renders its chrome around the page.
77
+ * - `none` — the host renders the page with NO app rail, topbar, or org
78
+ * switcher (a full-bleed surface). `public` routes are structurally
79
+ * chromeless (their route group ships no shell); `none` also lets an
80
+ * authenticated route opt out of the shell.
59
81
  */
60
- export type RouteAccess = 'member' | 'org-admin' | 'platform-admin';
82
+ export type PageChrome = 'host' | 'none';
61
83
 
62
84
  export interface RouteContribution {
63
85
  /** React-router path. May include params. */
@@ -75,9 +97,16 @@ export interface RouteContribution {
75
97
  readonly projectScoped: boolean;
76
98
  /**
77
99
  * Access tier required to render this route. The host resolver enforces it
78
- * declaratively. Defaults to `'member'` when omitted.
100
+ * declaratively. Defaults to `'member'` when omitted. `'public'` marks a
101
+ * no-auth route the host mounts outside its authenticated group.
79
102
  */
80
103
  readonly access?: RouteAccess;
104
+ /**
105
+ * Whether the host wraps this route in its application shell. Defaults to
106
+ * `'host'` when omitted; `'none'` renders the page with no app rail,
107
+ * topbar, or org switcher. See {@link PageChrome}.
108
+ */
109
+ readonly chrome?: PageChrome;
81
110
  }
82
111
 
83
112
  /**
@@ -194,7 +223,7 @@ export interface OutputRendererContribution {
194
223
  *
195
224
  * This factory shape keeps biome code free of any direct dependency
196
225
  * on host-shell primitives (router, auth client, toast library), which
197
- * is what allows the same biome package to load into a Vite SPA host
198
- * today and a Next.js host tomorrow.
226
+ * is what allows the same biome package to load into one host runtime
227
+ * today and a different host runtime tomorrow.
199
228
  */
200
229
  export type FrontendBiomeFactory = (bridge: HostBridge) => FrontendBiome;
@@ -5,6 +5,7 @@ import { createContext, useContext, type ReactNode } from 'react';
5
5
  import type { CapabilityPort } from '../capabilities';
6
6
  import type { SystemBus } from '../system-bus';
7
7
 
8
+ import type { I18nSource } from './i18n-port';
8
9
  import type { RealtimeSource } from './realtime-port';
9
10
 
10
11
  /**
@@ -235,6 +236,18 @@ export interface HostBridge {
235
236
  * actionable error if a biome uses them before the host wires `bridge.realtime`.
236
237
  */
237
238
  readonly realtime?: RealtimeSource;
239
+ /**
240
+ * Localization port. The HOST injects a concrete {@link I18nSource} backed by
241
+ * its locale provider (authoritative locale from org/app/user settings +
242
+ * catalog loading); biomes reach it through the kernel `useT` / `useLocale`
243
+ * hooks so biome code never imports an i18n library directly — same decoupling
244
+ * as `navigation` / `realtime`.
245
+ *
246
+ * OPTIONAL: a host without localization omits it; the kernel `useT`/`useLocale`
247
+ * hooks fail fast with an actionable error if a biome uses them before the host
248
+ * wires `bridge.i18n`.
249
+ */
250
+ readonly i18n?: I18nSource;
238
251
  /**
239
252
  * Error-decoding surface — the canonical user-facing error decoder. Defaults
240
253
  * (in the kernel default bridge wiring) to {@link getUserFacingErrorMessage};