@consciousclouds/operator-shell 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 (145) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/LICENSE +194 -0
  3. package/README.md +319 -0
  4. package/dist/chief/ChiefDrawer.d.ts +11 -0
  5. package/dist/chief/ChiefDrawer.d.ts.map +1 -0
  6. package/dist/chief/ChiefDrawer.js +49 -0
  7. package/dist/chief/ChiefDrawer.js.map +1 -0
  8. package/dist/chief/ChiefPanel.d.ts +10 -0
  9. package/dist/chief/ChiefPanel.d.ts.map +1 -0
  10. package/dist/chief/ChiefPanel.js +46 -0
  11. package/dist/chief/ChiefPanel.js.map +1 -0
  12. package/dist/chief/ChiefRegion.d.ts +9 -0
  13. package/dist/chief/ChiefRegion.d.ts.map +1 -0
  14. package/dist/chief/ChiefRegion.js +55 -0
  15. package/dist/chief/ChiefRegion.js.map +1 -0
  16. package/dist/chief/ChiefSession.d.ts +21 -0
  17. package/dist/chief/ChiefSession.d.ts.map +1 -0
  18. package/dist/chief/ChiefSession.js +9 -0
  19. package/dist/chief/ChiefSession.js.map +1 -0
  20. package/dist/chief/directive.d.ts +15 -0
  21. package/dist/chief/directive.d.ts.map +1 -0
  22. package/dist/chief/directive.js +20 -0
  23. package/dist/chief/directive.js.map +1 -0
  24. package/dist/chief/turn-adapter.d.ts +52 -0
  25. package/dist/chief/turn-adapter.d.ts.map +1 -0
  26. package/dist/chief/turn-adapter.js +23 -0
  27. package/dist/chief/turn-adapter.js.map +1 -0
  28. package/dist/chief/use-chief-shortcut.d.ts +17 -0
  29. package/dist/chief/use-chief-shortcut.d.ts.map +1 -0
  30. package/dist/chief/use-chief-shortcut.js +61 -0
  31. package/dist/chief/use-chief-shortcut.js.map +1 -0
  32. package/dist/chief/use-chief-turn.d.ts +50 -0
  33. package/dist/chief/use-chief-turn.d.ts.map +1 -0
  34. package/dist/chief/use-chief-turn.js +99 -0
  35. package/dist/chief/use-chief-turn.js.map +1 -0
  36. package/dist/flavor/types.d.ts +189 -0
  37. package/dist/flavor/types.d.ts.map +1 -0
  38. package/dist/flavor/types.js +13 -0
  39. package/dist/flavor/types.js.map +1 -0
  40. package/dist/frame/OperatorShell.d.ts +91 -0
  41. package/dist/frame/OperatorShell.d.ts.map +1 -0
  42. package/dist/frame/OperatorShell.js +54 -0
  43. package/dist/frame/OperatorShell.js.map +1 -0
  44. package/dist/guard/PermissionProvider.d.ts +49 -0
  45. package/dist/guard/PermissionProvider.d.ts.map +1 -0
  46. package/dist/guard/PermissionProvider.js +103 -0
  47. package/dist/guard/PermissionProvider.js.map +1 -0
  48. package/dist/guard/RouteGuard.d.ts +45 -0
  49. package/dist/guard/RouteGuard.d.ts.map +1 -0
  50. package/dist/guard/RouteGuard.js +59 -0
  51. package/dist/guard/RouteGuard.js.map +1 -0
  52. package/dist/guard/permissions.d.ts +88 -0
  53. package/dist/guard/permissions.d.ts.map +1 -0
  54. package/dist/guard/permissions.js +105 -0
  55. package/dist/guard/permissions.js.map +1 -0
  56. package/dist/header/EnvironmentSwitcher.d.ts +5 -0
  57. package/dist/header/EnvironmentSwitcher.d.ts.map +1 -0
  58. package/dist/header/EnvironmentSwitcher.js +51 -0
  59. package/dist/header/EnvironmentSwitcher.js.map +1 -0
  60. package/dist/header/OperatorHeader.d.ts +25 -0
  61. package/dist/header/OperatorHeader.d.ts.map +1 -0
  62. package/dist/header/OperatorHeader.js +13 -0
  63. package/dist/header/OperatorHeader.js.map +1 -0
  64. package/dist/header/OperatorProfileMenu.d.ts +2 -0
  65. package/dist/header/OperatorProfileMenu.d.ts.map +1 -0
  66. package/dist/header/OperatorProfileMenu.js +14 -0
  67. package/dist/header/OperatorProfileMenu.js.map +1 -0
  68. package/dist/header/ThemeToggle.d.ts +2 -0
  69. package/dist/header/ThemeToggle.d.ts.map +1 -0
  70. package/dist/header/ThemeToggle.js +17 -0
  71. package/dist/header/ThemeToggle.js.map +1 -0
  72. package/dist/icons/registry.d.ts +57 -0
  73. package/dist/icons/registry.d.ts.map +1 -0
  74. package/dist/icons/registry.js +83 -0
  75. package/dist/icons/registry.js.map +1 -0
  76. package/dist/index.d.ts +77 -0
  77. package/dist/index.d.ts.map +1 -0
  78. package/dist/index.js +129 -0
  79. package/dist/index.js.map +1 -0
  80. package/dist/inspector/InspectorProvider.d.ts +41 -0
  81. package/dist/inspector/InspectorProvider.d.ts.map +1 -0
  82. package/dist/inspector/InspectorProvider.js +63 -0
  83. package/dist/inspector/InspectorProvider.js.map +1 -0
  84. package/dist/inspector/types.d.ts +91 -0
  85. package/dist/inspector/types.d.ts.map +1 -0
  86. package/dist/inspector/types.js +40 -0
  87. package/dist/inspector/types.js.map +1 -0
  88. package/dist/nav/routes.d.ts +39 -0
  89. package/dist/nav/routes.d.ts.map +1 -0
  90. package/dist/nav/routes.js +46 -0
  91. package/dist/nav/routes.js.map +1 -0
  92. package/dist/palette/CommandPalette.d.ts +40 -0
  93. package/dist/palette/CommandPalette.d.ts.map +1 -0
  94. package/dist/palette/CommandPalette.js +108 -0
  95. package/dist/palette/CommandPalette.js.map +1 -0
  96. package/dist/palette/commands.d.ts +58 -0
  97. package/dist/palette/commands.d.ts.map +1 -0
  98. package/dist/palette/commands.js +81 -0
  99. package/dist/palette/commands.js.map +1 -0
  100. package/dist/palette/search-sources.d.ts +126 -0
  101. package/dist/palette/search-sources.d.ts.map +1 -0
  102. package/dist/palette/search-sources.js +101 -0
  103. package/dist/palette/search-sources.js.map +1 -0
  104. package/dist/palette/use-search-results.d.ts +4 -0
  105. package/dist/palette/use-search-results.d.ts.map +1 -0
  106. package/dist/palette/use-search-results.js +51 -0
  107. package/dist/palette/use-search-results.js.map +1 -0
  108. package/dist/sidebar/OperatorSidebar.d.ts +44 -0
  109. package/dist/sidebar/OperatorSidebar.d.ts.map +1 -0
  110. package/dist/sidebar/OperatorSidebar.js +30 -0
  111. package/dist/sidebar/OperatorSidebar.js.map +1 -0
  112. package/dist/workspace/CanvasWorkspace.d.ts +78 -0
  113. package/dist/workspace/CanvasWorkspace.d.ts.map +1 -0
  114. package/dist/workspace/CanvasWorkspace.js +44 -0
  115. package/dist/workspace/CanvasWorkspace.js.map +1 -0
  116. package/dist/workspace/DashboardWorkspace.d.ts +91 -0
  117. package/dist/workspace/DashboardWorkspace.d.ts.map +1 -0
  118. package/dist/workspace/DashboardWorkspace.js +39 -0
  119. package/dist/workspace/DashboardWorkspace.js.map +1 -0
  120. package/dist/workspace/FormWorkspace.d.ts +107 -0
  121. package/dist/workspace/FormWorkspace.d.ts.map +1 -0
  122. package/dist/workspace/FormWorkspace.js +38 -0
  123. package/dist/workspace/FormWorkspace.js.map +1 -0
  124. package/dist/workspace/GraphWorkspace.d.ts +112 -0
  125. package/dist/workspace/GraphWorkspace.d.ts.map +1 -0
  126. package/dist/workspace/GraphWorkspace.js +147 -0
  127. package/dist/workspace/GraphWorkspace.js.map +1 -0
  128. package/dist/workspace/TableWorkspace.d.ts +74 -0
  129. package/dist/workspace/TableWorkspace.d.ts.map +1 -0
  130. package/dist/workspace/TableWorkspace.js +25 -0
  131. package/dist/workspace/TableWorkspace.js.map +1 -0
  132. package/dist/workspace/WorkspaceHost.d.ts +26 -0
  133. package/dist/workspace/WorkspaceHost.d.ts.map +1 -0
  134. package/dist/workspace/WorkspaceHost.js +21 -0
  135. package/dist/workspace/WorkspaceHost.js.map +1 -0
  136. package/dist/workspace/registry.d.ts +30 -0
  137. package/dist/workspace/registry.d.ts.map +1 -0
  138. package/dist/workspace/registry.js +33 -0
  139. package/dist/workspace/registry.js.map +1 -0
  140. package/dist/workspace/types.d.ts +79 -0
  141. package/dist/workspace/types.d.ts.map +1 -0
  142. package/dist/workspace/types.js +48 -0
  143. package/dist/workspace/types.js.map +1 -0
  144. package/package.json +64 -0
  145. package/styles/source.css +16 -0
@@ -0,0 +1,108 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ /**
4
+ * CommandPalette — the shell-owned ⌘K palette over the registry `command`
5
+ * component (@consciousclouds/ui, cmdk). The shell owns the BEHAVIOR
6
+ * (keybinding, open/close, filtering, execution); the Flavor owns the
7
+ * VOCABULARY (nav-derived route commands + inert declared references, see
8
+ * palette/commands.ts); the product owns the MEANING of non-route commands
9
+ * through the injected `runCommand` executor — the same injection pattern as
10
+ * icons and the permission resolver.
11
+ *
12
+ * The palette is ALSO the Search surface (the doctrine skeleton's Search
13
+ * experience): product-injected `searchSources` (see search-sources.ts) run
14
+ * against the typed text and their results render alongside the command
15
+ * groups, gated by the SAME fail-closed permission snapshot. An erroring,
16
+ * in-flight, or absent source contributes nothing. A result's action either
17
+ * navigates (the palette's own door) or opens the shell-owned inspector by
18
+ * entity kind — which keeps its own fail-closed gate (an undeclared or
19
+ * view-less kind refuses to open and the palette simply stays).
20
+ *
21
+ * Controlled open state: the frame holds it so the header's search button
22
+ * and the ⌘K keybinding open the same instance.
23
+ */
24
+ import * as React from "react";
25
+ import { useRouter } from "next/navigation";
26
+ import { CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from "@consciousclouds/ui";
27
+ import { collectPermissionRefs } from "../guard/permissions.js";
28
+ import { usePermissionDecisions } from "../guard/PermissionProvider.js";
29
+ import { resolveIcon } from "../icons/registry.js";
30
+ import { useInspector } from "../inspector/InspectorProvider.js";
31
+ import { derivePaletteGroups } from "./commands.js";
32
+ import { useSearchResults } from "./use-search-results.js";
33
+ export function CommandPalette({ flavor, resolvePermission, icons, iconFamily, runCommand, searchSources, open, onOpenChange, placeholder = "Type a command or search…", }) {
34
+ const router = useRouter();
35
+ const inspector = useInspector();
36
+ // The palette text — controlled so the search sources can see it. Reset on
37
+ // close: the next open starts commands-only, no stale query.
38
+ const [query, setQuery] = React.useState("");
39
+ React.useEffect(() => {
40
+ if (!open)
41
+ setQuery("");
42
+ }, [open]);
43
+ // The registry ⌘K binding: meta/ctrl+K toggles, anywhere on the document.
44
+ React.useEffect(() => {
45
+ const down = (event) => {
46
+ if (event.key === "k" && (event.metaKey || event.ctrlKey)) {
47
+ event.preventDefault();
48
+ onOpenChange(!open);
49
+ }
50
+ };
51
+ document.addEventListener("keydown", down);
52
+ return () => document.removeEventListener("keydown", down);
53
+ }, [open, onOpenChange]);
54
+ // Resolve every gated reference (nav + declared commands + search sources)
55
+ // through the live seam, then snapshot the decisions into the SYNC lookup
56
+ // the pure derivations take: allowed passes, loading/denied/absent ⇒
57
+ // unknown ⇒ denied (fail-closed while an async resolver is still deciding).
58
+ const permissionRefs = collectPermissionRefs([
59
+ ...flavor.nav.flatMap((group) => group.items),
60
+ ...(flavor.commands ?? []).flatMap((group) => group.items),
61
+ ...(searchSources ?? []),
62
+ ]);
63
+ const decisions = usePermissionDecisions(permissionRefs, resolvePermission);
64
+ const snapshotResolver = (permission) => decisions[permission] === "allowed" ? "allowed" : "unknown";
65
+ const groups = derivePaletteGroups(flavor, snapshotResolver, Boolean(runCommand));
66
+ // The Search experience: the injected sources run against the typed text
67
+ // under the SAME decisions snapshot; in-flight/erroring/absent sources
68
+ // contribute nothing (use-search-results.ts holds the fail-closed rules).
69
+ const searchGroups = useSearchResults(searchSources, open ? query : "", decisions);
70
+ const select = (action) => {
71
+ // Close first so navigation lands on an unobstructed surface.
72
+ onOpenChange(false);
73
+ if (action.kind === "navigate")
74
+ router.push(action.href);
75
+ else
76
+ runCommand?.(action.command);
77
+ };
78
+ const selectResult = (action) => {
79
+ if (action.kind === "navigate") {
80
+ onOpenChange(false);
81
+ router.push(action.href);
82
+ return;
83
+ }
84
+ // Inspector door: open FIRST, close only on acceptance — a fail-closed
85
+ // refusal (undeclared/view-less kind) leaves the palette exactly as it
86
+ // was (no panel, no crash, no dead-end close).
87
+ const opened = inspector.open({
88
+ kind: action.entityKind,
89
+ entity: action.entity,
90
+ title: action.title,
91
+ description: action.description,
92
+ });
93
+ if (opened)
94
+ onOpenChange(false);
95
+ };
96
+ return (_jsxs(CommandDialog, { open: open, onOpenChange: onOpenChange, title: "Command palette", description: "Search the console's destinations and commands", children: [_jsx(CommandInput, { placeholder: placeholder, value: query, onValueChange: setQuery }), _jsxs(CommandList, { children: [_jsx(CommandEmpty, { children: "No results found." }), groups.map((group) => (_jsx(CommandGroup, { heading: group.label, children: group.commands.map((command) => {
97
+ const Icon = command.icon ? resolveIcon(command.icon, icons, iconFamily) : null;
98
+ return (_jsxs(CommandItem, { value: command.label, keywords: command.keywords, onSelect: () => select(command.action), children: [Icon && _jsx(Icon, {}), _jsx("span", { children: command.label })] }, command.id));
99
+ }) }, group.id))), searchGroups.map((group) => (_jsx(CommandGroup, { heading: group.label, children: group.results.map((result) => {
100
+ const Icon = result.icon ? resolveIcon(result.icon, icons, iconFamily) : null;
101
+ return (_jsxs(CommandItem, { value: `${group.id}:${result.id}`,
102
+ // The source already matched these results against the
103
+ // typed text (it may match fields cmdk can't see), so pin
104
+ // them past cmdk's own filter by including the query.
105
+ keywords: [query, result.label, ...(result.keywords ?? [])], onSelect: () => selectResult(result.action), children: [Icon && _jsx(Icon, {}), _jsx("span", { children: result.label })] }, `${group.id}:${result.id}`));
106
+ }) }, group.id)))] })] }));
107
+ }
108
+ //# sourceMappingURL=CommandPalette.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CommandPalette.js","sourceRoot":"","sources":["../../src/palette/CommandPalette.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * The palette's pure command derivation. Commands are DECLARATIVE:
3
+ *
4
+ * - Route commands are AUTO-DERIVED from `flavor.nav` — the same tree the
5
+ * sidebar renders, so the palette can never disagree with navigation
6
+ * (no second route list anywhere, per nav/routes.ts).
7
+ * - `flavor.commands` adds inert reference entries (Declarative-Flavor
8
+ * law): `href` entries navigate; `command` entries name a reference the
9
+ * product's injected runner interprets — and are HIDDEN when no runner
10
+ * is injected (fail-closed: a reference nobody interprets cannot
11
+ * execute).
12
+ * - Every entry passes the SAME permission gate as the sidebar
13
+ * (isPermitted — unknown ⇒ denied, contracts §5). The palette is a
14
+ * second door to the console; it opens exactly as wide as the first.
15
+ *
16
+ * Pure data-in/data-out so the gating behavior is provable without a DOM.
17
+ */
18
+ import type { OperatorFlavor } from "../flavor/types.js";
19
+ import type { SyncPermissionResolver } from "../guard/permissions.js";
20
+ /** What a selected command does. The palette executes; products never inject functions here. */
21
+ export type PaletteAction = {
22
+ kind: "navigate";
23
+ href: string;
24
+ } | {
25
+ kind: "run";
26
+ command: string;
27
+ };
28
+ /** One resolved, permitted palette entry. */
29
+ export interface PaletteCommand {
30
+ id: string;
31
+ label: string;
32
+ /** Icon NAME for the shell's icon registry (nav items always carry one). */
33
+ icon?: string;
34
+ /** Extra match terms beyond the label. */
35
+ keywords?: string[];
36
+ action: PaletteAction;
37
+ }
38
+ /** A rendered palette section. */
39
+ export interface PaletteGroup {
40
+ id: string;
41
+ label: string;
42
+ commands: PaletteCommand[];
43
+ }
44
+ /**
45
+ * Derive the visible palette groups for a Flavor under a permission
46
+ * resolver. `hasRunner` reports whether the product injected a `runCommand`
47
+ * executor — without one, `command`-reference entries are dropped
48
+ * (fail-closed), while `href` entries remain (the shell itself can navigate).
49
+ *
50
+ * The resolver here is SYNC by design (the derivation stays pure/provable):
51
+ * for an async product resolver, the palette component snapshots the live
52
+ * decisions from usePermissionDecisions into a sync lookup — an in-flight
53
+ * (`loading`) decision snapshots to `unknown` ⇒ denied, so the palette is
54
+ * fail-closed WHILE resolving and the entry appears once allowed (a palette
55
+ * has no useful loading row; the sidebar carries the visible loading state).
56
+ */
57
+ export declare function derivePaletteGroups(flavor: OperatorFlavor, resolvePermission: SyncPermissionResolver, hasRunner: boolean): PaletteGroup[];
58
+ //# sourceMappingURL=commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/palette/commands.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAGtE,gGAAgG;AAChG,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAErC,6CAA6C;AAC7C,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,EAAE,aAAa,CAAC;CACvB;AAED,kCAAkC;AAClC,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,cAAc,EAAE,CAAC;CAC5B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,cAAc,EACtB,iBAAiB,EAAE,sBAAsB,EACzC,SAAS,EAAE,OAAO,GACjB,YAAY,EAAE,CAmChB"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * The palette's pure command derivation. Commands are DECLARATIVE:
3
+ *
4
+ * - Route commands are AUTO-DERIVED from `flavor.nav` — the same tree the
5
+ * sidebar renders, so the palette can never disagree with navigation
6
+ * (no second route list anywhere, per nav/routes.ts).
7
+ * - `flavor.commands` adds inert reference entries (Declarative-Flavor
8
+ * law): `href` entries navigate; `command` entries name a reference the
9
+ * product's injected runner interprets — and are HIDDEN when no runner
10
+ * is injected (fail-closed: a reference nobody interprets cannot
11
+ * execute).
12
+ * - Every entry passes the SAME permission gate as the sidebar
13
+ * (isPermitted — unknown ⇒ denied, contracts §5). The palette is a
14
+ * second door to the console; it opens exactly as wide as the first.
15
+ *
16
+ * Pure data-in/data-out so the gating behavior is provable without a DOM.
17
+ */
18
+ import { isPermitted } from "../guard/permissions.js";
19
+ /**
20
+ * Derive the visible palette groups for a Flavor under a permission
21
+ * resolver. `hasRunner` reports whether the product injected a `runCommand`
22
+ * executor — without one, `command`-reference entries are dropped
23
+ * (fail-closed), while `href` entries remain (the shell itself can navigate).
24
+ *
25
+ * The resolver here is SYNC by design (the derivation stays pure/provable):
26
+ * for an async product resolver, the palette component snapshots the live
27
+ * decisions from usePermissionDecisions into a sync lookup — an in-flight
28
+ * (`loading`) decision snapshots to `unknown` ⇒ denied, so the palette is
29
+ * fail-closed WHILE resolving and the entry appears once allowed (a palette
30
+ * has no useful loading row; the sidebar carries the visible loading state).
31
+ */
32
+ export function derivePaletteGroups(flavor, resolvePermission, hasRunner) {
33
+ const groups = [];
34
+ // Route commands, straight from the nav tree the sidebar renders.
35
+ for (const group of flavor.nav) {
36
+ const commands = group.items
37
+ .filter((item) => isPermitted(item.requiredPermission, resolvePermission))
38
+ .map((item) => ({
39
+ id: `nav:${item.id}`,
40
+ label: item.label,
41
+ icon: item.icon,
42
+ action: { kind: "navigate", href: item.href },
43
+ }));
44
+ if (commands.length > 0)
45
+ groups.push({ id: `nav:${group.id}`, label: group.label, commands });
46
+ }
47
+ // Declared commands — additive, same gate, runner-aware.
48
+ for (const group of flavor.commands ?? []) {
49
+ const commands = [];
50
+ for (const item of group.items) {
51
+ if (!isPermitted(item.requiredPermission, resolvePermission))
52
+ continue;
53
+ const action = toAction(item.href, item.command, hasRunner);
54
+ if (!action)
55
+ continue;
56
+ commands.push({
57
+ id: `cmd:${item.id}`,
58
+ label: item.label,
59
+ icon: item.icon,
60
+ keywords: item.keywords,
61
+ action,
62
+ });
63
+ }
64
+ if (commands.length > 0)
65
+ groups.push({ id: `cmd:${group.id}`, label: group.label, commands });
66
+ }
67
+ return groups;
68
+ }
69
+ /**
70
+ * An entry resolves to exactly one action or none: `href` wins if both are
71
+ * (mis)declared, a `command` reference needs an injected runner, and an
72
+ * entry with neither is inert misdeclaration — dropped, never guessed at.
73
+ */
74
+ function toAction(href, command, hasRunner) {
75
+ if (href)
76
+ return { kind: "navigate", href };
77
+ if (command && hasRunner)
78
+ return { kind: "run", command };
79
+ return null;
80
+ }
81
+ //# sourceMappingURL=commands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.js","sourceRoot":"","sources":["../../src/palette/commands.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAyBtD;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,mBAAmB,CACjC,MAAsB,EACtB,iBAAyC,EACzC,SAAkB;IAElB,MAAM,MAAM,GAAmB,EAAE,CAAC;IAElC,kEAAkE;IAClE,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,GAAG,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAqB,KAAK,CAAC,KAAK;aAC3C,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,kBAAkB,EAAE,iBAAiB,CAAC,CAAC;aACzE,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACd,EAAE,EAAE,OAAO,IAAI,CAAC,EAAE,EAAE;YACpB,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,MAAM,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE;SAC9C,CAAC,CAAC,CAAC;QACN,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,OAAO,KAAK,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;IAChG,CAAC;IAED,yDAAyD;IACzD,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC;QAC1C,MAAM,QAAQ,GAAqB,EAAE,CAAC;QACtC,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YAC/B,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,kBAAkB,EAAE,iBAAiB,CAAC;gBAAE,SAAS;YACvE,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;YAC5D,IAAI,CAAC,MAAM;gBAAE,SAAS;YACtB,QAAQ,CAAC,IAAI,CAAC;gBACZ,EAAE,EAAE,OAAO,IAAI,CAAC,EAAE,EAAE;gBACpB,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,MAAM;aACP,CAAC,CAAC;QACL,CAAC;QACD,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,OAAO,KAAK,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;IAChG,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACH,SAAS,QAAQ,CACf,IAAwB,EACxB,OAA2B,EAC3B,SAAkB;IAElB,IAAI,IAAI;QAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;IAC5C,IAAI,OAAO,IAAI,SAAS;QAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;IAC1D,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Search sources — the doctrine skeleton's Search experience as a typed
3
+ * injection seam (frontend-platform-doctrine.md, Operator Platform contract
4
+ * skeleton: "Search — the platform owns the search surface, invocation, and
5
+ * result rendering contract; the product fills WHICH sources and scopes are
6
+ * searchable").
7
+ *
8
+ * The split, same as every other slot:
9
+ * - The SHELL owns the surface — the ⌘K palette renders source results
10
+ * alongside its command groups and executes the selected action.
11
+ * - The PRODUCT injects `SearchSource` values through the shell's
12
+ * `searchSources` prop (the same injection pattern as icons / the
13
+ * permission resolver / `inspectorViews` / `runCommand`) — product code,
14
+ * never Flavor logic (a Flavor is inert data; `query` is a function).
15
+ * - A source's `query` runs on the PRODUCT'S side of the membrane (its own
16
+ * BFF / already-held reads). No transport, URL, or credential ever
17
+ * enters this package.
18
+ *
19
+ * FAIL-CLOSED, three ways (the palette dual of the inspector's refusal):
20
+ * - a source gated by `requiredPermission` that is not explicitly allowed
21
+ * under the SAME snapshot the palette's commands gate on is NEVER
22
+ * QUERIED (denied/unknown/in-flight resolution ⇒ zero results),
23
+ * - an erroring source (rejection or synchronous throw) contributes
24
+ * nothing — siblings are unaffected, the palette never crashes,
25
+ * - an in-flight source contributes nothing until it resolves (the hook
26
+ * in use-search-results.ts enforces this; absent sources trivially
27
+ * contribute nothing).
28
+ *
29
+ * A result's action is PaletteAction-shaped: `navigate` reuses the palette's
30
+ * href navigation; `inspect` opens the shell-owned inspector by entity KIND
31
+ * with an ALREADY-RESOLVED entity (the source fetched it — the inspector
32
+ * contract's "the opener hands it the entity" rule, and the inspector's own
33
+ * fail-closed gate still applies: an undeclared or view-less kind refuses).
34
+ *
35
+ * This module is PURE (no React): the contract + the query machinery,
36
+ * provable without a DOM. The React half lives in use-search-results.ts.
37
+ */
38
+ import type { SyncPermissionResolver } from "../guard/permissions.js";
39
+ /**
40
+ * What selecting a search result does — PaletteAction-shaped, extended with
41
+ * the inspector door (the palette executes; sources return data, never
42
+ * callbacks).
43
+ */
44
+ export type SearchResultAction = {
45
+ kind: "navigate";
46
+ href: string;
47
+ } | {
48
+ kind: "inspect";
49
+ /**
50
+ * Entity-kind REFERENCE — must be Flavor-declared AND have an injected
51
+ * inspector view, or the open fails closed (no panel, palette stays).
52
+ */
53
+ entityKind: string;
54
+ /** The already-resolved entity the inspector view will present. */
55
+ entity: unknown;
56
+ /** Inspector panel title (defaults to the kind reference). */
57
+ title?: string;
58
+ /** Optional one-line description under the title. */
59
+ description?: string;
60
+ };
61
+ /** One result a source returns for a query. Data only — no components, no callbacks. */
62
+ export interface SearchResult {
63
+ /** Stable id, unique within the source. */
64
+ id: string;
65
+ label: string;
66
+ /** Icon NAME for the shell's icon registry (unknown names fail closed to UNKNOWN_ICON). */
67
+ icon?: string;
68
+ /** Extra match terms beyond the label. */
69
+ keywords?: string[];
70
+ action: SearchResultAction;
71
+ }
72
+ /**
73
+ * A product-injected searchable source. `query` is product code (it runs the
74
+ * product's own reads) — which is exactly why sources arrive through a shell
75
+ * PROP and never through the Flavor (Declarative-Flavor law: a Flavor holds
76
+ * no functions).
77
+ */
78
+ export interface SearchSource {
79
+ /** Stable source id (unique across the injected set). */
80
+ id: string;
81
+ /** The palette group heading its results render under. */
82
+ label: string;
83
+ /**
84
+ * Permission REFERENCE gating the whole source — resolved through the SAME
85
+ * fail-closed snapshot as the palette's commands: anything but an explicit
86
+ * `allowed` means the source is never queried.
87
+ */
88
+ requiredPermission?: string;
89
+ /**
90
+ * The product's search. Receives the trimmed palette text; returns results
91
+ * already filtered/ranked by the source. A rejection (or throw) makes the
92
+ * source contribute nothing for that query — never a crash.
93
+ */
94
+ query: (text: string) => Promise<SearchResult[]>;
95
+ }
96
+ /** A rendered search section — the source's results under its group label. */
97
+ export interface SearchResultGroup {
98
+ /** `search:${source.id}` — disjoint from the command groups' `nav:`/`cmd:` namespaces. */
99
+ id: string;
100
+ label: string;
101
+ results: SearchResult[];
102
+ }
103
+ /**
104
+ * The permission gate, shared with the command derivation: a gated source
105
+ * passes only on an explicit `allowed` (unknown/denied/in-flight ⇒ dropped —
106
+ * and a dropped source is never queried, so denial can't even leak timing).
107
+ */
108
+ export declare function permittedSearchSources(sources: readonly SearchSource[] | undefined, resolvePermission: SyncPermissionResolver): SearchSource[];
109
+ /**
110
+ * A result is rendered only if it is SHAPED like one — a malformed entry
111
+ * from a product source is dropped, never guessed at (fail-closed at the
112
+ * data boundary, same posture as the icon registry's unknown names).
113
+ */
114
+ export declare function isSearchResult(value: unknown): value is SearchResult;
115
+ /**
116
+ * Run one query pass across the injected sources — the pure core the palette
117
+ * hook awaits:
118
+ *
119
+ * - blank text queries NOTHING (the palette starts as commands-only),
120
+ * - only permitted sources are queried (fail-closed gate above),
121
+ * - each source is independent: an error/throw or an empty/malformed
122
+ * answer contributes nothing while siblings still land,
123
+ * - group order follows injection order (deterministic rendering).
124
+ */
125
+ export declare function querySearchSources(sources: readonly SearchSource[] | undefined, text: string, resolvePermission: SyncPermissionResolver): Promise<SearchResultGroup[]>;
126
+ //# sourceMappingURL=search-sources.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-sources.d.ts","sourceRoot":"","sources":["../../src/palette/search-sources.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAGtE;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAC1B;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAClC;IACE,IAAI,EAAE,SAAS,CAAC;IAChB;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,MAAM,EAAE,OAAO,CAAC;IAChB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qDAAqD;IACrD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEN,wFAAwF;AACxF,MAAM,WAAW,YAAY;IAC3B,2CAA2C;IAC3C,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,2FAA2F;IAC3F,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,EAAE,kBAAkB,CAAC;CAC5B;AAED;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,yDAAyD;IACzD,EAAE,EAAE,MAAM,CAAC;IACX,0DAA0D;IAC1D,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;OAIG;IACH,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;CAClD;AAED,8EAA8E;AAC9E,MAAM,WAAW,iBAAiB;IAChC,0FAA0F;IAC1F,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,YAAY,EAAE,CAAC;CACzB;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,SAAS,YAAY,EAAE,GAAG,SAAS,EAC5C,iBAAiB,EAAE,sBAAsB,GACxC,YAAY,EAAE,CAKhB;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,YAAY,CASpE;AAED;;;;;;;;;GASG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,SAAS,YAAY,EAAE,GAAG,SAAS,EAC5C,IAAI,EAAE,MAAM,EACZ,iBAAiB,EAAE,sBAAsB,GACxC,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAsB9B"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Search sources — the doctrine skeleton's Search experience as a typed
3
+ * injection seam (frontend-platform-doctrine.md, Operator Platform contract
4
+ * skeleton: "Search — the platform owns the search surface, invocation, and
5
+ * result rendering contract; the product fills WHICH sources and scopes are
6
+ * searchable").
7
+ *
8
+ * The split, same as every other slot:
9
+ * - The SHELL owns the surface — the ⌘K palette renders source results
10
+ * alongside its command groups and executes the selected action.
11
+ * - The PRODUCT injects `SearchSource` values through the shell's
12
+ * `searchSources` prop (the same injection pattern as icons / the
13
+ * permission resolver / `inspectorViews` / `runCommand`) — product code,
14
+ * never Flavor logic (a Flavor is inert data; `query` is a function).
15
+ * - A source's `query` runs on the PRODUCT'S side of the membrane (its own
16
+ * BFF / already-held reads). No transport, URL, or credential ever
17
+ * enters this package.
18
+ *
19
+ * FAIL-CLOSED, three ways (the palette dual of the inspector's refusal):
20
+ * - a source gated by `requiredPermission` that is not explicitly allowed
21
+ * under the SAME snapshot the palette's commands gate on is NEVER
22
+ * QUERIED (denied/unknown/in-flight resolution ⇒ zero results),
23
+ * - an erroring source (rejection or synchronous throw) contributes
24
+ * nothing — siblings are unaffected, the palette never crashes,
25
+ * - an in-flight source contributes nothing until it resolves (the hook
26
+ * in use-search-results.ts enforces this; absent sources trivially
27
+ * contribute nothing).
28
+ *
29
+ * A result's action is PaletteAction-shaped: `navigate` reuses the palette's
30
+ * href navigation; `inspect` opens the shell-owned inspector by entity KIND
31
+ * with an ALREADY-RESOLVED entity (the source fetched it — the inspector
32
+ * contract's "the opener hands it the entity" rule, and the inspector's own
33
+ * fail-closed gate still applies: an undeclared or view-less kind refuses).
34
+ *
35
+ * This module is PURE (no React): the contract + the query machinery,
36
+ * provable without a DOM. The React half lives in use-search-results.ts.
37
+ */
38
+ import { isPermitted } from "../guard/permissions.js";
39
+ /**
40
+ * The permission gate, shared with the command derivation: a gated source
41
+ * passes only on an explicit `allowed` (unknown/denied/in-flight ⇒ dropped —
42
+ * and a dropped source is never queried, so denial can't even leak timing).
43
+ */
44
+ export function permittedSearchSources(sources, resolvePermission) {
45
+ if (!sources || sources.length === 0)
46
+ return [];
47
+ return sources.filter((source) => isPermitted(source.requiredPermission, resolvePermission));
48
+ }
49
+ /**
50
+ * A result is rendered only if it is SHAPED like one — a malformed entry
51
+ * from a product source is dropped, never guessed at (fail-closed at the
52
+ * data boundary, same posture as the icon registry's unknown names).
53
+ */
54
+ export function isSearchResult(value) {
55
+ if (typeof value !== "object" || value === null)
56
+ return false;
57
+ const result = value;
58
+ if (typeof result.id !== "string" || typeof result.label !== "string")
59
+ return false;
60
+ const action = result.action;
61
+ if (typeof action !== "object" || action === null)
62
+ return false;
63
+ if (action.kind === "navigate")
64
+ return typeof action.href === "string";
65
+ if (action.kind === "inspect")
66
+ return typeof action.entityKind === "string";
67
+ return false;
68
+ }
69
+ /**
70
+ * Run one query pass across the injected sources — the pure core the palette
71
+ * hook awaits:
72
+ *
73
+ * - blank text queries NOTHING (the palette starts as commands-only),
74
+ * - only permitted sources are queried (fail-closed gate above),
75
+ * - each source is independent: an error/throw or an empty/malformed
76
+ * answer contributes nothing while siblings still land,
77
+ * - group order follows injection order (deterministic rendering).
78
+ */
79
+ export async function querySearchSources(sources, text, resolvePermission) {
80
+ const trimmed = text.trim();
81
+ if (trimmed.length === 0)
82
+ return [];
83
+ const permitted = permittedSearchSources(sources, resolvePermission);
84
+ if (permitted.length === 0)
85
+ return [];
86
+ const settled = await Promise.all(permitted.map(async (source) => {
87
+ let results;
88
+ try {
89
+ const answer = await source.query(trimmed);
90
+ results = Array.isArray(answer) ? answer.filter(isSearchResult) : [];
91
+ }
92
+ catch {
93
+ return null; // an erroring source contributes nothing — never a crash
94
+ }
95
+ if (results.length === 0)
96
+ return null;
97
+ return { id: `search:${source.id}`, label: source.label, results };
98
+ }));
99
+ return settled.filter((group) => group !== null);
100
+ }
101
+ //# sourceMappingURL=search-sources.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-sources.js","sourceRoot":"","sources":["../../src/palette/search-sources.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAGH,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAqEtD;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CACpC,OAA4C,EAC5C,iBAAyC;IAEzC,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAChD,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAC/B,WAAW,CAAC,MAAM,CAAC,kBAAkB,EAAE,iBAAiB,CAAC,CAC1D,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,MAAM,GAAG,KAA8B,CAAC;IAC9C,IAAI,OAAO,MAAM,CAAC,EAAE,KAAK,QAAQ,IAAI,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACpF,MAAM,MAAM,GAAG,MAAM,CAAC,MAAiD,CAAC;IACxE,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAChE,IAAI,MAAM,CAAC,IAAI,KAAK,UAAU;QAAE,OAAO,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,CAAC;IACvE,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,OAAO,MAAM,CAAC,UAAU,KAAK,QAAQ,CAAC;IAC5E,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,OAA4C,EAC5C,IAAY,EACZ,iBAAyC;IAEzC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAC5B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEpC,MAAM,SAAS,GAAG,sBAAsB,CAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC;IACrE,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEtC,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAC/B,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,EAAqC,EAAE;QAChE,IAAI,OAAuB,CAAC;QAC5B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC3C,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACvE,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC,CAAC,yDAAyD;QACxE,CAAC;QACD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QACtC,OAAO,EAAE,EAAE,EAAE,UAAU,MAAM,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC;IACrE,CAAC,CAAC,CACH,CAAC;IAEF,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAA8B,EAAE,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC;AAC/E,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { PermissionGateStatus } from "../guard/permissions.js";
2
+ import type { SearchResultGroup, SearchSource } from "./search-sources.js";
3
+ export declare function useSearchResults(sources: readonly SearchSource[] | undefined, text: string, decisions: Readonly<Record<string, PermissionGateStatus>>): SearchResultGroup[];
4
+ //# sourceMappingURL=use-search-results.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-search-results.d.ts","sourceRoot":"","sources":["../../src/palette/use-search-results.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,oBAAoB,EAA0B,MAAM,yBAAyB,CAAC;AAC5F,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAG3E,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,SAAS,YAAY,EAAE,GAAG,SAAS,EAC5C,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC,GACxD,iBAAiB,EAAE,CAoCrB"}
@@ -0,0 +1,51 @@
1
+ "use client";
2
+ /**
3
+ * The React half of the search-source seam: run the injected sources against
4
+ * the palette text and hold the CURRENT results — with the fail-closed
5
+ * in-flight rule enforced here:
6
+ *
7
+ * - while a query pass is in flight, the sources contribute NOTHING (the
8
+ * previous pass's results are cleared the moment the text changes — the
9
+ * palette never shows results for a query the user is no longer typing),
10
+ * - a stale pass (the text moved on before it settled) is DISCARDED, never
11
+ * rendered,
12
+ * - blank text / no sources ⇒ no query, no results, synchronously.
13
+ *
14
+ * Permission gating rides the SAME live decisions map the palette's command
15
+ * derivation snapshots (usePermissionDecisions state): anything but an
16
+ * explicit `allowed` on a gated source means it is never queried. The
17
+ * decisions map is React state with stable identity between settlements, so
18
+ * it is the correct effect dependency (a per-render closure would not be).
19
+ */
20
+ import * as React from "react";
21
+ import { permittedSearchSources, querySearchSources } from "./search-sources.js";
22
+ export function useSearchResults(sources, text, decisions) {
23
+ const [groups, setGroups] = React.useState([]);
24
+ React.useEffect(() => {
25
+ // Snapshot the live decisions into the sync gate the pure core takes:
26
+ // allowed passes; loading/denied/absent ⇒ unknown ⇒ the source is
27
+ // dropped before it is ever queried (fail-closed while resolving).
28
+ const snapshot = (permission) => decisions[permission] === "allowed" ? "allowed" : "unknown";
29
+ // Nothing searchable (blank text / no sources / none permitted) ⇒
30
+ // nothing rendered, no query fired, no async pass left dangling.
31
+ if (text.trim().length === 0 ||
32
+ permittedSearchSources(sources, snapshot).length === 0) {
33
+ setGroups([]);
34
+ return;
35
+ }
36
+ // In-flight contributes nothing: clear immediately, render on settle.
37
+ setGroups([]);
38
+ let stale = false;
39
+ querySearchSources(sources, text, snapshot).then((next) => {
40
+ if (!stale)
41
+ setGroups(next);
42
+ });
43
+ // querySearchSources never rejects (per-source errors are absorbed), so
44
+ // no catch is needed — but staleness still guards the resolution.
45
+ return () => {
46
+ stale = true;
47
+ };
48
+ }, [sources, text, decisions]);
49
+ return groups;
50
+ }
51
+ //# sourceMappingURL=use-search-results.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-search-results.js","sourceRoot":"","sources":["../../src/palette/use-search-results.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,OAAO,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAEjF,MAAM,UAAU,gBAAgB,CAC9B,OAA4C,EAC5C,IAAY,EACZ,SAAyD;IAEzD,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAsB,EAAE,CAAC,CAAC;IAEpE,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,sEAAsE;QACtE,kEAAkE;QAClE,mEAAmE;QACnE,MAAM,QAAQ,GAA2B,CAAC,UAAU,EAAE,EAAE,CACtD,SAAS,CAAC,UAAU,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;QAE9D,kEAAkE;QAClE,iEAAiE;QACjE,IACE,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;YACxB,sBAAsB,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,MAAM,KAAK,CAAC,EACtD,CAAC;YACD,SAAS,CAAC,EAAE,CAAC,CAAC;YACd,OAAO;QACT,CAAC;QAED,sEAAsE;QACtE,SAAS,CAAC,EAAE,CAAC,CAAC;QACd,IAAI,KAAK,GAAG,KAAK,CAAC;QAElB,kBAAkB,CAAC,OAAO,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;YACxD,IAAI,CAAC,KAAK;gBAAE,SAAS,CAAC,IAAI,CAAC,CAAC;QAC9B,CAAC,CAAC,CAAC;QACH,wEAAwE;QACxE,kEAAkE;QAElE,OAAO,GAAG,EAAE;YACV,KAAK,GAAG,IAAI,CAAC;QACf,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC;IAE/B,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * OperatorSidebar — the shell's nav engine over the open-registry sidebar
3
+ * primitives (icon-collapsible, per the sidebar-07/sidebar-16 block
4
+ * patterns). It reads the Flavor: the product-context block from
5
+ * `flavor.product`, groups/items from `flavor.nav` (string icon names
6
+ * resolved through the icon registry), and gates items through the injected
7
+ * permission resolver — fail-closed, with EXPLICIT states (contracts §5):
8
+ *
9
+ * - `allowed` → the item renders,
10
+ * - `loading` → a registry SidebarMenuSkeleton holds the slot while an
11
+ * async resolver decides (no flash of a link the decision may remove),
12
+ * - `denied`/unknown → the item does not exist here (hidden, not disabled —
13
+ * a denied destination is not advertised),
14
+ * - a group whose gated items all vanish vanishes with them (no orphan
15
+ * group labels).
16
+ *
17
+ * The shell owns navigation behavior; the product declares the tree.
18
+ */
19
+ import type * as React from "react";
20
+ import type { OperatorFlavor } from "../flavor/types.js";
21
+ import type { PermissionResolver } from "../guard/permissions.js";
22
+ import type { IconRegistry } from "../icons/registry.js";
23
+ export interface OperatorSidebarProps {
24
+ flavor: OperatorFlavor;
25
+ /** The product's permission resolution (§5), sync or async. Fail-closed on unknown. */
26
+ resolvePermission: PermissionResolver;
27
+ /** Product icon-vocabulary extensions for the registry. */
28
+ icons?: IconRegistry;
29
+ /**
30
+ * The resolved icon family (the frame reads the Brand's inert
31
+ * `iconFamily` declaration and resolves it via `resolveIconFamily`).
32
+ * Absent ⇒ the shipped family — same deterministic default.
33
+ */
34
+ iconFamily?: string;
35
+ /**
36
+ * Workspace-switcher SLOT (typed seam, INERT until backend PRE-1 — see
37
+ * OperatorShellProps.workspaceSwitcher and the Flavor's `workspaces`
38
+ * field). Nothing injected (today: every product) ⇒ nothing renders; the
39
+ * static product block below stays the whole context chrome.
40
+ */
41
+ workspaceSwitcher?: React.ReactNode;
42
+ }
43
+ export declare function OperatorSidebar({ flavor, resolvePermission, icons, iconFamily, workspaceSwitcher, }: OperatorSidebarProps): import("react/jsx-runtime").JSX.Element;
44
+ //# sourceMappingURL=OperatorSidebar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OperatorSidebar.d.ts","sourceRoot":"","sources":["../../src/sidebar/OperatorSidebar.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AAkBpC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAGlE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAIzD,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,cAAc,CAAC;IACvB,uFAAuF;IACvF,iBAAiB,EAAE,kBAAkB,CAAC;IACtC,2DAA2D;IAC3D,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CACrC;AAED,wBAAgB,eAAe,CAAC,EAC9B,MAAM,EACN,iBAAiB,EACjB,KAAK,EACL,UAAU,EACV,iBAAiB,GAClB,EAAE,oBAAoB,2CA8EtB"}