@hanzo/ui 8.0.54 → 8.0.56
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/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/gui-config.cjs +37 -12
- package/dist/gui-config.d.ts +1 -582
- package/dist/gui-config.d.ts.map +1 -1
- package/dist/gui-config.js +37 -12
- package/dist/gui-config.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 +247 -247
- package/package.json +1 -1
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface CopyButtonProps {
|
|
2
|
+
/** The text placed on the clipboard. */
|
|
3
|
+
value: string;
|
|
4
|
+
/** Resting label — the tooltip and the accessible name. */
|
|
5
|
+
label?: string;
|
|
6
|
+
/** Edge of the square hit area. Default 24. */
|
|
7
|
+
size?: number;
|
|
8
|
+
/** Names the copied thing in analytics ("api-key", "address"). Never the value. */
|
|
9
|
+
id?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function CopyButton({ value, label, size, id }: CopyButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
//# sourceMappingURL=CopyButton.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CopyButton.d.ts","sourceRoot":"","sources":["../../src/product/CopyButton.tsx"],"names":[],"mappings":"AA2BA,MAAM,WAAW,eAAe;IAC9B,wCAAwC;IACxC,KAAK,EAAE,MAAM,CAAA;IACb,2DAA2D;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,+CAA+C;IAC/C,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,mFAAmF;IACnF,EAAE,CAAC,EAAE,MAAM,CAAA;CACZ;AAED,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,KAAc,EAAE,IAAS,EAAE,EAAE,EAAE,EAAE,eAAe,2CAgDnF"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* CopyButton — copy with a confirmation tick.
|
|
5
|
+
*
|
|
6
|
+
* The tick is the point. A control that looks identical before and after leaves
|
|
7
|
+
* the reader unsure whether it fired, and the usual response is to press it
|
|
8
|
+
* again. Reverts after `CONFIRM`, and clears its timer on unmount so it cannot
|
|
9
|
+
* set state on a component that is gone.
|
|
10
|
+
*
|
|
11
|
+
* It lived inside `chat/Code.tsx` and was reachable only as `@hanzo/ui/chat`.
|
|
12
|
+
* Nobody looks in a chat module for a copy button, so every surface wrote its
|
|
13
|
+
* own — an API key here, a deposit address there, a referral link, a wire
|
|
14
|
+
* instruction — each with its own confirmation window, or none. Copying text is
|
|
15
|
+
* not a chat idea; it belongs to the product layer, and `Code` now imports it
|
|
16
|
+
* from here rather than owning it.
|
|
17
|
+
*/
|
|
18
|
+
import { useEffect, useRef, useState } from 'react';
|
|
19
|
+
import { XStack } from '@hanzo/gui';
|
|
20
|
+
import { Check, Copy } from '@hanzogui/lucide-icons-2';
|
|
21
|
+
import { tip } from '../backends/gui/slot.js';
|
|
22
|
+
import { useEmit } from './instrument.js';
|
|
23
|
+
/** How long the control shows its confirmation, in ms. */
|
|
24
|
+
const CONFIRM = 2000;
|
|
25
|
+
export function CopyButton({ value, label = 'Copy', size = 24, id }) {
|
|
26
|
+
const [copied, setCopied] = useState(false);
|
|
27
|
+
const timer = useRef(undefined);
|
|
28
|
+
const track = useEmit();
|
|
29
|
+
useEffect(() => () => clearTimeout(timer.current), []);
|
|
30
|
+
const copy = () => {
|
|
31
|
+
// Optional-chained rather than assumed: `navigator.clipboard` is undefined
|
|
32
|
+
// in insecure contexts and absent entirely on native, and these components
|
|
33
|
+
// are meant to run on both.
|
|
34
|
+
navigator?.clipboard
|
|
35
|
+
?.writeText(value)
|
|
36
|
+
.then(() => {
|
|
37
|
+
// The LENGTH, never the text — a copied value is a password as often as
|
|
38
|
+
// it is a URL.
|
|
39
|
+
track({ component: 'CopyButton', action: 'click', id, value: value.length });
|
|
40
|
+
setCopied(true);
|
|
41
|
+
clearTimeout(timer.current);
|
|
42
|
+
timer.current = setTimeout(() => setCopied(false), CONFIRM);
|
|
43
|
+
})
|
|
44
|
+
.catch(() => {
|
|
45
|
+
// A refused clipboard needs no toast — the missing tick already says it,
|
|
46
|
+
// and an error dialog for a failed copy is louder than the failure.
|
|
47
|
+
track({ component: 'CopyButton', action: 'error', id });
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
return (_jsx(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, ...tip(copied ? 'Copied' : label), hoverStyle: { bg: '$color4', opacity: 1 }, pressStyle: { bg: '$color5' }, children: copied ? _jsx(Check, { size: Math.round(size * 0.58) }) : _jsx(Copy, { size: Math.round(size * 0.58) }) }));
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=CopyButton.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CopyButton.js","sourceRoot":"","sources":["../../src/product/CopyButton.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AACnD,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AACnC,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAA;AAEtD,OAAO,EAAE,GAAG,EAAE,MAAM,sBAAsB,CAAA;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAEtC,0DAA0D;AAC1D,MAAM,OAAO,GAAG,IAAI,CAAA;AAapB,MAAM,UAAU,UAAU,CAAC,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM,EAAE,IAAI,GAAG,EAAE,EAAE,EAAE,EAAmB;IAClF,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IAC3C,MAAM,KAAK,GAAG,MAAM,CAA4C,SAAS,CAAC,CAAA;IAC1E,MAAM,KAAK,GAAG,OAAO,EAAE,CAAA;IAEvB,SAAS,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC,CAAA;IAEtD,MAAM,IAAI,GAAG,GAAG,EAAE;QAChB,2EAA2E;QAC3E,2EAA2E;QAC3E,4BAA4B;QAC5B,SAAS,EAAE,SAAS;YAClB,EAAE,SAAS,CAAC,KAAK,CAAC;aACjB,IAAI,CAAC,GAAG,EAAE;YACT,wEAAwE;YACxE,eAAe;YACf,KAAK,CAAC,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAA;YAC5E,SAAS,CAAC,IAAI,CAAC,CAAA;YACf,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;YAC3B,KAAK,CAAC,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,CAAA;QAC7D,CAAC,CAAC;aACD,KAAK,CAAC,GAAG,EAAE;YACV,yEAAyE;YACzE,oEAAoE;YACpE,KAAK,CAAC,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAA;QACzD,CAAC,CAAC,CAAA;IACN,CAAC,CAAA;IAED,OAAO,CACL,KAAC,MAAM,IACL,KAAK,EAAE,IAAI,EACX,MAAM,EAAE,IAAI,EACZ,KAAK,EAAC,QAAQ,EACd,OAAO,EAAC,QAAQ,EAChB,OAAO,EAAC,IAAI,EACZ,MAAM,EAAC,SAAS,EAChB,OAAO,EAAE,GAAG,EACZ,OAAO,EAAE,IAAI,EACb,IAAI,EAAC,QAAQ,EACb,QAAQ,EAAE,CAAC,gBACC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,KACjC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,EAClC,UAAU,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE,EACzC,UAAU,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,YAE5B,MAAM,CAAC,CAAC,CAAC,KAAC,KAAK,IAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,GAAI,CAAC,CAAC,CAAC,KAAC,IAAI,IAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,GAAI,GACrF,CACV,CAAA;AACH,CAAC"}
|
package/dist/product/Field.cjs
CHANGED
|
@@ -18,6 +18,7 @@ const jsx_runtime_1 = require("react/jsx-runtime");
|
|
|
18
18
|
const react_1 = require("react");
|
|
19
19
|
const gui_1 = require("@hanzo/gui");
|
|
20
20
|
const instrument_1 = require("./instrument.cjs");
|
|
21
|
+
const SecretInput_1 = require("./SecretInput.cjs");
|
|
21
22
|
// The label the caller ALREADY typed on the FieldRow becomes the analytics name —
|
|
22
23
|
// so an app never labels a field twice and no field reports as "unnamed".
|
|
23
24
|
const FieldNameContext = (0, react_1.createContext)(undefined);
|
|
@@ -41,7 +42,7 @@ function FieldText({ value, onChange, disabled, secure, placeholder, }) {
|
|
|
41
42
|
return ((0, jsx_runtime_1.jsx)(gui_1.Input, { value: value, onChangeText: (v) => {
|
|
42
43
|
track({ action: 'change', value: secure ? 0 : (0, instrument_1.textSize)(v) });
|
|
43
44
|
onChange(v);
|
|
44
|
-
}, disabled: disabled,
|
|
45
|
+
}, disabled: disabled, ...(0, SecretInput_1.masked)(Boolean(secure)), placeholder: placeholder, autoCapitalize: "none" }));
|
|
45
46
|
}
|
|
46
47
|
function FieldTextArea({ value, onChange, disabled, rows = 6, }) {
|
|
47
48
|
const track = useFieldTrack('FieldTextArea');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Field.d.ts","sourceRoot":"","sources":["../../src/product/Field.tsx"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,OAAO,EAAiD,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"Field.d.ts","sourceRoot":"","sources":["../../src/product/Field.tsx"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,OAAO,EAAiD,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AA2BrF;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,SAAS,CAAA;CAAE,2CAanF;AAED,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,MAAM,EACN,WAAW,GACZ,EAAE;IACD,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IAC7B,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,2CAkBA;AAED,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,IAAQ,GACT,EAAE;IACD,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IAC7B,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,2CAaA;AAED,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,QAAQ,EACR,QAAQ,GACT,EAAE;IACD,OAAO,EAAE,OAAO,CAAA;IAChB,QAAQ,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;IAC9B,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,2CAeA;AA0CD,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,OAAO,EACP,QAAQ,EACR,QAAQ,EACR,WAAuB,GACxB,EAAE;IACD,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IAC7B,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,2CA4BA;AAED,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,GAAG,EACH,GAAG,EACH,IAAI,EACJ,QAAQ,EACR,QAAQ,GACT,EAAE;IACD,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IAC7B,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,2CA0BA"}
|
package/dist/product/Field.js
CHANGED
|
@@ -10,6 +10,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
10
10
|
import { createContext, useContext } from 'react';
|
|
11
11
|
import { Input, Label, Switch, TextArea, Slider, Text, XStack, YStack, } from '@hanzo/gui';
|
|
12
12
|
import { textSize, useEmit } from './instrument.js';
|
|
13
|
+
import { masked } from './SecretInput.js';
|
|
13
14
|
// The label the caller ALREADY typed on the FieldRow becomes the analytics name —
|
|
14
15
|
// so an app never labels a field twice and no field reports as "unnamed".
|
|
15
16
|
const FieldNameContext = createContext(undefined);
|
|
@@ -33,7 +34,7 @@ export function FieldText({ value, onChange, disabled, secure, placeholder, }) {
|
|
|
33
34
|
return (_jsx(Input, { value: value, onChangeText: (v) => {
|
|
34
35
|
track({ action: 'change', value: secure ? 0 : textSize(v) });
|
|
35
36
|
onChange(v);
|
|
36
|
-
}, disabled: disabled,
|
|
37
|
+
}, disabled: disabled, ...masked(Boolean(secure)), placeholder: placeholder, autoCapitalize: "none" }));
|
|
37
38
|
}
|
|
38
39
|
export function FieldTextArea({ value, onChange, disabled, rows = 6, }) {
|
|
39
40
|
const track = useFieldTrack('FieldTextArea');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Field.js","sourceRoot":"","sources":["../../src/product/Field.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ;;;;;;GAMG;AACH,OAAO,EAAE,aAAa,EAAE,UAAU,EAAsC,MAAM,OAAO,CAAA;AACrF,OAAO,EACL,KAAK,EACL,KAAK,EACL,MAAM,EACN,QAAQ,EACR,MAAM,EACN,IAAI,EACJ,MAAM,EACN,MAAM,GACP,MAAM,YAAY,CAAA;AAEnB,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAgB,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"Field.js","sourceRoot":"","sources":["../../src/product/Field.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
'use client';"use strict";
|
|
2
|
+
'use client';
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.Fieldset = Fieldset;
|
|
5
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
|
+
const gui_1 = require("@hanzo/gui");
|
|
7
|
+
function Fieldset({ title, icon, description, action, danger, children }) {
|
|
8
|
+
const edge = danger ? '$red9' : '$borderColor';
|
|
9
|
+
return ((0, jsx_runtime_1.jsxs)(gui_1.YStack, { width: "100%", p: "$4", gap: "$3", rounded: "$4", borderWidth: 1, borderColor: edge, bg: "$color2", children: [title || action ? ((0, jsx_runtime_1.jsxs)(gui_1.XStack, { items: "center", gap: "$2", justify: "space-between", flexWrap: "wrap", children: [(0, jsx_runtime_1.jsxs)(gui_1.XStack, { items: "center", gap: "$2", flex: 1, minW: 0, children: [icon, title ? ((0, jsx_runtime_1.jsx)(gui_1.Text, { fontSize: "$3", fontWeight: "500", color: danger ? '$red10' : '$color12', children: title })) : null] }), action] })) : null, description ? (
|
|
10
|
+
// Sits under the legend, not beside it: a description that shares the
|
|
11
|
+
// line with the title competes with it and wraps first on a phone.
|
|
12
|
+
(0, jsx_runtime_1.jsx)(gui_1.Text, { fontSize: "$2", color: "$color11", children: description })) : null, (0, jsx_runtime_1.jsx)(gui_1.YStack, { gap: "$3", children: children })] }));
|
|
13
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fieldset — a titled group of settings, and the surface it sits on.
|
|
3
|
+
*
|
|
4
|
+
* The name is the HTML one and the right one: a bounded set of related controls
|
|
5
|
+
* under a legend. `FieldRow` and the `Field*` controls are what go inside it;
|
|
6
|
+
* this is the box they were always drawn in and that no package ever owned, so
|
|
7
|
+
* every settings page drew its own — hanzo.app twice, the console once, billing
|
|
8
|
+
* across six panels — and they disagreed on the radius, the fill and, in one
|
|
9
|
+
* case, on whether a destructive group should be a solid red card.
|
|
10
|
+
*
|
|
11
|
+
* `Panel` (from `Metric`) is NOT this: it is a dashboard tile sized to sit in a
|
|
12
|
+
* row of metrics. A settings group is full width, carries a legend and an icon,
|
|
13
|
+
* and has a destructive register. Two boxes, two jobs, two names.
|
|
14
|
+
*
|
|
15
|
+
* Danger says danger with its EDGE. A solid saturated fill makes the one group
|
|
16
|
+
* you should find only when you are looking for it the loudest object on the
|
|
17
|
+
* page; colour is an accent here, so the border carries it and the surface stays
|
|
18
|
+
* on the ladder with its neighbours.
|
|
19
|
+
*/
|
|
20
|
+
import type { ReactNode } from 'react';
|
|
21
|
+
export type FieldsetProps = {
|
|
22
|
+
/** The legend. Sentence case — it names a group, it is not a headline. */
|
|
23
|
+
title?: string;
|
|
24
|
+
/** Optional glyph before the legend, at the legend's own size. */
|
|
25
|
+
icon?: ReactNode;
|
|
26
|
+
/** One line under the legend saying what the group is for. */
|
|
27
|
+
description?: string;
|
|
28
|
+
/** Right-aligned control on the legend line (a link, a small action). */
|
|
29
|
+
action?: ReactNode;
|
|
30
|
+
/** The destructive register — irreversible actions only. */
|
|
31
|
+
danger?: boolean;
|
|
32
|
+
children: ReactNode;
|
|
33
|
+
};
|
|
34
|
+
export declare function Fieldset({ title, icon, description, action, danger, children }: FieldsetProps): import("react/jsx-runtime").JSX.Element;
|
|
35
|
+
//# sourceMappingURL=Fieldset.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Fieldset.d.ts","sourceRoot":"","sources":["../../src/product/Fieldset.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGtC,MAAM,MAAM,aAAa,GAAG;IAC1B,0EAA0E;IAC1E,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,kEAAkE;IAClE,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,yEAAyE;IACzE,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB,4DAA4D;IAC5D,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,QAAQ,EAAE,SAAS,CAAA;CACpB,CAAA;AAED,wBAAgB,QAAQ,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,aAAa,2CAqC7F"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Text, XStack, YStack } from '@hanzo/gui';
|
|
4
|
+
export function Fieldset({ title, icon, description, action, danger, children }) {
|
|
5
|
+
const edge = danger ? '$red9' : '$borderColor';
|
|
6
|
+
return (_jsxs(YStack, { width: "100%", p: "$4", gap: "$3", rounded: "$4", borderWidth: 1, borderColor: edge, bg: "$color2", children: [title || action ? (_jsxs(XStack, { items: "center", gap: "$2", justify: "space-between", flexWrap: "wrap", children: [_jsxs(XStack, { items: "center", gap: "$2", flex: 1, minW: 0, children: [icon, title ? (_jsx(Text, { fontSize: "$3", fontWeight: "500", color: danger ? '$red10' : '$color12', children: title })) : null] }), action] })) : null, description ? (
|
|
7
|
+
// Sits under the legend, not beside it: a description that shares the
|
|
8
|
+
// line with the title competes with it and wraps first on a phone.
|
|
9
|
+
_jsx(Text, { fontSize: "$2", color: "$color11", children: description })) : null, _jsx(YStack, { gap: "$3", children: children })] }));
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=Fieldset.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Fieldset.js","sourceRoot":"","sources":["../../src/product/Fieldset.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAsBZ,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAgBjD,MAAM,UAAU,QAAQ,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAiB;IAC5F,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,cAAc,CAAA;IAC9C,OAAO,CACL,MAAC,MAAM,IACL,KAAK,EAAC,MAAM,EACZ,CAAC,EAAC,IAAI,EACN,GAAG,EAAC,IAAI,EACR,OAAO,EAAC,IAAI,EACZ,WAAW,EAAE,CAAC,EACd,WAAW,EAAE,IAAI,EACjB,EAAE,EAAC,SAAS,aAEX,KAAK,IAAI,MAAM,CAAC,CAAC,CAAC,CACjB,MAAC,MAAM,IAAC,KAAK,EAAC,QAAQ,EAAC,GAAG,EAAC,IAAI,EAAC,OAAO,EAAC,eAAe,EAAC,QAAQ,EAAC,MAAM,aACrE,MAAC,MAAM,IAAC,KAAK,EAAC,QAAQ,EAAC,GAAG,EAAC,IAAI,EAAC,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,aAC7C,IAAI,EACJ,KAAK,CAAC,CAAC,CAAC,CACP,KAAC,IAAI,IAAC,QAAQ,EAAC,IAAI,EAAC,UAAU,EAAC,KAAK,EAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,YACvE,KAAK,GACD,CACR,CAAC,CAAC,CAAC,IAAI,IACD,EACR,MAAM,IACA,CACV,CAAC,CAAC,CAAC,IAAI,EAEP,WAAW,CAAC,CAAC,CAAC;YACb,sEAAsE;YACtE,mEAAmE;YACnE,KAAC,IAAI,IAAC,QAAQ,EAAC,IAAI,EAAC,KAAK,EAAC,UAAU,YACjC,WAAW,GACP,CACR,CAAC,CAAC,CAAC,IAAI,EAER,KAAC,MAAM,IAAC,GAAG,EAAC,IAAI,YAAE,QAAQ,GAAU,IAC7B,CACV,CAAA;AACH,CAAC"}
|
package/dist/product/OrgMark.cjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client';"use strict";
|
|
2
2
|
'use client';
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.isEmoji = isEmoji;
|
|
4
5
|
exports.monogram = monogram;
|
|
5
6
|
exports.OrgMark = OrgMark;
|
|
6
7
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
@@ -13,9 +14,37 @@ const jsx_runtime_1 = require("react/jsx-runtime");
|
|
|
13
14
|
* customer's console must show the customer's identity, not ours.
|
|
14
15
|
*
|
|
15
16
|
* Monochrome by construction (`$color4` tile, `$color12` glyph) so it belongs to
|
|
16
|
-
* the chrome; colour stays with content
|
|
17
|
+
* the chrome; colour stays with content — with one exception: an emoji logo is
|
|
18
|
+
* rendered in full colour, because a monochrome emoji is not the emoji.
|
|
19
|
+
*
|
|
20
|
+
* Three kinds of `logo`, resolved in order: a URL, a short emoji string, or
|
|
21
|
+
* nothing. A URL that FAILS to load falls back to the monogram rather than
|
|
22
|
+
* leaving a broken-image glyph in the chrome — org logos are tenant-supplied and
|
|
23
|
+
* a dead link is the normal case, not the exceptional one.
|
|
17
24
|
*/
|
|
25
|
+
const react_1 = require("react");
|
|
18
26
|
const gui_1 = require("@hanzo/gui");
|
|
27
|
+
/**
|
|
28
|
+
* An emoji mark rather than a URL.
|
|
29
|
+
*
|
|
30
|
+
* Tested by absence of a scheme/path and by shortness — an emoji sequence is a
|
|
31
|
+
* handful of code points, and `Intl.Segmenter` counts grapheme clusters so a
|
|
32
|
+
* flag or a family (several code points, one glyph) reads as one character.
|
|
33
|
+
* Falls back to code-point length where `Segmenter` is unavailable.
|
|
34
|
+
*/
|
|
35
|
+
function isEmoji(logo) {
|
|
36
|
+
const s = logo.trim();
|
|
37
|
+
if (!s || /^(https?:|data:|\/)/.test(s))
|
|
38
|
+
return false;
|
|
39
|
+
// Regional_Indicator as well as Extended_Pictographic: a flag is a pair of
|
|
40
|
+
// regional indicators and carries no pictographic code point at all, so the
|
|
41
|
+
// pictographic test alone rejects every country mark.
|
|
42
|
+
if (!/[\p{Extended_Pictographic}\p{Regional_Indicator}]/u.test(s))
|
|
43
|
+
return false;
|
|
44
|
+
const Seg = Intl.Segmenter;
|
|
45
|
+
const units = Seg ? [...new Seg().segment(s)].length : [...s].length;
|
|
46
|
+
return units <= 3;
|
|
47
|
+
}
|
|
19
48
|
/**
|
|
20
49
|
* The monogram for a name: the first letter of each of the first two words,
|
|
21
50
|
* uppercased — the SAME rule @hanzo/iam's account widget applies to a person, so
|
|
@@ -32,11 +61,18 @@ function monogram(name) {
|
|
|
32
61
|
return (letters || name.trim().slice(0, 2)).toUpperCase();
|
|
33
62
|
}
|
|
34
63
|
function OrgMark({ org, size = 22, maxW }) {
|
|
35
|
-
|
|
64
|
+
// Reset when the mark changes: switcher rows reuse instances, so a failure on
|
|
65
|
+
// one org must not blank the next org's logo.
|
|
66
|
+
const [broken, setBroken] = (0, react_1.useState)(false);
|
|
67
|
+
(0, react_1.useEffect)(() => setBroken(false), [org.logo]);
|
|
68
|
+
if (org.logo && isEmoji(org.logo)) {
|
|
69
|
+
return ((0, jsx_runtime_1.jsx)(gui_1.YStack, { width: size, height: size, items: "center", justify: "center", style: { flexShrink: 0 }, children: (0, jsx_runtime_1.jsx)(gui_1.Text, { fontSize: Math.round(size * 0.72), lineHeight: size, children: org.logo.trim() }) }));
|
|
70
|
+
}
|
|
71
|
+
if (org.logo && !broken) {
|
|
36
72
|
// An arbitrary tenant-supplied URL — a raw <img>, since next/image would need
|
|
37
73
|
// a remote allow-list per customer domain.
|
|
38
74
|
// eslint-disable-next-line @next/next/no-img-element
|
|
39
|
-
return ((0, jsx_runtime_1.jsx)("img", { src: org.logo, alt: "", style: {
|
|
75
|
+
return ((0, jsx_runtime_1.jsx)("img", { src: org.logo, alt: "", onError: () => setBroken(true), style: {
|
|
40
76
|
height: size,
|
|
41
77
|
width: maxW ? 'auto' : size,
|
|
42
78
|
maxWidth: maxW ?? size,
|
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
import type { Org } from './scope.js';
|
|
2
|
+
/**
|
|
3
|
+
* An emoji mark rather than a URL.
|
|
4
|
+
*
|
|
5
|
+
* Tested by absence of a scheme/path and by shortness — an emoji sequence is a
|
|
6
|
+
* handful of code points, and `Intl.Segmenter` counts grapheme clusters so a
|
|
7
|
+
* flag or a family (several code points, one glyph) reads as one character.
|
|
8
|
+
* Falls back to code-point length where `Segmenter` is unavailable.
|
|
9
|
+
*/
|
|
10
|
+
export declare function isEmoji(logo: string): boolean;
|
|
2
11
|
/**
|
|
3
12
|
* The monogram for a name: the first letter of each of the first two words,
|
|
4
13
|
* uppercased — the SAME rule @hanzo/iam's account widget applies to a person, so
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OrgMark.d.ts","sourceRoot":"","sources":["../../src/product/OrgMark.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"OrgMark.d.ts","sourceRoot":"","sources":["../../src/product/OrgMark.tsx"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,SAAS,CAAA;AAElC;;;;;;;GAOG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAU7C;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAO7C;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,gFAAgF;IAChF,GAAG,EAAE,GAAG,CAAA;IACR,gEAAgE;IAChE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,CAAA;AAED,wBAAgB,OAAO,CAAC,EAAE,GAAG,EAAE,IAAS,EAAE,IAAI,EAAE,EAAE,YAAY,2CA0D7D"}
|
package/dist/product/OrgMark.js
CHANGED
|
@@ -9,9 +9,37 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
9
9
|
* customer's console must show the customer's identity, not ours.
|
|
10
10
|
*
|
|
11
11
|
* Monochrome by construction (`$color4` tile, `$color12` glyph) so it belongs to
|
|
12
|
-
* the chrome; colour stays with content
|
|
12
|
+
* the chrome; colour stays with content — with one exception: an emoji logo is
|
|
13
|
+
* rendered in full colour, because a monochrome emoji is not the emoji.
|
|
14
|
+
*
|
|
15
|
+
* Three kinds of `logo`, resolved in order: a URL, a short emoji string, or
|
|
16
|
+
* nothing. A URL that FAILS to load falls back to the monogram rather than
|
|
17
|
+
* leaving a broken-image glyph in the chrome — org logos are tenant-supplied and
|
|
18
|
+
* a dead link is the normal case, not the exceptional one.
|
|
13
19
|
*/
|
|
20
|
+
import { useEffect, useState } from 'react';
|
|
14
21
|
import { Text, YStack } from '@hanzo/gui';
|
|
22
|
+
/**
|
|
23
|
+
* An emoji mark rather than a URL.
|
|
24
|
+
*
|
|
25
|
+
* Tested by absence of a scheme/path and by shortness — an emoji sequence is a
|
|
26
|
+
* handful of code points, and `Intl.Segmenter` counts grapheme clusters so a
|
|
27
|
+
* flag or a family (several code points, one glyph) reads as one character.
|
|
28
|
+
* Falls back to code-point length where `Segmenter` is unavailable.
|
|
29
|
+
*/
|
|
30
|
+
export function isEmoji(logo) {
|
|
31
|
+
const s = logo.trim();
|
|
32
|
+
if (!s || /^(https?:|data:|\/)/.test(s))
|
|
33
|
+
return false;
|
|
34
|
+
// Regional_Indicator as well as Extended_Pictographic: a flag is a pair of
|
|
35
|
+
// regional indicators and carries no pictographic code point at all, so the
|
|
36
|
+
// pictographic test alone rejects every country mark.
|
|
37
|
+
if (!/[\p{Extended_Pictographic}\p{Regional_Indicator}]/u.test(s))
|
|
38
|
+
return false;
|
|
39
|
+
const Seg = Intl.Segmenter;
|
|
40
|
+
const units = Seg ? [...new Seg().segment(s)].length : [...s].length;
|
|
41
|
+
return units <= 3;
|
|
42
|
+
}
|
|
15
43
|
/**
|
|
16
44
|
* The monogram for a name: the first letter of each of the first two words,
|
|
17
45
|
* uppercased — the SAME rule @hanzo/iam's account widget applies to a person, so
|
|
@@ -28,11 +56,18 @@ export function monogram(name) {
|
|
|
28
56
|
return (letters || name.trim().slice(0, 2)).toUpperCase();
|
|
29
57
|
}
|
|
30
58
|
export function OrgMark({ org, size = 22, maxW }) {
|
|
31
|
-
|
|
59
|
+
// Reset when the mark changes: switcher rows reuse instances, so a failure on
|
|
60
|
+
// one org must not blank the next org's logo.
|
|
61
|
+
const [broken, setBroken] = useState(false);
|
|
62
|
+
useEffect(() => setBroken(false), [org.logo]);
|
|
63
|
+
if (org.logo && isEmoji(org.logo)) {
|
|
64
|
+
return (_jsx(YStack, { width: size, height: size, items: "center", justify: "center", style: { flexShrink: 0 }, children: _jsx(Text, { fontSize: Math.round(size * 0.72), lineHeight: size, children: org.logo.trim() }) }));
|
|
65
|
+
}
|
|
66
|
+
if (org.logo && !broken) {
|
|
32
67
|
// An arbitrary tenant-supplied URL — a raw <img>, since next/image would need
|
|
33
68
|
// a remote allow-list per customer domain.
|
|
34
69
|
// eslint-disable-next-line @next/next/no-img-element
|
|
35
|
-
return (_jsx("img", { src: org.logo, alt: "", style: {
|
|
70
|
+
return (_jsx("img", { src: org.logo, alt: "", onError: () => setBroken(true), style: {
|
|
36
71
|
height: size,
|
|
37
72
|
width: maxW ? 'auto' : size,
|
|
38
73
|
maxWidth: maxW ?? size,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OrgMark.js","sourceRoot":"","sources":["../../src/product/OrgMark.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ
|
|
1
|
+
{"version":3,"file":"OrgMark.js","sourceRoot":"","sources":["../../src/product/OrgMark.tsx"],"names":[],"mappings":"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"}
|