@hanzo/rooms 0.1.95 → 0.1.97
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/dist/Chrome.js +3 -1
- package/dist/Column.js +5 -2
- package/dist/Me.js +10 -5
- package/dist/Orgs.js +1 -1
- package/dist/Palette.js +1 -1
- package/dist/ProviderMark.js +5 -2
- package/dist/Rail.js +5 -2
- package/dist/Settings.js +3 -1
- package/dist/Top.js +5 -2
- package/dist/dev.js +14 -6
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -2
- package/dist/lib/signout.d.ts +5 -0
- package/dist/lib/signout.js +7 -1
- package/package.json +5 -2
package/dist/Chrome.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useI18n } from '@hanzo/i18n/react';
|
|
3
4
|
import { useIam } from '@hanzo/iam/react';
|
|
4
5
|
import { Text, View, XStack, YStack } from '@hanzo/gui';
|
|
5
6
|
import { Avatar, Button, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from '@hanzo/ui';
|
|
@@ -13,6 +14,7 @@ const HEIGHT = 60;
|
|
|
13
14
|
/** The notch's inset. gui carries a CSS function to the page unchanged; its types only name custom properties. */
|
|
14
15
|
const NOTCH = 'env(safe-area-inset-top)';
|
|
15
16
|
export function Chrome({ back }) {
|
|
17
|
+
const { t } = useI18n();
|
|
16
18
|
const { user, isAuthenticated } = useIam();
|
|
17
19
|
const signOut = useSignOut();
|
|
18
20
|
const claimed = named();
|
|
@@ -27,5 +29,5 @@ export function Chrome({ back }) {
|
|
|
27
29
|
};
|
|
28
30
|
const out = () => void signOut();
|
|
29
31
|
const signed = isAuthenticated && Boolean(who);
|
|
30
|
-
return (_jsxs(XStack, { render: "header", "data-slot": "masthead", position: "sticky", t: 0, z: "var(--z-header)", shrink: 0, boxSizing: "content-box", height: HEIGHT, pt: NOTCH, px: "var(--page-gutter, 20px)", gap: 12, items: "center", bg: "var(--background)", borderBottomWidth: 1, borderColor: "var(--border)", children: [_jsx(Text, { render: _jsx("a", { href: "/", "aria-label":
|
|
32
|
+
return (_jsxs(XStack, { render: "header", "data-slot": "masthead", position: "sticky", t: 0, z: "var(--z-header)", shrink: 0, boxSizing: "content-box", height: HEIGHT, pt: NOTCH, px: "var(--page-gutter, 20px)", gap: 12, items: "center", bg: "var(--background)", borderBottomWidth: 1, borderColor: "var(--border)", children: [_jsx(Text, { render: _jsx("a", { href: "/", "aria-label": t('{brandName} home', { brandName: brand() }) }), display: "flex", items: "center", shrink: 0, color: "var(--foreground)", textDecorationLine: "none", children: _jsx(HanzoLockup, { label: brand(), size: 22 }) }), back, _jsx(View, { flex: 1 }), signed ? (_jsxs(_Fragment, { children: [_jsxs(XStack, { render: "section", "aria-label": t('Account'), items: "center", gap: 12, minW: 0, "$max-md": { display: 'none' }, children: [_jsx(Avatar, { src: avatar || null, name: who, alt: "" }), _jsxs(YStack, { minW: 0, gap: 2, children: [_jsx(Text, { fontSize: "$3", fontWeight: "500", color: "$ink", numberOfLines: 1, children: who }), name && email ? (_jsx(Text, { fontSize: "$2", color: "$soft", numberOfLines: 1, children: email })) : null] }), _jsx(Button, { variant: "ghost", size: "sm", onClick: other, children: t('Use another account') }), _jsx(Button, { variant: "ghost", size: "sm", onClick: out, children: t('Log out') })] }), _jsx(View, { display: "none", "$max-md": { display: 'flex' }, children: _jsxs(DropdownMenu, { placement: "bottom-end", offset: 8, children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(XStack, { render: "button", "aria-label": t('Account: {name}', { name: who }), width: 40, height: 40, rounded: 999, items: "center", justify: "center", borderWidth: 0, bg: "transparent", hoverStyle: { bg: '$hover' }, focusVisibleStyle: { outlineWidth: 2, outlineStyle: 'solid', outlineColor: '$outlineColor' }, cursor: "pointer", children: _jsx(Avatar, { src: avatar || null, name: who, alt: "" }) }) }), _jsxs(DropdownMenuContent, { minW: 240, maxW: "calc(100vw - 16px)", "aria-labelledby": undefined, "aria-label": t('Account'), children: [_jsx(DropdownMenuLabel, { children: _jsxs(YStack, { minW: 0, gap: 2, children: [_jsx(Text, { fontSize: "$3", fontWeight: "500", color: "$ink", numberOfLines: 1, children: who }), name && email ? (_jsx(Text, { fontSize: "$2", color: "$soft", numberOfLines: 1, children: email })) : null] }) }), _jsx(DropdownMenuSeparator, {}), _jsx(DropdownMenuItem, { onSelect: other, children: _jsx(Text, { fontSize: "$3", color: "$ink", children: t('Use another account') }) }), _jsx(DropdownMenuItem, { onSelect: out, children: _jsx(Text, { fontSize: "$3", color: "$ink", children: t('Log out') }) })] })] }) })] })) : null] }));
|
|
31
33
|
}
|
package/dist/Column.js
CHANGED
|
@@ -20,6 +20,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
20
20
|
// Its width is the edge's in the gutter to its right: dragged, keyed or
|
|
21
21
|
// double-clicked back (@hanzo/build's `Grip`), kept per browser. Pulled in past
|
|
22
22
|
// its floor it shuts (`onShut`), as the sidebar's toggle shuts it.
|
|
23
|
+
import { useI18n } from '@hanzo/i18n/react';
|
|
23
24
|
import { Text, XStack, YStack } from '@hanzo/gui';
|
|
24
25
|
import { ArrowLeft, Bell, Search, Settings2, SquarePen } from 'lucide-react';
|
|
25
26
|
import { DOTS, Gutter, library, NEW, own, path as written, sections, tabs, useScheduled, useSessions } from '@hanzo/build';
|
|
@@ -74,6 +75,7 @@ const TITLE = { library: 'Library', scheduled: 'Scheduled tasks', customize: 'Cu
|
|
|
74
75
|
/** A header mark: a 16px glyph in a 32px target. */
|
|
75
76
|
const HEAD = { width: 32, height: 32, rounded: '$3' };
|
|
76
77
|
function Activity({ host }) {
|
|
78
|
+
const { t } = useI18n();
|
|
77
79
|
const store = useSessions(host);
|
|
78
80
|
const latest = [...store.value].sort((a, b) => (a.updatedAt < b.updatedAt ? 1 : -1)).slice(0, RECENT);
|
|
79
81
|
const items = [
|
|
@@ -90,9 +92,10 @@ function Activity({ host }) {
|
|
|
90
92
|
{ type: 'separator', key: 'line' },
|
|
91
93
|
{ key: 'settings', label: 'Notification settings', icon: _jsx(Settings2, { size: 16, "aria-hidden": true }), onSelect: () => host.go(written({ kind: 'settings', section: 'notifications' })) },
|
|
92
94
|
];
|
|
93
|
-
return (_jsx(DropdownMenu, { minWidth: 280, trigger: _jsx(XStack, { render: "button", "aria-label": "Activity", ...{ title: 'Activity' }, ...HEAD, items: "center", justify: "center", cursor: "pointer", opacity: 0.7, hoverStyle: { bg: '$hover', opacity: 1 }, focusVisibleStyle: { outlineWidth: 2, outlineStyle: 'solid', outlineColor: '$outlineColor' }, children: _jsx(Bell, { size: 16, "aria-hidden": true }) }), items: items }));
|
|
95
|
+
return (_jsx(DropdownMenu, { minWidth: 280, trigger: _jsx(XStack, { render: "button", "aria-label": t("Activity"), ...{ title: 'Activity' }, ...HEAD, items: "center", justify: "center", cursor: "pointer", opacity: 0.7, hoverStyle: { bg: '$hover', opacity: 1 }, focusVisibleStyle: { outlineWidth: 2, outlineStyle: 'solid', outlineColor: '$outlineColor' }, children: _jsx(Bell, { size: 16, "aria-hidden": true }) }), items: items }));
|
|
94
96
|
}
|
|
95
97
|
export function Column({ host, mode, thread, go, onShut, onSeek, onBack, drawer = false, foot, places, }) {
|
|
98
|
+
const { t } = useI18n();
|
|
96
99
|
// The desktop sidebar and a phone's drawer can both be in the page: each names its own group labels.
|
|
97
100
|
const uid = useId();
|
|
98
101
|
const span = useSpan(SPAN, WIDTH, FLOOR, CEIL);
|
|
@@ -118,5 +121,5 @@ export function Column({ host, mode, thread, go, onShut, onSeek, onBack, drawer
|
|
|
118
121
|
};
|
|
119
122
|
return (_jsxs(YStack, { "data-slot": "app-column", ref: drawer ? undefined : span.ref,
|
|
120
123
|
// inline-style: `--span`, the width a drag rewrites on the element itself (span.ts); no prop sets a variable.
|
|
121
|
-
style: drawer ? undefined : span.style, position: "relative", flex: drawer ? 1 : undefined, shrink: drawer ? 1 : 0, minW: 0, height: "100%", width: drawer ? undefined : 'var(--span)', children: [_jsxs(Sidebar, { "data-slot": "app-rail", role: "navigation", "aria-label": brand(), ...(drawer ? { ...pane(), ...FLOAT } : { bg: 'transparent', borderWidth: 0 }), ...FILL, px: "$2", pt: drawer ? '$2' : 0, pb: "$2", gap: "$0", children: [at ? (_jsxs(XStack, { "data-slot": "app-head", items: "center", gap: "$1", pl: 2, pr: 2, height: 52, shrink: 0, minW: 0, children: [_jsx(SidebarIconButton, { label: "Back", "data-slot": "place-back", onPress: onBack, ...HEAD, children: _jsx(ArrowLeft, { size: 16, "aria-hidden": true }) }), _jsx(Text, { flex: 1, minW: 0, fontSize: 18, lineHeight: 24, fontWeight: "600", color: "$ink", numberOfLines: 1, "data-slot": "place-name", children: TITLE[at] })] })) : (_jsxs(XStack, { "data-slot": "app-head", items: "center", gap: "$1", pl: "$2", pr: 2, height: 52, shrink: 0, minW: 0, children: [_jsx(Text, { flex: 1, minW: 0, fontSize: 18, lineHeight: 24, fontWeight: "600", color: "$ink", numberOfLines: 1, children: wordmark(mode) }), _jsx(Activity, { host: host }), _jsx(SidebarIconButton, { label: "Search", onPress: onSeek, "aria-haspopup": "dialog", ...HEAD, children: _jsx(Search, { size: 16, "aria-hidden": true }) })] })), mode === 'chat' ? (_jsx(YStack, { role: "list", gap: 2, shrink: 0, children: _jsx(YStack, { role: "listitem", children: _jsx(SidebarItem, { "data-slot": "app-new", ...ROW, active: !thread, bg: "transparent", hoverStyle: { bg: '$hover' }, icon: _jsx(SquarePen, { size: 16, "aria-hidden": true }), onPress: create, ...keys(create), children: _jsx(Text, { fontSize: "$3", color: "$ink", children: "New chat" }) }) }) })) : (_jsx(YStack, { "data-slot": "app-places", shrink: 0, children: groups.map((g, i) => (_jsxs(YStack, { pt: i ? '$2' : 0, children: [i && g.label ? (_jsx(Text, { id: `${uid}-${g.id}`, pl: "$2", pb: 2, fontSize: "$3", color: "$soft", children: g.label })) : null, _jsx(YStack, { role: "list", "aria-labelledby": i && g.label ? `${uid}-${g.id}` : undefined, "aria-label": i && g.label ? undefined : g.label || (at ? TITLE[at] : g.id), gap: 1, children: g.links.map((link) => (_jsx(YStack, { role: "listitem", children: _jsx(SidebarItem, { ...ROW, ...(link.id === NEW ? { 'data-slot': 'app-new' } : {}), active: link.active, icon: link.id === NEW ? _jsx(SquarePen, { size: 16, "aria-hidden": true }) : link.icon, onPress: link.onPress, ...keys(() => link.onPress?.()), children: _jsx(Text, { fontSize: "$3", color: link.active ? '$ink' : '$quiet', children: link.label }) }) }, link.id))) })] }, g.id))) })), mode === 'studio' || mode === 'tabs' || at ? null : _jsx(Recents, { host: host, mode: mode, thread: thread, onOpen: open, onNew: create }), at ? null : (_jsxs(YStack, { gap: "$2", pt: "$2", shrink: 0, children: [_jsx(Made, {}), _jsx(Setup, {})] })), foot] }), drawer ? null : (_jsx(Gutter, { side: "left", span: span.span, floor: FLOOR, ceil: CEIL, reset: WIDTH, onSpan: span.move, onKeep: span.keep, onShut: onShut, label: "Resize sidebar" }))] }));
|
|
124
|
+
style: drawer ? undefined : span.style, position: "relative", flex: drawer ? 1 : undefined, shrink: drawer ? 1 : 0, minW: 0, height: "100%", width: drawer ? undefined : 'var(--span)', children: [_jsxs(Sidebar, { "data-slot": "app-rail", role: "navigation", "aria-label": brand(), ...(drawer ? { ...pane(), ...FLOAT } : { bg: 'transparent', borderWidth: 0 }), ...FILL, px: "$2", pt: drawer ? '$2' : 0, pb: "$2", gap: "$0", children: [at ? (_jsxs(XStack, { "data-slot": "app-head", items: "center", gap: "$1", pl: 2, pr: 2, height: 52, shrink: 0, minW: 0, children: [_jsx(SidebarIconButton, { label: t("Back"), "data-slot": "place-back", onPress: onBack, ...HEAD, children: _jsx(ArrowLeft, { size: 16, "aria-hidden": true }) }), _jsx(Text, { flex: 1, minW: 0, fontSize: 18, lineHeight: 24, fontWeight: "600", color: "$ink", numberOfLines: 1, "data-slot": "place-name", children: TITLE[at] })] })) : (_jsxs(XStack, { "data-slot": "app-head", items: "center", gap: "$1", pl: "$2", pr: 2, height: 52, shrink: 0, minW: 0, children: [_jsx(Text, { flex: 1, minW: 0, fontSize: 18, lineHeight: 24, fontWeight: "600", color: "$ink", numberOfLines: 1, children: wordmark(mode) }), _jsx(Activity, { host: host }), _jsx(SidebarIconButton, { label: t("Search"), onPress: onSeek, "aria-haspopup": "dialog", ...HEAD, children: _jsx(Search, { size: 16, "aria-hidden": true }) })] })), mode === 'chat' ? (_jsx(YStack, { role: "list", gap: 2, shrink: 0, children: _jsx(YStack, { role: "listitem", children: _jsx(SidebarItem, { "data-slot": "app-new", ...ROW, active: !thread, bg: "transparent", hoverStyle: { bg: '$hover' }, icon: _jsx(SquarePen, { size: 16, "aria-hidden": true }), onPress: create, ...keys(create), children: _jsx(Text, { fontSize: "$3", color: "$ink", children: t("New chat") }) }) }) })) : (_jsx(YStack, { "data-slot": "app-places", shrink: 0, children: groups.map((g, i) => (_jsxs(YStack, { pt: i ? '$2' : 0, children: [i && g.label ? (_jsx(Text, { id: `${uid}-${g.id}`, pl: "$2", pb: 2, fontSize: "$3", color: "$soft", children: g.label })) : null, _jsx(YStack, { role: "list", "aria-labelledby": i && g.label ? `${uid}-${g.id}` : undefined, "aria-label": i && g.label ? undefined : g.label || (at ? TITLE[at] : g.id), gap: 1, children: g.links.map((link) => (_jsx(YStack, { role: "listitem", children: _jsx(SidebarItem, { ...ROW, ...(link.id === NEW ? { 'data-slot': 'app-new' } : {}), active: link.active, icon: link.id === NEW ? _jsx(SquarePen, { size: 16, "aria-hidden": true }) : link.icon, onPress: link.onPress, ...keys(() => link.onPress?.()), children: _jsx(Text, { fontSize: "$3", color: link.active ? '$ink' : '$quiet', children: link.label }) }) }, link.id))) })] }, g.id))) })), mode === 'studio' || mode === 'tabs' || at ? null : _jsx(Recents, { host: host, mode: mode, thread: thread, onOpen: open, onNew: create }), at ? null : (_jsxs(YStack, { gap: "$2", pt: "$2", shrink: 0, children: [_jsx(Made, {}), _jsx(Setup, {})] })), foot] }), drawer ? null : (_jsx(Gutter, { side: "left", span: span.span, floor: FLOOR, ceil: CEIL, reset: WIDTH, onSpan: span.move, onKeep: span.keep, onShut: onShut, label: t("Resize sidebar") }))] }));
|
|
122
125
|
}
|
package/dist/Me.js
CHANGED
|
@@ -28,6 +28,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
28
28
|
// the plan from billing and the limits. An organization is created by the same
|
|
29
29
|
// `POST /v1/account/orgs` the no-organization gate uses (Orgs.tsx); the next load
|
|
30
30
|
// mints a token that carries it.
|
|
31
|
+
import { useI18n } from '@hanzo/i18n/react';
|
|
31
32
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
32
33
|
import { SizableText, XStack, YStack } from '@hanzo/gui';
|
|
33
34
|
import { ArrowUpRight, Building2, Check, ChevronsUpDown, CircleHelp, CircleUserRound, Download, FileText, FlaskConical, Info, Keyboard, LifeBuoy, LogOut, Palette, Plus, ScrollText, Settings, ShieldCheck, Sparkles, Users, X, } from 'lucide-react';
|
|
@@ -135,6 +136,7 @@ export const upgrade = (family) => family === 'Enterprise' ? 'Enterprise plan' :
|
|
|
135
136
|
/** The keys a row prints, in this platform's spelling. */
|
|
136
137
|
const mac = () => typeof navigator !== 'undefined' && /Mac|iPhone|iPad/.test(navigator.platform || navigator.userAgent);
|
|
137
138
|
export function Me({ host, narrow = false, above = false, onOrg, }) {
|
|
139
|
+
const { t } = useI18n();
|
|
138
140
|
const [open, setOpen] = useState(false);
|
|
139
141
|
const [account, setAccount] = useState(false);
|
|
140
142
|
const [help, setHelp] = useState(false);
|
|
@@ -197,11 +199,12 @@ export function Me({ host, narrow = false, above = false, onOrg, }) {
|
|
|
197
199
|
// Clear of the rail's mark (or the row) it opens from, and flush with its foot.
|
|
198
200
|
offset: 8, children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(XStack, { ref: (el) => {
|
|
199
201
|
row.current = el instanceof HTMLElement ? el : null;
|
|
200
|
-
}, render: "button", "data-slot": "me", "aria-label": `Account: ${[who, current].filter(Boolean).join(' · ')}`, ...{ title: who }, width: narrow ? 36 : '100%', minH: narrow ? 36 : 48, px: narrow ? 0 : '$2', gap: "$2.5", rounded: narrow ? 999 : '$5', items: "center", justify: narrow ? 'center' : 'flex-start', hoverStyle: { bg: '$hover' }, focusVisibleStyle: { outlineWidth: 2, outlineStyle: 'solid', outlineColor: '$outlineColor' }, cursor: "pointer", children: [_jsx(Face, { host: host, size: narrow ? 26 : 32 }), narrow ? null : (_jsxs(_Fragment, { children: [_jsxs(YStack, { flex: 1, minW: 0, children: [_jsx(SizableText, { size: "$3", fontWeight: "600", color: "$ink", numberOfLines: 1, text: "left", children: who }), working ? (_jsx(SizableText, { size: "$1", color: "$soft", numberOfLines: 1, text: "left", children: working })) : null] }), _jsx(ChevronsUpDown, { size: 14, "aria-hidden": true, opacity: 0.6 })] }))] }) }), _jsxs(DropdownMenuContent, { minW: 260, maxW: "calc(100vw - 16px)", "aria-labelledby": undefined, "aria-label": "Account", onCloseAutoFocus: back, children: [_jsxs(DropdownMenuSub, { open: account, onOpenChange: setAccount, children: [_jsxs(DropdownMenuSubTrigger, { minH: 48, gap: "$2.5", children: [_jsx(Face, { host: host, size: 28 }), _jsxs(YStack, { flex: 1, minW: 0, children: [_jsx(SizableText, { size: "$2", fontWeight: "600", color: "$ink", numberOfLines: 1, children: who }), _jsx(SizableText, { size: "$1", color: "$soft", numberOfLines: 1, children: host.person.email || working })] })] }), account ? (_jsxs(DropdownMenuSubContent, { minW: 260, maxW: "calc(100vw - 16px)", pointerEvents: "auto", children: [_jsx(Meter, { read: read, onPlans: () => leave('here'), onBilling: () => (setOpen(false), showSettings('billing')) }), _jsx(DropdownMenuSeparator, {}), _jsx(DropdownMenuLabel, { children: "Organizations" }), all.map((o) => (_jsx(DropdownMenuItem, { onSelect: () => o.name !== current && onOrg(o.name), children: _jsx(Line, { icon: _jsx(Mark, { org: o }), sub: [o.personal ? 'Personal' : said(o.role), planFor(o)].filter(Boolean).join(' · '), trail: o.name === current ? _jsx(Check, { size: 16, "aria-label": "Current" }) : null, children: o.display }) }, o.name))), _jsx(DropdownMenuItem, { onSelect: () => setCreating(true), children: _jsx(Line, { icon: _jsx(Plus, { size: 16, "aria-hidden": true }), children: "Create organization" }) }), _jsx(DropdownMenuItem, { onSelect: () =>
|
|
202
|
+
}, render: "button", "data-slot": "me", "aria-label": `Account: ${[who, current].filter(Boolean).join(' · ')}`, ...{ title: who }, width: narrow ? 36 : '100%', minH: narrow ? 36 : 48, px: narrow ? 0 : '$2', gap: "$2.5", rounded: narrow ? 999 : '$5', items: "center", justify: narrow ? 'center' : 'flex-start', hoverStyle: { bg: '$hover' }, focusVisibleStyle: { outlineWidth: 2, outlineStyle: 'solid', outlineColor: '$outlineColor' }, cursor: "pointer", children: [_jsx(Face, { host: host, size: narrow ? 26 : 32 }), narrow ? null : (_jsxs(_Fragment, { children: [_jsxs(YStack, { flex: 1, minW: 0, children: [_jsx(SizableText, { size: "$3", fontWeight: "600", color: "$ink", numberOfLines: 1, text: "left", children: who }), working ? (_jsx(SizableText, { size: "$1", color: "$soft", numberOfLines: 1, text: "left", children: working })) : null] }), _jsx(ChevronsUpDown, { size: 14, "aria-hidden": true, opacity: 0.6 })] }))] }) }), _jsxs(DropdownMenuContent, { minW: 260, maxW: "calc(100vw - 16px)", "aria-labelledby": undefined, "aria-label": t("Account"), onCloseAutoFocus: back, children: [_jsxs(DropdownMenuSub, { open: account, onOpenChange: setAccount, children: [_jsxs(DropdownMenuSubTrigger, { minH: 48, gap: "$2.5", children: [_jsx(Face, { host: host, size: 28 }), _jsxs(YStack, { flex: 1, minW: 0, children: [_jsx(SizableText, { size: "$2", fontWeight: "600", color: "$ink", numberOfLines: 1, children: who }), _jsx(SizableText, { size: "$1", color: "$soft", numberOfLines: 1, children: host.person.email || working })] })] }), account ? (_jsxs(DropdownMenuSubContent, { minW: 260, maxW: "calc(100vw - 16px)", pointerEvents: "auto", children: [_jsx(Meter, { read: read, onPlans: () => leave('here'), onBilling: () => (setOpen(false), showSettings('billing')) }), _jsx(DropdownMenuSeparator, {}), _jsx(DropdownMenuLabel, { children: t("Organizations") }), all.map((o) => (_jsx(DropdownMenuItem, { onSelect: () => o.name !== current && onOrg(o.name), children: _jsx(Line, { icon: _jsx(Mark, { org: o }), sub: [o.personal ? 'Personal' : said(o.role), planFor(o)].filter(Boolean).join(' · '), trail: o.name === current ? _jsx(Check, { size: 16, "aria-label": t("Current") }) : null, children: o.display }) }, o.name))), _jsx(DropdownMenuItem, { onSelect: () => setCreating(true), children: _jsx(Line, { icon: _jsx(Plus, { size: 16, "aria-hidden": true }), children: t("Create organization") }) }), _jsx(DropdownMenuItem, { onSelect: () => out(site(ORG_SETTINGS)), children: _jsx(Line, { icon: _jsx(Settings, { size: 16, "aria-hidden": true }), children: t("Organization settings") }) }), where().team ? null : here && !here.personal ? (_jsx(DropdownMenuItem, { onSelect: () => out(`${ENTRY}&org=${encodeURIComponent(here.name)}`), children: _jsx(Line, { icon: _jsx(Users, { size: 16, "aria-hidden": true }), trail: _jsx(ArrowUpRight, { size: 14, "aria-hidden": true, opacity: 0.6 }), children: t("Open in Hanzo Team") }) })) : (_jsx(DropdownMenuItem, { onSelect: () => setCreating(true), children: _jsx(Line, { icon: _jsx(Users, { size: 16, "aria-hidden": true }), children: t("Create a team") }) })), superAdmin() ? (_jsx(DropdownMenuItem, { onSelect: choose, children: _jsx(Line, { icon: _jsx(Building2, { size: 16, "aria-hidden": true }), children: t("All organizations") }) })) : null] })) : null] }), team ? (_jsx(DropdownMenuItem, { "data-slot": "me-team", onSelect: () => team.name !== current && onOrg(team.name), children: _jsx(Line, { icon: _jsx(Mark, { org: team }), trail: team.name === current ? _jsx(Check, { size: 16, "aria-label": t("Current") }) : null, children: `${team.display} · ${teamPlan || 'No plan'}` }) })) : null, personal ? (_jsx(DropdownMenuItem, { "data-slot": "me-personal", onSelect: () => personal.name !== current && onOrg(personal.name), actionsVisibility: "always", actions: ownPlan ? undefined : (_jsx(Button, { size: "xs", variant: "outline", onPress: getPro, children: t("Get Pro") })), children: _jsx(Line, { icon: _jsx(CircleUserRound, { size: 16, "aria-hidden": true }), trail: personal.name === current ? _jsx(Check, { size: 16, "aria-label": t("Current") }) : null, children: `Personal · ${ownPlan || 'No plan'}` }) })) : null, _jsx(DropdownMenuSeparator, {}), _jsx(DropdownMenuItem, { onSelect: () => leave('here'), children: _jsx(Line, { icon: _jsx(Sparkles, { size: 16, "aria-hidden": true }), children: upgrade(read.value?.label?.name) }) }), _jsx(DropdownMenuItem, { onSelect: () => showSettings('general'), children: _jsx(Line, { icon: _jsx(Palette, { size: 16, "aria-hidden": true }), children: t("Personalization") }) }), _jsx(DropdownMenuItem, { onSelect: () => showSettings('account'), children: _jsx(Line, { icon: _jsx(CircleUserRound, { size: 16, "aria-hidden": true }), children: t("Profile") }) }), _jsx(DropdownMenuItem, { "aria-keyshortcuts": "Control+, Meta+,", onSelect: () => showSettings('general'), children: _jsx(Line, { icon: _jsx(Settings, { size: 16, "aria-hidden": true }), trail: _jsx(DropdownMenuShortcut, { "aria-hidden": true, children: apple ? '⌘,' : 'Ctrl+,' }), children: t("Settings") }) }), _jsx(DropdownMenuItem, { onSelect: () => window.location.assign(site('/beta/status')), children: _jsx(Line, { icon: _jsx(FlaskConical, { size: 16, "aria-hidden": true }), children: t("Beta") }) }), _jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuSub, { open: help, onOpenChange: setHelp, children: [_jsx(DropdownMenuSubTrigger, { children: _jsx(Line, { icon: _jsx(LifeBuoy, { size: 16, "aria-hidden": true }), children: t("Help") }) }), help ? (_jsxs(DropdownMenuSubContent, { minW: 240, maxW: "calc(100vw - 16px)", pointerEvents: "auto", children: [pages().map((p) => (_jsx(DropdownMenuItem, { onSelect: () => out(p.href), children: _jsx(Line, { icon: ICONS[p.key], trail: p.key === 'help' ? _jsx(ArrowUpRight, { size: 14, "aria-hidden": true, opacity: 0.6 }) : null, children: p.label }) }, p.key))), _jsx(DropdownMenuItem, { "aria-keyshortcuts": "Control+/ Meta+/", onSelect: () => setKeys(true), children: _jsx(Line, { icon: _jsx(Keyboard, { size: 16, "aria-hidden": true }), trail: _jsx(DropdownMenuShortcut, { "aria-hidden": true, children: apple ? '⌘/' : 'Ctrl+/' }), children: t("Keyboard shortcuts") }) }), _jsx(DropdownMenuSeparator, {}), legal().map((p) => (_jsx(DropdownMenuItem, { onSelect: () => out(p.href), children: _jsx(Line, { icon: ICONS[p.key], children: p.label }) }, p.key)))] })) : null] }), host.signOut ? (_jsx(DropdownMenuItem, { onSelect: () => host.signOut?.(), children: _jsx(Line, { icon: _jsx(LogOut, { size: 16, "aria-hidden": true }), children: t("Log out") }) })) : null] })] }), _jsx(Create, { open: creating, onOpenChange: setCreating }), _jsx(Shortcuts, { open: keys, onOpenChange: setKeys, apple: apple })] }));
|
|
201
203
|
}
|
|
202
204
|
/** The app's keys (help.ts `KEYS`), one row each, spelled for this platform. */
|
|
203
205
|
function Shortcuts({ open, onOpenChange, apple }) {
|
|
204
|
-
|
|
206
|
+
const { t } = useI18n();
|
|
207
|
+
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { maxW: 440, rounded: "$6", gap: "$3", children: [_jsx(DialogTitle, { children: t("Keyboard shortcuts") }), _jsx(YStack, { gap: "$1", children: KEYS.map((k) => (_jsxs(XStack, { items: "center", justify: "space-between", gap: "$3", minH: 32, children: [_jsx(SizableText, { size: "$2", color: "$ink", children: k.does }), _jsx(Kbd, { children: apple ? k.mac : k.pc })] }, k.does))) })] }) }));
|
|
205
208
|
}
|
|
206
209
|
/**
|
|
207
210
|
* The sheet that creates an organization: a name, then `POST /v1/account/orgs`,
|
|
@@ -210,6 +213,7 @@ function Shortcuts({ open, onOpenChange, apple }) {
|
|
|
210
213
|
* it yet.
|
|
211
214
|
*/
|
|
212
215
|
function Create({ open, onOpenChange }) {
|
|
216
|
+
const { t } = useI18n();
|
|
213
217
|
const [name, setName] = useState('');
|
|
214
218
|
const [busy, setBusy] = useState(false);
|
|
215
219
|
const [refused, setRefused] = useState('');
|
|
@@ -244,16 +248,17 @@ function Create({ open, onOpenChange }) {
|
|
|
244
248
|
setBusy(false);
|
|
245
249
|
}
|
|
246
250
|
};
|
|
247
|
-
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { maxW: 440, rounded: "$6", gap: "$3", children: [_jsx(DialogTitle, { children: "Create organization" }), _jsx(SizableText, { size: "$2", color: "$soft", children: "You own it. Agents, credits and members belong to it, and you switch to it from this menu." }), _jsx(Input, { autoFocus: true, value: name, onChangeText: setName, "aria-label": "Organization name", placeholder: "Acme", maxLength: 80, onKeyDown: (e) => {
|
|
251
|
+
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { maxW: 440, rounded: "$6", gap: "$3", children: [_jsx(DialogTitle, { children: t("Create organization") }), _jsx(SizableText, { size: "$2", color: "$soft", children: t("You own it. Agents, credits and members belong to it, and you switch to it from this menu.") }), _jsx(Input, { autoFocus: true, value: name, onChangeText: setName, "aria-label": t("Organization name"), placeholder: t("Acme"), maxLength: 80, onKeyDown: (e) => {
|
|
248
252
|
if (e.key === 'Enter')
|
|
249
253
|
void submit();
|
|
250
|
-
} }), refused ? (_jsx(SizableText, { size: "$1", color: BAD, role: "alert", children: refused })) : null, _jsxs(XStack, { gap: "$2", justify: "flex-end", children: [_jsx(Button, { size: "sm", variant: "outline", rounded: 999, onPress: () => onOpenChange(false), children: "Cancel" }), _jsx(Button, { size: "sm", rounded: 999, disabled: busy || !name.trim(), onPress: () => void submit(), children: busy ? 'Creating…' : 'Create' })] })] }) }));
|
|
254
|
+
} }), refused ? (_jsx(SizableText, { size: "$1", color: BAD, role: "alert", children: refused })) : null, _jsxs(XStack, { gap: "$2", justify: "flex-end", children: [_jsx(Button, { size: "sm", variant: "outline", rounded: 999, onPress: () => onOpenChange(false), children: t("Cancel") }), _jsx(Button, { size: "sm", rounded: 999, disabled: busy || !name.trim(), onPress: () => void submit(), children: busy ? 'Creating…' : 'Create' })] })] }) }));
|
|
251
255
|
}
|
|
252
256
|
/**
|
|
253
257
|
* What became of an organization this tab created: nothing to say once the token
|
|
254
258
|
* carries it (the app is already in it), and a plain line while it does not.
|
|
255
259
|
*/
|
|
256
260
|
export function Made() {
|
|
261
|
+
const { t } = useI18n();
|
|
257
262
|
const [made, setMade] = useState(null);
|
|
258
263
|
useEffect(() => {
|
|
259
264
|
try {
|
|
@@ -284,5 +289,5 @@ export function Made() {
|
|
|
284
289
|
}
|
|
285
290
|
setMade(null);
|
|
286
291
|
};
|
|
287
|
-
return (_jsxs(XStack, { "data-slot": "made", role: "status", items: "flex-start", gap: "$2", px: "$3", py: "$2.5", rounded: "$5", borderWidth: 1, borderColor: "$borderColor", bg: "$edge", children: [_jsx(SizableText, { flex: 1, minW: 0, size: "$1", color: "$ink", children: `${made.name} was created, and your sign-in does not include it yet, so you are still in this organization. It appears here once IAM adds you to it.` }), _jsx(XStack, { render: "button", "aria-label": "Dismiss", onPress: dismiss, p: "$1", rounded: 999, hoverStyle: { bg: '$hover' }, children: _jsx(X, { size: 13, "aria-hidden": true }) })] }));
|
|
292
|
+
return (_jsxs(XStack, { "data-slot": "made", role: "status", items: "flex-start", gap: "$2", px: "$3", py: "$2.5", rounded: "$5", borderWidth: 1, borderColor: "$borderColor", bg: "$edge", children: [_jsx(SizableText, { flex: 1, minW: 0, size: "$1", color: "$ink", children: `${made.name} was created, and your sign-in does not include it yet, so you are still in this organization. It appears here once IAM adds you to it.` }), _jsx(XStack, { render: "button", "aria-label": t("Dismiss"), onPress: dismiss, p: "$1", rounded: 999, hoverStyle: { bg: '$hover' }, children: _jsx(X, { size: 13, "aria-hidden": true }) })] }));
|
|
288
293
|
}
|
package/dist/Orgs.js
CHANGED
|
@@ -277,7 +277,7 @@ function useArrival(door, hydrated, isLoading, isAuthenticated) {
|
|
|
277
277
|
const SHEET = 'calc(var(--z-modal) + 10)';
|
|
278
278
|
const DIALOG = 'calc(var(--z-modal) + 20)';
|
|
279
279
|
/** The sheet's top inset: a share of the window's height, held between two lengths. */
|
|
280
|
-
const TOP = 'clamp(
|
|
280
|
+
const TOP = 'clamp(72px, 12vh, 120px)';
|
|
281
281
|
const muted = 'var(--muted-foreground)';
|
|
282
282
|
/** The black that veils and shadows, at a share of itself. */
|
|
283
283
|
const shade = (share) => mix('var(--pure-black)', share, 'srgb');
|
package/dist/Palette.js
CHANGED
|
@@ -79,7 +79,7 @@ export function places(host, go) {
|
|
|
79
79
|
{ id: 'tabs', label: 'Tabs', icon: _jsx(SquareTerminal, { ...ICON }), run: () => go(TERMINALS) },
|
|
80
80
|
...(host ? [{ id: 'team', label: 'Team', icon: _jsx(Users, { ...ICON }), keywords: ['workspace'], run: () => out(WORKSPACE) }] : []),
|
|
81
81
|
...(host ? rows(nav(host, builder).map((g) => ({ links: g.links.filter((l) => l.id !== NEW) })), '', 'dev') : []),
|
|
82
|
-
{ id: 'settings', label: 'Settings', icon: _jsx(Settings, { ...ICON }), shortcut: ['Mod', ','], run: () => go(
|
|
82
|
+
{ id: 'settings', label: 'Settings', icon: _jsx(Settings, { ...ICON }), shortcut: ['Mod', ','], run: () => go('/settings') },
|
|
83
83
|
// The site's own pages, outside the app: the browser loads them.
|
|
84
84
|
{ id: 'referral', label: 'Referral', icon: _jsx(Gift, { ...ICON }), keywords: ['invite', 'share'], run: () => window.location.assign(site('/referral')) },
|
|
85
85
|
{ id: 'beta', label: 'Beta status', icon: _jsx(FlaskConical, { ...ICON }), keywords: ['waitlist'], run: () => window.location.assign(site('/beta/status')) },
|
package/dist/ProviderMark.js
CHANGED
|
@@ -107,7 +107,8 @@ const MARKS = {
|
|
|
107
107
|
// disc (zoo-disc.svg): top green, bottom-left red, bottom-right blue, whose
|
|
108
108
|
// pairwise overlaps are the secondaries the full disc shows. The enclosing
|
|
109
109
|
// ring stays `currentColor` — it is the frame, not one of the three, and a
|
|
110
|
-
// fourth hue there would read as a fourth petal.
|
|
110
|
+
// fourth hue there would read as a fourth petal. Under `ink` the petals'
|
|
111
|
+
// strokes come off (`INKED`) and they take the group's `currentColor`.
|
|
111
112
|
'<defs><clipPath id="zoo-cut"><circle cx="12" cy="12" r="11.5"></circle></clipPath></defs><g clip-path="url(#zoo-cut)" fill="none" stroke="currentColor"><circle cx="12.203" cy="6.27" r="9.509" stroke-width="1.341" stroke="#00A652"></circle><circle cx="6.189" cy="15.454" r="9.509" stroke-width="1.341" stroke="#ED1C24"></circle><circle cx="17.486" cy="15.454" r="9.509" stroke-width="1.341" stroke="#2E3192"></circle><circle cx="12" cy="12" r="10.769" stroke-width="1.463"></circle></g>',
|
|
112
113
|
// THE CHANNELS, keyed by the registry's provider id. These are the brands'
|
|
113
114
|
// own published marks, from simple-icons, so a connect tile is drawn the way
|
|
@@ -169,6 +170,8 @@ const OF = {
|
|
|
169
170
|
'z-ai': 'zai',
|
|
170
171
|
zen: 'zoo',
|
|
171
172
|
};
|
|
173
|
+
/** A body's own stroke hues: under `ink` they come off, so it draws in the page's ink. */
|
|
174
|
+
const INKED = / stroke="#[0-9A-Fa-f]{6}"/g;
|
|
172
175
|
/**
|
|
173
176
|
* The mark's NAME, resolved through the alias table — not its body. The body
|
|
174
177
|
* and the hue are both read under this one key, so a lab cannot end up wearing
|
|
@@ -270,5 +273,5 @@ export function ProviderMark({ org, provider, model, size = 32, ink, }) {
|
|
|
270
273
|
// stroke, so one presentation attribute colours a filled glyph and a
|
|
271
274
|
// drawn one alike. A lab with no hue inherits the page's ink, which is
|
|
272
275
|
// the whole point.
|
|
273
|
-
color: ink ? undefined : HUE[mark], dangerouslySetInnerHTML: { __html: MARKS[mark] } }) }));
|
|
276
|
+
color: ink ? undefined : HUE[mark], dangerouslySetInnerHTML: { __html: ink ? MARKS[mark].replace(INKED, '') : MARKS[mark] } }) }));
|
|
274
277
|
}
|
package/dist/Rail.js
CHANGED
|
@@ -28,6 +28,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
28
28
|
// rail lies along the foot of the drawer (`flat`), the account at its end; the
|
|
29
29
|
// drawer is too narrow for every mark, so Library, Scheduled tasks and
|
|
30
30
|
// Customize lead More there.
|
|
31
|
+
import { useI18n } from '@hanzo/i18n/react';
|
|
31
32
|
import { Clock, Code2, Ellipsis, FlaskConical, Library, MessageSquare, PanelLeft, Puzzle, Sparkles, SquareTerminal, Users, WandSparkles } from 'lucide-react';
|
|
32
33
|
import { useState } from 'react';
|
|
33
34
|
import { XStack, YStack } from '@hanzo/gui';
|
|
@@ -61,7 +62,8 @@ export function raised(at) {
|
|
|
61
62
|
}
|
|
62
63
|
/** One mark: an icon button named for where it goes, its name its tooltip, lit where the reader is. */
|
|
63
64
|
export function Mark({ label, on = false, onPress, children, ...rest }) {
|
|
64
|
-
|
|
65
|
+
const { t } = useI18n();
|
|
66
|
+
return (_jsx(XStack, { render: "button", "aria-label": t(label), ...{ title: label }, "aria-current": on ? 'page' : undefined, onPress: onPress, width: MARK, height: MARK, rounded: "$4", items: "center", justify: "center", shrink: 0, cursor: "pointer", opacity: on ? 1 : 0.62, bg: on ? '$edge' : 'transparent', hoverStyle: { bg: on ? '$edge' : '$hover', opacity: 1 }, pressStyle: { bg: '$raised' }, focusVisibleStyle: { outlineWidth: 2, outlineStyle: 'solid', outlineColor: '$outlineColor' },
|
|
65
67
|
// A thumb's 44px on a touch screen.
|
|
66
68
|
$touchable: { width: 44, height: 44 }, ...rest, children: children }));
|
|
67
69
|
}
|
|
@@ -70,6 +72,7 @@ export function Toggle({ open, onPress, label }) {
|
|
|
70
72
|
return (_jsx(Mark, { label: label ?? (open ? 'Close sidebar' : 'Open sidebar'), "data-slot": "sidebar-toggle", "aria-expanded": open, onPress: onPress, children: _jsx(PanelLeft, { size: 18, "aria-hidden": true }) }));
|
|
71
73
|
}
|
|
72
74
|
export function Rail({ host, mode, at, open, toggle, flat = false, onChat, onDev, onStudio, onTabs, go, onOrg, }) {
|
|
75
|
+
const { t } = useI18n();
|
|
73
76
|
const here = mode === 'dev' ? spot(at) : null;
|
|
74
77
|
const to = (p) => () => go(app(p));
|
|
75
78
|
// The products on hosts of their own, behind More; on a phone the raised places lead it.
|
|
@@ -98,5 +101,5 @@ export function Rail({ host, mode, at, open, toggle, flat = false, onChat, onDev
|
|
|
98
101
|
: []),
|
|
99
102
|
];
|
|
100
103
|
const Stack = flat ? XStack : YStack;
|
|
101
|
-
return (_jsxs(Stack, { "data-slot": "rail", render: "nav", "aria-label": "Products", ...(flat ? { pt: '$2', mt: '$2', borderTopWidth: 1, borderColor: '$borderColor', gap: '$1' } : { width: RAIL, flex: 1, pt: '$2', pb: '$2', gap: 8 }), shrink: 0, items: "center", children: [toggle ? (_jsx(YStack, { pb: 8, children: _jsx(Toggle, { open: open, onPress: toggle }) })) : null, _jsx(Mark, { label: "Chat", on: mode === 'chat', onPress: onChat, "data-slot": "rail-chat", children: _jsx(MessageSquare, { size: 18, "aria-hidden": true }) }), _jsx(Mark, { label: "Dev", on: mode === 'dev' && !here, onPress: onDev, "data-slot": "rail-dev", children: _jsx(Code2, { size: 18, "aria-hidden": true }) }), _jsx(Mark, { label: "Studio", on: mode === 'studio', onPress: onStudio, "data-slot": "rail-studio", children: _jsx(WandSparkles, { size: 18, "aria-hidden": true }) }), _jsx(Mark, { label: "Tabs", on: mode === 'tabs', onPress: onTabs, "data-slot": "rail-tabs", children: _jsx(SquareTerminal, { size: 18, "aria-hidden": true }) }), flat ? null : (_jsxs(_Fragment, { children: [_jsx(Mark, { label: "Library", on: here === 'library', onPress: to(RAISED.library), "data-slot": "rail-library", children: _jsx(Library, { size: 18, "aria-hidden": true }) }), _jsx(Mark, { label: "Scheduled tasks", on: here === 'scheduled', onPress: to(RAISED.scheduled), "data-slot": "rail-scheduled", children: _jsx(Clock, { size: 18, "aria-hidden": true }) }), _jsx(Mark, { label: "Customize", on: here === 'customize', onPress: to(RAISED.customize), "data-slot": "rail-customize", children: _jsx(Puzzle, { size: 18, "aria-hidden": true }) })] })), _jsx(BetasDialog, { open: betas, onOpenChange: setBetas, go: out }), rest.length ? (_jsx(DropdownMenu, { placement: flat ? 'top-start' : 'right-start', minWidth: 220, trigger: _jsx(XStack, { render: "button", "aria-label": "More", ...{ title: 'More' }, "data-slot": "rail-more", width: MARK, height: MARK, rounded: "$4", items: "center", justify: "center", cursor: "pointer", opacity: 0.62, hoverStyle: { bg: '$hover', opacity: 1 }, pressStyle: { bg: '$raised' }, focusVisibleStyle: { outlineWidth: 2, outlineStyle: 'solid', outlineColor: '$outlineColor' }, $touchable: { width: 44, height: 44 }, children: _jsx(Ellipsis, { size: 18, "aria-hidden": true }) }), items: rest })) : null, _jsx(YStack, { flex: 1 }), _jsx(Me, { host: host, narrow: true, above: flat, onOrg: onOrg })] }));
|
|
104
|
+
return (_jsxs(Stack, { "data-slot": "rail", render: "nav", "aria-label": t("Products"), ...(flat ? { pt: '$2', mt: '$2', borderTopWidth: 1, borderColor: '$borderColor', gap: '$1' } : { width: RAIL, flex: 1, pt: '$2', pb: '$2', gap: 8 }), shrink: 0, items: "center", children: [toggle ? (_jsx(YStack, { pb: 8, children: _jsx(Toggle, { open: open, onPress: toggle }) })) : null, _jsx(Mark, { label: t("Chat"), on: mode === 'chat', onPress: onChat, "data-slot": "rail-chat", children: _jsx(MessageSquare, { size: 18, "aria-hidden": true }) }), _jsx(Mark, { label: t("Dev"), on: mode === 'dev' && !here, onPress: onDev, "data-slot": "rail-dev", children: _jsx(Code2, { size: 18, "aria-hidden": true }) }), _jsx(Mark, { label: t("Studio"), on: mode === 'studio', onPress: onStudio, "data-slot": "rail-studio", children: _jsx(WandSparkles, { size: 18, "aria-hidden": true }) }), _jsx(Mark, { label: t("Tabs"), on: mode === 'tabs', onPress: onTabs, "data-slot": "rail-tabs", children: _jsx(SquareTerminal, { size: 18, "aria-hidden": true }) }), flat ? null : (_jsxs(_Fragment, { children: [_jsx(Mark, { label: t("Library"), on: here === 'library', onPress: to(RAISED.library), "data-slot": "rail-library", children: _jsx(Library, { size: 18, "aria-hidden": true }) }), _jsx(Mark, { label: t("Scheduled tasks"), on: here === 'scheduled', onPress: to(RAISED.scheduled), "data-slot": "rail-scheduled", children: _jsx(Clock, { size: 18, "aria-hidden": true }) }), _jsx(Mark, { label: t("Customize"), on: here === 'customize', onPress: to(RAISED.customize), "data-slot": "rail-customize", children: _jsx(Puzzle, { size: 18, "aria-hidden": true }) })] })), _jsx(BetasDialog, { open: betas, onOpenChange: setBetas, go: out }), rest.length ? (_jsx(DropdownMenu, { placement: flat ? 'top-start' : 'right-start', minWidth: 220, trigger: _jsx(XStack, { render: "button", "aria-label": t("More"), ...{ title: 'More' }, "data-slot": "rail-more", width: MARK, height: MARK, rounded: "$4", items: "center", justify: "center", cursor: "pointer", opacity: 0.62, hoverStyle: { bg: '$hover', opacity: 1 }, pressStyle: { bg: '$raised' }, focusVisibleStyle: { outlineWidth: 2, outlineStyle: 'solid', outlineColor: '$outlineColor' }, $touchable: { width: 44, height: 44 }, children: _jsx(Ellipsis, { size: 18, "aria-hidden": true }) }), items: rest })) : null, _jsx(YStack, { flex: 1 }), _jsx(Me, { host: host, narrow: true, above: flat, onOrg: onOrg })] }));
|
|
102
105
|
}
|
package/dist/Settings.js
CHANGED
|
@@ -23,6 +23,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
23
23
|
//
|
|
24
24
|
// The panes that are not yet built say so in one sentence rather than pretending
|
|
25
25
|
// to a screen. An honest empty pane is a smaller lie than an invented one.
|
|
26
|
+
import { useI18n } from '@hanzo/i18n/react';
|
|
26
27
|
import { app, dev } from './lib/host.js';
|
|
27
28
|
import { ArrowLeft, ArrowUpRight } from 'lucide-react';
|
|
28
29
|
import { Text } from '@hanzo/ui';
|
|
@@ -104,11 +105,12 @@ export const FIRST = ROWS[0].id;
|
|
|
104
105
|
* the address never moves.
|
|
105
106
|
*/
|
|
106
107
|
export function SettingsList({ active, filter, onPick, onBack, }) {
|
|
108
|
+
const { t } = useI18n();
|
|
107
109
|
const q = filter.trim().toLowerCase();
|
|
108
110
|
const groups = q
|
|
109
111
|
? GROUPS.map((g) => ({ ...g, rows: g.rows.filter((r) => r.label.toLowerCase().includes(q)) })).filter((g) => g.rows.length > 0)
|
|
110
112
|
: GROUPS;
|
|
111
|
-
return (_jsxs(_Fragment, { children: [_jsx(SidebarItem, { icon: _jsx(ArrowLeft, { size: 15, "aria-hidden": true }), onPress: onBack, children: "Workspace" }), q && groups.length === 0 ? (_jsxs(Text, { px: "$3", py: "$2", fontSize: "$2", color: "$quiet", children: ["No matches for
|
|
113
|
+
return (_jsxs(_Fragment, { children: [_jsx(SidebarItem, { icon: _jsx(ArrowLeft, { size: 15, "aria-hidden": true }), onPress: onBack, children: t("Workspace") }), q && groups.length === 0 ? (_jsxs(Text, { px: "$3", py: "$2", fontSize: "$2", color: "$quiet", children: [t("No matches for “"), filter.trim(), "\u201D."] })) : null, groups.map((g) => (_jsx(SidebarSection, { label: g.label, children: g.rows.map((r) => (_jsx(SidebarItem, {
|
|
112
114
|
// The arrow says this row LEAVES. Without it a row that navigates
|
|
113
115
|
// away and one that draws a pane in place look identical until
|
|
114
116
|
// pressed.
|
package/dist/Top.js
CHANGED
|
@@ -16,6 +16,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
16
16
|
// was last). In Studio neither is lit. New is Chat's New chat, and elsewhere the
|
|
17
17
|
// mode's own New row (@hanzo/build's and @hanzo/studio's `nav`, `NEW`), so the
|
|
18
18
|
// bar names it as the sidebar does.
|
|
19
|
+
import { useI18n } from '@hanzo/i18n/react';
|
|
19
20
|
import { SquarePen } from 'lucide-react';
|
|
20
21
|
import { Text, XStack } from '@hanzo/gui';
|
|
21
22
|
import { nav, NEW } from '@hanzo/build';
|
|
@@ -33,7 +34,8 @@ const SEGMENT = 73;
|
|
|
33
34
|
const GLASS = { ...pane(), borderColor: 'var(--border-strong)', borderRadius: 9999 };
|
|
34
35
|
/** A round glass button named for what it does. */
|
|
35
36
|
function Round({ label, onPress, children, ...rest }) {
|
|
36
|
-
|
|
37
|
+
const { t } = useI18n();
|
|
38
|
+
return (_jsx(XStack, { render: "button", "aria-label": t(label), onPress: onPress, ...GLASS, width: ROUND, height: ROUND, items: "center", justify: "center", shrink: 0, cursor: "pointer", pressStyle: { bg: '$raised' }, focusVisibleStyle: { outlineWidth: 2, outlineStyle: 'solid', outlineColor: '$outlineColor' }, ...rest, children: children }));
|
|
37
39
|
}
|
|
38
40
|
/** The drawer's mark: two strokes, the lower one shorter, 19×11 as the reference draws it. */
|
|
39
41
|
function Lines() {
|
|
@@ -44,6 +46,7 @@ function Side({ value, children }) {
|
|
|
44
46
|
return (_jsx(ToggleGroupItem, { value: value, size: "lg", width: SEGMENT, rounded: 9999, bg: "transparent", borderColor: "transparent", hoverStyle: { bg: '$hover' }, activeStyle: { bg: '$raised', borderColor: 'transparent' }, children: _jsx(Text, { fontSize: "$4", fontWeight: "500", color: "$ink", numberOfLines: 1, children: children }) }));
|
|
45
47
|
}
|
|
46
48
|
export function Top({ host, mode, places, go, onMenu, onChat, onDev, }) {
|
|
49
|
+
const { t } = useI18n();
|
|
47
50
|
const made = mode === 'chat' ? null : (places ?? nav(host, (p) => host.go(p))).flatMap((g) => g.links).find((l) => l.id === NEW);
|
|
48
51
|
const fresh = mode === 'chat'
|
|
49
52
|
? {
|
|
@@ -54,5 +57,5 @@ export function Top({ host, mode, places, go, onMenu, onChat, onDev, }) {
|
|
|
54
57
|
},
|
|
55
58
|
}
|
|
56
59
|
: { label: made?.label ?? 'New', onPress: made?.onPress };
|
|
57
|
-
return (_jsxs(XStack, { "data-slot": "app-bar", height: `calc(${LINE}px + var(--safe-top))`, pt: "var(--safe-top)", px: "$4.5", items: "center", justify: "space-between", shrink: 0, $md: { display: 'none' }, children: [_jsx(Round, { label: "Open sidebar", "data-slot": "bar-menu", onPress: onMenu, children: _jsx(Lines, {}) }), _jsx(XStack, { "data-slot": "bar-mode", ...GLASS, height: LINE, p: "$1", items: "center", children: _jsxs(ToggleGroup, { type: "single", "aria-label": "App", value: mode === 'chat' || mode === 'dev' ? mode : '', disableDeactivation: true, onValueChange: (v) => (v === 'chat' ? onChat() : v === 'dev' ? onDev() : undefined), children: [_jsx(Side, { value: "chat", children: "Chat" }), _jsx(Side, { value: "dev", children: "Dev" })] }) }), _jsx(Round, { label: fresh.label, "data-slot": "bar-new", onPress: fresh.onPress, children: _jsx(SquarePen, { size: 20, "aria-hidden": true }) })] }));
|
|
60
|
+
return (_jsxs(XStack, { "data-slot": "app-bar", height: `calc(${LINE}px + var(--safe-top))`, pt: "var(--safe-top)", px: "$4.5", items: "center", justify: "space-between", shrink: 0, $md: { display: 'none' }, children: [_jsx(Round, { label: t("Open sidebar"), "data-slot": "bar-menu", onPress: onMenu, children: _jsx(Lines, {}) }), _jsx(XStack, { "data-slot": "bar-mode", ...GLASS, height: LINE, p: "$1", items: "center", children: _jsxs(ToggleGroup, { type: "single", "aria-label": t("App"), value: mode === 'chat' || mode === 'dev' ? mode : '', disableDeactivation: true, onValueChange: (v) => (v === 'chat' ? onChat() : v === 'dev' ? onDev() : undefined), children: [_jsx(Side, { value: "chat", children: t("Chat") }), _jsx(Side, { value: "dev", children: t("Dev") })] }) }), _jsx(Round, { label: fresh.label, "data-slot": "bar-new", onPress: fresh.onPress, children: _jsx(SquarePen, { size: 20, "aria-hidden": true }) })] }));
|
|
58
61
|
}
|
package/dist/dev.js
CHANGED
|
@@ -44,7 +44,7 @@ import { showSettings, useOpen } from './open.js';
|
|
|
44
44
|
const LINKS = {
|
|
45
45
|
github: app('-/sync'),
|
|
46
46
|
customize: app('-/customize'),
|
|
47
|
-
settings:
|
|
47
|
+
settings: '/settings',
|
|
48
48
|
home: '/',
|
|
49
49
|
};
|
|
50
50
|
/** The builder's address for the plans. */
|
|
@@ -80,15 +80,23 @@ export function useDevHost() {
|
|
|
80
80
|
const here = path(route(at(pathname)));
|
|
81
81
|
const look = useLook();
|
|
82
82
|
const { settings } = useOpen();
|
|
83
|
-
//
|
|
84
|
-
// one it does not have opens Settings itself.
|
|
83
|
+
// Settings is top-level /settings; organization settings are at /settings/organization.
|
|
85
84
|
useEffect(() => {
|
|
86
85
|
if (!settings)
|
|
87
86
|
return;
|
|
88
87
|
showSettings(null);
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
88
|
+
const s = settings.toLowerCase();
|
|
89
|
+
if (s.includes('member'))
|
|
90
|
+
router.push('/settings/organization/members');
|
|
91
|
+
else if (s.includes('billing'))
|
|
92
|
+
router.push('/settings/organization/billing');
|
|
93
|
+
else if (s.includes('usage'))
|
|
94
|
+
router.push('/settings/organization/usage');
|
|
95
|
+
else if (s.includes('org'))
|
|
96
|
+
router.push('/settings/organization');
|
|
97
|
+
else
|
|
98
|
+
router.push('/settings');
|
|
99
|
+
}, [settings, router]);
|
|
92
100
|
// The token's claims change only when the token does: a sign-in, a refresh.
|
|
93
101
|
const memberships = useMemo(() => orgs(), [accessToken]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
94
102
|
const scoped = useMemo(() => org(), [accessToken]); // eslint-disable-line react-hooks/exhaustive-deps
|
package/dist/index.d.ts
CHANGED
|
@@ -12,4 +12,3 @@ export { configure, where, site, brand } from './where.js';
|
|
|
12
12
|
export type { Addresses } from './where.js';
|
|
13
13
|
export { Rooms, Look, useRooms, useLook, useOrg } from './host.js';
|
|
14
14
|
export type { Router, Link, RoomsProps } from './host.js';
|
|
15
|
-
export { Chrome } from './Chrome.js';
|
package/dist/index.js
CHANGED
|
@@ -10,5 +10,3 @@
|
|
|
10
10
|
*/
|
|
11
11
|
export { configure, where, site, brand } from './where.js';
|
|
12
12
|
export { Rooms, Look, useRooms, useLook, useOrg } from './host.js';
|
|
13
|
-
// The bar over a focused page (the chooser, the way in, the way to pay): one for every host.
|
|
14
|
-
export { Chrome } from './Chrome.js';
|
package/dist/lib/signout.d.ts
CHANGED
|
@@ -10,5 +10,10 @@ export declare const OUT = "/login?from=logout";
|
|
|
10
10
|
* IAM's /v1/iam routes, IAM's session here ended, the local session cleared —
|
|
11
11
|
* counted as logout_completed, and then /login?from=logout, where the person can
|
|
12
12
|
* sign straight back in. IAM's own logout page is never visited.
|
|
13
|
+
*
|
|
14
|
+
* EVERY HANZO SITE AT ONCE (`everywhere`). hanzo.ai and platform.hanzo.ai sign in
|
|
15
|
+
* from one session, so leaving one of them must leave the other: IAM retires the
|
|
16
|
+
* person's grant to every application of this client's organization along with
|
|
17
|
+
* the session. A third-party app the person signed into keeps its own.
|
|
13
18
|
*/
|
|
14
19
|
export declare function useSignOut(): () => Promise<void>;
|
package/dist/lib/signout.js
CHANGED
|
@@ -28,7 +28,13 @@ export const OUT = '/login?from=logout';
|
|
|
28
28
|
* IAM's /v1/iam routes, IAM's session here ended, the local session cleared —
|
|
29
29
|
* counted as logout_completed, and then /login?from=logout, where the person can
|
|
30
30
|
* sign straight back in. IAM's own logout page is never visited.
|
|
31
|
+
*
|
|
32
|
+
* EVERY HANZO SITE AT ONCE (`everywhere`). hanzo.ai and platform.hanzo.ai sign in
|
|
33
|
+
* from one session, so leaving one of them must leave the other: IAM retires the
|
|
34
|
+
* person's grant to every application of this client's organization along with
|
|
35
|
+
* the session. A third-party app the person signed into keeps its own.
|
|
31
36
|
*/
|
|
32
37
|
export function useSignOut() {
|
|
33
|
-
|
|
38
|
+
// @ts-expect-error everywhere option added in @hanzo/ui / IAM shared sign-out
|
|
39
|
+
return useUiSignOut({ to: OUT, track, everywhere: true });
|
|
34
40
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzo/rooms",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.97",
|
|
4
4
|
"description": "The workspace rooms — Home, Chat, Inbox, Contacts, Meet, Cal, Drive, Board, Work, Bots — and the frame that holds them, once, for every Hanzo surface.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
"@hanzo/composer": ">=0.2.0",
|
|
54
54
|
"@hanzo/event": ">=0.3.56",
|
|
55
55
|
"@hanzo/gui": "^8.3.5",
|
|
56
|
+
"@hanzo/i18n": ">=0.2.0",
|
|
56
57
|
"@hanzo/iam": ">=0.21.35",
|
|
57
58
|
"@hanzo/logo": ">=1.0.26",
|
|
58
59
|
"@hanzo/studio": ">=0.1.0",
|
|
@@ -73,10 +74,11 @@
|
|
|
73
74
|
"@hanzo/design": "0.5.34",
|
|
74
75
|
"@hanzo/event": "0.3.56",
|
|
75
76
|
"@hanzo/gui": "8.3.5",
|
|
77
|
+
"@hanzo/i18n": "0.2.0",
|
|
76
78
|
"@hanzo/iam": "0.21.35",
|
|
77
79
|
"@hanzo/logo": "1.0.26",
|
|
78
80
|
"@hanzo/studio": "0.1.0",
|
|
79
|
-
"@hanzo/tabs": "github:hanzo-
|
|
81
|
+
"@hanzo/tabs": "github:hanzo-inc/tabs",
|
|
80
82
|
"@hanzo/ui": "8.29.0",
|
|
81
83
|
"@hanzo/usage": "0.1.18",
|
|
82
84
|
"@hanzo/voice": "0.1.19",
|
|
@@ -88,6 +90,7 @@
|
|
|
88
90
|
"@types/node": "26.5.0",
|
|
89
91
|
"@types/react": "19.2.18",
|
|
90
92
|
"@types/react-dom": "19.2.7",
|
|
93
|
+
"happy-dom": "20.14.6",
|
|
91
94
|
"lucide-react": "1.42.0",
|
|
92
95
|
"react": "19.2.8",
|
|
93
96
|
"react-dom": "19.2.8",
|