@hanzo/ui 8.0.56 → 8.0.58

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.
@@ -3,13 +3,53 @@
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.Switch = void 0;
5
5
  const jsx_runtime_1 = require("react/jsx-runtime");
6
- /** Switch — 36×20 track, 16px thumb, `touch()` to the 44px floor everywhere. */
6
+ /**
7
+ * Switch — a 36×20 pill, 16px thumb, `touch()` to the 44px floor everywhere.
8
+ *
9
+ * `minHeight` is NOT redundant beside `height`, and omitting it was the bug.
10
+ * gui's own `size` variant returns `{ height, minHeight, width }` for the
11
+ * default `$true` size, and setting `height` here overrode only two of the
12
+ * three — min-height beats height, always — so the 29px floor survived and
13
+ * every switch in every app rendered 36×29 instead of 36×20. At that ratio the
14
+ * browser clamps `borderRadius: 1000` to 10px, so it was not even a pill: a
15
+ * rounded rectangle with a small dot adrift inside it.
16
+ *
17
+ * State is carried by COLOUR, not by the thumb's 14px of travel alone. gui's
18
+ * default checked treatment is `$backgroundActive`, which in this theme resolves
19
+ * to the same value the unchecked track already has — measured live, on and off
20
+ * were pixel-identical apart from that translation. A state expressed only as
21
+ * position is one a screenshot, a narrow column and a low-vision reader all fail
22
+ * to read. `activeStyle` is the hook gui honours for exactly this: it is pulled
23
+ * out of props before Tamagui can mistake it for the press pseudo-style, and it
24
+ * REPLACES `$backgroundActive` rather than layering over it.
25
+ *
26
+ * The white is spent on ON and nowhere else. A resting page is mostly switches
27
+ * that are off, and a near-white thumb on every one of them is a field of lights
28
+ * with no signal in it — which is what a full page of these actually looked
29
+ * like. Off is muted, on is filled, and the loudest treatment is the one that
30
+ * means something.
31
+ */
7
32
  const gui_1 = require("@hanzo/gui");
8
33
  const slot_1 = require("./slot.cjs");
9
34
  const gesture_1 = require("./gesture.cjs");
35
+ const TRACK_W = 36;
10
36
  const TRACK_H = 20;
11
37
  const THUMB = 16;
12
- const Switch = (props) => ((0, jsx_runtime_1.jsx)(gui_1.Switch, { ...(0, slot_1.slot)('switch'), width: 36, height: TRACK_H, padding: 2, flexShrink: 0,
38
+ const Switch = ({ disabled, ...props }) => ((0, jsx_runtime_1.jsx)(gui_1.Switch, { ...(0, slot_1.slot)('switch'), width: TRACK_W, height: TRACK_H, minHeight: TRACK_H, padding: 2, flexShrink: 0, backgroundColor: "$color3",
13
39
  // Without an explicit border the UA's 2px outset button border shows.
14
- borderWidth: 1, borderColor: "$borderColor", ...(0, gesture_1.touch)(TRACK_H, 44, 'y'), ...props, children: (0, jsx_runtime_1.jsx)(gui_1.Switch.Thumb, { ...(0, slot_1.slot)('switch-thumb'), width: THUMB, height: THUMB }) }));
40
+ borderWidth: 1, borderColor: "$borderColor", activeStyle: { backgroundColor: '$color12', borderColor: '$color12' },
41
+ // Stated rather than left to a `disabledStyle` this backend uses nowhere
42
+ // else: a control that ignores its own disabled prop looks identical to a
43
+ // live one, and the page this was found on had a disabled switch sitting
44
+ // beside eight enabled ones with nothing to tell them apart.
45
+ disabled: disabled, opacity: disabled ? 0.5 : 1, cursor: disabled ? 'not-allowed' : 'pointer', ...(0, gesture_1.touch)(TRACK_H, 44, 'y'), ...props, children: (0, jsx_runtime_1.jsx)(gui_1.Switch.Thumb, { ...(0, slot_1.slot)('switch-thumb'), width: THUMB, height: THUMB, backgroundColor: "$color10",
46
+ // `bg`, and it is the only one of the three spellings that is both typed
47
+ // and correct. The Thumb's activeStyle is typed as the SHORTHAND style set:
48
+ // `backgroundColor` paints but is not in that type; `background` is in it
49
+ // and compiles to a SEPARATE `_background-` class that races the base
50
+ // `_bg-` one on load order rather than replacing it; `bg` replaces it.
51
+ // Consumers build with ignoreBuildErrors, so the first would have shipped
52
+ // an error nobody sees and the second a colour that lands or does not
53
+ // depending on stylesheet order.
54
+ activeStyle: { bg: '$color1' } }) }));
15
55
  exports.Switch = Switch;
@@ -1,7 +1,32 @@
1
- /** Switch — 36×20 track, 16px thumb, `touch()` to the 44px floor everywhere. */
1
+ /**
2
+ * Switch — a 36×20 pill, 16px thumb, `touch()` to the 44px floor everywhere.
3
+ *
4
+ * `minHeight` is NOT redundant beside `height`, and omitting it was the bug.
5
+ * gui's own `size` variant returns `{ height, minHeight, width }` for the
6
+ * default `$true` size, and setting `height` here overrode only two of the
7
+ * three — min-height beats height, always — so the 29px floor survived and
8
+ * every switch in every app rendered 36×29 instead of 36×20. At that ratio the
9
+ * browser clamps `borderRadius: 1000` to 10px, so it was not even a pill: a
10
+ * rounded rectangle with a small dot adrift inside it.
11
+ *
12
+ * State is carried by COLOUR, not by the thumb's 14px of travel alone. gui's
13
+ * default checked treatment is `$backgroundActive`, which in this theme resolves
14
+ * to the same value the unchecked track already has — measured live, on and off
15
+ * were pixel-identical apart from that translation. A state expressed only as
16
+ * position is one a screenshot, a narrow column and a low-vision reader all fail
17
+ * to read. `activeStyle` is the hook gui honours for exactly this: it is pulled
18
+ * out of props before Tamagui can mistake it for the press pseudo-style, and it
19
+ * REPLACES `$backgroundActive` rather than layering over it.
20
+ *
21
+ * The white is spent on ON and nowhere else. A resting page is mostly switches
22
+ * that are off, and a near-white thumb on every one of them is a field of lights
23
+ * with no signal in it — which is what a full page of these actually looked
24
+ * like. Off is muted, on is filled, and the loudest treatment is the one that
25
+ * means something.
26
+ */
2
27
  import { Switch as GuiSwitch } from '@hanzo/gui';
3
28
  import type { ComponentProps } from 'react';
4
29
  export type SwitchProps = ComponentProps<typeof GuiSwitch>;
5
- declare const Switch: (props: SwitchProps) => import("react/jsx-runtime").JSX.Element;
30
+ declare const Switch: ({ disabled, ...props }: SwitchProps) => import("react/jsx-runtime").JSX.Element;
6
31
  export { Switch };
7
32
  //# sourceMappingURL=switch.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"switch.d.ts","sourceRoot":"","sources":["../../../src/backends/gui/switch.tsx"],"names":[],"mappings":"AAEA,gFAAgF;AAChF,OAAO,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,YAAY,CAAA;AAChD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAA;AAO3C,MAAM,MAAM,WAAW,GAAG,cAAc,CAAC,OAAO,SAAS,CAAC,CAAA;AAE1D,QAAA,MAAM,MAAM,GAAI,OAAO,WAAW,4CAmBjC,CAAA;AAED,OAAO,EAAE,MAAM,EAAE,CAAA"}
1
+ {"version":3,"file":"switch.d.ts","sourceRoot":"","sources":["../../../src/backends/gui/switch.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,YAAY,CAAA;AAChD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAA;AAQ3C,MAAM,MAAM,WAAW,GAAG,cAAc,CAAC,OAAO,SAAS,CAAC,CAAA;AAE1D,QAAA,MAAM,MAAM,GAAI,wBAAwB,WAAW,4CAuClD,CAAA;AAED,OAAO,EAAE,MAAM,EAAE,CAAA"}
@@ -1,13 +1,53 @@
1
1
  'use client';
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
- /** Switch — 36×20 track, 16px thumb, `touch()` to the 44px floor everywhere. */
3
+ /**
4
+ * Switch — a 36×20 pill, 16px thumb, `touch()` to the 44px floor everywhere.
5
+ *
6
+ * `minHeight` is NOT redundant beside `height`, and omitting it was the bug.
7
+ * gui's own `size` variant returns `{ height, minHeight, width }` for the
8
+ * default `$true` size, and setting `height` here overrode only two of the
9
+ * three — min-height beats height, always — so the 29px floor survived and
10
+ * every switch in every app rendered 36×29 instead of 36×20. At that ratio the
11
+ * browser clamps `borderRadius: 1000` to 10px, so it was not even a pill: a
12
+ * rounded rectangle with a small dot adrift inside it.
13
+ *
14
+ * State is carried by COLOUR, not by the thumb's 14px of travel alone. gui's
15
+ * default checked treatment is `$backgroundActive`, which in this theme resolves
16
+ * to the same value the unchecked track already has — measured live, on and off
17
+ * were pixel-identical apart from that translation. A state expressed only as
18
+ * position is one a screenshot, a narrow column and a low-vision reader all fail
19
+ * to read. `activeStyle` is the hook gui honours for exactly this: it is pulled
20
+ * out of props before Tamagui can mistake it for the press pseudo-style, and it
21
+ * REPLACES `$backgroundActive` rather than layering over it.
22
+ *
23
+ * The white is spent on ON and nowhere else. A resting page is mostly switches
24
+ * that are off, and a near-white thumb on every one of them is a field of lights
25
+ * with no signal in it — which is what a full page of these actually looked
26
+ * like. Off is muted, on is filled, and the loudest treatment is the one that
27
+ * means something.
28
+ */
4
29
  import { Switch as GuiSwitch } from '@hanzo/gui';
5
30
  import { slot } from './slot.js';
6
31
  import { touch } from './gesture.js';
32
+ const TRACK_W = 36;
7
33
  const TRACK_H = 20;
8
34
  const THUMB = 16;
9
- const Switch = (props) => (_jsx(GuiSwitch, { ...slot('switch'), width: 36, height: TRACK_H, padding: 2, flexShrink: 0,
35
+ const Switch = ({ disabled, ...props }) => (_jsx(GuiSwitch, { ...slot('switch'), width: TRACK_W, height: TRACK_H, minHeight: TRACK_H, padding: 2, flexShrink: 0, backgroundColor: "$color3",
10
36
  // Without an explicit border the UA's 2px outset button border shows.
11
- borderWidth: 1, borderColor: "$borderColor", ...touch(TRACK_H, 44, 'y'), ...props, children: _jsx(GuiSwitch.Thumb, { ...slot('switch-thumb'), width: THUMB, height: THUMB }) }));
37
+ borderWidth: 1, borderColor: "$borderColor", activeStyle: { backgroundColor: '$color12', borderColor: '$color12' },
38
+ // Stated rather than left to a `disabledStyle` this backend uses nowhere
39
+ // else: a control that ignores its own disabled prop looks identical to a
40
+ // live one, and the page this was found on had a disabled switch sitting
41
+ // beside eight enabled ones with nothing to tell them apart.
42
+ disabled: disabled, opacity: disabled ? 0.5 : 1, cursor: disabled ? 'not-allowed' : 'pointer', ...touch(TRACK_H, 44, 'y'), ...props, children: _jsx(GuiSwitch.Thumb, { ...slot('switch-thumb'), width: THUMB, height: THUMB, backgroundColor: "$color10",
43
+ // `bg`, and it is the only one of the three spellings that is both typed
44
+ // and correct. The Thumb's activeStyle is typed as the SHORTHAND style set:
45
+ // `backgroundColor` paints but is not in that type; `background` is in it
46
+ // and compiles to a SEPARATE `_background-` class that races the base
47
+ // `_bg-` one on load order rather than replacing it; `bg` replaces it.
48
+ // Consumers build with ignoreBuildErrors, so the first would have shipped
49
+ // an error nobody sees and the second a colour that lands or does not
50
+ // depending on stylesheet order.
51
+ activeStyle: { bg: '$color1' } }) }));
12
52
  export { Switch };
13
53
  //# sourceMappingURL=switch.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"switch.js","sourceRoot":"","sources":["../../../src/backends/gui/switch.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,gFAAgF;AAChF,OAAO,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,YAAY,CAAA;AAEhD,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AAC7B,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAA;AAEjC,MAAM,OAAO,GAAG,EAAE,CAAA;AAClB,MAAM,KAAK,GAAG,EAAE,CAAA;AAIhB,MAAM,MAAM,GAAG,CAAC,KAAkB,EAAE,EAAE,CAAC,CACrC,KAAC,SAAS,OACJ,IAAI,CAAC,QAAQ,CAAC,EAClB,KAAK,EAAE,EAAE,EACT,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,CAAC,EACV,UAAU,EAAE,CAAC;IACb,sEAAsE;IACtE,WAAW,EAAE,CAAC,EACd,WAAW,EAAC,cAAc,KACtB,KAAK,CAAC,OAAO,EAAE,EAAE,EAAE,GAAG,CAAC,KACvB,KAAK,YAET,KAAC,SAAS,CAAC,KAAK,OACV,IAAI,CAAC,cAAc,CAAC,EACxB,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,KAAK,GACb,GACQ,CACb,CAAA;AAED,OAAO,EAAE,MAAM,EAAE,CAAA"}
1
+ {"version":3,"file":"switch.js","sourceRoot":"","sources":["../../../src/backends/gui/switch.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,YAAY,CAAA;AAEhD,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AAC7B,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAA;AAEjC,MAAM,OAAO,GAAG,EAAE,CAAA;AAClB,MAAM,OAAO,GAAG,EAAE,CAAA;AAClB,MAAM,KAAK,GAAG,EAAE,CAAA;AAIhB,MAAM,MAAM,GAAG,CAAC,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAe,EAAE,EAAE,CAAC,CACtD,KAAC,SAAS,OACJ,IAAI,CAAC,QAAQ,CAAC,EAClB,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,OAAO,EACf,SAAS,EAAE,OAAO,EAClB,OAAO,EAAE,CAAC,EACV,UAAU,EAAE,CAAC,EACb,eAAe,EAAC,SAAS;IACzB,sEAAsE;IACtE,WAAW,EAAE,CAAC,EACd,WAAW,EAAC,cAAc,EAC1B,WAAW,EAAE,EAAE,eAAe,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE;IACrE,yEAAyE;IACzE,0EAA0E;IAC1E,yEAAyE;IACzE,6DAA6D;IAC7D,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAC3B,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,KACxC,KAAK,CAAC,OAAO,EAAE,EAAE,EAAE,GAAG,CAAC,KACvB,KAAK,YAET,KAAC,SAAS,CAAC,KAAK,OACV,IAAI,CAAC,cAAc,CAAC,EACxB,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,KAAK,EACb,eAAe,EAAC,UAAU;QAC1B,yEAAyE;QACzE,4EAA4E;QAC5E,0EAA0E;QAC1E,sEAAsE;QACtE,uEAAuE;QACvE,0EAA0E;QAC1E,sEAAsE;QACtE,iCAAiC;QACjC,WAAW,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,GAC9B,GACQ,CACb,CAAA;AAED,OAAO,EAAE,MAAM,EAAE,CAAA"}
@@ -0,0 +1,65 @@
1
+ 'use client';"use strict";
2
+ 'use client';
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.Palette = Palette;
5
+ const jsx_runtime_1 = require("react/jsx-runtime");
6
+ /**
7
+ * Palette — the ⌘K bar, once, for every Hanzo surface.
8
+ *
9
+ * hanzo.app, console and chat each grew their own: three lists of what can be
10
+ * run, all hand-written, already disagreeing with each other and with the API.
11
+ * This is the component half of ending that. The other half is `GET /v1/commands`,
12
+ * which projects every operation the cloud answers out of the one route table —
13
+ * so a surface passes that list in and stops maintaining one.
14
+ *
15
+ * It is composed from `CommandDialog`, not from a hand-rolled Dialog around a
16
+ * bare `Command`. That distinction is the whole history: the stock dialog used to
17
+ * render `<Command>` with none of the palette's props forwarded, so a host could
18
+ * not read the highlighted row and therefore could not draw a preview beside the
19
+ * list — and hanzo.app rebuilt the dialog by hand for exactly that reason. The
20
+ * forwarding landed upstream in this package and named collapsing the hand-rolled
21
+ * palettes as the follow-up. This is that follow-up, so the hand-rolled shape does
22
+ * not come back here under a new name.
23
+ *
24
+ * Props in, callbacks out, like everything else in this layer: no transport, no
25
+ * store, no routing. It does not fetch the commands, does not run them, and does
26
+ * not know what a project or an org is. `onRun` receives the Op and the surface
27
+ * decides whether that means an HTTP call or a function in the page — which is
28
+ * why a route command and a local command are one type (see `match.ts`).
29
+ *
30
+ * The right-hand slot is `children`, and it is why a shared bar costs no surface
31
+ * its personality: hanzo.app keeps its live project preview and console keeps its
32
+ * `>` AI mode by passing them, not by forking this.
33
+ *
34
+ * Filtering is `survivors`. The safe-methods-browse rule, the per-group cap and
35
+ * the empty-query behaviour all live in `match.ts` as pure functions, so
36
+ * `shouldFilter` is off and the Command primitive is left holding the cursor and
37
+ * the view — which is also what makes the rule testable without a DOM.
38
+ */
39
+ const react_1 = require("react");
40
+ const gui_1 = require("@hanzo/gui");
41
+ const gui_2 = require("../backends/gui/index.cjs");
42
+ const match_1 = require("./match.cjs");
43
+ function Palette({ open, onOpenChange, ops, onRun, active, onActive, search: controlled, onSearch, placeholder = "Search commands…", empty = "No results found.", limit, children, footer, height = 320, title = "Search commands", }) {
44
+ const [own, setOwn] = (0, react_1.useState)("");
45
+ const search = controlled ?? own;
46
+ const setSearch = (s) => {
47
+ setOwn(s);
48
+ onSearch?.(s);
49
+ };
50
+ const groups = (0, react_1.useMemo)(() => (0, match_1.survivors)(ops, search, limit), [ops, search, limit]);
51
+ return ((0, jsx_runtime_1.jsxs)(gui_2.CommandDialog, { open: open, onOpenChange: onOpenChange, title: title, description: placeholder, value: active, onValueChange: onActive, shouldFilter: false, children: [(0, jsx_runtime_1.jsx)(gui_2.CommandInput, { value: search, onValueChange: setSearch, placeholder: placeholder, color: "$color", placeholderTextColor: "$color11" }), (0, jsx_runtime_1.jsxs)(gui_1.XStack, { minH: height, children: [(0, jsx_runtime_1.jsxs)(gui_2.CommandList, { maxH: height, width: children ? "50%" : "100%", borderRightWidth: children ? 1 : 0, borderColor: "$borderColor", py: "$1", children: [groups.length === 0 && ((0, jsx_runtime_1.jsx)(gui_2.CommandEmpty, { children: (0, jsx_runtime_1.jsx)(gui_1.SizableText, { color: "$color11", children: empty }) })), groups.map(([group, items]) => ((0, jsx_runtime_1.jsx)(gui_2.CommandGroup, { heading: group, children: items.map((op) => ((0, jsx_runtime_1.jsxs)(gui_2.CommandItem, { value: op.id, onSelect: () => onRun(op), gap: "$2", children: [op.icon, (0, jsx_runtime_1.jsx)(gui_1.SizableText, { numberOfLines: 1, children: op.label }), op.hint ? ((0, jsx_runtime_1.jsx)(gui_1.SizableText, { numberOfLines: 1, fontSize: "$1", color: "$color11", children: op.hint })) : null, (0, jsx_runtime_1.jsx)(Method, { of: op }), op.end] }, op.id))) }, group)))] }), children ? (0, jsx_runtime_1.jsx)(gui_1.YStack, { width: "50%", children: children }) : null] }), footer ? ((0, jsx_runtime_1.jsx)(gui_1.XStack, { items: "center", gap: "$4", borderTopWidth: 1, borderColor: "$borderColor", px: "$3", py: "$2", children: footer })) : null] }));
52
+ }
53
+ /**
54
+ * The method, at the end of the row, for route commands only.
55
+ *
56
+ * It is the rule made visible rather than decoration: GET is what you reached by
57
+ * browsing, anything else is what you reached by naming it exactly, and somebody
58
+ * about to press ↵ on `platform apps-rollback` should be able to see which
59
+ * without reading the docs. A local command has no method and gets no tag.
60
+ */
61
+ function Method({ of }) {
62
+ if (of.method === undefined)
63
+ return null;
64
+ return ((0, jsx_runtime_1.jsx)(gui_1.SizableText, { ml: "auto", fontFamily: "$mono", fontSize: 10, letterSpacing: 0.4, color: of.method === match_1.SAFE ? "$color11" : "$color", children: of.method }));
65
+ }
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Palette — the ⌘K bar, once, for every Hanzo surface.
3
+ *
4
+ * hanzo.app, console and chat each grew their own: three lists of what can be
5
+ * run, all hand-written, already disagreeing with each other and with the API.
6
+ * This is the component half of ending that. The other half is `GET /v1/commands`,
7
+ * which projects every operation the cloud answers out of the one route table —
8
+ * so a surface passes that list in and stops maintaining one.
9
+ *
10
+ * It is composed from `CommandDialog`, not from a hand-rolled Dialog around a
11
+ * bare `Command`. That distinction is the whole history: the stock dialog used to
12
+ * render `<Command>` with none of the palette's props forwarded, so a host could
13
+ * not read the highlighted row and therefore could not draw a preview beside the
14
+ * list — and hanzo.app rebuilt the dialog by hand for exactly that reason. The
15
+ * forwarding landed upstream in this package and named collapsing the hand-rolled
16
+ * palettes as the follow-up. This is that follow-up, so the hand-rolled shape does
17
+ * not come back here under a new name.
18
+ *
19
+ * Props in, callbacks out, like everything else in this layer: no transport, no
20
+ * store, no routing. It does not fetch the commands, does not run them, and does
21
+ * not know what a project or an org is. `onRun` receives the Op and the surface
22
+ * decides whether that means an HTTP call or a function in the page — which is
23
+ * why a route command and a local command are one type (see `match.ts`).
24
+ *
25
+ * The right-hand slot is `children`, and it is why a shared bar costs no surface
26
+ * its personality: hanzo.app keeps its live project preview and console keeps its
27
+ * `>` AI mode by passing them, not by forking this.
28
+ *
29
+ * Filtering is `survivors`. The safe-methods-browse rule, the per-group cap and
30
+ * the empty-query behaviour all live in `match.ts` as pure functions, so
31
+ * `shouldFilter` is off and the Command primitive is left holding the cursor and
32
+ * the view — which is also what makes the rule testable without a DOM.
33
+ */
34
+ import { type ReactNode } from "react";
35
+ import { type Op } from "./match.js";
36
+ export type PaletteProps = {
37
+ open: boolean;
38
+ onOpenChange: (open: boolean) => void;
39
+ /** Everything runnable, local commands and route commands alike. */
40
+ ops: Op[];
41
+ /** Run one. The surface decides what running it means. */
42
+ onRun: (op: Op) => void;
43
+ /**
44
+ * The highlighted row's id (controlled). A surface drawing a preview beside the
45
+ * list needs to know what is highlighted.
46
+ */
47
+ active?: string;
48
+ onActive?: (id: string) => void;
49
+ /** The typed query. Uncontrolled unless a surface wants to read it. */
50
+ search?: string;
51
+ onSearch?: (search: string) => void;
52
+ placeholder?: string;
53
+ /** Shown when nothing survives the search. */
54
+ empty?: ReactNode;
55
+ /** Rows per group. See `survivors` — a render budget, not a preference. */
56
+ limit?: number;
57
+ /** The right-hand panel. Absent, the list takes the full width. */
58
+ children?: ReactNode;
59
+ /** The bar along the bottom — key hints, usually. */
60
+ footer?: ReactNode;
61
+ /** How tall the list is. It scrolls inside this. */
62
+ height?: number;
63
+ /** The dialog's accessible name. */
64
+ title?: string;
65
+ };
66
+ export declare function Palette({ open, onOpenChange, ops, onRun, active, onActive, search: controlled, onSearch, placeholder, empty, limit, children, footer, height, title, }: PaletteProps): import("react/jsx-runtime").JSX.Element;
67
+ //# sourceMappingURL=Palette.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Palette.d.ts","sourceRoot":"","sources":["../../src/product/Palette.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,OAAO,EAAE,KAAK,SAAS,EAAqB,MAAM,OAAO,CAAA;AAWzD,OAAO,EAAmB,KAAK,EAAE,EAAE,MAAM,SAAS,CAAA;AAElD,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,oEAAoE;IACpE,GAAG,EAAE,EAAE,EAAE,CAAA;IACT,0DAA0D;IAC1D,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,IAAI,CAAA;IACvB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/B,uEAAuE;IACvE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IACnC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,8CAA8C;IAC9C,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,2EAA2E;IAC3E,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,mEAAmE;IACnE,QAAQ,CAAC,EAAE,SAAS,CAAA;IACpB,qDAAqD;IACrD,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB,oDAAoD;IACpD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAED,wBAAgB,OAAO,CAAC,EACtB,IAAI,EACJ,YAAY,EACZ,GAAG,EACH,KAAK,EACL,MAAM,EACN,QAAQ,EACR,MAAM,EAAE,UAAU,EAClB,QAAQ,EACR,WAAgC,EAChC,KAA2B,EAC3B,KAAK,EACL,QAAQ,EACR,MAAM,EACN,MAAY,EACZ,KAAyB,GAC1B,EAAE,YAAY,2CA0Ed"}
@@ -0,0 +1,63 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ /**
4
+ * Palette — the ⌘K bar, once, for every Hanzo surface.
5
+ *
6
+ * hanzo.app, console and chat each grew their own: three lists of what can be
7
+ * run, all hand-written, already disagreeing with each other and with the API.
8
+ * This is the component half of ending that. The other half is `GET /v1/commands`,
9
+ * which projects every operation the cloud answers out of the one route table —
10
+ * so a surface passes that list in and stops maintaining one.
11
+ *
12
+ * It is composed from `CommandDialog`, not from a hand-rolled Dialog around a
13
+ * bare `Command`. That distinction is the whole history: the stock dialog used to
14
+ * render `<Command>` with none of the palette's props forwarded, so a host could
15
+ * not read the highlighted row and therefore could not draw a preview beside the
16
+ * list — and hanzo.app rebuilt the dialog by hand for exactly that reason. The
17
+ * forwarding landed upstream in this package and named collapsing the hand-rolled
18
+ * palettes as the follow-up. This is that follow-up, so the hand-rolled shape does
19
+ * not come back here under a new name.
20
+ *
21
+ * Props in, callbacks out, like everything else in this layer: no transport, no
22
+ * store, no routing. It does not fetch the commands, does not run them, and does
23
+ * not know what a project or an org is. `onRun` receives the Op and the surface
24
+ * decides whether that means an HTTP call or a function in the page — which is
25
+ * why a route command and a local command are one type (see `match.ts`).
26
+ *
27
+ * The right-hand slot is `children`, and it is why a shared bar costs no surface
28
+ * its personality: hanzo.app keeps its live project preview and console keeps its
29
+ * `>` AI mode by passing them, not by forking this.
30
+ *
31
+ * Filtering is `survivors`. The safe-methods-browse rule, the per-group cap and
32
+ * the empty-query behaviour all live in `match.ts` as pure functions, so
33
+ * `shouldFilter` is off and the Command primitive is left holding the cursor and
34
+ * the view — which is also what makes the rule testable without a DOM.
35
+ */
36
+ import { useMemo, useState } from "react";
37
+ import { SizableText, XStack, YStack } from "@hanzo/gui";
38
+ import { CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from "../backends/gui/index.js";
39
+ import { SAFE, survivors } from "./match.js";
40
+ export function Palette({ open, onOpenChange, ops, onRun, active, onActive, search: controlled, onSearch, placeholder = "Search commands…", empty = "No results found.", limit, children, footer, height = 320, title = "Search commands", }) {
41
+ const [own, setOwn] = useState("");
42
+ const search = controlled ?? own;
43
+ const setSearch = (s) => {
44
+ setOwn(s);
45
+ onSearch?.(s);
46
+ };
47
+ const groups = useMemo(() => survivors(ops, search, limit), [ops, search, limit]);
48
+ return (_jsxs(CommandDialog, { open: open, onOpenChange: onOpenChange, title: title, description: placeholder, value: active, onValueChange: onActive, shouldFilter: false, children: [_jsx(CommandInput, { value: search, onValueChange: setSearch, placeholder: placeholder, color: "$color", placeholderTextColor: "$color11" }), _jsxs(XStack, { minH: height, children: [_jsxs(CommandList, { maxH: height, width: children ? "50%" : "100%", borderRightWidth: children ? 1 : 0, borderColor: "$borderColor", py: "$1", children: [groups.length === 0 && (_jsx(CommandEmpty, { children: _jsx(SizableText, { color: "$color11", children: empty }) })), groups.map(([group, items]) => (_jsx(CommandGroup, { heading: group, children: items.map((op) => (_jsxs(CommandItem, { value: op.id, onSelect: () => onRun(op), gap: "$2", children: [op.icon, _jsx(SizableText, { numberOfLines: 1, children: op.label }), op.hint ? (_jsx(SizableText, { numberOfLines: 1, fontSize: "$1", color: "$color11", children: op.hint })) : null, _jsx(Method, { of: op }), op.end] }, op.id))) }, group)))] }), children ? _jsx(YStack, { width: "50%", children: children }) : null] }), footer ? (_jsx(XStack, { items: "center", gap: "$4", borderTopWidth: 1, borderColor: "$borderColor", px: "$3", py: "$2", children: footer })) : null] }));
49
+ }
50
+ /**
51
+ * The method, at the end of the row, for route commands only.
52
+ *
53
+ * It is the rule made visible rather than decoration: GET is what you reached by
54
+ * browsing, anything else is what you reached by naming it exactly, and somebody
55
+ * about to press ↵ on `platform apps-rollback` should be able to see which
56
+ * without reading the docs. A local command has no method and gets no tag.
57
+ */
58
+ function Method({ of }) {
59
+ if (of.method === undefined)
60
+ return null;
61
+ return (_jsx(SizableText, { ml: "auto", fontFamily: "$mono", fontSize: 10, letterSpacing: 0.4, color: of.method === SAFE ? "$color11" : "$color", children: of.method }));
62
+ }
63
+ //# sourceMappingURL=Palette.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Palette.js","sourceRoot":"","sources":["../../src/product/Palette.tsx"],"names":[],"mappings":"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"}
@@ -111,6 +111,13 @@ __exportStar(require("./FadeIn.cjs"), exports);
111
111
  __exportStar(require("./Field.cjs"), exports);
112
112
  __exportStar(require("./HanzoMark.cjs"), exports);
113
113
  __exportStar(require("./PageHeader.cjs"), exports);
114
+ // Palette — the ONE ⌘K bar, plus its opener. The list it renders is not written
115
+ // down anywhere: cloud publishes `GET /v1/commands`, the fifth projection of the
116
+ // one route table, and a surface passes it in. `match.ts` holds the matching
117
+ // rule (safe methods browse, unsafe methods must be named) as pure functions.
118
+ __exportStar(require("./Palette.cjs"), exports);
119
+ __exportStar(require("./match.cjs"), exports);
120
+ __exportStar(require("./useCommandK.cjs"), exports);
114
121
  __exportStar(require("./PrimaryButton.cjs"), exports);
115
122
  __exportStar(require("./ProductIcon.cjs"), exports);
116
123
  __exportStar(require("./ProviderLogo.cjs"), exports);
@@ -31,6 +31,9 @@ export * from './FadeIn.js';
31
31
  export * from './Field.js';
32
32
  export * from './HanzoMark.js';
33
33
  export * from './PageHeader.js';
34
+ export * from './Palette.js';
35
+ export * from './match.js';
36
+ export * from './useCommandK.js';
34
37
  export * from './PrimaryButton.js';
35
38
  export * from './ProductIcon.js';
36
39
  export * from './ProviderLogo.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/product/index.ts"],"names":[],"mappings":"AAaA,cAAc,UAAU,CAAA;AAKxB,OAAO,EACL,MAAM,EACN,aAAa,EACb,SAAS,EACT,SAAS,IAAI,eAAe,EAC5B,QAAQ,EACR,OAAO,EACP,SAAS,EACT,UAAU,EACV,UAAU,EACV,KAAK,GACN,MAAM,UAAU,CAAA;AAGjB,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,SAAS,CAAA;AAK/D,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AACrC,cAAc,mBAAmB,CAAA;AASjC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA;AACzB,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,SAAS,CAAA;AAKvB,cAAc,QAAQ,CAAA;AAKtB,cAAc,WAAW,CAAA;AACzB,cAAc,cAAc,CAAA;AAK5B,cAAc,QAAQ,CAAA;AACtB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,oBAAoB,CAAA;AAClC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,WAAW,CAAA;AAOzB,cAAc,cAAc,CAAA;AAK5B,cAAc,YAAY,CAAA;AAO1B,cAAc,cAAc,CAAA;AAC5B,cAAc,eAAe,CAAA;AAI7B,cAAc,cAAc,CAAA;AAG5B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,SAAS,CAAA;AACvB,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,UAAU,CAAA;AACxB,cAAc,SAAS,CAAA;AACvB,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,eAAe,CAAA;AAC7B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,WAAW,CAAA;AACzB,cAAc,cAAc,CAAA;AAC5B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA;AAC7B,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACnD,cAAc,SAAS,CAAA;AACvB,cAAc,UAAU,CAAA;AACxB,cAAc,SAAS,CAAA;AASvB,cAAc,UAAU,CAAA;AAKxB,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,aAAa,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/product/index.ts"],"names":[],"mappings":"AAaA,cAAc,UAAU,CAAA;AAKxB,OAAO,EACL,MAAM,EACN,aAAa,EACb,SAAS,EACT,SAAS,IAAI,eAAe,EAC5B,QAAQ,EACR,OAAO,EACP,SAAS,EACT,UAAU,EACV,UAAU,EACV,KAAK,GACN,MAAM,UAAU,CAAA;AAGjB,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,SAAS,CAAA;AAK/D,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AACrC,cAAc,mBAAmB,CAAA;AASjC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA;AACzB,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,SAAS,CAAA;AAKvB,cAAc,QAAQ,CAAA;AAKtB,cAAc,WAAW,CAAA;AACzB,cAAc,cAAc,CAAA;AAK5B,cAAc,QAAQ,CAAA;AACtB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,oBAAoB,CAAA;AAClC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,WAAW,CAAA;AAOzB,cAAc,cAAc,CAAA;AAK5B,cAAc,YAAY,CAAA;AAO1B,cAAc,cAAc,CAAA;AAC5B,cAAc,eAAe,CAAA;AAI7B,cAAc,cAAc,CAAA;AAG5B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,SAAS,CAAA;AACvB,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,UAAU,CAAA;AACxB,cAAc,SAAS,CAAA;AACvB,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAM5B,cAAc,WAAW,CAAA;AACzB,cAAc,SAAS,CAAA;AACvB,cAAc,eAAe,CAAA;AAC7B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,eAAe,CAAA;AAC7B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,WAAW,CAAA;AACzB,cAAc,cAAc,CAAA;AAC5B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA;AAC7B,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACnD,cAAc,SAAS,CAAA;AACvB,cAAc,UAAU,CAAA;AACxB,cAAc,SAAS,CAAA;AASvB,cAAc,UAAU,CAAA;AAKxB,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,aAAa,CAAA"}
@@ -82,6 +82,13 @@ export * from './FadeIn.js';
82
82
  export * from './Field.js';
83
83
  export * from './HanzoMark.js';
84
84
  export * from './PageHeader.js';
85
+ // Palette — the ONE ⌘K bar, plus its opener. The list it renders is not written
86
+ // down anywhere: cloud publishes `GET /v1/commands`, the fifth projection of the
87
+ // one route table, and a surface passes it in. `match.ts` holds the matching
88
+ // rule (safe methods browse, unsafe methods must be named) as pure functions.
89
+ export * from './Palette.js';
90
+ export * from './match.js';
91
+ export * from './useCommandK.js';
85
92
  export * from './PrimaryButton.js';
86
93
  export * from './ProductIcon.js';
87
94
  export * from './ProviderLogo.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/product/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/product/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,76 @@
1
+ 'use client';"use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.survivors = exports.match = exports.SAFE = void 0;
4
+ const command_logic_1 = require("../backends/gui/command.logic.cjs");
5
+ /** The one method that is safe to stumble onto: no side effect, idempotent. */
6
+ exports.SAFE = 'GET';
7
+ /**
8
+ * Whether `op` survives `search`, and how well. `0` hides it.
9
+ *
10
+ * # Safe methods browse; unsafe methods must be named
11
+ *
12
+ * Typing "delete" into a bar holding 2,323 operations offers four dozen
13
+ * destructive fleet operations to somebody who wanted to delete a project. The
14
+ * rule that fixes it needs no second list and no curation, because the method is
15
+ * already in the registry and already means exactly this:
16
+ *
17
+ * - GET matches fuzzily. It is safe and idempotent; browsing costs nothing.
18
+ * - every other method needs an exact PREFIX of `group label` — the same
19
+ * "service operation" a CLI would type. You cannot stumble onto
20
+ * `platform apps-rollback`; you have to name it.
21
+ *
22
+ * One rule, over data already present, mapping onto the semantics the methods
23
+ * already carry.
24
+ *
25
+ * # An empty search hides every route, and that is not a shortcut
26
+ *
27
+ * With no query a fuzzy matcher matches everything, and everything is 2,323 rows
28
+ * the moment the dialog opens — which is not a list, it is a stalled browser and
29
+ * a scroll bar nobody can use. So the Run group is a SEARCH, not a browse: it is
30
+ * empty until you type, and what remains on open is the surface's own handful of
31
+ * local commands, which is what a person opening ⌘K with no query wants anyway.
32
+ *
33
+ * A local command has no method and is therefore always reachable — it is one of
34
+ * a few, declared by the surface, and there is nothing to protect anyone from.
35
+ */
36
+ const match = (op, search) => {
37
+ const q = search.trim().toLowerCase();
38
+ const route = op.method !== undefined;
39
+ if (!q)
40
+ return route ? 0 : 1;
41
+ const named = `${op.group} ${op.label}`.toLowerCase();
42
+ if (route && op.method !== exports.SAFE)
43
+ return named.startsWith(q) ? 1 : 0;
44
+ return (0, command_logic_1.score)(named, q, op.hint ? [op.hint] : undefined);
45
+ };
46
+ exports.match = match;
47
+ /**
48
+ * The ops that survive `search`, grouped for rendering: headings in the order the
49
+ * caller listed them, rows ranked within each, empty groups dropped.
50
+ *
51
+ * `limit` caps each group. A GET search for "list" still matches hundreds, and a
52
+ * palette that renders hundreds is the same stalled browser the empty query would
53
+ * be — the cap is what keeps one keystroke's cost bounded whatever was typed.
54
+ */
55
+ const survivors = (ops, search, limit = 50) => {
56
+ const groups = new Map();
57
+ for (const op of ops) {
58
+ const rank = (0, exports.match)(op, search);
59
+ if (rank <= 0)
60
+ continue;
61
+ const got = groups.get(op.group);
62
+ if (got)
63
+ got.push([op, rank]);
64
+ else
65
+ groups.set(op.group, [[op, rank]]);
66
+ }
67
+ const out = [];
68
+ for (const [group, ranked] of groups) {
69
+ // Sort by rank, ties keeping the caller's order — a surface that ordered its
70
+ // projects by recency must not have that undone by a scoring tie.
71
+ ranked.sort((a, b) => b[1] - a[1]);
72
+ out.push([group, ranked.slice(0, limit).map(([op]) => op)]);
73
+ }
74
+ return out;
75
+ };
76
+ exports.survivors = survivors;
@@ -0,0 +1,78 @@
1
+ /**
2
+ * What the ⌘K bar shows for what you typed, as one pure function.
3
+ *
4
+ * The bar's list is not hand-written any more. hanzo.app's cloud publishes
5
+ * `GET /v1/commands` — every operation the API answers, projected from the same
6
+ * route table the OpenAPI document, the MCP tools and the `hanzo` CLI come from —
7
+ * so a surface hands the whole list in and this decides what survives a search.
8
+ *
9
+ * That list is 2,323 operations, and that number is what makes the rule below
10
+ * more than a preference.
11
+ */
12
+ import type { ReactNode } from 'react';
13
+ /**
14
+ * One runnable thing in the bar.
15
+ *
16
+ * A command that names a route and a command that runs in the page are the SAME
17
+ * type here; only the surface's `onRun` tells them apart. `method` is what says
18
+ * which — it is present exactly when the command is an HTTP operation, and it is
19
+ * the only field the matching rule reads.
20
+ */
21
+ export interface Op {
22
+ /** Stable identity. The registry's operationId, or a surface-local name. */
23
+ id: string;
24
+ /** The heading it files under — a service ("projects"), or a surface's own. */
25
+ group: string;
26
+ /** What it is called: the operation token ("deployments-list"), or a phrase. */
27
+ label: string;
28
+ /** One line of help. The operation's summary, when it has one. */
29
+ hint?: string;
30
+ /** The HTTP method, when this names a route. Absent means it runs in the page. */
31
+ method?: string;
32
+ /** Drawn at the start of the row. */
33
+ icon?: ReactNode;
34
+ /** Drawn at the end of the row — a status dot, a shortcut. */
35
+ end?: ReactNode;
36
+ }
37
+ /** The one method that is safe to stumble onto: no side effect, idempotent. */
38
+ export declare const SAFE = "GET";
39
+ /**
40
+ * Whether `op` survives `search`, and how well. `0` hides it.
41
+ *
42
+ * # Safe methods browse; unsafe methods must be named
43
+ *
44
+ * Typing "delete" into a bar holding 2,323 operations offers four dozen
45
+ * destructive fleet operations to somebody who wanted to delete a project. The
46
+ * rule that fixes it needs no second list and no curation, because the method is
47
+ * already in the registry and already means exactly this:
48
+ *
49
+ * - GET matches fuzzily. It is safe and idempotent; browsing costs nothing.
50
+ * - every other method needs an exact PREFIX of `group label` — the same
51
+ * "service operation" a CLI would type. You cannot stumble onto
52
+ * `platform apps-rollback`; you have to name it.
53
+ *
54
+ * One rule, over data already present, mapping onto the semantics the methods
55
+ * already carry.
56
+ *
57
+ * # An empty search hides every route, and that is not a shortcut
58
+ *
59
+ * With no query a fuzzy matcher matches everything, and everything is 2,323 rows
60
+ * the moment the dialog opens — which is not a list, it is a stalled browser and
61
+ * a scroll bar nobody can use. So the Run group is a SEARCH, not a browse: it is
62
+ * empty until you type, and what remains on open is the surface's own handful of
63
+ * local commands, which is what a person opening ⌘K with no query wants anyway.
64
+ *
65
+ * A local command has no method and is therefore always reachable — it is one of
66
+ * a few, declared by the surface, and there is nothing to protect anyone from.
67
+ */
68
+ export declare const match: (op: Op, search: string) => number;
69
+ /**
70
+ * The ops that survive `search`, grouped for rendering: headings in the order the
71
+ * caller listed them, rows ranked within each, empty groups dropped.
72
+ *
73
+ * `limit` caps each group. A GET search for "list" still matches hundreds, and a
74
+ * palette that renders hundreds is the same stalled browser the empty query would
75
+ * be — the cap is what keeps one keystroke's cost bounded whatever was typed.
76
+ */
77
+ export declare const survivors: (ops: Op[], search: string, limit?: number) => Array<[string, Op[]]>;
78
+ //# sourceMappingURL=match.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"match.d.ts","sourceRoot":"","sources":["../../src/product/match.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAItC;;;;;;;GAOG;AACH,MAAM,WAAW,EAAE;IACjB,4EAA4E;IAC5E,EAAE,EAAE,MAAM,CAAA;IACV,+EAA+E;IAC/E,KAAK,EAAE,MAAM,CAAA;IACb,gFAAgF;IAChF,KAAK,EAAE,MAAM,CAAA;IACb,kEAAkE;IAClE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,kFAAkF;IAClF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,qCAAqC;IACrC,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,8DAA8D;IAC9D,GAAG,CAAC,EAAE,SAAS,CAAA;CAChB;AAED,+EAA+E;AAC/E,eAAO,MAAM,IAAI,QAAQ,CAAA;AAEzB;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,KAAK,GAAI,IAAI,EAAE,EAAE,QAAQ,MAAM,KAAG,MAQ9C,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS,GAAI,KAAK,EAAE,EAAE,EAAE,QAAQ,MAAM,EAAE,cAAU,KAAG,KAAK,CAAC,CAAC,MAAM,EAAE,EAAE,EAAE,CAAC,CAiBrF,CAAA"}
@@ -0,0 +1,72 @@
1
+ 'use client';import { score } from '../backends/gui/command.logic.js';
2
+ /** The one method that is safe to stumble onto: no side effect, idempotent. */
3
+ export const SAFE = 'GET';
4
+ /**
5
+ * Whether `op` survives `search`, and how well. `0` hides it.
6
+ *
7
+ * # Safe methods browse; unsafe methods must be named
8
+ *
9
+ * Typing "delete" into a bar holding 2,323 operations offers four dozen
10
+ * destructive fleet operations to somebody who wanted to delete a project. The
11
+ * rule that fixes it needs no second list and no curation, because the method is
12
+ * already in the registry and already means exactly this:
13
+ *
14
+ * - GET matches fuzzily. It is safe and idempotent; browsing costs nothing.
15
+ * - every other method needs an exact PREFIX of `group label` — the same
16
+ * "service operation" a CLI would type. You cannot stumble onto
17
+ * `platform apps-rollback`; you have to name it.
18
+ *
19
+ * One rule, over data already present, mapping onto the semantics the methods
20
+ * already carry.
21
+ *
22
+ * # An empty search hides every route, and that is not a shortcut
23
+ *
24
+ * With no query a fuzzy matcher matches everything, and everything is 2,323 rows
25
+ * the moment the dialog opens — which is not a list, it is a stalled browser and
26
+ * a scroll bar nobody can use. So the Run group is a SEARCH, not a browse: it is
27
+ * empty until you type, and what remains on open is the surface's own handful of
28
+ * local commands, which is what a person opening ⌘K with no query wants anyway.
29
+ *
30
+ * A local command has no method and is therefore always reachable — it is one of
31
+ * a few, declared by the surface, and there is nothing to protect anyone from.
32
+ */
33
+ export const match = (op, search) => {
34
+ const q = search.trim().toLowerCase();
35
+ const route = op.method !== undefined;
36
+ if (!q)
37
+ return route ? 0 : 1;
38
+ const named = `${op.group} ${op.label}`.toLowerCase();
39
+ if (route && op.method !== SAFE)
40
+ return named.startsWith(q) ? 1 : 0;
41
+ return score(named, q, op.hint ? [op.hint] : undefined);
42
+ };
43
+ /**
44
+ * The ops that survive `search`, grouped for rendering: headings in the order the
45
+ * caller listed them, rows ranked within each, empty groups dropped.
46
+ *
47
+ * `limit` caps each group. A GET search for "list" still matches hundreds, and a
48
+ * palette that renders hundreds is the same stalled browser the empty query would
49
+ * be — the cap is what keeps one keystroke's cost bounded whatever was typed.
50
+ */
51
+ export const survivors = (ops, search, limit = 50) => {
52
+ const groups = new Map();
53
+ for (const op of ops) {
54
+ const rank = match(op, search);
55
+ if (rank <= 0)
56
+ continue;
57
+ const got = groups.get(op.group);
58
+ if (got)
59
+ got.push([op, rank]);
60
+ else
61
+ groups.set(op.group, [[op, rank]]);
62
+ }
63
+ const out = [];
64
+ for (const [group, ranked] of groups) {
65
+ // Sort by rank, ties keeping the caller's order — a surface that ordered its
66
+ // projects by recency must not have that undone by a scoring tie.
67
+ ranked.sort((a, b) => b[1] - a[1]);
68
+ out.push([group, ranked.slice(0, limit).map(([op]) => op)]);
69
+ }
70
+ return out;
71
+ };
72
+ //# sourceMappingURL=match.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"match.js","sourceRoot":"","sources":["../../src/product/match.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,KAAK,EAAE,MAAM,+BAA+B,CAAA;AA2BrD,+EAA+E;AAC/E,MAAM,CAAC,MAAM,IAAI,GAAG,KAAK,CAAA;AAEzB;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,EAAM,EAAE,MAAc,EAAU,EAAE;IACtD,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;IACrC,MAAM,KAAK,GAAG,EAAE,CAAC,MAAM,KAAK,SAAS,CAAA;IACrC,IAAI,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAE5B,MAAM,KAAK,GAAG,GAAG,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,CAAA;IACrD,IAAI,KAAK,IAAI,EAAE,CAAC,MAAM,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IACnE,OAAO,KAAK,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;AACzD,CAAC,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,GAAS,EAAE,MAAc,EAAE,KAAK,GAAG,EAAE,EAAyB,EAAE;IACxF,MAAM,MAAM,GAAG,IAAI,GAAG,EAA+B,CAAA;IACrD,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;QACrB,MAAM,IAAI,GAAG,KAAK,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;QAC9B,IAAI,IAAI,IAAI,CAAC;YAAE,SAAQ;QACvB,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,CAAA;QAChC,IAAI,GAAG;YAAE,GAAG,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,CAAA;;YACxB,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,CAAA;IACzC,CAAC;IACD,MAAM,GAAG,GAA0B,EAAE,CAAA;IACrC,KAAK,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACrC,6EAA6E;QAC7E,kEAAkE;QAClE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QAClC,GAAG,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;IAC7D,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC,CAAA"}
@@ -0,0 +1,46 @@
1
+ 'use client';"use strict";
2
+ 'use client';
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.useCommandK = useCommandK;
5
+ /**
6
+ * useCommandK — the ONE keybinding for the command palette.
7
+ *
8
+ * ⌘K / Ctrl-K always toggles (even from a text field — it is the universal
9
+ * palette shortcut and nothing else may claim it). `/` also opens it, but ONLY
10
+ * when the user is NOT typing in a field, so a slash inside an input, a textarea
11
+ * or a contenteditable types a slash. Pass `slash: false` to bind ⌘K alone.
12
+ *
13
+ * That second rule is why chat needs no exception written for it. Its composer is
14
+ * a TEXTAREA, so the global `/` never fires there and the composer's own prompts
15
+ * popover keeps the key it has. The same rule makes a `/` typed into the palette's
16
+ * own input a literal slash. There is no precedence to encode — the one condition
17
+ * already answers all three, and the instinct to add an exception would be adding
18
+ * dead code.
19
+ *
20
+ * One hook, so every surface that hosts the palette shares identical behaviour
21
+ * instead of a divergent inline listener each. It moved here from hanzo.app,
22
+ * which is where it was already correct.
23
+ */
24
+ const react_1 = require("react");
25
+ function isEditable(target) {
26
+ const el = target;
27
+ if (!el || typeof HTMLElement === "undefined" || !(el instanceof HTMLElement))
28
+ return false;
29
+ const tag = el.tagName;
30
+ return tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT" || el.isContentEditable;
31
+ }
32
+ function useCommandK(onTrigger, opts) {
33
+ const slash = opts?.slash !== false;
34
+ (0, react_1.useEffect)(() => {
35
+ const onKey = (e) => {
36
+ const cmdK = (e.metaKey || e.ctrlKey) && !e.altKey && e.key.toLowerCase() === "k";
37
+ const slashKey = slash && e.key === "/" && !e.metaKey && !e.ctrlKey && !e.altKey && !isEditable(e.target);
38
+ if (cmdK || slashKey) {
39
+ e.preventDefault();
40
+ onTrigger();
41
+ }
42
+ };
43
+ window.addEventListener("keydown", onKey);
44
+ return () => window.removeEventListener("keydown", onKey);
45
+ }, [onTrigger, slash]);
46
+ }
@@ -0,0 +1,4 @@
1
+ export declare function useCommandK(onTrigger: () => void, opts?: {
2
+ slash?: boolean;
3
+ }): void;
4
+ //# sourceMappingURL=useCommandK.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCommandK.d.ts","sourceRoot":"","sources":["../../src/product/useCommandK.ts"],"names":[],"mappings":"AA8BA,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,IAAI,EAAE,IAAI,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAenF"}
@@ -0,0 +1,44 @@
1
+ 'use client';
2
+ /**
3
+ * useCommandK — the ONE keybinding for the command palette.
4
+ *
5
+ * ⌘K / Ctrl-K always toggles (even from a text field — it is the universal
6
+ * palette shortcut and nothing else may claim it). `/` also opens it, but ONLY
7
+ * when the user is NOT typing in a field, so a slash inside an input, a textarea
8
+ * or a contenteditable types a slash. Pass `slash: false` to bind ⌘K alone.
9
+ *
10
+ * That second rule is why chat needs no exception written for it. Its composer is
11
+ * a TEXTAREA, so the global `/` never fires there and the composer's own prompts
12
+ * popover keeps the key it has. The same rule makes a `/` typed into the palette's
13
+ * own input a literal slash. There is no precedence to encode — the one condition
14
+ * already answers all three, and the instinct to add an exception would be adding
15
+ * dead code.
16
+ *
17
+ * One hook, so every surface that hosts the palette shares identical behaviour
18
+ * instead of a divergent inline listener each. It moved here from hanzo.app,
19
+ * which is where it was already correct.
20
+ */
21
+ import { useEffect } from "react";
22
+ function isEditable(target) {
23
+ const el = target;
24
+ if (!el || typeof HTMLElement === "undefined" || !(el instanceof HTMLElement))
25
+ return false;
26
+ const tag = el.tagName;
27
+ return tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT" || el.isContentEditable;
28
+ }
29
+ export function useCommandK(onTrigger, opts) {
30
+ const slash = opts?.slash !== false;
31
+ useEffect(() => {
32
+ const onKey = (e) => {
33
+ const cmdK = (e.metaKey || e.ctrlKey) && !e.altKey && e.key.toLowerCase() === "k";
34
+ const slashKey = slash && e.key === "/" && !e.metaKey && !e.ctrlKey && !e.altKey && !isEditable(e.target);
35
+ if (cmdK || slashKey) {
36
+ e.preventDefault();
37
+ onTrigger();
38
+ }
39
+ };
40
+ window.addEventListener("keydown", onKey);
41
+ return () => window.removeEventListener("keydown", onKey);
42
+ }, [onTrigger, slash]);
43
+ }
44
+ //# sourceMappingURL=useCommandK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCommandK.js","sourceRoot":"","sources":["../../src/product/useCommandK.ts"],"names":[],"mappings":"AAAA,YAAY,CAAA;AAEZ;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEjC,SAAS,UAAU,CAAC,MAA0B;IAC5C,MAAM,EAAE,GAAG,MAA4B,CAAA;IACvC,IAAI,CAAC,EAAE,IAAI,OAAO,WAAW,KAAK,WAAW,IAAI,CAAC,CAAC,EAAE,YAAY,WAAW,CAAC;QAAE,OAAO,KAAK,CAAA;IAC3F,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAA;IACtB,OAAO,GAAG,KAAK,OAAO,IAAI,GAAG,KAAK,UAAU,IAAI,GAAG,KAAK,QAAQ,IAAI,EAAE,CAAC,iBAAiB,CAAA;AAC1F,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,SAAqB,EAAE,IAA0B;IAC3E,MAAM,KAAK,GAAG,IAAI,EAAE,KAAK,KAAK,KAAK,CAAA;IACnC,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,KAAK,GAAG,CAAC,CAAgB,EAAE,EAAE;YACjC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,GAAG,CAAA;YACjF,MAAM,QAAQ,GACZ,KAAK,IAAI,CAAC,CAAC,GAAG,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;YAC1F,IAAI,IAAI,IAAI,QAAQ,EAAE,CAAC;gBACrB,CAAC,CAAC,cAAc,EAAE,CAAA;gBAClB,SAAS,EAAE,CAAA;YACb,CAAC;QACH,CAAC,CAAA;QACD,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,KAAK,CAAC,CAAA;QACzC,OAAO,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,KAAK,CAAC,CAAA;IAC3D,CAAC,EAAE,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,CAAA;AACxB,CAAC"}
package/dist/styles.css CHANGED
@@ -2109,6 +2109,8 @@
2109
2109
  :root ._bg-background{background-color:var(--background);}
2110
2110
  :root ._bg-backgroundA94988999{background-color:var(--backgroundActive);}
2111
2111
  :root ._bg-borderColor{background-color:var(--borderColor);}
2112
+ :root ._bg-color1{background-color:var(--color1);}
2113
+ :root ._bg-color10{background-color:var(--color10);}
2112
2114
  :root ._bg-color12{background-color:var(--color12);}
2113
2115
  :root ._bg-color2{background-color:var(--color2);}
2114
2116
  :root ._bg-color3{background-color:var(--color3);}
@@ -2128,6 +2130,7 @@
2128
2130
  @media (hover) {:root:root ._borderBottomColor-0hover-color8._borderBottomColor-0hover-color8:hover{border-bottom-color:var(--color8) !important;}}
2129
2131
  :root ._borderBottomColor-backgroundF3405682._borderBottomColor-backgroundF3405682{border-bottom-color:var(--backgroundFocus);}
2130
2132
  :root ._borderBottomColor-borderColor._borderBottomColor-borderColor{border-bottom-color:var(--borderColor);}
2133
+ :root ._borderBottomColor-color12._borderBottomColor-color12{border-bottom-color:var(--color12);}
2131
2134
  :root ._borderBottomColor-color6._borderBottomColor-color6{border-bottom-color:var(--color6);}
2132
2135
  :root ._borderBottomColor-transparent._borderBottomColor-transparent{border-bottom-color:transparent;}
2133
2136
  :root ._borderBottomWidth-0px._borderBottomWidth-0px{border-bottom-width:0px;}
@@ -2143,6 +2146,7 @@
2143
2146
  @media (hover) {:root:root ._borderLeftColor-0hover-color8._borderLeftColor-0hover-color8:hover{border-left-color:var(--color8) !important;}}
2144
2147
  :root ._borderLeftColor-backgroundF3405682._borderLeftColor-backgroundF3405682{border-left-color:var(--backgroundFocus);}
2145
2148
  :root ._borderLeftColor-borderColor._borderLeftColor-borderColor{border-left-color:var(--borderColor);}
2149
+ :root ._borderLeftColor-color12._borderLeftColor-color12{border-left-color:var(--color12);}
2146
2150
  :root ._borderLeftColor-color6._borderLeftColor-color6{border-left-color:var(--color6);}
2147
2151
  :root ._borderLeftColor-transparent._borderLeftColor-transparent{border-left-color:transparent;}
2148
2152
  :root ._borderLeftWidth-0px._borderLeftWidth-0px{border-left-width:0px;}
@@ -2158,6 +2162,7 @@
2158
2162
  @media (hover) {:root:root ._brc-0hover-color8._brc-0hover-color8:hover{border-right-color:var(--color8) !important;}}
2159
2163
  :root ._brc-backgroundF3405682._brc-backgroundF3405682{border-right-color:var(--backgroundFocus);}
2160
2164
  :root ._brc-borderColor._brc-borderColor{border-right-color:var(--borderColor);}
2165
+ :root ._brc-color12._brc-color12{border-right-color:var(--color12);}
2161
2166
  :root ._brc-color6._brc-color6{border-right-color:var(--color6);}
2162
2167
  :root ._brc-transparent._brc-transparent{border-right-color:transparent;}
2163
2168
  :root ._brs-solid._brs-solid{border-right-style:solid;}
@@ -2174,6 +2179,7 @@
2174
2179
  @media (hover) {:root:root ._btc-0hover-color8._btc-0hover-color8:hover{border-top-color:var(--color8) !important;}}
2175
2180
  :root ._btc-backgroundF3405682._btc-backgroundF3405682{border-top-color:var(--backgroundFocus);}
2176
2181
  :root ._btc-borderColor._btc-borderColor{border-top-color:var(--borderColor);}
2182
+ :root ._btc-color12._btc-color12{border-top-color:var(--color12);}
2177
2183
  :root ._btc-color6._btc-color6{border-top-color:var(--color6);}
2178
2184
  :root ._btc-transparent._btc-transparent{border-top-color:transparent;}
2179
2185
  :root ._btlr-100000px{border-top-left-radius:100000px;}
@@ -2278,8 +2284,8 @@
2278
2284
  :root ._maxW-32px{max-width:32px;}
2279
2285
  :root ._maxW-600px{max-width:600px;}
2280
2286
  :root ._minH-16px{min-height:16px;}
2287
+ :root ._minH-20px{min-height:20px;}
2281
2288
  :root ._minH-24px{min-height:24px;}
2282
- :root ._minH-29px{min-height:29px;}
2283
2289
  :root ._minH-32px{min-height:32px;}
2284
2290
  :root ._minH-36px{min-height:36px;}
2285
2291
  :root ._minH-40px{min-height:40px;}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hanzo/ui",
3
- "version": "8.0.56",
3
+ "version": "8.0.58",
4
4
  "type": "module",
5
5
  "description": "Hanzo UI — the one canonical Hanzo component library, on @hanzo/gui + @hanzo/design. ONE substrate: every component renders through @hanzo/gui primitives, so the same import works on web, native (expo) and desktop (Tauri). Self-contained (theme.css), presentational, host-agnostic, clean-room.",
6
6
  "exports": {