@hanzo/ui 8.0.55 → 8.0.57
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 +5 -1
- package/dist/backends/gui/switch.cjs +43 -3
- package/dist/backends/gui/switch.d.ts +27 -2
- package/dist/backends/gui/switch.d.ts.map +1 -1
- package/dist/backends/gui/switch.js +43 -3
- package/dist/backends/gui/switch.js.map +1 -1
- package/dist/chat/Code.cjs +2 -36
- package/dist/chat/Code.d.ts +0 -13
- package/dist/chat/Code.d.ts.map +1 -1
- package/dist/chat/Code.js +2 -35
- package/dist/chat/Code.js.map +1 -1
- package/dist/chat/index.cjs +7 -2
- package/dist/chat/index.d.ts +7 -1
- package/dist/chat/index.d.ts.map +1 -1
- package/dist/chat/index.js +7 -1
- package/dist/chat/index.js.map +1 -1
- package/dist/product/AppHeader.cjs +8 -6
- package/dist/product/AppHeader.d.ts.map +1 -1
- package/dist/product/AppHeader.js +11 -9
- package/dist/product/AppHeader.js.map +1 -1
- package/dist/product/CopyButton.cjs +54 -0
- package/dist/product/CopyButton.d.ts +12 -0
- package/dist/product/CopyButton.d.ts.map +1 -0
- package/dist/product/CopyButton.js +52 -0
- package/dist/product/CopyButton.js.map +1 -0
- package/dist/product/Field.cjs +2 -1
- package/dist/product/Field.d.ts.map +1 -1
- package/dist/product/Field.js +2 -1
- package/dist/product/Field.js.map +1 -1
- package/dist/product/Fieldset.cjs +13 -0
- package/dist/product/Fieldset.d.ts +35 -0
- package/dist/product/Fieldset.d.ts.map +1 -0
- package/dist/product/Fieldset.js +11 -0
- package/dist/product/Fieldset.js.map +1 -0
- package/dist/product/OrgMark.cjs +39 -3
- package/dist/product/OrgMark.d.ts +9 -0
- package/dist/product/OrgMark.d.ts.map +1 -1
- package/dist/product/OrgMark.js +38 -3
- package/dist/product/OrgMark.js.map +1 -1
- package/dist/product/OrgSwitcher.cjs +3 -3
- package/dist/product/OrgSwitcher.d.ts +29 -1
- package/dist/product/OrgSwitcher.d.ts.map +1 -1
- package/dist/product/OrgSwitcher.js +3 -3
- package/dist/product/OrgSwitcher.js.map +1 -1
- package/dist/product/Pagination.cjs +72 -0
- package/dist/product/Pagination.d.ts +21 -0
- package/dist/product/Pagination.d.ts.map +1 -0
- package/dist/product/Pagination.js +68 -0
- package/dist/product/Pagination.js.map +1 -0
- package/dist/product/SecretInput.cjs +59 -0
- package/dist/product/SecretInput.d.ts +24 -0
- package/dist/product/SecretInput.d.ts.map +1 -0
- package/dist/product/SecretInput.js +55 -0
- package/dist/product/SecretInput.js.map +1 -0
- package/dist/product/UserMenu.cjs +63 -0
- package/dist/product/UserMenu.d.ts +56 -0
- package/dist/product/UserMenu.d.ts.map +1 -0
- package/dist/product/UserMenu.js +60 -0
- package/dist/product/UserMenu.js.map +1 -0
- package/dist/product/index.cjs +21 -2
- package/dist/product/index.d.ts +5 -0
- package/dist/product/index.d.ts.map +1 -1
- package/dist/product/index.js +21 -2
- package/dist/product/index.js.map +1 -1
- package/dist/styles.css +7 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -26,7 +26,11 @@ import { RecordsView, DataTable, BoardView, RecordDetail, registerField } from '
|
|
|
26
26
|
- **Charts** (dependency-free inline SVG): `Sparkline`, `LineChart`, `BarChart`, `Donut`, `BarRows` — hover tooltips, axis ticks, honest `null` under two real points.
|
|
27
27
|
- **Metric** tiles/panels: `MetricCard`, `MiniBars`, `UtilBar`, `LegendDot`, `Panel`, `HintButton` (+ `MetricSparkline`).
|
|
28
28
|
- **Chrome**: `PageHeader`, `StatusTag`, `EmptyState` (DO/Vercel-class first-run), `PrimaryButton`, `HanzoMark`, `ProductIcon`, `ProviderLogo`.
|
|
29
|
-
- **
|
|
29
|
+
- **The identity trio** — one row of chrome, three switchers: `OrgSwitcher` (which workspace, `direction` up or down, `footer` for a surface's own rows), the app switcher inside `AppHeader`, and `UserMenu` (who I am). `AppHeader` composes all three; each is importable alone, so a surface that wants an account menu without the whole header does not write a sixth one.
|
|
30
|
+
- **Settings**: `Fieldset` — the titled, optionally destructive group the `Field*` rows sit in. Not `Panel`: that is a dashboard metric tile.
|
|
31
|
+
- **Interaction**: `ComboBox` (typeable, ReDoS-safe filter), `SelectMenu`, `SlideOver` (a11y drawer), `Toast` (`useToast`), `Reorder` (pointer DnD), `Field*` rows, `CopyButton`, `SecretInput` (mask · reveal · copy), `Pagination` (+ the pure `pages` rule), `FadeIn`, `ThemeToggle`, generic `DataTable<T>`.
|
|
32
|
+
|
|
33
|
+
> **Masking needs both spellings.** `secureTextEntry` is React Native's and gui **drops it on web**, so an input carrying only that prop renders the secret in plain text; `type="password"` is the web one and native ignores it. `masked(on)` sets both and is what `SecretInput` and `<FieldText secure>` use. Never pass `secureTextEntry` alone.
|
|
30
34
|
- **Tokens**: `tokens`, `TAG_TONES`, `tagTone` — the calm, dark-first, zinc-on-black identity.
|
|
31
35
|
- **`@hanzo/ui/data`** — the metadata-driven record layer (source of truth: `@hanzo/data`): `RecordsView` (table ⇆ board, filter/sort/search/group, inline edit, saved views), the record-grid `DataTable`, `BoardView`, `RecordDetail`/`RecordForm`, every typed field editor, the field registry, and the pure view/sort/filter logic.
|
|
32
36
|
|
|
@@ -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
|
-
/**
|
|
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:
|
|
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",
|
|
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
|
-
/**
|
|
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
|
|
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
|
-
/**
|
|
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:
|
|
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",
|
|
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
|
|
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"}
|
package/dist/chat/Code.cjs
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
'use client';
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
exports.Code = Code;
|
|
5
|
-
exports.CopyButton = CopyButton;
|
|
6
5
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
7
6
|
/**
|
|
8
7
|
* Code — a labelled, copyable code region.
|
|
@@ -17,11 +16,8 @@ const jsx_runtime_1 = require("react/jsx-runtime");
|
|
|
17
16
|
* plain text or its own highlighted nodes.
|
|
18
17
|
*/
|
|
19
18
|
const gui_1 = require("@hanzo/gui");
|
|
20
|
-
const lucide_icons_2_1 = require("@hanzogui/lucide-icons-2");
|
|
21
|
-
const react_1 = require("react");
|
|
22
19
|
const slot_1 = require("../backends/gui/slot.cjs");
|
|
23
|
-
|
|
24
|
-
const CONFIRM = 2000;
|
|
20
|
+
const CopyButton_1 = require("../product/CopyButton.cjs");
|
|
25
21
|
/**
|
|
26
22
|
* Monospace stack, applied through `style` rather than a `$mono` token.
|
|
27
23
|
*
|
|
@@ -36,35 +32,5 @@ const MONO = {
|
|
|
36
32
|
};
|
|
37
33
|
function Code({ children, language = 'text', value, actions }) {
|
|
38
34
|
const text = value ?? (typeof children === 'string' ? children : '');
|
|
39
|
-
return ((0, jsx_runtime_1.jsxs)(gui_1.YStack, { ...(0, slot_1.slot)('code'), width: "100%", rounded: "$4", borderWidth: 1, borderColor: "$borderColor", bg: "$color2", overflow: "hidden", children: [(0, jsx_runtime_1.jsxs)(gui_1.XStack, { items: "center", gap: "$1", px: "$3", py: "$1.5", borderBottomWidth: 1, borderColor: "$borderColor", children: [(0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$1", style: MONO, color: "$color10", children: language }), (0, jsx_runtime_1.jsx)(gui_1.XStack, { flex: 1 }), actions, text ? (0, jsx_runtime_1.jsx)(CopyButton, { value: text }) : null] }), (0, jsx_runtime_1.jsx)(gui_1.YStack, { px: "$3", py: "$2.5", children: (0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$1", style: MONO, lineHeight: 20, children: children }) })] }));
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* CopyButton — copy with a confirmation tick.
|
|
43
|
-
*
|
|
44
|
-
* The tick is the point. A control that looks identical before and after leaves
|
|
45
|
-
* the reader unsure whether it fired, and the usual response is to press it
|
|
46
|
-
* again. Reverts after `CONFIRM`, and clears its timer on unmount so it cannot
|
|
47
|
-
* set state on a component that is gone.
|
|
48
|
-
*/
|
|
49
|
-
function CopyButton({ value, label = 'Copy' }) {
|
|
50
|
-
const [copied, setCopied] = (0, react_1.useState)(false);
|
|
51
|
-
const timer = (0, react_1.useRef)(undefined);
|
|
52
|
-
(0, react_1.useEffect)(() => () => clearTimeout(timer.current), []);
|
|
53
|
-
const copy = () => {
|
|
54
|
-
// Optional-chained rather than assumed: `navigator.clipboard` is undefined
|
|
55
|
-
// in insecure contexts and absent entirely on native, and these components
|
|
56
|
-
// are meant to run on both.
|
|
57
|
-
navigator?.clipboard
|
|
58
|
-
?.writeText(value)
|
|
59
|
-
.then(() => {
|
|
60
|
-
setCopied(true);
|
|
61
|
-
clearTimeout(timer.current);
|
|
62
|
-
timer.current = setTimeout(() => setCopied(false), CONFIRM);
|
|
63
|
-
})
|
|
64
|
-
.catch(() => {
|
|
65
|
-
// A refused clipboard needs no toast — the missing tick already says it,
|
|
66
|
-
// and an error dialog for a failed copy is louder than the failure.
|
|
67
|
-
});
|
|
68
|
-
};
|
|
69
|
-
return ((0, jsx_runtime_1.jsx)(gui_1.XStack, { width: 24, height: 24, items: "center", justify: "center", rounded: "$2", cursor: "pointer", opacity: 0.7, onPress: copy, role: "button", tabIndex: 0, "aria-label": copied ? 'Copied' : label, ...(0, slot_1.tip)(copied ? 'Copied' : label), hoverStyle: { bg: '$color4', opacity: 1 }, pressStyle: { bg: '$color5' }, children: copied ? (0, jsx_runtime_1.jsx)(lucide_icons_2_1.Check, { size: 14 }) : (0, jsx_runtime_1.jsx)(lucide_icons_2_1.Copy, { size: 14 }) }));
|
|
35
|
+
return ((0, jsx_runtime_1.jsxs)(gui_1.YStack, { ...(0, slot_1.slot)('code'), width: "100%", rounded: "$4", borderWidth: 1, borderColor: "$borderColor", bg: "$color2", overflow: "hidden", children: [(0, jsx_runtime_1.jsxs)(gui_1.XStack, { items: "center", gap: "$1", px: "$3", py: "$1.5", borderBottomWidth: 1, borderColor: "$borderColor", children: [(0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$1", style: MONO, color: "$color10", children: language }), (0, jsx_runtime_1.jsx)(gui_1.XStack, { flex: 1 }), actions, text ? (0, jsx_runtime_1.jsx)(CopyButton_1.CopyButton, { value: text }) : null] }), (0, jsx_runtime_1.jsx)(gui_1.YStack, { px: "$3", py: "$2.5", children: (0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$1", style: MONO, lineHeight: 20, children: children }) })] }));
|
|
70
36
|
}
|
package/dist/chat/Code.d.ts
CHANGED
|
@@ -9,17 +9,4 @@ export interface CodeProps {
|
|
|
9
9
|
actions?: ReactNode;
|
|
10
10
|
}
|
|
11
11
|
export declare function Code({ children, language, value, actions }: CodeProps): import("react/jsx-runtime").JSX.Element;
|
|
12
|
-
export interface CopyButtonProps {
|
|
13
|
-
value: string;
|
|
14
|
-
label?: string;
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* CopyButton — copy with a confirmation tick.
|
|
18
|
-
*
|
|
19
|
-
* The tick is the point. A control that looks identical before and after leaves
|
|
20
|
-
* the reader unsure whether it fired, and the usual response is to press it
|
|
21
|
-
* again. Reverts after `CONFIRM`, and clears its timer on unmount so it cannot
|
|
22
|
-
* set state on a component that is gone.
|
|
23
|
-
*/
|
|
24
|
-
export declare function CopyButton({ value, label }: CopyButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
25
12
|
//# sourceMappingURL=Code.d.ts.map
|
package/dist/chat/Code.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Code.d.ts","sourceRoot":"","sources":["../../src/chat/Code.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Code.d.ts","sourceRoot":"","sources":["../../src/chat/Code.tsx"],"names":[],"mappings":"AAeA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAkBtC,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,EAAE,SAAS,CAAA;IACpB,8BAA8B;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,mFAAmF;IACnF,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,oDAAoD;IACpD,OAAO,CAAC,EAAE,SAAS,CAAA;CACpB;AAED,wBAAgB,IAAI,CAAC,EAAE,QAAQ,EAAE,QAAiB,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,SAAS,2CAoC9E"}
|
package/dist/chat/Code.js
CHANGED
|
@@ -13,11 +13,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
13
13
|
* plain text or its own highlighted nodes.
|
|
14
14
|
*/
|
|
15
15
|
import { SizableText, XStack, YStack } from '@hanzo/gui';
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import { slot, tip } from '../backends/gui/slot.js';
|
|
19
|
-
/** How long the copy control shows its confirmation, in ms. */
|
|
20
|
-
const CONFIRM = 2000;
|
|
16
|
+
import { slot } from '../backends/gui/slot.js';
|
|
17
|
+
import { CopyButton } from '../product/CopyButton.js';
|
|
21
18
|
/**
|
|
22
19
|
* Monospace stack, applied through `style` rather than a `$mono` token.
|
|
23
20
|
*
|
|
@@ -34,34 +31,4 @@ export function Code({ children, language = 'text', value, actions }) {
|
|
|
34
31
|
const text = value ?? (typeof children === 'string' ? children : '');
|
|
35
32
|
return (_jsxs(YStack, { ...slot('code'), width: "100%", rounded: "$4", borderWidth: 1, borderColor: "$borderColor", bg: "$color2", overflow: "hidden", children: [_jsxs(XStack, { items: "center", gap: "$1", px: "$3", py: "$1.5", borderBottomWidth: 1, borderColor: "$borderColor", children: [_jsx(SizableText, { size: "$1", style: MONO, color: "$color10", children: language }), _jsx(XStack, { flex: 1 }), actions, text ? _jsx(CopyButton, { value: text }) : null] }), _jsx(YStack, { px: "$3", py: "$2.5", children: _jsx(SizableText, { size: "$1", style: MONO, lineHeight: 20, children: children }) })] }));
|
|
36
33
|
}
|
|
37
|
-
/**
|
|
38
|
-
* CopyButton — copy with a confirmation tick.
|
|
39
|
-
*
|
|
40
|
-
* The tick is the point. A control that looks identical before and after leaves
|
|
41
|
-
* the reader unsure whether it fired, and the usual response is to press it
|
|
42
|
-
* again. Reverts after `CONFIRM`, and clears its timer on unmount so it cannot
|
|
43
|
-
* set state on a component that is gone.
|
|
44
|
-
*/
|
|
45
|
-
export function CopyButton({ value, label = 'Copy' }) {
|
|
46
|
-
const [copied, setCopied] = useState(false);
|
|
47
|
-
const timer = useRef(undefined);
|
|
48
|
-
useEffect(() => () => clearTimeout(timer.current), []);
|
|
49
|
-
const copy = () => {
|
|
50
|
-
// Optional-chained rather than assumed: `navigator.clipboard` is undefined
|
|
51
|
-
// in insecure contexts and absent entirely on native, and these components
|
|
52
|
-
// are meant to run on both.
|
|
53
|
-
navigator?.clipboard
|
|
54
|
-
?.writeText(value)
|
|
55
|
-
.then(() => {
|
|
56
|
-
setCopied(true);
|
|
57
|
-
clearTimeout(timer.current);
|
|
58
|
-
timer.current = setTimeout(() => setCopied(false), CONFIRM);
|
|
59
|
-
})
|
|
60
|
-
.catch(() => {
|
|
61
|
-
// A refused clipboard needs no toast — the missing tick already says it,
|
|
62
|
-
// and an error dialog for a failed copy is louder than the failure.
|
|
63
|
-
});
|
|
64
|
-
};
|
|
65
|
-
return (_jsx(XStack, { width: 24, height: 24, items: "center", justify: "center", rounded: "$2", cursor: "pointer", opacity: 0.7, onPress: copy, role: "button", tabIndex: 0, "aria-label": copied ? 'Copied' : label, ...tip(copied ? 'Copied' : label), hoverStyle: { bg: '$color4', opacity: 1 }, pressStyle: { bg: '$color5' }, children: copied ? _jsx(Check, { size: 14 }) : _jsx(Copy, { size: 14 }) }));
|
|
66
|
-
}
|
|
67
34
|
//# sourceMappingURL=Code.js.map
|
package/dist/chat/Code.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Code.js","sourceRoot":"","sources":["../../src/chat/Code.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"Code.js","sourceRoot":"","sources":["../../src/chat/Code.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAGxD,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAA;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAElD;;;;;;;;GAQG;AACH,MAAM,IAAI,GAAG;IACX,UAAU,EAAE,wEAAwE;CAC5E,CAAA;AAYV,MAAM,UAAU,IAAI,CAAC,EAAE,QAAQ,EAAE,QAAQ,GAAG,MAAM,EAAE,KAAK,EAAE,OAAO,EAAa;IAC7E,MAAM,IAAI,GAAG,KAAK,IAAI,CAAC,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IAEpE,OAAO,CACL,MAAC,MAAM,OACD,IAAI,CAAC,MAAM,CAAC,EAChB,KAAK,EAAC,MAAM,EACZ,OAAO,EAAC,IAAI,EACZ,WAAW,EAAE,CAAC,EACd,WAAW,EAAC,cAAc,EAC1B,EAAE,EAAC,SAAS,EACZ,QAAQ,EAAC,QAAQ,aAEjB,MAAC,MAAM,IACL,KAAK,EAAC,QAAQ,EACd,GAAG,EAAC,IAAI,EACR,EAAE,EAAC,IAAI,EACP,EAAE,EAAC,MAAM,EACT,iBAAiB,EAAE,CAAC,EACpB,WAAW,EAAC,cAAc,aAE1B,KAAC,WAAW,IAAC,IAAI,EAAC,IAAI,EAAC,KAAK,EAAE,IAAI,EAAE,KAAK,EAAC,UAAU,YACjD,QAAQ,GACG,EACd,KAAC,MAAM,IAAC,IAAI,EAAE,CAAC,GAAI,EAClB,OAAO,EACP,IAAI,CAAC,CAAC,CAAC,KAAC,UAAU,IAAC,KAAK,EAAE,IAAI,GAAI,CAAC,CAAC,CAAC,IAAI,IACnC,EAET,KAAC,MAAM,IAAC,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,MAAM,YACvB,KAAC,WAAW,IAAC,IAAI,EAAC,IAAI,EAAC,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,YAC/C,QAAQ,GACG,GACP,IACF,CACV,CAAA;AACH,CAAC"}
|
package/dist/chat/index.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SourceChip = exports.Sources = exports.
|
|
3
|
+
exports.SourceChip = exports.Sources = exports.Code = exports.Aside = exports.AsideToggle = exports.ShareButton = exports.HeaderButton = exports.Header = exports.SidebarUser = exports.SidebarFolder = exports.SidebarItem = exports.SidebarSection = exports.SidebarScroll = exports.SidebarNewChat = exports.SidebarIconButton = exports.SidebarHeader = exports.Sidebar = exports.SLACK = exports.pinned = exports.sends = exports.ready = exports.Thread = exports.Message = exports.ASK = exports.Composer = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* @hanzo/ui/chat — the one chat shell, on @hanzo/gui primitives.
|
|
6
6
|
*
|
|
@@ -20,6 +20,12 @@ exports.SourceChip = exports.Sources = exports.CopyButton = exports.Code = expor
|
|
|
20
20
|
*
|
|
21
21
|
* The model picker is not here: it already ships at `@hanzo/ui/models`, and it
|
|
22
22
|
* is not chat-specific.
|
|
23
|
+
*
|
|
24
|
+
* `CopyButton` is not here either, for the same reason and after the same
|
|
25
|
+
* mistake: it lived in `Code.tsx`, so the only way to reach it was
|
|
26
|
+
* `@hanzo/ui/chat`, and no one hunting for a copy button looks in a chat module.
|
|
27
|
+
* Six surfaces wrote their own instead. It now ships at `@hanzo/ui/product`;
|
|
28
|
+
* `Code` imports it from there.
|
|
23
29
|
*/
|
|
24
30
|
var Composer_1 = require("./Composer.cjs");
|
|
25
31
|
Object.defineProperty(exports, "Composer", { enumerable: true, get: function () { return Composer_1.Composer; } });
|
|
@@ -52,7 +58,6 @@ Object.defineProperty(exports, "AsideToggle", { enumerable: true, get: function
|
|
|
52
58
|
Object.defineProperty(exports, "Aside", { enumerable: true, get: function () { return Header_1.Aside; } });
|
|
53
59
|
var Code_1 = require("./Code.cjs");
|
|
54
60
|
Object.defineProperty(exports, "Code", { enumerable: true, get: function () { return Code_1.Code; } });
|
|
55
|
-
Object.defineProperty(exports, "CopyButton", { enumerable: true, get: function () { return Code_1.CopyButton; } });
|
|
56
61
|
var Sources_1 = require("./Sources.cjs");
|
|
57
62
|
Object.defineProperty(exports, "Sources", { enumerable: true, get: function () { return Sources_1.Sources; } });
|
|
58
63
|
Object.defineProperty(exports, "SourceChip", { enumerable: true, get: function () { return Sources_1.SourceChip; } });
|
package/dist/chat/index.d.ts
CHANGED
|
@@ -17,6 +17,12 @@
|
|
|
17
17
|
*
|
|
18
18
|
* The model picker is not here: it already ships at `@hanzo/ui/models`, and it
|
|
19
19
|
* is not chat-specific.
|
|
20
|
+
*
|
|
21
|
+
* `CopyButton` is not here either, for the same reason and after the same
|
|
22
|
+
* mistake: it lived in `Code.tsx`, so the only way to reach it was
|
|
23
|
+
* `@hanzo/ui/chat`, and no one hunting for a copy button looks in a chat module.
|
|
24
|
+
* Six surfaces wrote their own instead. It now ships at `@hanzo/ui/product`;
|
|
25
|
+
* `Code` imports it from there.
|
|
20
26
|
*/
|
|
21
27
|
export { Composer, ASK, type ComposerProps } from './Composer.js';
|
|
22
28
|
export { Message, type MessageProps, type Role } from './Message.js';
|
|
@@ -25,6 +31,6 @@ export { ready, sends, type Mods } from './send.js';
|
|
|
25
31
|
export { pinned, SLACK, type Track } from './stick.js';
|
|
26
32
|
export { Sidebar, SidebarHeader, SidebarIconButton, SidebarNewChat, SidebarScroll, SidebarSection, SidebarItem, SidebarFolder, SidebarUser, type SidebarProps, type SidebarHeaderProps, type SidebarIconButtonProps, type SidebarNewChatProps, type SidebarSectionProps, type SidebarItemProps, type SidebarFolderProps, type SidebarUserProps, } from './Sidebar.js';
|
|
27
33
|
export { Header, HeaderButton, ShareButton, AsideToggle, Aside, type HeaderProps, type HeaderButtonProps, type ShareButtonProps, type AsideToggleProps, type AsideProps, } from './Header.js';
|
|
28
|
-
export { Code,
|
|
34
|
+
export { Code, type CodeProps } from './Code.js';
|
|
29
35
|
export { Sources, SourceChip, type Source, type SourcesProps, type SourceChipProps, } from './Sources.js';
|
|
30
36
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/chat/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/chat/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/chat/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,aAAa,EAAE,MAAM,YAAY,CAAA;AAC9D,OAAO,EAAE,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,IAAI,EAAE,MAAM,WAAW,CAAA;AACjE,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,MAAM,UAAU,CAAA;AACnD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,IAAI,EAAE,MAAM,QAAQ,CAAA;AAChD,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,KAAK,EAAE,MAAM,SAAS,CAAA;AAEnD,OAAO,EACL,OAAO,EACP,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,cAAc,EACd,WAAW,EACX,aAAa,EACb,WAAW,EACX,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,EAC3B,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,GACtB,MAAM,WAAW,CAAA;AAElB,OAAO,EACL,MAAM,EACN,YAAY,EACZ,WAAW,EACX,WAAW,EACX,KAAK,EACL,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,UAAU,GAChB,MAAM,UAAU,CAAA;AAEjB,OAAO,EAAE,IAAI,EAAE,KAAK,SAAS,EAAE,MAAM,QAAQ,CAAA;AAE7C,OAAO,EACL,OAAO,EACP,UAAU,EACV,KAAK,MAAM,EACX,KAAK,YAAY,EACjB,KAAK,eAAe,GACrB,MAAM,WAAW,CAAA"}
|
package/dist/chat/index.js
CHANGED
|
@@ -17,6 +17,12 @@
|
|
|
17
17
|
*
|
|
18
18
|
* The model picker is not here: it already ships at `@hanzo/ui/models`, and it
|
|
19
19
|
* is not chat-specific.
|
|
20
|
+
*
|
|
21
|
+
* `CopyButton` is not here either, for the same reason and after the same
|
|
22
|
+
* mistake: it lived in `Code.tsx`, so the only way to reach it was
|
|
23
|
+
* `@hanzo/ui/chat`, and no one hunting for a copy button looks in a chat module.
|
|
24
|
+
* Six surfaces wrote their own instead. It now ships at `@hanzo/ui/product`;
|
|
25
|
+
* `Code` imports it from there.
|
|
20
26
|
*/
|
|
21
27
|
export { Composer, ASK } from './Composer.js';
|
|
22
28
|
export { Message } from './Message.js';
|
|
@@ -25,6 +31,6 @@ export { ready, sends } from './send.js';
|
|
|
25
31
|
export { pinned, SLACK } from './stick.js';
|
|
26
32
|
export { Sidebar, SidebarHeader, SidebarIconButton, SidebarNewChat, SidebarScroll, SidebarSection, SidebarItem, SidebarFolder, SidebarUser, } from './Sidebar.js';
|
|
27
33
|
export { Header, HeaderButton, ShareButton, AsideToggle, Aside, } from './Header.js';
|
|
28
|
-
export { Code
|
|
34
|
+
export { Code } from './Code.js';
|
|
29
35
|
export { Sources, SourceChip, } from './Sources.js';
|
|
30
36
|
//# sourceMappingURL=index.js.map
|
package/dist/chat/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/chat/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/chat/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAsB,MAAM,YAAY,CAAA;AAC9D,OAAO,EAAE,OAAO,EAAgC,MAAM,WAAW,CAAA;AACjE,OAAO,EAAE,MAAM,EAAoB,MAAM,UAAU,CAAA;AACnD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAa,MAAM,QAAQ,CAAA;AAChD,OAAO,EAAE,MAAM,EAAE,KAAK,EAAc,MAAM,SAAS,CAAA;AAEnD,OAAO,EACL,OAAO,EACP,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,cAAc,EACd,WAAW,EACX,aAAa,EACb,WAAW,GASZ,MAAM,WAAW,CAAA;AAElB,OAAO,EACL,MAAM,EACN,YAAY,EACZ,WAAW,EACX,WAAW,EACX,KAAK,GAMN,MAAM,UAAU,CAAA;AAEjB,OAAO,EAAE,IAAI,EAAkB,MAAM,QAAQ,CAAA;AAE7C,OAAO,EACL,OAAO,EACP,UAAU,GAIX,MAAM,WAAW,CAAA"}
|
|
@@ -18,7 +18,7 @@ const react_1 = require("react");
|
|
|
18
18
|
const gui_1 = require("@hanzo/gui");
|
|
19
19
|
const lucide_icons_2_1 = require("@hanzogui/lucide-icons-2");
|
|
20
20
|
const BrandMark_1 = require("./BrandMark.cjs");
|
|
21
|
-
const
|
|
21
|
+
const UserMenu_1 = require("./UserMenu.cjs");
|
|
22
22
|
const surfaces_data_1 = require("./surfaces.data.cjs");
|
|
23
23
|
/** The per-surface glyph — keyed by `Surface.id`, so each surface reads distinctly. */
|
|
24
24
|
const SURFACE_ICON = {
|
|
@@ -34,12 +34,8 @@ const openHref = (href) => {
|
|
|
34
34
|
if (typeof window !== 'undefined')
|
|
35
35
|
window.open(href, '_blank', 'noopener');
|
|
36
36
|
};
|
|
37
|
-
function MenuRow({ icon, label, onPress }) {
|
|
38
|
-
return ((0, jsx_runtime_1.jsxs)(gui_1.XStack, { onPress: onPress, cursor: "pointer", items: "center", gap: "$2.5", px: "$2", py: "$2", rounded: "$3", hoverStyle: { bg: '$color5' }, children: [icon, (0, jsx_runtime_1.jsx)(gui_1.Text, { fontSize: "$2", color: "$color12", children: label })] }));
|
|
39
|
-
}
|
|
40
37
|
function AppHeader({ brand, wordmark = 'Hanzo', onBrand, org, children, current, surfaces, open, user, menu, theme, onProfile, onTeam, onBilling, onSignOut, }) {
|
|
41
38
|
const [appsOpen, setAppsOpen] = (0, react_1.useState)(false);
|
|
42
|
-
const [menuOpen, setMenuOpen] = (0, react_1.useState)(false);
|
|
43
39
|
const items = surfaces ?? (0, surfaces_data_1.otherSurfaces)(current);
|
|
44
40
|
const launch = (s) => {
|
|
45
41
|
setAppsOpen(false);
|
|
@@ -51,5 +47,11 @@ function AppHeader({ brand, wordmark = 'Hanzo', onBrand, org, children, current,
|
|
|
51
47
|
return ((0, jsx_runtime_1.jsxs)(gui_1.XStack, { items: "center", gap: "$2", px: "$3", height: 52, borderBottomWidth: 1, borderColor: "$borderColor", bg: "$background", children: [(0, jsx_runtime_1.jsx)(gui_1.XStack, { items: "center", cursor: onBrand ? 'pointer' : undefined, onPress: onBrand, children: brand ?? (0, jsx_runtime_1.jsx)(BrandMark_1.BrandMark, { wordmark: wordmark }) }), org ? ((0, jsx_runtime_1.jsx)(gui_1.XStack, { items: "center", minW: 0, children: org })) : null, (0, jsx_runtime_1.jsx)(gui_1.XStack, { flex: 1, items: "center", minW: 0, children: children }), items.length > 0 ? ((0, jsx_runtime_1.jsxs)(gui_1.Popover, { open: appsOpen, onOpenChange: setAppsOpen, placement: "bottom-end", children: [(0, jsx_runtime_1.jsx)(gui_1.Popover.Trigger, { asChild: true, children: (0, jsx_runtime_1.jsx)(gui_1.Button, { size: "$2", chromeless: true, icon: (0, jsx_runtime_1.jsx)(lucide_icons_2_1.Grip, { size: 16 }), "aria-label": "Apps" }) }), (0, jsx_runtime_1.jsx)(gui_1.Popover.Content, { bordered: true, elevate: true, p: "$2", width: 230, bg: "$color2", borderColor: "$borderColor", children: (0, jsx_runtime_1.jsx)(gui_1.YStack, { gap: "$1", children: items.map((s) => {
|
|
52
48
|
const Icon = SURFACE_ICON[s.id];
|
|
53
49
|
return ((0, jsx_runtime_1.jsxs)(gui_1.XStack, { onPress: () => launch(s), cursor: "pointer", items: "center", gap: "$2.5", px: "$2", py: "$2", rounded: "$3", hoverStyle: { bg: '$color5' }, children: [(0, jsx_runtime_1.jsx)(Icon, { size: 16 }), (0, jsx_runtime_1.jsx)(gui_1.Text, { flex: 1, fontSize: "$2", fontWeight: "600", color: "$color12", children: s.label }), s.hint ? ((0, jsx_runtime_1.jsx)(gui_1.Text, { fontSize: "$1", color: "$color10", children: s.hint })) : null] }, s.id));
|
|
54
|
-
}) }) })] })) : null, (0, jsx_runtime_1.
|
|
50
|
+
}) }) })] })) : null, (0, jsx_runtime_1.jsx)(UserMenu_1.UserMenu, { name: user, groups: [
|
|
51
|
+
[
|
|
52
|
+
onProfile ? { id: 'profile', label: 'Profile', icon: (0, jsx_runtime_1.jsx)(lucide_icons_2_1.UserRound, { size: 15 }), onPress: onProfile } : null,
|
|
53
|
+
onTeam ? { id: 'team', label: 'Team settings', icon: (0, jsx_runtime_1.jsx)(lucide_icons_2_1.Settings2, { size: 15 }), onPress: onTeam } : null,
|
|
54
|
+
onBilling ? { id: 'billing', label: 'Billing', icon: (0, jsx_runtime_1.jsx)(lucide_icons_2_1.CreditCard, { size: 15 }), onPress: onBilling } : null,
|
|
55
|
+
].filter((r) => r !== null),
|
|
56
|
+
], theme: theme, onSignOut: onSignOut, height: 36, children: menu })] }));
|
|
55
57
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppHeader.d.ts","sourceRoot":"","sources":["../../src/product/AppHeader.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AACH,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAMhD,OAAO,EAAiB,KAAK,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"AppHeader.d.ts","sourceRoot":"","sources":["../../src/product/AppHeader.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AACH,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAMhD,OAAO,EAAiB,KAAK,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAiB7E,MAAM,MAAM,cAAc,GAAG;IAC3B,4DAA4D;IAC5D,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,uDAAuD;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,uCAAuC;IACvC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,0EAA0E;IAC1E,GAAG,CAAC,EAAE,SAAS,CAAA;IACf,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,SAAS,CAAA;IACpB,qFAAqF;IACrF,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB,mFAAmF;IACnF,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAA;IACpB,wDAAwD;IACxD,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IACjC,qEAAqE;IACrE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,+CAA+C;IAC/C,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,wEAAwE;IACxE,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;CACvB,CAAA;AAED,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,QAAkB,EAClB,OAAO,EACP,GAAG,EACH,QAAQ,EACR,OAAO,EACP,QAAQ,EACR,IAAI,EACJ,IAAI,EACJ,IAAI,EACJ,KAAK,EACL,SAAS,EACT,MAAM,EACN,SAAS,EACT,SAAS,GACV,EAAE,cAAc,2CAwEhB"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
/**
|
|
4
4
|
* AppHeader — the shared shell header every Hanzo surface renders: the brand
|
|
5
5
|
* mark top-left (from @hanzo/logo, white-label via `wordmark`/`brand`), an
|
|
@@ -12,10 +12,10 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
12
12
|
* flex + truncation, the wordmark collapse is the brand motion itself.
|
|
13
13
|
*/
|
|
14
14
|
import { useState } from 'react';
|
|
15
|
-
import { Button, Popover,
|
|
16
|
-
import { AppWindow, Bot, CreditCard, Grip, LayoutGrid,
|
|
15
|
+
import { Button, Popover, Text, XStack, YStack } from '@hanzo/gui';
|
|
16
|
+
import { AppWindow, Bot, CreditCard, Grip, LayoutGrid, MessageCircle, Settings2, Sparkles, UserRound, Users } from '@hanzogui/lucide-icons-2';
|
|
17
17
|
import { BrandMark } from './BrandMark.js';
|
|
18
|
-
import {
|
|
18
|
+
import { UserMenu } from './UserMenu.js';
|
|
19
19
|
import { otherSurfaces } from './surfaces.data.js';
|
|
20
20
|
/** The per-surface glyph — keyed by `Surface.id`, so each surface reads distinctly. */
|
|
21
21
|
const SURFACE_ICON = {
|
|
@@ -31,12 +31,8 @@ const openHref = (href) => {
|
|
|
31
31
|
if (typeof window !== 'undefined')
|
|
32
32
|
window.open(href, '_blank', 'noopener');
|
|
33
33
|
};
|
|
34
|
-
function MenuRow({ icon, label, onPress }) {
|
|
35
|
-
return (_jsxs(XStack, { onPress: onPress, cursor: "pointer", items: "center", gap: "$2.5", px: "$2", py: "$2", rounded: "$3", hoverStyle: { bg: '$color5' }, children: [icon, _jsx(Text, { fontSize: "$2", color: "$color12", children: label })] }));
|
|
36
|
-
}
|
|
37
34
|
export function AppHeader({ brand, wordmark = 'Hanzo', onBrand, org, children, current, surfaces, open, user, menu, theme, onProfile, onTeam, onBilling, onSignOut, }) {
|
|
38
35
|
const [appsOpen, setAppsOpen] = useState(false);
|
|
39
|
-
const [menuOpen, setMenuOpen] = useState(false);
|
|
40
36
|
const items = surfaces ?? otherSurfaces(current);
|
|
41
37
|
const launch = (s) => {
|
|
42
38
|
setAppsOpen(false);
|
|
@@ -48,6 +44,12 @@ export function AppHeader({ brand, wordmark = 'Hanzo', onBrand, org, children, c
|
|
|
48
44
|
return (_jsxs(XStack, { items: "center", gap: "$2", px: "$3", height: 52, borderBottomWidth: 1, borderColor: "$borderColor", bg: "$background", children: [_jsx(XStack, { items: "center", cursor: onBrand ? 'pointer' : undefined, onPress: onBrand, children: brand ?? _jsx(BrandMark, { wordmark: wordmark }) }), org ? (_jsx(XStack, { items: "center", minW: 0, children: org })) : null, _jsx(XStack, { flex: 1, items: "center", minW: 0, children: children }), items.length > 0 ? (_jsxs(Popover, { open: appsOpen, onOpenChange: setAppsOpen, placement: "bottom-end", children: [_jsx(Popover.Trigger, { asChild: true, children: _jsx(Button, { size: "$2", chromeless: true, icon: _jsx(Grip, { size: 16 }), "aria-label": "Apps" }) }), _jsx(Popover.Content, { bordered: true, elevate: true, p: "$2", width: 230, bg: "$color2", borderColor: "$borderColor", children: _jsx(YStack, { gap: "$1", children: items.map((s) => {
|
|
49
45
|
const Icon = SURFACE_ICON[s.id];
|
|
50
46
|
return (_jsxs(XStack, { onPress: () => launch(s), cursor: "pointer", items: "center", gap: "$2.5", px: "$2", py: "$2", rounded: "$3", hoverStyle: { bg: '$color5' }, children: [_jsx(Icon, { size: 16 }), _jsx(Text, { flex: 1, fontSize: "$2", fontWeight: "600", color: "$color12", children: s.label }), s.hint ? (_jsx(Text, { fontSize: "$1", color: "$color10", children: s.hint })) : null] }, s.id));
|
|
51
|
-
}) }) })] })) : null,
|
|
47
|
+
}) }) })] })) : null, _jsx(UserMenu, { name: user, groups: [
|
|
48
|
+
[
|
|
49
|
+
onProfile ? { id: 'profile', label: 'Profile', icon: _jsx(UserRound, { size: 15 }), onPress: onProfile } : null,
|
|
50
|
+
onTeam ? { id: 'team', label: 'Team settings', icon: _jsx(Settings2, { size: 15 }), onPress: onTeam } : null,
|
|
51
|
+
onBilling ? { id: 'billing', label: 'Billing', icon: _jsx(CreditCard, { size: 15 }), onPress: onBilling } : null,
|
|
52
|
+
].filter((r) => r !== null),
|
|
53
|
+
], theme: theme, onSignOut: onSignOut, height: 36, children: menu })] }));
|
|
52
54
|
}
|
|
53
55
|
//# sourceMappingURL=AppHeader.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppHeader.js","sourceRoot":"","sources":["../../src/product/AppHeader.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ;;;;;;;;;;GAUG;AACH,OAAO,EAAE,QAAQ,EAAkB,MAAM,OAAO,CAAA;AAChD,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"AppHeader.js","sourceRoot":"","sources":["../../src/product/AppHeader.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
'use client';"use strict";
|
|
2
|
+
'use client';
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.CopyButton = CopyButton;
|
|
5
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
|
+
/**
|
|
7
|
+
* CopyButton — copy with a confirmation tick.
|
|
8
|
+
*
|
|
9
|
+
* The tick is the point. A control that looks identical before and after leaves
|
|
10
|
+
* the reader unsure whether it fired, and the usual response is to press it
|
|
11
|
+
* again. Reverts after `CONFIRM`, and clears its timer on unmount so it cannot
|
|
12
|
+
* set state on a component that is gone.
|
|
13
|
+
*
|
|
14
|
+
* It lived inside `chat/Code.tsx` and was reachable only as `@hanzo/ui/chat`.
|
|
15
|
+
* Nobody looks in a chat module for a copy button, so every surface wrote its
|
|
16
|
+
* own — an API key here, a deposit address there, a referral link, a wire
|
|
17
|
+
* instruction — each with its own confirmation window, or none. Copying text is
|
|
18
|
+
* not a chat idea; it belongs to the product layer, and `Code` now imports it
|
|
19
|
+
* from here rather than owning it.
|
|
20
|
+
*/
|
|
21
|
+
const react_1 = require("react");
|
|
22
|
+
const gui_1 = require("@hanzo/gui");
|
|
23
|
+
const lucide_icons_2_1 = require("@hanzogui/lucide-icons-2");
|
|
24
|
+
const slot_1 = require("../backends/gui/slot.cjs");
|
|
25
|
+
const instrument_1 = require("./instrument.cjs");
|
|
26
|
+
/** How long the control shows its confirmation, in ms. */
|
|
27
|
+
const CONFIRM = 2000;
|
|
28
|
+
function CopyButton({ value, label = 'Copy', size = 24, id }) {
|
|
29
|
+
const [copied, setCopied] = (0, react_1.useState)(false);
|
|
30
|
+
const timer = (0, react_1.useRef)(undefined);
|
|
31
|
+
const track = (0, instrument_1.useEmit)();
|
|
32
|
+
(0, react_1.useEffect)(() => () => clearTimeout(timer.current), []);
|
|
33
|
+
const copy = () => {
|
|
34
|
+
// Optional-chained rather than assumed: `navigator.clipboard` is undefined
|
|
35
|
+
// in insecure contexts and absent entirely on native, and these components
|
|
36
|
+
// are meant to run on both.
|
|
37
|
+
navigator?.clipboard
|
|
38
|
+
?.writeText(value)
|
|
39
|
+
.then(() => {
|
|
40
|
+
// The LENGTH, never the text — a copied value is a password as often as
|
|
41
|
+
// it is a URL.
|
|
42
|
+
track({ component: 'CopyButton', action: 'click', id, value: value.length });
|
|
43
|
+
setCopied(true);
|
|
44
|
+
clearTimeout(timer.current);
|
|
45
|
+
timer.current = setTimeout(() => setCopied(false), CONFIRM);
|
|
46
|
+
})
|
|
47
|
+
.catch(() => {
|
|
48
|
+
// A refused clipboard needs no toast — the missing tick already says it,
|
|
49
|
+
// and an error dialog for a failed copy is louder than the failure.
|
|
50
|
+
track({ component: 'CopyButton', action: 'error', id });
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
return ((0, jsx_runtime_1.jsx)(gui_1.XStack, { width: size, height: size, items: "center", justify: "center", rounded: "$2", cursor: "pointer", opacity: 0.7, onPress: copy, role: "button", tabIndex: 0, "aria-label": copied ? 'Copied' : label, ...(0, slot_1.tip)(copied ? 'Copied' : label), hoverStyle: { bg: '$color4', opacity: 1 }, pressStyle: { bg: '$color5' }, children: copied ? (0, jsx_runtime_1.jsx)(lucide_icons_2_1.Check, { size: Math.round(size * 0.58) }) : (0, jsx_runtime_1.jsx)(lucide_icons_2_1.Copy, { size: Math.round(size * 0.58) }) }));
|
|
54
|
+
}
|