@waveinno/ui 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/LICENSE +5 -0
  3. package/README.md +64 -2
  4. package/dist/brand/waveinno-logo.svg +1 -0
  5. package/dist/components/brand/index.d.ts +3 -0
  6. package/dist/components/brand/index.js +1 -0
  7. package/dist/components/brand/logo.d.ts +89 -0
  8. package/dist/components/brand/logo.js +1 -0
  9. package/dist/components/brand/waveinno-logo.d.ts +52 -0
  10. package/dist/components/brand/waveinno-logo.js +1 -0
  11. package/dist/components/charts/bar-chart.d.ts +20 -0
  12. package/dist/components/charts/bar-chart.js +1 -0
  13. package/dist/components/charts/bar-list.d.ts +27 -0
  14. package/dist/components/charts/bar-list.js +1 -0
  15. package/dist/components/charts/chart-card.d.ts +32 -0
  16. package/dist/components/charts/chart-card.js +1 -0
  17. package/dist/components/charts/donut-chart.d.ts +27 -0
  18. package/dist/components/charts/donut-chart.js +1 -0
  19. package/dist/components/charts/gauge-chart.d.ts +21 -0
  20. package/dist/components/charts/gauge-chart.js +1 -0
  21. package/dist/components/charts/heatmap.d.ts +23 -0
  22. package/dist/components/charts/heatmap.js +1 -0
  23. package/dist/components/charts/index.d.ts +10 -0
  24. package/dist/components/charts/index.js +1 -0
  25. package/dist/components/charts/line-chart.d.ts +24 -0
  26. package/dist/components/charts/line-chart.js +1 -0
  27. package/dist/components/charts/palette.d.ts +39 -0
  28. package/dist/components/charts/palette.js +1 -0
  29. package/dist/components/charts/trend-chart.d.ts +22 -0
  30. package/dist/components/charts/trend-chart.js +1 -0
  31. package/dist/components/patterns/activity-feed.d.ts +28 -0
  32. package/dist/components/patterns/activity-feed.js +1 -0
  33. package/dist/components/patterns/data-table-cells.d.ts +39 -0
  34. package/dist/components/patterns/data-table-cells.js +1 -0
  35. package/dist/components/patterns/data-table-edit.d.ts +56 -0
  36. package/dist/components/patterns/data-table-edit.js +1 -0
  37. package/dist/components/patterns/data-table-filters.d.ts +84 -0
  38. package/dist/components/patterns/data-table-filters.js +1 -0
  39. package/dist/components/patterns/data-table.d.ts +76 -0
  40. package/dist/components/patterns/data-table.js +2 -0
  41. package/dist/components/patterns/index.d.ts +16 -0
  42. package/dist/components/patterns/index.js +1 -0
  43. package/dist/components/patterns/kanban.d.ts +28 -0
  44. package/dist/components/patterns/kanban.js +1 -0
  45. package/dist/components/patterns/kpi-card.d.ts +22 -0
  46. package/dist/components/patterns/kpi-card.js +1 -0
  47. package/dist/components/patterns/money.d.ts +28 -0
  48. package/dist/components/patterns/money.js +1 -0
  49. package/dist/components/patterns/page-header.d.ts +38 -0
  50. package/dist/components/patterns/page-header.js +1 -0
  51. package/dist/components/patterns/page-hero.d.ts +50 -0
  52. package/dist/components/patterns/page-hero.js +1 -0
  53. package/dist/components/patterns/page-shell.d.ts +27 -0
  54. package/dist/components/patterns/page-shell.js +1 -0
  55. package/dist/components/patterns/stat-card.d.ts +56 -0
  56. package/dist/components/patterns/stat-card.js +1 -0
  57. package/dist/components/patterns/states.d.ts +27 -0
  58. package/dist/components/patterns/states.js +1 -0
  59. package/dist/components/patterns/timeline.d.ts +100 -0
  60. package/dist/components/patterns/timeline.js +1 -0
  61. package/dist/components/ui/avatar.d.ts +37 -0
  62. package/dist/components/ui/avatar.js +1 -0
  63. package/dist/components/ui/badge.d.ts +41 -0
  64. package/dist/components/ui/badge.js +1 -0
  65. package/dist/components/ui/button.d.ts +25 -0
  66. package/dist/components/ui/button.js +1 -0
  67. package/dist/components/ui/calendar.d.ts +42 -0
  68. package/dist/components/ui/calendar.js +1 -0
  69. package/dist/components/ui/card.d.ts +11 -0
  70. package/dist/components/ui/card.js +1 -0
  71. package/dist/components/ui/checkbox.d.ts +3 -0
  72. package/dist/components/ui/checkbox.js +1 -0
  73. package/dist/components/ui/combobox.d.ts +47 -0
  74. package/dist/components/ui/combobox.js +1 -0
  75. package/dist/components/ui/confirm-dialog.d.ts +34 -0
  76. package/dist/components/ui/confirm-dialog.js +1 -0
  77. package/dist/components/ui/date-picker.d.ts +80 -0
  78. package/dist/components/ui/date-picker.js +1 -0
  79. package/dist/components/ui/dialog.d.ts +54 -0
  80. package/dist/components/ui/dialog.js +1 -0
  81. package/dist/components/ui/dropdown-menu.d.ts +44 -0
  82. package/dist/components/ui/dropdown-menu.js +1 -0
  83. package/dist/components/ui/form-field.d.ts +27 -0
  84. package/dist/components/ui/form-field.js +1 -0
  85. package/dist/components/ui/index.d.ts +26 -0
  86. package/dist/components/ui/index.js +1 -0
  87. package/dist/components/ui/input.d.ts +21 -0
  88. package/dist/components/ui/input.js +1 -0
  89. package/dist/components/ui/label.d.ts +5 -0
  90. package/dist/components/ui/label.js +1 -0
  91. package/dist/components/ui/popover.d.ts +22 -0
  92. package/dist/components/ui/popover.js +1 -0
  93. package/dist/components/ui/progress.d.ts +43 -0
  94. package/dist/components/ui/progress.js +1 -0
  95. package/dist/components/ui/segmented.d.ts +42 -0
  96. package/dist/components/ui/segmented.js +1 -0
  97. package/dist/components/ui/select.d.ts +10 -0
  98. package/dist/components/ui/select.js +1 -0
  99. package/dist/components/ui/separator.d.ts +3 -0
  100. package/dist/components/ui/separator.js +1 -0
  101. package/dist/components/ui/sheet.d.ts +19 -0
  102. package/dist/components/ui/sheet.js +1 -0
  103. package/dist/components/ui/skeleton.d.ts +10 -0
  104. package/dist/components/ui/skeleton.js +1 -0
  105. package/dist/components/ui/switch.d.ts +3 -0
  106. package/dist/components/ui/switch.js +1 -0
  107. package/dist/components/ui/tabs.d.ts +19 -0
  108. package/dist/components/ui/tabs.js +1 -0
  109. package/dist/components/ui/time-picker.d.ts +52 -0
  110. package/dist/components/ui/time-picker.js +1 -0
  111. package/dist/components/ui/tooltip.d.ts +13 -0
  112. package/dist/components/ui/tooltip.js +1 -0
  113. package/dist/hooks/index.d.ts +4 -0
  114. package/dist/hooks/index.js +1 -0
  115. package/dist/hooks/use-hotkey.d.ts +16 -0
  116. package/dist/hooks/use-hotkey.js +1 -0
  117. package/dist/hooks/use-media-query.d.ts +4 -0
  118. package/dist/hooks/use-media-query.js +1 -0
  119. package/dist/hooks/use-mounted.d.ts +2 -0
  120. package/dist/hooks/use-mounted.js +1 -0
  121. package/dist/index.d.ts +17 -0
  122. package/dist/index.js +1 -0
  123. package/dist/lib/boot-script.d.ts +15 -0
  124. package/dist/lib/boot-script.js +12 -0
  125. package/dist/lib/currency.d.ts +55 -0
  126. package/dist/lib/currency.js +1 -0
  127. package/dist/lib/format.d.ts +28 -0
  128. package/dist/lib/format.js +1 -0
  129. package/dist/lib/index.d.ts +5 -0
  130. package/dist/lib/index.js +1 -0
  131. package/dist/lib/utils.d.ts +7 -0
  132. package/dist/lib/utils.js +1 -0
  133. package/dist/shell/app-shell.d.ts +18 -0
  134. package/dist/shell/app-shell.js +1 -0
  135. package/dist/shell/app-switcher.d.ts +9 -0
  136. package/dist/shell/app-switcher.js +1 -0
  137. package/dist/shell/appearance-controls.d.ts +13 -0
  138. package/dist/shell/appearance-controls.js +1 -0
  139. package/dist/shell/command-palette.d.ts +9 -0
  140. package/dist/shell/command-palette.js +1 -0
  141. package/dist/shell/config.d.ts +66 -0
  142. package/dist/shell/config.js +1 -0
  143. package/dist/shell/icon-rail.d.ts +2 -0
  144. package/dist/shell/icon-rail.js +1 -0
  145. package/dist/shell/index.d.ts +7 -0
  146. package/dist/shell/index.js +1 -0
  147. package/dist/shell/mobile-nav.d.ts +7 -0
  148. package/dist/shell/mobile-nav.js +1 -0
  149. package/dist/shell/module-sidebar.d.ts +9 -0
  150. package/dist/shell/module-sidebar.js +1 -0
  151. package/dist/shell/notifications.d.ts +5 -0
  152. package/dist/shell/notifications.js +1 -0
  153. package/dist/shell/placeholder-screen.d.ts +11 -0
  154. package/dist/shell/placeholder-screen.js +1 -0
  155. package/dist/shell/record-drawer.d.ts +32 -0
  156. package/dist/shell/record-drawer.js +1 -0
  157. package/dist/shell/registry.d.ts +24 -0
  158. package/dist/shell/registry.js +1 -0
  159. package/dist/shell/topbar.d.ts +9 -0
  160. package/dist/shell/topbar.js +1 -0
  161. package/dist/shell/use-active.d.ts +21 -0
  162. package/dist/shell/use-active.js +1 -0
  163. package/dist/shell/user-menu.d.ts +5 -0
  164. package/dist/shell/user-menu.js +1 -0
  165. package/dist/store/app-store.d.ts +10 -0
  166. package/dist/store/app-store.js +1 -0
  167. package/dist/store/drawer-store.d.ts +22 -0
  168. package/dist/store/drawer-store.js +1 -0
  169. package/dist/store/index.d.ts +5 -0
  170. package/dist/store/index.js +1 -0
  171. package/dist/store/settings-store.d.ts +22 -0
  172. package/dist/store/settings-store.js +1 -0
  173. package/dist/store/ui-store.d.ts +28 -0
  174. package/dist/store/ui-store.js +1 -0
  175. package/dist/styles.css +658 -0
  176. package/dist/types.d.ts +140 -0
  177. package/dist/types.js +0 -0
  178. package/package.json +98 -4
@@ -0,0 +1,11 @@
1
+ /**
2
+ * A registry screen whose page is not built yet.
3
+ *
4
+ * It tells the truth briefly — this screen is defined, its data is not
5
+ * connected — and points elsewhere in the module. Nothing pretends to be data.
6
+ */
7
+ export declare function PlaceholderScreen({ pathname, plannedIn, }: {
8
+ pathname: string;
9
+ /** Roadmap milestone, e.g. "M5". */
10
+ plannedIn?: string;
11
+ }): import("react").JSX.Element;
@@ -0,0 +1 @@
1
+ "use client";import{Fragment as y,jsx as a,jsxs as t}from"react/jsx-runtime";import{ArrowRight as h,Check as x,Database as f}from"lucide-react";import g from"next/link";import{PageHeader as u}from"../components/patterns/page-header.js";import{Card as m}from"../components/ui/card.js";import{useShell as b}from"./config.js";import{moduleFromPath as N,navItemFromPath as v}from"./registry.js";function I({pathname:n,plannedIn:o}){const i=b(),s=N(i.modules,n),d=v(n,s),l=d?.label??s.title,r=i.apps.find(e=>e.id===s.app),c=s.nav.filter(e=>e.kind==="item").filter(e=>e.href!==d?.href).slice(0,6),p=["Navigation, breadcrumb and page header","Plan / role gating from the registry","Data table \u2014 sort, pin, resize, export","Record drawer and command palette","Empty, loading and error states"];return t(y,{children:[a(u,{crumbs:[...i.homeCrumb?[i.homeCrumb]:[],...r&&r.href!==i.homeCrumb?.href?[{label:r.name,href:r.href}]:[],{label:s.title,href:s.href},{label:l}],title:l,description:s.description}),t("div",{className:"grid grid-cols-1 items-start gap-3 lg:grid-cols-[minmax(0,1fr)_320px]",children:[a(m,{className:"px-6 py-8",children:t("div",{className:"flex items-start gap-3.5",children:[a("span",{className:"grid size-9 shrink-0 place-items-center rounded-ctl bg-hover text-muted","aria-hidden":!0,children:a(f,{className:"size-4"})}),t("div",{className:"min-w-0",children:[t("h2",{className:"text-[15px] font-semibold text-text",children:[l," is designed but not built yet"]}),t("p",{className:"mt-1.5 max-w-[60ch] text-[13px] leading-5 text-muted",children:["The screen is in the navigation registry and inherits the page template. Its data contract is in the API specification; the page ships with its milestone",o?` (${o})`:"","."]})]})]})}),t("div",{className:"space-y-3",children:[t(m,{children:[a("h3",{className:"text-[13px] font-semibold text-text",children:"Already wired"}),a("ul",{className:"mt-2.5 space-y-2",children:p.map(e=>t("li",{className:"flex items-start gap-2 text-[13px] text-body",children:[a(x,{className:"mt-0.5 size-3.5 shrink-0 text-success"}),e]},e))})]}),c.length?t(m,{children:[t("h3",{className:"text-[13px] font-semibold text-text",children:["Elsewhere in ",s.title]}),a("ul",{className:"-mx-1.5 mt-2",children:c.map(e=>e.kind==="item"?a("li",{children:t(g,{href:e.href,className:"flex items-center gap-2 rounded-ctl px-1.5 py-1.5 text-[13px] text-body transition-colors hover:bg-hover hover:text-text",children:[a("span",{className:"min-w-0 flex-1 truncate",children:e.label}),a(h,{className:"size-3 shrink-0 text-muted"})]})},e.href):null)})]}):null]})]})]})}export{I as PlaceholderScreen};
@@ -0,0 +1,32 @@
1
+ import * as React from "react";
2
+ import type { Tone } from "../types";
3
+ /**
4
+ * THE DRAWER — docs/DESIGN-SYSTEM.md §2.2 (revised 2026-09-25, CEO).
5
+ *
6
+ * Opening a record still never navigates: the list keeps its scroll, filters
7
+ * and selection underneath. The drawer is modal — the list is blurred and
8
+ * inert until the drawer is closed (Escape, the close button or a click on the
9
+ * blurred list), so nothing behind it is clicked by accident.
10
+ *
11
+ * A record with its own page (`href`) gets "Open full page"; the widen button
12
+ * gives more room without leaving the list.
13
+ */
14
+ export declare function RecordDrawer(): React.JSX.Element | null;
15
+ export declare function DrawerFields({ fields, className, }: {
16
+ fields: {
17
+ label: string;
18
+ value: React.ReactNode;
19
+ mono?: boolean;
20
+ num?: boolean;
21
+ }[];
22
+ className?: string;
23
+ }): React.JSX.Element;
24
+ export declare function Timeline({ events, }: {
25
+ events: {
26
+ id: string;
27
+ title: string;
28
+ detail?: React.ReactNode;
29
+ meta?: string;
30
+ tone?: Tone;
31
+ }[];
32
+ }): React.JSX.Element;
@@ -0,0 +1 @@
1
+ "use client";import{jsx as e,jsxs as s}from"react/jsx-runtime";import{ArrowUpRight as p,Maximize2 as u,Minimize2 as b,X as g}from"lucide-react";import*as x from"react";import{StatusBadge as v}from"../components/ui/badge.js";import{Button as f}from"../components/ui/button.js";import{Sheet as h,SheetClose as N,SheetContent as w,SheetTitle as T}from"../components/ui/sheet.js";import{Tabs as y,TabsContent as C,TabsList as k,TabsTrigger as S}from"../components/ui/tabs.js";import{cn as n}from"../lib/utils.js";import{useDrawer as l}from"../store/drawer-store.js";function O(){const[i,r]=x.useState(!1),t=l(a=>a.record),o=l(a=>a.tab),d=l(a=>a.setTab),m=l(a=>a.close);if(!t)return null;const c=t.tabs.length===1;return e(h,{open:!0,onOpenChange:a=>!a&&m(),children:s(w,{hideClose:!0,width:i?"w-full sm:w-[min(980px,78vw)]":"w-full sm:w-[min(600px,100vw)]",className:"transition-[width] duration-[260ms] ease-wave","aria-describedby":void 0,children:[s("div",{className:"relative shrink-0 overflow-hidden border-b border-border",children:[e("div",{"aria-hidden":!0,className:"pointer-events-none absolute inset-0 bg-[radial-gradient(520px_160px_at_0%_0%,rgba(99,102,241,.14),transparent_70%)]"}),s("div",{className:"relative px-6 pb-5 pt-5",children:[s("div",{className:"flex items-start gap-4",children:[t.leading?e("div",{className:"shrink-0",children:t.leading}):null,s("div",{className:"min-w-0 flex-1",children:[s("div",{className:"flex flex-wrap items-center gap-2",children:[e(T,{className:"disp text-[20px] font-semibold leading-7 tracking-tight [overflow-wrap:anywhere]",children:t.title}),t.status?e(v,{label:t.status.label,tone:t.status.tone}):null]}),t.subtitle?e("p",{className:"mt-0.5 truncate text-[12.5px] text-muted",children:t.subtitle}):null]}),s("div",{className:"-mr-2 -mt-1 flex shrink-0 items-center gap-0.5 rounded-xl border border-border bg-raised/80 p-0.5 shadow-[0_1px_2px_rgba(5,8,22,.14)] backdrop-blur",children:[e("button",{type:"button",className:"hidden size-8 cursor-pointer place-items-center rounded-lg text-muted transition-colors hover:bg-hover hover:text-text sm:grid","aria-label":i?"Narrow drawer":"Widen drawer",title:i?"Narrow":"Widen",onClick:()=>r(a=>!a),children:i?e(b,{className:"size-4"}):e(u,{className:"size-4"})}),e(N,{className:"grid size-8 cursor-pointer place-items-center rounded-lg text-muted transition-colors hover:bg-hover hover:text-text","aria-label":"Close",title:"Close (Esc)",children:e(g,{className:"size-4"})})]})]}),t.actions||t.href?s("div",{className:"mt-4 flex flex-wrap items-center gap-2",children:[t.actions,t.href?e(f,{size:"sm",variant:"secondary",asChild:!0,children:s("a",{href:t.href,children:["Open full page",e(p,{})]})}):null]}):null]})]}),c?e("div",{className:"min-h-0 flex-1 overflow-y-auto bg-surface/40 px-6 py-5",children:t.tabs[0].content}):s(y,{value:o,onValueChange:d,className:"flex min-h-0 flex-1 flex-col",children:[e(k,{className:"shrink-0 px-4",children:t.tabs.map(a=>e(S,{value:a.id,count:a.count,children:a.label},a.id))}),e("div",{className:"min-h-0 flex-1 overflow-y-auto bg-surface/40",children:t.tabs.map(a=>e(C,{value:a.id,className:"px-6 py-5",children:a.content},a.id))})]})]})})}function W({fields:i,className:r}){return e("dl",{className:n("grid gap-x-6 gap-y-4 sm:grid-cols-2",r),children:i.map(t=>s("div",{children:[e("dt",{className:"ov mb-1",children:t.label}),e("dd",{className:n("min-w-0 text-[13px] text-text [overflow-wrap:anywhere]",t.mono&&"mono",t.num&&"num"),children:t.value})]},t.label))})}const z={neutral:"bg-muted",info:"bg-primary",success:"bg-success",warning:"bg-warning",danger:"bg-danger",accent:"bg-accent"};function A({events:i}){return e("ol",{className:"relative border-l border-border pl-5",children:i.map(r=>s("li",{className:"relative pb-5 last:pb-0",children:[e("span",{className:n("absolute -left-[25px] top-1 size-2 rounded-full ring-4 ring-raised",z[r.tone??"neutral"]),"aria-hidden":!0}),e("p",{className:"text-[13px] font-medium text-text",children:r.title}),r.detail?e("div",{className:"mt-0.5 text-xs text-muted",children:r.detail}):null,r.meta?e("p",{className:"mt-1 text-[11px] text-muted",children:r.meta}):null]},r.id))})}export{W as DrawerFields,O as RecordDrawer,A as Timeline};
@@ -0,0 +1,24 @@
1
+ import type { ModuleDef, NavNode } from "../types";
2
+ type Item = Extract<NavNode, {
3
+ kind: "item";
4
+ }>;
5
+ /**
6
+ * The module that owns a route: the longest href that prefixes it, across the
7
+ * module's own href and every screen it lists. Longest wins so `/agents/runs`
8
+ * belongs to the screen that names it, not to `/agents`.
9
+ */
10
+ export declare function moduleFromPath(modules: ModuleDef[], pathname: string): ModuleDef;
11
+ export declare function navItemFromPath(pathname: string, mod: ModuleDef): Item | undefined;
12
+ /**
13
+ * What the rail shows for an app: its entitled modules, the system modules at
14
+ * the foot, and — when the route belongs to neither — the current module as a
15
+ * "visiting" item, because a rail that highlights nothing has stopped saying
16
+ * where you are.
17
+ */
18
+ export declare function railModules(modules: ModuleDef[], appId: string, activeKey: string, isEntitled?: (m: ModuleDef) => boolean): {
19
+ modules: ModuleDef[];
20
+ platform: ModuleDef[];
21
+ visiting: ModuleDef | null;
22
+ order: ModuleDef[];
23
+ };
24
+ export {};
@@ -0,0 +1 @@
1
+ function u(e,r){return r==="/"?e==="/":e===r||e.startsWith(`${r}/`)}function d(e,r){let n,o=-1;for(const f of e){const s=[f.href,...f.nav.filter(i=>i.kind==="item").map(i=>i.href)];for(const i of s)u(r,i)&&i.length>o&&(n=f,o=i.length)}return n??e[0]}function m(e,r){let n;for(const o of r.nav)o.kind!=="item"||!u(e,o.href)||(!n||o.href.length>n.href.length)&&(n=o);return n}function c(e,r,n,o=()=>!0){const f=e.filter(t=>t.app===r&&!t.system&&o(t)),s=e.filter(t=>t.system&&o(t)),l=f.some(t=>t.key===n)||s.some(t=>t.key===n)?null:e.find(t=>t.key===n)??null;return{modules:f,platform:s,visiting:l,order:[...f,...l?[l]:[],...s]}}export{d as moduleFromPath,m as navItemFromPath,c as railModules};
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Topbar — §5 and §4.5c/§4.5d.
3
+ *
4
+ * Three zones: **[nav] [search] [account]**. The field is centred absolutely
5
+ * and sized from the clearance the sides need, collapsing to its icon below a
6
+ * 980px *header* container width (container query, not media query) — or
7
+ * 1220px when the product asks for the wide clearance.
8
+ */
9
+ export declare function Topbar(): import("react").JSX.Element;
@@ -0,0 +1 @@
1
+ "use client";import{jsx as e,jsxs as o}from"react/jsx-runtime";import{Menu as x,PanelLeft as h,Search as p}from"lucide-react";import{WaveMark as f}from"../components/brand/logo.js";import{Button as l}from"../components/ui/button.js";import{Tooltip as g}from"../components/ui/tooltip.js";import{cn as v}from"../lib/utils.js";import{useUi as s}from"../store/ui-store.js";import{AppSwitcher as N}from"./app-switcher.js";import{Notifications as k}from"./notifications.js";import{UserMenu as w}from"./user-menu.js";import{useActive as z}from"./use-active.js";function I(){const m=s(a=>a.togglePanel),i=s(a=>a.setPalette),c=s(a=>a.setMobileNav),u=s(a=>a.setLauncher),{cfg:t,app:r}=z(),b=r.icon,n=t.showAppSwitcher!==!1&&t.apps.length>1,d=t.searchClearance==="wide";return e("header",{className:"col-start-3 col-end-4 row-start-1 flex border-b border-border bg-canvas px-4 sm:px-6",children:o("div",{className:"@container relative mx-auto flex w-full max-w-[1600px] items-center gap-2 sm:gap-2.5",children:[o("div",{className:"flex min-w-0 items-center gap-2",children:[e(g,{label:"Toggle panel",shortcut:"\u2318B",children:e(l,{variant:"ghost",size:"icon",onClick:m,className:"-ml-2.5 hidden lg:inline-flex","aria-label":"Toggle module panel",children:e(h,{})})}),e(l,{variant:"ghost",size:"icon",onClick:()=>c(!0),className:"-ml-2.5 lg:hidden","aria-label":"Open navigation",children:e(x,{})}),o("button",{type:"button",onClick:()=>n?u(!0):c(!0),"aria-label":`WAVEINNO, ${r.name}`,className:"flex min-w-0 shrink items-center gap-2 rounded-full py-1 pl-0.5 pr-2 transition-colors hover:bg-hover lg:hidden",children:[o("span",{className:"relative shrink-0",children:[e(f,{size:"sm"}),e("span",{className:"absolute -bottom-0.5 -right-0.5 grid size-[13px] place-items-center rounded-full text-white ring-2 ring-canvas",style:{background:r.tint},children:e(b,{className:"size-2"})})]}),e("span",{className:`disp truncate text-[13px] font-semibold text-text ${n?"md:hidden":""}`,children:r.name})]})]}),o("button",{type:"button",onClick:()=>i(!0),className:v("absolute left-1/2 top-1/2 hidden h-[38px] -translate-x-1/2 -translate-y-1/2 cursor-text items-center gap-2.5 rounded-full border border-border bg-surface px-[15px] text-left transition-[background-color,border-color,box-shadow] hover:border-border2 focus-visible:border-primary focus-visible:bg-raised focus-visible:shadow-elev2",d?"w-[clamp(260px,calc(100%-960px),560px)] @min-[1220px]:flex":"w-[clamp(260px,calc(100%-720px),560px)] @min-[980px]:flex"),children:[e(p,{className:"size-4 shrink-0 text-muted"}),e("span",{className:"flex-1 truncate text-sm text-placeholder",children:t.searchPlaceholder??"Search screens and records\u2026"}),e("kbd",{className:"hidden rounded-[5px] border border-border2 px-1.5 text-[11px] font-medium text-muted md:inline",children:"\u2318K"})]}),o("div",{className:"ml-auto flex items-center gap-2 sm:gap-2.5",children:[e(l,{variant:"ghost",size:"icon",onClick:()=>i(!0),className:d?"@min-[1220px]:hidden":"@min-[980px]:hidden","aria-label":"Search",children:e(p,{})}),t.topbarExtra?e("div",{className:"hidden sm:block",children:t.topbarExtra}):null,t.scope?e("div",{className:"hidden sm:block",children:t.scope}):null,n?e("div",{className:"hidden md:block",children:e(N,{})}):null,e(k,{}),e(w,{})]})]})})}export{I as Topbar};
@@ -0,0 +1,21 @@
1
+ /** One read of "where am I" shared by rail, panel, topbar and mobile sheet. */
2
+ export declare function useActive(): {
3
+ cfg: import("./config").ShellConfig;
4
+ pathname: string;
5
+ mod: import("..").ModuleDef;
6
+ item: {
7
+ kind: "item";
8
+ label: string;
9
+ href: string;
10
+ count?: string;
11
+ hot?: boolean;
12
+ } | undefined;
13
+ app: import("..").AppDef;
14
+ rail: {
15
+ modules: import("..").ModuleDef[];
16
+ platform: import("..").ModuleDef[];
17
+ visiting: import("..").ModuleDef | null;
18
+ order: import("..").ModuleDef[];
19
+ };
20
+ isEntitled: (m: import("..").ModuleDef) => boolean;
21
+ };
@@ -0,0 +1 @@
1
+ "use client";import{usePathname as l}from"next/navigation";import{useApp as u}from"../store/app-store.js";import{useShell as f}from"./config.js";import{moduleFromPath as h,navItemFromPath as v,railModules as I}from"./registry.js";function F(){const t=f(),e=l(),p=u(o=>o.appId),s=h(t.modules,e),m=v(e,s),a=o=>!!o&&t.apps.some(c=>c.id===o),r=s.app??(a(p)?p:t.apps[0].id),n=t.apps.find(o=>o.id===r)??t.apps[0],i=t.isEntitled??(()=>!0),d=I(t.modules,n.id,s.key,i);return{cfg:t,pathname:e,mod:s,item:m,app:n,rail:d,isEntitled:i}}export{F as useActive};
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Account menu — and the home of appearance. Theme and density are
3
+ * preferences set once, so they live here rather than in the topbar.
4
+ */
5
+ export declare function UserMenu(): import("react").JSX.Element;
@@ -0,0 +1 @@
1
+ "use client";import{Fragment as D,jsx as e,jsxs as t}from"react/jsx-runtime";import{LogOut as l}from"lucide-react";import c from"next/link";import{Avatar as s}from"../components/ui/avatar.js";import{DropdownMenu as u,DropdownMenuContent as d,DropdownMenuItem as m,DropdownMenuLabel as x,DropdownMenuSeparator as a,DropdownMenuTrigger as g}from"../components/ui/dropdown-menu.js";import{DensityPicker as f,ThemeToggle as w}from"./appearance-controls.js";import{useShell as v}from"./config.js";function L(){const{user:n,userLinks:r=[],onSignOut:p}=v();return t(u,{children:[e(g,{className:"shrink-0 cursor-pointer rounded-full","aria-label":"Account menu",children:e(s,{name:n.name,src:n.avatar})}),t(d,{align:"end",className:"w-72",children:[t("div",{className:"flex items-center gap-2.5 px-2.5 py-2.5",children:[e(s,{name:n.name,size:"lg"}),t("div",{className:"min-w-0",children:[e("p",{className:"truncate text-[13px] font-semibold text-text",children:n.name}),e("p",{className:"truncate text-[11px] text-muted",children:n.email})]})]}),t("div",{className:"mx-2.5 mb-1 rounded-ctl bg-hover px-2.5 py-2",children:[e("p",{className:"ov",children:"Signed in as"}),e("p",{className:"mt-0.5 text-xs text-text",children:n.role}),n.context?e("p",{className:"mt-0.5 text-[11px] text-muted",children:n.context}):null]}),e(a,{}),e(x,{children:"Appearance"}),t("div",{className:"flex items-center justify-between gap-2 px-2.5 py-1.5",onClick:o=>o.stopPropagation(),children:[e("span",{className:"text-[13px] text-body",children:"Theme"}),e(w,{})]}),t("div",{className:"flex items-center justify-between gap-2 px-2.5 pb-2 pt-1",onClick:o=>o.stopPropagation(),children:[e("span",{className:"text-[13px] text-body",children:"Density"}),e(f,{compact:!0})]}),r.length?e(a,{}):null,r.map(o=>{const i=o.icon;return e(m,{asChild:!0,children:t(c,{href:o.href,children:[e(i,{}),o.label]})},o.href)}),p?t(D,{children:[e(a,{}),t(m,{danger:!0,onSelect:p,children:[e(l,{}),"Sign out"]})]}):null]})]})}export{L as UserMenu};
@@ -0,0 +1,10 @@
1
+ interface AppState {
2
+ appId: string | null;
3
+ hydrated: boolean;
4
+ hydrate: (valid: string[]) => void;
5
+ setApp: (id: string) => void;
6
+ /** Follow the route when a link lands in another app's module. Not persisted. */
7
+ syncFromRoute: (id: string | null) => void;
8
+ }
9
+ export declare const useApp: import("zustand").UseBoundStore<import("zustand").StoreApi<AppState>>;
10
+ export {};
@@ -0,0 +1 @@
1
+ "use client";import{create as o}from"zustand";const d="waveinno.app",i=o((e,r)=>({appId:null,hydrated:!1,hydrate:t=>{if(r().hydrated)return;let a=r().appId;try{const p=window.localStorage.getItem(d);p&&t.includes(p)&&!a&&(a=p)}catch{}e({appId:a,hydrated:!0})},setApp:t=>{try{window.localStorage.setItem(d,t)}catch{}e({appId:t})},syncFromRoute:t=>{!t||t===r().appId||e({appId:t})}}));export{i as useApp};
@@ -0,0 +1,22 @@
1
+ import type { DrawerRecord } from "../types";
2
+ /**
3
+ * The drawer law — docs/DESIGN-SYSTEM.md §2.2.
4
+ *
5
+ * Opening a record must never navigate: the list keeps its scroll, filters and
6
+ * selection. The URL still gets `?record=` via history.replaceState, so a
7
+ * drawer is linkable without a Next.js navigation.
8
+ *
9
+ * Unlike the ERP template, the record carries its own tabs: an SEO issue, an
10
+ * agent run and a server have nothing in common beyond a title and a status.
11
+ */
12
+ interface DrawerState {
13
+ record: DrawerRecord | null;
14
+ tab: string;
15
+ open: (record: DrawerRecord, tab?: string) => void;
16
+ close: () => void;
17
+ setTab: (tab: string) => void;
18
+ /** Re-open from `?record=` on first mount. */
19
+ restore: (lookup: (id: string) => DrawerRecord | undefined) => void;
20
+ }
21
+ export declare const useDrawer: import("zustand").UseBoundStore<import("zustand").StoreApi<DrawerState>>;
22
+ export {};
@@ -0,0 +1 @@
1
+ "use client";import{create as i}from"zustand";function n(r){if(typeof window>"u")return;const e=new URL(window.location.href);r?e.searchParams.set("record",r):e.searchParams.delete("record"),window.history.replaceState(window.history.state,"",e)}const d=i(r=>({record:null,tab:"",open:(e,t)=>{n(e.id),r({record:e,tab:t??e.tabs[0]?.id??""})},close:()=>{n(null),r({record:null})},setTab:e=>r({tab:e}),restore:e=>{if(typeof window>"u")return;const t=new URL(window.location.href).searchParams.get("record");if(!t)return;const o=e(t);o&&r({record:o,tab:o.tabs[0]?.id??""})}}));export{d as useDrawer};
@@ -0,0 +1,5 @@
1
+ /** State: zustand stores the shell and patterns share. */
2
+ export * from "./app-store";
3
+ export * from "./drawer-store";
4
+ export * from "./settings-store";
5
+ export * from "./ui-store";
@@ -0,0 +1 @@
1
+ export*from"./app-store.js";export*from"./drawer-store.js";export*from"./settings-store.js";export*from"./ui-store.js";
@@ -0,0 +1,22 @@
1
+ import { type CurrencyConfig, type Grouping, type SymbolPosition } from "../lib/currency";
2
+ /**
3
+ * Reader preferences for money.
4
+ *
5
+ * The **code** is the organisation's billing currency and comes from the shell
6
+ * config; grouping, decimals and symbol position are per-user preferences —
7
+ * a Dhaka team reading a USD invoice still reads in lakh.
8
+ */
9
+ interface SettingsState {
10
+ overrideCode: string | null;
11
+ grouping: Grouping | null;
12
+ decimals: number | null;
13
+ position: SymbolPosition;
14
+ setOverrideCode: (code: string | null) => void;
15
+ setGrouping: (g: Grouping | null) => void;
16
+ setDecimals: (d: number | null) => void;
17
+ setPosition: (p: SymbolPosition) => void;
18
+ }
19
+ export declare const useSettings: import("zustand").UseBoundStore<import("zustand").StoreApi<SettingsState>>;
20
+ /** The resolved currency every amount in the product formats with. */
21
+ export declare function useCurrency(): CurrencyConfig;
22
+ export {};
@@ -0,0 +1 @@
1
+ "use client";import{create as l}from"zustand";import{configFor as u}from"../lib/currency.js";import{useShellCurrency as d}from"../shell/config.js";const n=l(i=>({overrideCode:null,grouping:null,decimals:null,position:"before",setOverrideCode:o=>i({overrideCode:o}),setGrouping:o=>i({grouping:o}),setDecimals:o=>i({decimals:o}),setPosition:o=>i({position:o})}));function m(){const i=d(),o=n(e=>e.overrideCode),r=n(e=>e.grouping),t=n(e=>e.decimals),s=n(e=>e.position);return u(o??i,{...r?{grouping:r}:{},...t!==null?{decimals:t}:{},position:s})}export{m as useCurrency,n as useSettings};
@@ -0,0 +1,28 @@
1
+ import type { Density, Theme } from "../types";
2
+ /**
3
+ * Shell UI state.
4
+ *
5
+ * Theme and density live on <html> as data-attributes — set by a blocking
6
+ * script before first paint (see app/layout.tsx), so there is no flash and no
7
+ * hydration mismatch. This store is the *controller*, not the source of truth:
8
+ * it reads the DOM on mount and writes back on change.
9
+ */
10
+ interface UiState {
11
+ theme: Theme;
12
+ density: Density;
13
+ panelCollapsed: boolean;
14
+ paletteOpen: boolean;
15
+ launcherOpen: boolean;
16
+ mobileNavOpen: boolean;
17
+ hydrated: boolean;
18
+ hydrate: () => void;
19
+ setTheme: (t: Theme) => void;
20
+ toggleTheme: () => void;
21
+ setDensity: (d: Density) => void;
22
+ togglePanel: () => void;
23
+ setPalette: (open: boolean) => void;
24
+ setLauncher: (open: boolean) => void;
25
+ setMobileNav: (open: boolean) => void;
26
+ }
27
+ export declare const useUi: import("zustand").UseBoundStore<import("zustand").StoreApi<UiState>>;
28
+ export {};
@@ -0,0 +1 @@
1
+ "use client";import{create as s}from"zustand";import{DENSITY_KEY as m,PANEL_KEY as n,THEME_KEY as r}from"../lib/boot-script.js";function l(t,a){try{window.localStorage.setItem(t,a)}catch{}}const h=s((t,a)=>({theme:"light",density:"default",panelCollapsed:!1,paletteOpen:!1,launcherOpen:!1,mobileNavOpen:!1,hydrated:!1,hydrate:()=>{if(a().hydrated)return;const e=document.documentElement,d=e.getAttribute("data-theme")??"light",i=e.getAttribute("data-density")??"default";let o=!1;try{o=window.localStorage.getItem(n)==="1"}catch{}e.toggleAttribute("data-panel-collapsed",o),t({theme:d,density:i,panelCollapsed:o,hydrated:!0})},setTheme:e=>{document.documentElement.setAttribute("data-theme",e),document.documentElement.style.colorScheme=e,l(r,e),t({theme:e})},toggleTheme:()=>a().setTheme(a().theme==="dark"?"light":"dark"),setDensity:e=>{document.documentElement.setAttribute("data-density",e),l(m,e),t({density:e})},togglePanel:()=>{const e=!a().panelCollapsed;document.documentElement.toggleAttribute("data-panel-collapsed",e),l(n,e?"1":"0"),t({panelCollapsed:e})},setPalette:e=>t({paletteOpen:e}),setLauncher:e=>t({launcherOpen:e}),setMobileNav:e=>t({mobileNavOpen:e})}));export{h as useUi};