@hanzo/ui 8.0.57 → 8.0.59
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.
- package/README.md +92 -0
- package/dist/product/Palette.cjs +65 -0
- package/dist/product/Palette.d.ts +67 -0
- package/dist/product/Palette.d.ts.map +1 -0
- package/dist/product/Palette.js +63 -0
- package/dist/product/Palette.js.map +1 -0
- package/dist/product/index.cjs +7 -0
- package/dist/product/index.d.ts +3 -0
- package/dist/product/index.d.ts.map +1 -1
- package/dist/product/index.js +7 -0
- package/dist/product/index.js.map +1 -1
- package/dist/product/instrument.cjs +7 -2
- package/dist/product/instrument.d.ts.map +1 -1
- package/dist/product/instrument.js +7 -2
- package/dist/product/instrument.js.map +1 -1
- package/dist/product/match.cjs +76 -0
- package/dist/product/match.d.ts +78 -0
- package/dist/product/match.d.ts.map +1 -0
- package/dist/product/match.js +72 -0
- package/dist/product/match.js.map +1 -0
- package/dist/product/useCommandK.cjs +46 -0
- package/dist/product/useCommandK.d.ts +4 -0
- package/dist/product/useCommandK.d.ts.map +1 -0
- package/dist/product/useCommandK.js +44 -0
- package/dist/product/useCommandK.js.map +1 -0
- package/dist/root.cjs +10 -2
- package/dist/root.d.ts +26 -1
- package/dist/root.d.ts.map +1 -1
- package/dist/root.js +10 -2
- package/dist/root.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -114,6 +114,98 @@ import { registerField } from '@hanzo/ui/data'
|
|
|
114
114
|
registerField('rating', { Display: MyStars, Input: MyStarPicker })
|
|
115
115
|
```
|
|
116
116
|
|
|
117
|
+
## Analytics — one flag, and the components report themselves
|
|
118
|
+
|
|
119
|
+
```tsx
|
|
120
|
+
import { Hanzo } from '@hanzo/ui'
|
|
121
|
+
|
|
122
|
+
<Hanzo analytics={{ product: 'console', ingestKey: process.env.NEXT_PUBLIC_EVENT_INGEST_KEY }}>
|
|
123
|
+
<App />
|
|
124
|
+
</Hanzo>
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
That is the entire wiring. Every click, form change, submit and route change
|
|
128
|
+
inside the tree arrives on the ONE front door (`POST /v1/event`) annotated with
|
|
129
|
+
the component it happened on — no `onClick` handler anywhere reports anything,
|
|
130
|
+
and no call site changes:
|
|
131
|
+
|
|
132
|
+
```jsonc
|
|
133
|
+
{
|
|
134
|
+
"event": "$click",
|
|
135
|
+
"$el": "card/button[Save]", // the significant-node trail, root→leaf
|
|
136
|
+
"$role": "button",
|
|
137
|
+
"$component": "button", // from the data-slot every primitive carries
|
|
138
|
+
"$name": "Save"
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Component names are real **in production**. Every primitive here stamps a
|
|
143
|
+
`data-slot` through one helper (`slot()`), and the capture engine reads it — so
|
|
144
|
+
attribution does not depend on React fiber owners, which a production build
|
|
145
|
+
minifies away. A component that wants a better name than its slot says so once,
|
|
146
|
+
on itself: `data-hz-name="SaveButton"` outranks everything.
|
|
147
|
+
|
|
148
|
+
### What it is made of — one of each
|
|
149
|
+
|
|
150
|
+
| Concern | Package | There is exactly one |
|
|
151
|
+
|---|---|---|
|
|
152
|
+
| Client + wire | [`@hanzo/event`](../../pkgs/event) | endpoint `POST /v1/event`, batched, beacon-on-unload |
|
|
153
|
+
| Capture engine | [`@hanzo/observe`](../../pkgs/observe) | delegated listeners, semantic annotation, redaction |
|
|
154
|
+
| Provider + consent | `@hanzogui/telemetry` | `<TelemetryProvider/>`, which `analytics` renders |
|
|
155
|
+
|
|
156
|
+
`analytics` is a **prop, not a default**. Mounting a component library must
|
|
157
|
+
never start a network conversation the app did not ask for: without it `<Hanzo>`
|
|
158
|
+
renders no provider, installs no listener and sends nothing.
|
|
159
|
+
|
|
160
|
+
### It cannot double-count
|
|
161
|
+
|
|
162
|
+
An app that already mounts `<TelemetryProvider/>` itself leaves `analytics`
|
|
163
|
+
off — but if both are mounted, the events still do not double. The capture
|
|
164
|
+
engine installs *delegated* listeners on a root, so two engines on one root
|
|
165
|
+
would report every interaction twice; a page-wide claim (`Symbol.for`, so
|
|
166
|
+
duplicate copies of the package in one bundle still see each other) gives the
|
|
167
|
+
root to the first engine and leaves any later one inert. Both providers also
|
|
168
|
+
resolve the same client, so there is one session and one stream either way.
|
|
169
|
+
|
|
170
|
+
### Privacy
|
|
171
|
+
|
|
172
|
+
Consent decides whether any of it runs: Global Privacy Control, Do Not Track,
|
|
173
|
+
and a stored choice a consent banner records — which outranks the browser signal
|
|
174
|
+
in **both** directions, because that is what explicit means. Beyond that:
|
|
175
|
+
|
|
176
|
+
- **Input values are withheld.** `$input`/`$change` carry the field's kind and
|
|
177
|
+
value length, never the typed text.
|
|
178
|
+
- **Sensitive fields are never captured at all** — password, email, card, cvv,
|
|
179
|
+
token, ssn — not even a length.
|
|
180
|
+
- **`data-hz-private`** on any element excludes its whole subtree.
|
|
181
|
+
|
|
182
|
+
### One ingest key
|
|
183
|
+
|
|
184
|
+
`ingestKey` is a publishable `pk-…` — write-only, safe in a bundle, minted per
|
|
185
|
+
org with `POST /v1/keys {"type":"publishable"}`. Omit it and the client reads
|
|
186
|
+
`NEXT_PUBLIC_EVENT_INGEST_KEY` from the build env, which is the spelling the
|
|
187
|
+
fleet already carries end to end (KMS holds `deploy/EVENT_INGEST_KEY`; the
|
|
188
|
+
Dockerfile takes it as a build-arg and re-exports it with the `NEXT_PUBLIC_`
|
|
189
|
+
prefix Next needs to inline it). A surface with no key reports only for whoever
|
|
190
|
+
is signed in and silently drops every logged-out visitor — the door refuses an
|
|
191
|
+
unattributable write rather than filing it where its owner cannot read it.
|
|
192
|
+
|
|
193
|
+
For a page with no build step at all, the same client ships as a script tag:
|
|
194
|
+
`@hanzo/event/hz.js`, with `data-ingest-key="pk-…"`.
|
|
195
|
+
|
|
196
|
+
### Naming a moment the components cannot see
|
|
197
|
+
|
|
198
|
+
Autocapture reports what was clicked; it cannot know that a click *was* a
|
|
199
|
+
checkout. Curated events live on `@hanzo/ui/product`, one closed verb set over
|
|
200
|
+
one event name, on the same client and the same stream:
|
|
201
|
+
|
|
202
|
+
```tsx
|
|
203
|
+
import { useEmit, InstrumentSurface } from '@hanzo/ui/product'
|
|
204
|
+
|
|
205
|
+
const emit = useEmit()
|
|
206
|
+
emit({ component: 'PlanCard', action: 'select', id: 'pro' })
|
|
207
|
+
```
|
|
208
|
+
|
|
117
209
|
## Design principles
|
|
118
210
|
|
|
119
211
|
- **One home, no duplication** — components are extracted, never copied. The record layer's source of truth stays in `@hanzo/data`; `@hanzo/ui` composes it.
|
|
@@ -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.jsxs)(gui_1.XStack, { flex: 1, items: "center", gap: "$2", overflow: "hidden", children: [(0, jsx_runtime_1.jsx)(gui_1.SizableText, { numberOfLines: 1, shrink: 0, children: op.label }), op.hint ? ((0, jsx_runtime_1.jsx)(gui_1.SizableText, { numberOfLines: 1, flex: 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, { shrink: 0, 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,2CAqFd"}
|
|
@@ -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, _jsxs(XStack, { flex: 1, items: "center", gap: "$2", overflow: "hidden", children: [_jsx(SizableText, { numberOfLines: 1, shrink: 0, children: op.label }), op.hint ? (_jsx(SizableText, { numberOfLines: 1, flex: 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, { shrink: 0, 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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"}
|
package/dist/product/index.cjs
CHANGED
|
@@ -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);
|
package/dist/product/index.d.ts
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/product/index.js
CHANGED
|
@@ -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":"AAAA,qEAAqE;AACrE,EAAE;AACF,2EAA2E;AAC3E,gFAAgF;AAChF,iFAAiF;AACjF,gFAAgF;AAChF,wEAAwE;AACxE,EAAE;AACF,kFAAkF;AAClF,6EAA6E;AAE7E,kFAAkF;AAClF,gEAAgE;AAChE,cAAc,UAAU,CAAA;AAExB,2EAA2E;AAC3E,+EAA+E;AAC/E,4CAA4C;AAC5C,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;AAEjB,6EAA6E;AAC7E,OAAO,EAAE,KAAK,IAAI,SAAS,EAAqB,MAAM,SAAS,CAAA;AAE/D,+EAA+E;AAC/E,8EAA8E;AAC9E,qCAAqC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AACrC,cAAc,mBAAmB,CAAA;AAEjC,gFAAgF;AAChF,6EAA6E;AAC7E,mFAAmF;AACnF,EAAE;AACF,+EAA+E;AAC/E,6EAA6E;AAC7E,8DAA8D;AAC9D,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;AAEvB,mFAAmF;AACnF,qFAAqF;AACrF,qFAAqF;AACrF,cAAc,QAAQ,CAAA;AAEtB,iFAAiF;AACjF,2EAA2E;AAC3E,8EAA8E;AAC9E,cAAc,WAAW,CAAA;AACzB,cAAc,cAAc,CAAA;AAE5B,6EAA6E;AAC7E,yEAAyE;AACzE,qFAAqF;AACrF,cAAc,QAAQ,CAAA;AACtB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,oBAAoB,CAAA;AAClC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,WAAW,CAAA;AAEzB,8EAA8E;AAC9E,8EAA8E;AAC9E,+EAA+E;AAC/E,6EAA6E;AAC7E,mDAAmD;AACnD,cAAc,cAAc,CAAA;AAE5B,gFAAgF;AAChF,iFAAiF;AACjF,2EAA2E;AAC3E,cAAc,YAAY,CAAA;AAE1B,kFAAkF;AAClF,mFAAmF;AACnF,gFAAgF;AAChF,kFAAkF;AAClF,oBAAoB;AACpB,cAAc,cAAc,CAAA;AAC5B,cAAc,eAAe,CAAA;AAE7B,8EAA8E;AAC9E,sBAAsB;AACtB,cAAc,cAAc,CAAA;AAE5B,mEAAmE;AACnE,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;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/product/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,8 +7,13 @@
|
|
|
7
7
|
// `EmptyState` or `ThemeToggle` reports the SAME interaction vocabulary with
|
|
8
8
|
// zero app code. Nothing to wire, nothing to remember, nothing to drift.
|
|
9
9
|
//
|
|
10
|
-
// import { emit, useEmit, InstrumentSurface } from '@hanzo/ui'
|
|
11
|
-
// <InstrumentSurface
|
|
10
|
+
// import { emit, useEmit, InstrumentSurface } from '@hanzo/ui/product'
|
|
11
|
+
// <InstrumentSurface value="billing">…</InstrumentSurface> // optional context
|
|
12
|
+
//
|
|
13
|
+
// This is the CURATED half. `<Hanzo analytics>` already reports every click,
|
|
14
|
+
// change, submit and route change on its own, annotated with the component it
|
|
15
|
+
// happened on — what autocapture cannot know is that a click WAS a checkout.
|
|
16
|
+
// That is what this names. Same client, same stream, same front door.
|
|
12
17
|
//
|
|
13
18
|
// ONE event name, structured properties — the taxonomy in @hanzo/event is a
|
|
14
19
|
// CLOSED set, and its own rule for a product-specific moment is "the `action`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrument.d.ts","sourceRoot":"","sources":["../../src/product/instrument.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"instrument.d.ts","sourceRoot":"","sources":["../../src/product/instrument.ts"],"names":[],"mappings":"AAkCA;kFACkF;AAClF,MAAM,MAAM,QAAQ,GAChB,OAAO,GACP,MAAM,GACN,OAAO,GACP,QAAQ,GACR,QAAQ,GACR,MAAM,GACN,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,MAAM,GACN,OAAO,CAAA;AAEX;+EAC+E;AAC/E,MAAM,WAAW,OAAO;IACtB,8EAA8E;IAC9E,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,QAAQ,CAAA;IAChB,yEAAyE;IACzE,EAAE,CAAC,EAAE,MAAM,CAAA;IACX;;0CAEsC;IACtC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAA;IACjC,8EAA8E;IAC9E,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAOD;mEACmE;AACnE,eAAO,MAAM,iBAAiB,8CAA0B,CAAA;AAExD,iEAAiE;AACjE,eAAO,MAAM,UAAU,QAAO,MAAM,GAAG,SAAuC,CAAA;AAE9E;8EAC8E;AAC9E,wBAAgB,IAAI,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI,CAQrC;AAED;gFACgF;AAChF,wBAAgB,OAAO,IAAI,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAM9C;AAED;;;;mFAImF;AACnF,wBAAgB,OAAO,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAO7D;AAED;;kCAEkC;AAClC,eAAO,MAAM,QAAQ,GAAI,GAAG,MAAM,GAAG,SAAS,KAAG,MAA4B,CAAA"}
|
|
@@ -6,8 +6,13 @@
|
|
|
6
6
|
// `EmptyState` or `ThemeToggle` reports the SAME interaction vocabulary with
|
|
7
7
|
// zero app code. Nothing to wire, nothing to remember, nothing to drift.
|
|
8
8
|
//
|
|
9
|
-
// import { emit, useEmit, InstrumentSurface } from '@hanzo/ui'
|
|
10
|
-
// <InstrumentSurface
|
|
9
|
+
// import { emit, useEmit, InstrumentSurface } from '@hanzo/ui/product'
|
|
10
|
+
// <InstrumentSurface value="billing">…</InstrumentSurface> // optional context
|
|
11
|
+
//
|
|
12
|
+
// This is the CURATED half. `<Hanzo analytics>` already reports every click,
|
|
13
|
+
// change, submit and route change on its own, annotated with the component it
|
|
14
|
+
// happened on — what autocapture cannot know is that a click WAS a checkout.
|
|
15
|
+
// That is what this names. Same client, same stream, same front door.
|
|
11
16
|
//
|
|
12
17
|
// ONE event name, structured properties — the taxonomy in @hanzo/event is a
|
|
13
18
|
// CLOSED set, and its own rule for a product-specific moment is "the `action`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrument.js","sourceRoot":"","sources":["../../src/product/instrument.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,EAAE;AACF,2EAA2E;AAC3E,gFAAgF;AAChF,yEAAyE;AACzE,6EAA6E;AAC7E,yEAAyE;AACzE,EAAE;AACF,
|
|
1
|
+
{"version":3,"file":"instrument.js","sourceRoot":"","sources":["../../src/product/instrument.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 @@
|
|
|
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/root.cjs
CHANGED
|
@@ -38,6 +38,7 @@ const jsx_runtime_1 = require("react/jsx-runtime");
|
|
|
38
38
|
* this flag controls.
|
|
39
39
|
*/
|
|
40
40
|
const gui_1 = require("@hanzo/gui");
|
|
41
|
+
const telemetry_1 = require("@hanzogui/telemetry");
|
|
41
42
|
const gui_config_1 = require("./gui-config.cjs");
|
|
42
43
|
require("./styles.css");
|
|
43
44
|
/** A stylesheet that did not reach the document is invisible until someone opens
|
|
@@ -58,10 +59,17 @@ const assertStylesheet = () => {
|
|
|
58
59
|
'that does not process CSS imported from node_modules has dropped it. Import it ' +
|
|
59
60
|
"directly instead: import '@hanzo/ui/styles.css'.");
|
|
60
61
|
};
|
|
61
|
-
const Hanzo = ({ children, theme = 'dark' }) => {
|
|
62
|
+
const Hanzo = ({ children, theme = 'dark', analytics }) => {
|
|
62
63
|
if (process.env.NODE_ENV !== 'production')
|
|
63
64
|
assertStylesheet();
|
|
64
|
-
|
|
65
|
+
const tree = ((0, jsx_runtime_1.jsx)(gui_1.GuiProvider, { config: gui_config_1.config, defaultTheme: theme, disableInjectCSS: true, children: children }));
|
|
66
|
+
// OUTSIDE the gui provider: capture is delegated at the document, so it does
|
|
67
|
+
// not need to be inside the styled tree, and an app that renders its own
|
|
68
|
+
// `<TelemetryProvider/>` above `<Hanzo>` then nests two providers of the same
|
|
69
|
+
// kind rather than interleaving them with the theme.
|
|
70
|
+
if (!analytics)
|
|
71
|
+
return tree;
|
|
72
|
+
return (0, jsx_runtime_1.jsx)(telemetry_1.TelemetryProvider, { ...(analytics === true ? {} : analytics), children: tree });
|
|
65
73
|
};
|
|
66
74
|
exports.Hanzo = Hanzo;
|
|
67
75
|
exports.default = exports.Hanzo;
|
package/dist/root.d.ts
CHANGED
|
@@ -1,10 +1,35 @@
|
|
|
1
|
+
import { type TelemetryConfig } from '@hanzogui/telemetry';
|
|
1
2
|
import type { ReactNode } from 'react';
|
|
2
3
|
import './styles.css';
|
|
3
4
|
export type HanzoProps = {
|
|
4
5
|
children?: ReactNode;
|
|
5
6
|
/** Dark-first Hanzo identity. `light` retunes it. */
|
|
6
7
|
theme?: 'dark' | 'light';
|
|
8
|
+
/**
|
|
9
|
+
* Interaction analytics for everything inside — OFF unless you ask.
|
|
10
|
+
*
|
|
11
|
+
* <Hanzo analytics> // zero config
|
|
12
|
+
* <Hanzo analytics={{ product: 'console' }}> // named surface
|
|
13
|
+
*
|
|
14
|
+
* `true` is the whole setup: every click, form change, submit and route
|
|
15
|
+
* change inside this tree arrives on the ONE front door (`POST /v1/event`)
|
|
16
|
+
* annotated with the component it happened on — `card/button[Save]` — with
|
|
17
|
+
* input values withheld. Nothing to instrument at a call site.
|
|
18
|
+
*
|
|
19
|
+
* It is a PROP, not the default, because mounting a component library must
|
|
20
|
+
* never start a network conversation an app did not ask for. Off, this
|
|
21
|
+
* renders no provider, installs no listener and sends nothing.
|
|
22
|
+
*
|
|
23
|
+
* On, it is the one wiring: ONE client (@hanzo/event), ONE capture engine
|
|
24
|
+
* (@hanzo/observe), ONE endpoint, ONE publishable key — and consent decides
|
|
25
|
+
* whether any of it runs (Global Privacy Control, Do Not Track, and a stored
|
|
26
|
+
* choice a banner records, which outranks both). An app that already mounts
|
|
27
|
+
* `<TelemetryProvider/>` itself leaves this off; both would be the same
|
|
28
|
+
* client on the same stream, and the capture engine refuses a root another
|
|
29
|
+
* engine holds, so the events do not double either way.
|
|
30
|
+
*/
|
|
31
|
+
analytics?: boolean | TelemetryConfig;
|
|
7
32
|
};
|
|
8
|
-
export declare const Hanzo: ({ children, theme }: HanzoProps) => import("react/jsx-runtime").JSX.Element;
|
|
33
|
+
export declare const Hanzo: ({ children, theme, analytics }: HanzoProps) => import("react/jsx-runtime").JSX.Element;
|
|
9
34
|
export default Hanzo;
|
|
10
35
|
//# sourceMappingURL=root.d.ts.map
|
package/dist/root.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"root.d.ts","sourceRoot":"","sources":["../src/root.tsx"],"names":[],"mappings":"AAqCA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGtC,OAAO,cAAc,CAAA;AAErB,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,CAAC,EAAE,SAAS,CAAA;IACpB,qDAAqD;IACrD,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"root.d.ts","sourceRoot":"","sources":["../src/root.tsx"],"names":[],"mappings":"AAqCA,OAAO,EAAqB,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAC7E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGtC,OAAO,cAAc,CAAA;AAErB,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,CAAC,EAAE,SAAS,CAAA;IACpB,qDAAqD;IACrD,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;IACxB;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,SAAS,CAAC,EAAE,OAAO,GAAG,eAAe,CAAA;CACtC,CAAA;AAqBD,eAAO,MAAM,KAAK,GAAI,gCAAyC,UAAU,4CAaxE,CAAA;AAED,eAAe,KAAK,CAAA"}
|
package/dist/root.js
CHANGED
|
@@ -35,6 +35,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
35
35
|
* this flag controls.
|
|
36
36
|
*/
|
|
37
37
|
import { GuiProvider } from '@hanzo/gui';
|
|
38
|
+
import { TelemetryProvider } from '@hanzogui/telemetry';
|
|
38
39
|
import { config } from './gui-config.js';
|
|
39
40
|
import './styles.css';
|
|
40
41
|
/** A stylesheet that did not reach the document is invisible until someone opens
|
|
@@ -55,10 +56,17 @@ const assertStylesheet = () => {
|
|
|
55
56
|
'that does not process CSS imported from node_modules has dropped it. Import it ' +
|
|
56
57
|
"directly instead: import '@hanzo/ui/styles.css'.");
|
|
57
58
|
};
|
|
58
|
-
export const Hanzo = ({ children, theme = 'dark' }) => {
|
|
59
|
+
export const Hanzo = ({ children, theme = 'dark', analytics }) => {
|
|
59
60
|
if (process.env.NODE_ENV !== 'production')
|
|
60
61
|
assertStylesheet();
|
|
61
|
-
|
|
62
|
+
const tree = (_jsx(GuiProvider, { config: config, defaultTheme: theme, disableInjectCSS: true, children: children }));
|
|
63
|
+
// OUTSIDE the gui provider: capture is delegated at the document, so it does
|
|
64
|
+
// not need to be inside the styled tree, and an app that renders its own
|
|
65
|
+
// `<TelemetryProvider/>` above `<Hanzo>` then nests two providers of the same
|
|
66
|
+
// kind rather than interleaving them with the theme.
|
|
67
|
+
if (!analytics)
|
|
68
|
+
return tree;
|
|
69
|
+
return _jsx(TelemetryProvider, { ...(analytics === true ? {} : analytics), children: tree });
|
|
62
70
|
};
|
|
63
71
|
export default Hanzo;
|
|
64
72
|
//# sourceMappingURL=root.js.map
|
package/dist/root.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"root.js","sourceRoot":"","sources":["../src/root.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"root.js","sourceRoot":"","sources":["../src/root.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AACxC,OAAO,EAAE,iBAAiB,EAAwB,MAAM,qBAAqB,CAAA;AAG7E,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AACrC,OAAO,cAAc,CAAA;AAgCrB;;;;;;qEAMqE;AACrE,IAAI,OAAO,GAAG,KAAK,CAAA;AACnB,MAAM,gBAAgB,GAAG,GAAG,EAAE;IAC5B,IAAI,OAAO,IAAI,OAAO,QAAQ,KAAK,WAAW,IAAI,QAAQ,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC;QAAE,OAAM;IAC3F,OAAO,GAAG,IAAI,CAAA;IACd,IAAI,gBAAgB,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,gBAAgB,CAAC,mBAAmB,CAAC,CAAC,IAAI,EAAE;QAAE,OAAM;IACnG,MAAM,IAAI,KAAK,CACb,qFAAqF;QACnF,iFAAiF;QACjF,kDAAkD,CACrD,CAAA;AACH,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,EAAE,QAAQ,EAAE,KAAK,GAAG,MAAM,EAAE,SAAS,EAAc,EAAE,EAAE;IAC3E,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY;QAAE,gBAAgB,EAAE,CAAA;IAC7D,MAAM,IAAI,GAAG,CACX,KAAC,WAAW,IAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,gBAAgB,kBAC/D,QAAQ,GACG,CACf,CAAA;IACD,6EAA6E;IAC7E,yEAAyE;IACzE,8EAA8E;IAC9E,qDAAqD;IACrD,IAAI,CAAC,SAAS;QAAE,OAAO,IAAI,CAAA;IAC3B,OAAO,KAAC,iBAAiB,OAAK,CAAC,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,YAAG,IAAI,GAAqB,CAAA;AACjG,CAAC,CAAA;AAED,eAAe,KAAK,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzo/ui",
|
|
3
|
-
"version": "8.0.
|
|
3
|
+
"version": "8.0.59",
|
|
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": {
|