@hanzo/rooms 0.1.83 → 0.1.85

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/App.js CHANGED
@@ -69,7 +69,7 @@ import { isPlans, leave, useDevHost } from './dev.js';
69
69
  import { Orgs } from './Orgs.js';
70
70
  import { useRooms } from './host.js';
71
71
  import { useKept } from './kept.js';
72
- import { Mark, Rail, Toggle } from './Rail.js';
72
+ import { Mark, Rail, spot, Toggle } from './Rail.js';
73
73
  import { empty, openThread, useOpen } from './open.js';
74
74
  import { Paned } from './Shell.js';
75
75
  import { Palette } from './Palette.js';
@@ -191,6 +191,14 @@ function Window({ bar }) {
191
191
  last[mode] = `${window.location.pathname}${window.location.search}`;
192
192
  setDrawer(false);
193
193
  }, [mode, here]);
194
+ // Where the reader was before a place that takes the sidebar (Library, Scheduled
195
+ // tasks, Customize, Settings): its Back returns there, Chat or Dev alike.
196
+ const away = useRef('');
197
+ const inPlace = mode === 'dev' && spot(where) !== null;
198
+ useEffect(() => {
199
+ if (!inPlace)
200
+ away.current = `${window.location.pathname}${window.location.search}`;
201
+ }, [inPlace, here]);
194
202
  /** A move of the app's own: the drawer away, then the address. */
195
203
  const move = useCallback((address) => {
196
204
  shut();
@@ -227,6 +235,7 @@ function Window({ bar }) {
227
235
  window.addEventListener('keydown', key);
228
236
  return () => window.removeEventListener('keydown', key);
229
237
  }, [bar, router, move]);
238
+ const back = () => move(away.current || app(''));
230
239
  const toChat = () => move(last.chat || talk());
231
240
  const toDev = () => move(last.dev || app(''));
232
241
  const toStudio = () => move(last.studio || studio(''));
@@ -241,5 +250,5 @@ function Window({ bar }) {
241
250
  shut();
242
251
  setFinding(true);
243
252
  };
244
- return (_jsx(AiProvider, { client: client ?? undefined, children: _jsx(HostProvider, { host: host, children: _jsx(PrefsProvider, { children: _jsxs(YStack, { ref: view, "data-slot": "window", ...(bar ? floor : null), flex: 1, minW: 0, height: "100dvh", overflow: "hidden", "$max-md": { position: 'fixed', t: TOP, l: 0, r: 0, height: HEIGHT }, children: [bar ? (_jsxs(XStack, { "data-slot": "title-bar", height: BAR, shrink: 0, items: "center", gap: 2, pl: (bar.inset ?? 0) + 6, pr: "$2", ...bar.drag, children: [_jsx(XStack, { "$max-md": { display: 'none' }, children: _jsx(Toggle, { open: !closed, onPress: flip }) }), _jsx(XStack, { $md: { display: 'none' }, children: _jsx(Toggle, { open: drawer, onPress: () => setDrawer(true), label: "Open sidebar" }) }), _jsx(Mark, { label: "Back", "data-slot": "back", onPress: () => router.back(), children: _jsx(ArrowLeft, { size: 18, "aria-hidden": true }) }), _jsx(Mark, { label: "Forward", "data-slot": "forward", onPress: () => router.forward(), children: _jsx(ArrowRight, { size: 18, "aria-hidden": true }) }), _jsx(XStack, { flex: 1, self: "stretch", ...bar.drag })] })) : null, _jsxs(XStack, { "data-room": "", "data-app": mode, "data-slot": "frame", ...(bar ? null : floor), flex: 1, minW: 0, minH: 0, pr: "var(--pane-gap)", pb: "var(--pane-gap)", pt: bar ? 0 : 'var(--pane-gap)', "$max-md": { p: 0 }, children: [_jsx(YStack, { height: "100%", "$max-md": { display: 'none' }, children: _jsx(Rail, { host: host, mode: mode, open: !closed, toggle: bar ? undefined : flip, onChat: toChat, onDev: toDev, onStudio: toStudio, onOrg: org }) }), closed ? null : (_jsx(YStack, { "$max-md": { display: 'none' }, children: _jsx(Column, { host: host, mode: mode, thread: thread, go: move, onShut: () => setClosed(true), onSeek: seek, places: sidebar }) })), _jsxs(YStack, { "data-slot": "stage", flex: 1, minW: 0, minH: 0, ml: "var(--pane-gap)", "$max-md": { ml: 0, pb: FOOT }, children: [bar ? null : _jsx(Top, { host: host, mode: mode, places: sidebar, go: move, onMenu: () => setDrawer(true), onChat: toChat, onDev: toDev }), _jsx(XStack, { "data-slot": "panes", flex: 1, minW: 0, minH: 0, children: mode === 'chat' ? (_jsx(Paned, { framed: true, children: _jsx(Chat, {}) })) : mode === 'studio' ? (_jsx(YStack, { render: "main", "data-slot": "pane", ...pane(), overflow: "hidden", flex: 1, minW: 0, minH: 0, children: _jsx(YStack, { flex: 1, minH: 0, height: "100%", children: _jsx(Suspense, { fallback: null, children: _jsx(Studio, { host: maker, rail: false }) }) }) })) : (_jsx(YStack, { render: "main", "data-slot": "pane", ...(run ? null : { ...pane(), overflow: 'hidden' }), flex: 1, minW: 0, minH: 0, children: plans ? null : _jsx(Builder, { host: host, rail: false }) })) })] })] }), drawer ? (_jsxs(_Fragment, { children: [_jsx(YStack, { render: "button", "aria-label": "Close sidebar", onPress: () => setDrawer(false), bg: DIM, position: "absolute", t: 0, r: 0, b: 0, l: 0, z: "var(--z-overlay)", $md: { display: 'none' } }), _jsx(YStack, { "data-slot": "drawer", position: "absolute", t: INSET.top, b: INSET.bottom, l: INSET.left, width: "min(320px, calc(100% - 2 * var(--pane-gap) - 48px))", z: "var(--z-modal)", $md: { display: 'none' }, children: _jsx(Column, { host: host, mode: mode, thread: thread, go: move, onShut: shut, onSeek: seek, drawer: true, places: sidebar, foot: _jsx(Rail, { host: host, mode: mode, open: true, flat: true, onChat: toChat, onDev: toDev, onStudio: toStudio, onOrg: org }) }) })] })) : null, finding ? _jsx(Palette, { go: move, onClose: found, mode: mode, host: host }) : null] }) }) }) }));
253
+ return (_jsx(AiProvider, { client: client ?? undefined, children: _jsx(HostProvider, { host: host, children: _jsx(PrefsProvider, { children: _jsxs(YStack, { ref: view, "data-slot": "window", ...(bar ? floor : null), flex: 1, minW: 0, height: "100dvh", overflow: "hidden", "$max-md": { position: 'fixed', t: TOP, l: 0, r: 0, height: HEIGHT }, children: [bar ? (_jsxs(XStack, { "data-slot": "title-bar", height: BAR, shrink: 0, items: "center", gap: 2, pl: (bar.inset ?? 0) + 6, pr: "$2", ...bar.drag, children: [_jsx(XStack, { "$max-md": { display: 'none' }, children: _jsx(Toggle, { open: !closed, onPress: flip }) }), _jsx(XStack, { $md: { display: 'none' }, children: _jsx(Toggle, { open: drawer, onPress: () => setDrawer(true), label: "Open sidebar" }) }), _jsx(Mark, { label: "Back", "data-slot": "back", onPress: () => router.back(), children: _jsx(ArrowLeft, { size: 18, "aria-hidden": true }) }), _jsx(Mark, { label: "Forward", "data-slot": "forward", onPress: () => router.forward(), children: _jsx(ArrowRight, { size: 18, "aria-hidden": true }) }), _jsx(XStack, { flex: 1, self: "stretch", ...bar.drag })] })) : null, _jsxs(XStack, { "data-room": "", "data-app": mode, "data-slot": "frame", ...(bar ? null : floor), flex: 1, minW: 0, minH: 0, pr: "var(--pane-gap)", pb: "var(--pane-gap)", pt: bar ? 0 : 'var(--pane-gap)', "$max-md": { p: 0 }, children: [_jsx(YStack, { height: "100%", "$max-md": { display: 'none' }, children: _jsx(Rail, { host: host, mode: mode, at: where, open: !closed, toggle: bar ? undefined : flip, onChat: toChat, onDev: toDev, onStudio: toStudio, go: move, onOrg: org }) }), closed ? null : (_jsx(YStack, { "$max-md": { display: 'none' }, children: _jsx(Column, { host: host, mode: mode, thread: thread, go: move, onShut: () => setClosed(true), onSeek: seek, onBack: back, places: sidebar }) })), _jsxs(YStack, { "data-slot": "stage", flex: 1, minW: 0, minH: 0, ml: "var(--pane-gap)", "$max-md": { ml: 0, pb: FOOT }, children: [bar ? null : _jsx(Top, { host: host, mode: mode, places: sidebar, go: move, onMenu: () => setDrawer(true), onChat: toChat, onDev: toDev }), _jsx(XStack, { "data-slot": "panes", flex: 1, minW: 0, minH: 0, children: mode === 'chat' ? (_jsx(Paned, { framed: true, children: _jsx(Chat, {}) })) : mode === 'studio' ? (_jsx(YStack, { render: "main", "data-slot": "pane", ...pane(), overflow: "hidden", flex: 1, minW: 0, minH: 0, children: _jsx(YStack, { flex: 1, minH: 0, height: "100%", children: _jsx(Suspense, { fallback: null, children: _jsx(Studio, { host: maker, rail: false }) }) }) })) : (_jsx(YStack, { render: "main", "data-slot": "pane", ...(run ? null : { ...pane(), overflow: 'hidden' }), flex: 1, minW: 0, minH: 0, children: plans ? null : _jsx(Builder, { host: host, rail: false }) })) })] })] }), drawer ? (_jsxs(_Fragment, { children: [_jsx(YStack, { render: "button", "aria-label": "Close sidebar", onPress: () => setDrawer(false), bg: DIM, position: "absolute", t: 0, r: 0, b: 0, l: 0, z: "var(--z-overlay)", $md: { display: 'none' } }), _jsx(YStack, { "data-slot": "drawer", position: "absolute", t: INSET.top, b: INSET.bottom, l: INSET.left, width: "min(320px, calc(100% - 2 * var(--pane-gap) - 48px))", z: "var(--z-modal)", $md: { display: 'none' }, children: _jsx(Column, { host: host, mode: mode, thread: thread, go: move, onShut: shut, onSeek: seek, onBack: back, drawer: true, places: sidebar, foot: _jsx(Rail, { host: host, mode: mode, at: where, open: true, flat: true, onChat: toChat, onDev: toDev, onStudio: toStudio, go: move, onOrg: org }) }) })] })) : null, finding ? _jsx(Palette, { go: move, onClose: found, mode: mode, host: host }) : null] }) }) }) }));
245
254
  }
package/dist/Chat.js CHANGED
@@ -44,7 +44,7 @@ import { Anchor, Box, Button, Text, View, XStack, YStack } from "@hanzo/ui";
44
44
  import { useMedia } from "@hanzo/gui";
45
45
  import { tidy } from "./lib/title.js";
46
46
  import { useIam, useIamToken } from "@hanzo/iam/react";
47
- import { bearer, hasSession, org } from "./lib/session.js";
47
+ import { bearer, hasSession, named, org } from "./lib/session.js";
48
48
  import { api } from "./lib/api.js";
49
49
  import { app, dev, go } from "./lib/host.js";
50
50
  import { useRooms } from "./host.js";
@@ -64,7 +64,7 @@ import { Share } from "./Share.js";
64
64
  import { ConversationMenu, ThreadMore, useThreadList } from "./thread.js";
65
65
  import { Schedule } from "./Schedule.js";
66
66
  import { keepFile, useAttach, useKeep } from "./keep.js";
67
- import { artifacts, lead, nameOf, PAGE, Panel, PanelToggle, prompt, renders, Reply, Served, Summary, useDeck, useKey, Suggestions, useCarry, useLanguage, useMention, useMind, useSuggestions, usePanel, frames, name as tabName, } from "@hanzo/build";
67
+ import { artifacts, lead, nameOf, PAGE, Panel, PanelToggle, prompt, renders, Reply, Served, Summary, useDeck, useKey, Suggestions, useCarry, useLanguage, useMention, useMind, useSuggestions, CHAT_MODES, usePanel, frames, name as tabName, } from "@hanzo/build";
68
68
  import { checkoutUrl } from './lib/pay.js';
69
69
  import { Upgrade, useFree } from './Upgrade.js';
70
70
  import { enter, LOGIN } from './lib/destination.js';
@@ -224,8 +224,6 @@ export function Ask({ placeholder, idle, }) {
224
224
  * accessible name does not move when the placeholder does.
225
225
  */
226
226
  const message = () => `Ask ${brand()}`;
227
- /** The line every answer is sent under, above the composer, as the reference sets it. */
228
- const caution = () => `${brand()} can make mistakes. Check important info.`;
229
227
  /** The panel offers no kinds of its own in Chat: its tabs are pages. */
230
228
  const NONE = [];
231
229
  /** The id the room's system turn is held under in the thread. */
@@ -527,8 +525,10 @@ function Thread({ placeholder = message(), initial, anonymous = false, heading,
527
525
  setDraft: (x) => lent.current.setDraft(x),
528
526
  attach: (f) => lent.current.attach(f),
529
527
  go: (href) => (/^https?:/.test(href) ? void window.open(href, "_blank", "noopener,noreferrer") : go(href)),
530
- });
528
+ }, CHAT_MODES);
531
529
  const agentShelf = useAgentShelf();
530
+ // The opening greets the person by their first name, as their account spells it.
531
+ const firstName = useMemo(() => (anonymous ? "" : ((named()?.name ?? "").trim().split(/\s+/)[0] ?? "")), [anonymous]);
532
532
  // Up to three things worth doing next, from the person's own activity (@hanzo/build).
533
533
  const hints = useSuggestions(target);
534
534
  // THE HEADER'S ⋯ IS THE ROW'S MENU (thread.tsx): rename, pin, share, its
@@ -1362,7 +1362,6 @@ function Thread({ placeholder = message(), initial, anonymous = false, heading,
1362
1362
  ready: Boolean(draft.trim() || files.length),
1363
1363
  // `label` keeps the accessible name constant; the placeholder is the composer's own.
1364
1364
  label: ASK,
1365
- note: caution(),
1366
1365
  // A phone's pill is the tall one: the field across the top, the controls under it.
1367
1366
  narrow: !media.sm,
1368
1367
  // In the app's frame the composer floats on the pane as a pane of its own.
@@ -1411,7 +1410,7 @@ function Thread({ placeholder = message(), initial, anonymous = false, heading,
1411
1410
  ],
1412
1411
  slash: anonymous ? undefined : carry.skills,
1413
1412
  at: anonymous ? undefined : carry.plugins,
1414
- mode: anonymous ? null : { value: carry.gear.gear.mode, set: (m) => carry.gear.set({ mode: m }) },
1413
+ mode: anonymous ? null : carry.mode,
1415
1414
  // The model, the effort and the mode under the frame (@hanzo/build).
1416
1415
  mind,
1417
1416
  limits,
@@ -1530,10 +1529,10 @@ function Thread({ placeholder = message(), initial, anonymous = false, heading,
1530
1529
  // with it when it did. `Ask` passes its own, because the page embedding
1531
1530
  // it already has an `<h1>` and a second is the defect that took the
1532
1531
  // landings off /chat and /dev.
1533
- empty: heading === null ? undefined : (_jsxs(YStack, { "data-slot": "opening", items: "center", width: "100%", py: "$6", ...(media.sm ? null : { flex: 1, justify: "center" }), children: [room.length > 1 ? (_jsx(XStack, { items: "center", justify: "center", mb: "$4", children: room.map((name, i) => (_jsx(Box, { ml: i ? -16 : 0, rounded: 40, borderWidth: 2, borderColor: "$background", z: room.length - i, children: _jsx(Face, { src: faces[name]?.avatar, emoji: faces[name]?.emoji, name: name, size: 72 }) }, name))) })) : portrait ? (_jsx(Box, { width: 160, height: 160, rounded: 24, overflow: "hidden", borderWidth: 2, borderColor: "var(--white-15)", boxShadow: "0 10px 24px color-mix(in srgb, var(--pure-black) 70%, transparent)", mb: "$4", bg: "var(--neutral-900)", children: _jsx(View, { render: _jsx("img", { src: portrait, alt: withWhom }), width: "100%", height: "100%", objectFit: "cover" }) })) : null, room.length === 1 && !portrait ? (_jsx(Box, { mb: "$3", children: _jsx(Face, { emoji: mate?.emoji ?? persona?.emoji, name: withWhom, size: 72 }) })) : null, _jsx(Text, { render: "h1", fontSize: "$7", lineHeight: "$7", fontWeight: "700", color: "$ink", text: "center", mb: "$1", children: join(room) || heading || "What can I help with?" }), room.length === 1 && (mate?.description ?? persona?.description) ? (_jsx(Text, { fontSize: "$3", color: "$soft", text: "center", maxW: 500, mb: "$4", lineHeight: "$4", children: mate?.description ?? persona?.description })) : null] })),
1532
+ empty: heading === null ? undefined : (_jsxs(YStack, { "data-slot": "opening", items: "center", width: "100%", pt: "$6", pb: "$2", ...(media.sm ? null : { flex: 1, justify: "center" }), children: [room.length > 1 ? (_jsx(XStack, { items: "center", justify: "center", mb: "$4", children: room.map((name, i) => (_jsx(Box, { ml: i ? -16 : 0, rounded: 40, borderWidth: 2, borderColor: "$background", z: room.length - i, children: _jsx(Face, { src: faces[name]?.avatar, emoji: faces[name]?.emoji, name: name, size: 72 }) }, name))) })) : portrait ? (_jsx(Box, { width: 160, height: 160, rounded: 24, overflow: "hidden", borderWidth: 2, borderColor: "var(--white-15)", boxShadow: "0 10px 24px color-mix(in srgb, var(--pure-black) 70%, transparent)", mb: "$4", bg: "var(--neutral-900)", children: _jsx(View, { render: _jsx("img", { src: portrait, alt: withWhom }), width: "100%", height: "100%", objectFit: "cover" }) })) : null, room.length === 1 && !portrait ? (_jsx(Box, { mb: "$3", children: _jsx(Face, { emoji: mate?.emoji ?? persona?.emoji, name: withWhom, size: 72 }) })) : null, _jsx(Text, { render: "h1", fontSize: "$7", lineHeight: "$7", fontWeight: "700", color: "$ink", text: "center", mb: "$1", children: join(room) || heading || (firstName ? `What’s up, ${firstName}?` : "What’s up?") }), room.length === 1 && (mate?.description ?? persona?.description) ? (_jsx(Text, { fontSize: "$3", color: "$soft", text: "center", maxW: 500, mb: "$4", lineHeight: "$4", children: mate?.description ?? persona?.description })) : null] })),
1534
1533
  // The transcript reads at the same measure. `Thread` defaults to 768
1535
1534
  // itself; handing it the column here is what lets Width reach it.
1536
- column: { maxW: MEASURE }, composer: prompt({ ...composing, field: mention.field }, mention.state) }), _jsx(AgentMaker, {}), carry.dialog, _jsxs(XStack, { items: "center", justify: "center", py: "$1", px: "$3", gap: "$2", children: [_jsx(Anchor, { href: site("/legal/terms"), fontSize: "$1", color: "$soft", children: "Terms" }), _jsx(Text, { fontSize: "$1", color: "$soft", children: "\u00B7" }), _jsx(Anchor, { href: site("/legal/privacy"), fontSize: "$1", color: "$soft", children: "Privacy" })] }), consent, _jsx(Upgrade, { ask: ask, onClose: () => setAsk(null) })] }));
1535
+ column: { maxW: MEASURE }, composer: prompt({ ...composing, field: mention.field }, mention.state) }), _jsx(AgentMaker, {}), carry.dialog, _jsx(XStack, { items: "center", justify: "center", py: "$1", px: "$3", gap: "$2", children: isFree(model) ? (_jsxs(Text, { fontSize: "$1", color: "$soft", children: ["Free chat is data-shared to train models.", " ", _jsx(Anchor, { href: site("/legal/privacy"), fontSize: "$1", color: "$ink", children: "Privacy" })] })) : (_jsxs(_Fragment, { children: [_jsx(Anchor, { href: site("/legal/terms"), fontSize: "$1", color: "$soft", children: "Terms" }), _jsx(Text, { fontSize: "$1", color: "$soft", children: "\u00B7" }), _jsx(Anchor, { href: site("/legal/privacy"), fontSize: "$1", color: "$soft", children: "Privacy" })] })) }), consent, _jsx(Upgrade, { ask: ask, onClose: () => setAsk(null) })] }));
1537
1536
  }
1538
1537
  /** The agreement free asks for, in @hanzo/ai's words so every surface says one thing. */
1539
1538
  function Consent({ question, onAgree, onCancel, }) {
package/dist/Column.d.ts CHANGED
@@ -2,7 +2,7 @@ import { type Host } from '@hanzo/build';
2
2
  import { type RailLink } from '@hanzo/ui/chat';
3
3
  import { type ReactNode } from 'react';
4
4
  import { type Place } from './lib/host.js';
5
- export declare function Column({ host, mode, thread, go, onShut, onSeek, drawer, foot, places, }: {
5
+ export declare function Column({ host, mode, thread, go, onShut, onSeek, onBack, drawer, foot, places, }: {
6
6
  host: Host;
7
7
  mode: Place['mode'];
8
8
  /** The Chat conversation the pane holds, or null. */
@@ -13,6 +13,8 @@ export declare function Column({ host, mode, thread, go, onShut, onSeek, drawer,
13
13
  onShut: () => void;
14
14
  /** Opens the palette. */
15
15
  onSeek?: () => void;
16
+ /** Leaves a place that took the sidebar, for where the reader was before it. */
17
+ onBack: () => void;
16
18
  /** Drawn in the phone's drawer: no edge, cut as a floating pane. */
17
19
  drawer?: boolean;
18
20
  /** What stands at its foot: the drawer's rail, laid flat. */
package/dist/Column.js CHANGED
@@ -12,21 +12,24 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
12
12
  // that moved last, each one press from its page, and Notifications in Settings.
13
13
  // The search opens the palette (Palette.tsx; ⌘K / Ctrl+K from anywhere).
14
14
  //
15
- // Dev's places are @hanzo/build's `nav`, the one list every rail draws, each a
16
- // row under a quiet group label.
15
+ // Dev's places are @hanzo/build's `own`: New run, Projects, Issues, Templates.
16
+ // A place the rail holds (Library, Scheduled tasks, Customize) and Settings
17
+ // each take the sidebar instead (Rail.tsx `spot`): Back, the place's name, and
18
+ // its own rows (Settings' sections, Customize's tabs), never Dev's beside them.
17
19
  //
18
20
  // Its width is the edge's in the gutter to its right: dragged, keyed or
19
21
  // double-clicked back (@hanzo/build's `Grip`), kept per browser. Pulled in past
20
22
  // its floor it shuts (`onShut`), as the sidebar's toggle shuts it.
21
23
  import { Text, XStack, YStack } from '@hanzo/gui';
22
- import { Bell, Search, Settings2, SquarePen } from 'lucide-react';
23
- import { DOTS, Grip, nav, NEW, path as written, useSessions } from '@hanzo/build';
24
+ import { ArrowLeft, Bell, Search, Settings2, SquarePen } from 'lucide-react';
25
+ import { DOTS, Gutter, NEW, own, path as written, sections, tabs, useSessions } from '@hanzo/build';
24
26
  import { tidy } from './lib/title.js';
25
27
  import { DropdownMenu } from '@hanzo/ui';
26
28
  import { Sidebar, SidebarIconButton, SidebarItem, StatusDot } from '@hanzo/ui/chat';
27
29
  import { useId } from 'react';
28
30
  import { Made } from './Me.js';
29
31
  import { Recents } from './Recents.js';
32
+ import { spot } from './Rail.js';
30
33
  import { Setup } from './Setup.js';
31
34
  import { ago } from './recent.js';
32
35
  import { empty } from './open.js';
@@ -34,9 +37,6 @@ import { useSpan } from './span.js';
34
37
  import { pane } from './ground.js';
35
38
  import { talk } from './lib/host.js';
36
39
  import { brand, wordmark } from './where.js';
37
- /** The grip stands in the gutter between the sidebar and the panel. gui carries a `calc()` to the page unchanged; its
38
- * types only name custom properties. */
39
- const GUTTER_END = 'calc(-1 * var(--pane-gap))';
40
40
  /** The sidebar fills the column. Its `width` is typed in px alone, though its frame takes any gui size — the type is
41
41
  * @hanzo/ui's to widen. */
42
42
  const FILL = { width: '100%' };
@@ -69,6 +69,8 @@ const keys = (press) => ({
69
69
  const ROW = { rounded: '$4', minH: 31, py: 0, px: '$2' };
70
70
  /** The run activity behind the bell: the runs that moved last, newest first. */
71
71
  const RECENT = 6;
72
+ /** What a place that takes the sidebar is called at its head. */
73
+ const TITLE = { library: 'Library', scheduled: 'Scheduled tasks', customize: 'Customize', settings: 'Settings' };
72
74
  /** A header mark: a 16px glyph in a 32px target. */
73
75
  const HEAD = { width: 32, height: 32, rounded: '$3' };
74
76
  function Activity({ host }) {
@@ -90,12 +92,13 @@ function Activity({ host }) {
90
92
  ];
91
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 }));
92
94
  }
93
- export function Column({ host, mode, thread, go, onShut, onSeek, drawer = false, foot, places, }) {
95
+ export function Column({ host, mode, thread, go, onShut, onSeek, onBack, drawer = false, foot, places, }) {
94
96
  // The desktop sidebar and a phone's drawer can both be in the page: each names its own group labels.
95
97
  const uid = useId();
96
98
  const span = useSpan(SPAN, WIDTH, FLOOR, CEIL);
97
99
  const dev = (p) => host.go(p);
98
- const groups = places ?? nav(host, dev);
100
+ const at = mode === 'dev' ? spot(host.path) : null;
101
+ const groups = places ?? (at === 'settings' ? sections(host, dev) : at === 'customize' ? tabs(host, dev) : at ? [] : own(host, dev));
99
102
  const open = (row) => (row.kind === 'chat' ? go(talk(row.id)) : dev(row.id));
100
103
  // Chat's New; Dev's is the first of its places.
101
104
  const create = () => {
@@ -104,7 +107,5 @@ export function Column({ host, mode, thread, go, onShut, onSeek, drawer = false,
104
107
  };
105
108
  return (_jsxs(YStack, { "data-slot": "app-column", ref: drawer ? undefined : span.ref,
106
109
  // inline-style: `--span`, the width a drag rewrites on the element itself (span.ts); no prop sets a variable.
107
- 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: [_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 ? (_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 ? `${uid}-${g.id}` : undefined, "aria-label": i ? undefined : g.label, 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' ? null : _jsx(Recents, { host: host, mode: mode, thread: thread, onOpen: open, onNew: create }), _jsxs(YStack, { gap: "$2", pt: "$2", shrink: 0, children: [_jsx(Made, {}), _jsx(Setup, {})] }), foot] }), drawer ? null : (_jsx(Grip, { side: "left", span: span.span, floor: FLOOR, ceil: CEIL, reset: WIDTH, onSpan: span.move, onKeep: span.keep, onShut: onShut, label: "Resize sidebar",
108
- // In the gutter between the sidebar and the panel, not over the sidebar's own rows.
109
- r: GUTTER_END, width: "var(--pane-gap)" }))] }));
110
+ 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' || 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" }))] }));
110
111
  }
package/dist/Me.js CHANGED
@@ -20,7 +20,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
20
20
  // personal one first, with its plan — switched with `onOrg`; Create
21
21
  // organization, Organization settings, and Hanzo Team (opened in the same
22
22
  // organization for a team, offered as "Create a team" for a personal one); and
23
- // All organizations for a SuperAdmin (support mode).
23
+ // All organizations for a SuperAdmin, which opens the chooser (Orgs.tsx).
24
24
  //
25
25
  // IAM is the source of every fact here: names and roles from the token's `orgs`
26
26
  // claim, display names and which org is personal from `GET /v1/iam/organizations`,
@@ -36,13 +36,13 @@ import { Button, Dialog, DialogContent, DialogTitle, DropdownMenu, DropdownMenuC
36
36
  import { api } from './lib/api.js';
37
37
  import { createOrg, say } from './lib/org.js';
38
38
  import { list } from './lib/list.js';
39
- import { org as current, orgs, pick, renew, scope, superAdmin } from './lib/session.js';
39
+ import { choose, org as current, orgs, pick, renew, scope, superAdmin } from './lib/session.js';
40
40
  import { ENTRY } from './lib/host.js';
41
41
  import { leave } from './dev.js';
42
+ import { payUrl } from './lib/pay.js';
42
43
  import { asked, KEYS, legal, out, pages } from './help.js';
43
44
  import { showSettings } from './open.js';
44
45
  import { site, where } from './where.js';
45
- import { SupportPicker } from './Support.js';
46
46
  import { BAD } from './lib/mix.js';
47
47
  /** Where an organization's own settings live: a page of the site (`site()`), not of the app. */
48
48
  const ORG_SETTINGS = '/settings/organization';
@@ -138,7 +138,6 @@ export function Me({ host, narrow = false, above = false, onOrg, }) {
138
138
  const [account, setAccount] = useState(false);
139
139
  const [help, setHelp] = useState(false);
140
140
  const [creating, setCreating] = useState(false);
141
- const [supporting, setSupporting] = useState(false);
142
141
  const [keys, setKeys] = useState(false);
143
142
  const row = useRef(null);
144
143
  const current = host.org;
@@ -148,9 +147,20 @@ export function Me({ host, narrow = false, above = false, onOrg, }) {
148
147
  const target = useMemo(() => ({ api: host.api, token: host.token, org: host.org }), [host.api, host.token, host.org]);
149
148
  const read = useStanding(target, open);
150
149
  const other = usePlan(personal && personal.name !== current ? personal.name : null, open);
150
+ // THE TWO ACCOUNTS, apart: the organization's plan (hanzo · Enterprise) and the
151
+ // person's own (Personal · Pro, or none and the way to one). A business plan is
152
+ // the organization's; a personal plan is held beside it, never instead of it.
153
+ const team = here && !here.personal ? here : (all.find((o) => !o.personal) ?? null);
154
+ const teamOther = usePlan(team && team.name !== current ? team.name : null, open);
151
155
  const planFor = (o) => (o.name === current ? spoken(read.value?.label ?? null) : o.personal ? other : '');
152
156
  const who = host.person?.name || host.person?.email || '';
153
157
  const plan = spoken(read.value?.label ?? null);
158
+ const teamPlan = team ? (team.name === current ? plan : teamOther) : '';
159
+ const ownPlan = personal ? (personal.name === current ? plan : other) : '';
160
+ // Where the reader works, and on what: "hanzo · Enterprise", "Personal · Pro".
161
+ const working = [here ? (here.personal ? 'Personal' : here.display) : current, plan].filter(Boolean).join(' · ');
162
+ // A personal plan is bought for the personal organization, beside any business one.
163
+ const getPro = () => personal && window.location.assign(payUrl('/cart', { plan: 'pro', org: personal.name, returnUrl: window.location.href }));
154
164
  const apple = mac();
155
165
  useEffect(() => {
156
166
  if (above)
@@ -182,9 +192,11 @@ export function Me({ host, narrow = false, above = false, onOrg, }) {
182
192
  return;
183
193
  setAccount(false);
184
194
  setHelp(false);
185
- }, placement: narrow && !above ? 'right-end' : above ? 'top-end' : 'top-start', children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(XStack, { ref: (el) => {
195
+ }, placement: narrow && !above ? 'right-end' : above ? 'top-end' : 'top-start',
196
+ // Clear of the rail's mark (or the row) it opens from, and flush with its foot.
197
+ offset: 8, children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(XStack, { ref: (el) => {
186
198
  row.current = el instanceof HTMLElement ? el : null;
187
- }, 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 }), plan ? (_jsx(SizableText, { size: "$1", color: "$soft", numberOfLines: 1, text: "left", children: plan })) : 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: plan || host.person.email || '' })] })] }), 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: () => window.location.assign(site(ORG_SETTINGS)), children: _jsx(Line, { icon: _jsx(Settings, { size: 16, "aria-hidden": true }), children: "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: "Open in Hanzo Team" }) })) : (_jsx(DropdownMenuItem, { onSelect: () => setCreating(true), children: _jsx(Line, { icon: _jsx(Users, { size: 16, "aria-hidden": true }), children: "Create a team" }) })), superAdmin() ? (_jsx(DropdownMenuItem, { onSelect: () => setSupporting(true), children: _jsx(Line, { icon: _jsx(Building2, { size: 16, "aria-hidden": true }), children: "All organizations" }) })) : null] })) : 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: "Personalization" }) }), _jsx(DropdownMenuItem, { onSelect: () => showSettings('account'), children: _jsx(Line, { icon: _jsx(CircleUserRound, { size: 16, "aria-hidden": true }), children: "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: "Settings" }) }), _jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuSub, { open: help, onOpenChange: setHelp, children: [_jsx(DropdownMenuSubTrigger, { children: _jsx(Line, { icon: _jsx(LifeBuoy, { size: 16, "aria-hidden": true }), children: "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: "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: "Log out" }) })) : null] })] }), _jsx(Create, { open: creating, onOpenChange: setCreating }), _jsx(Shortcuts, { open: keys, onOpenChange: setKeys, apple: apple }), supporting ? _jsx(SupportPicker, { onClose: () => setSupporting(false) }) : null] }));
199
+ }, 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: () => window.location.assign(site(ORG_SETTINGS)), children: _jsx(Line, { icon: _jsx(Settings, { size: 16, "aria-hidden": true }), children: "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: "Open in Hanzo Team" }) })) : (_jsx(DropdownMenuItem, { onSelect: () => setCreating(true), children: _jsx(Line, { icon: _jsx(Users, { size: 16, "aria-hidden": true }), children: "Create a team" }) })), superAdmin() ? (_jsx(DropdownMenuItem, { onSelect: choose, children: _jsx(Line, { icon: _jsx(Building2, { size: 16, "aria-hidden": true }), children: "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": "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: "Get Pro" })), children: _jsx(Line, { icon: _jsx(CircleUserRound, { size: 16, "aria-hidden": true }), trail: personal.name === current ? _jsx(Check, { size: 16, "aria-label": "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: "Personalization" }) }), _jsx(DropdownMenuItem, { onSelect: () => showSettings('account'), children: _jsx(Line, { icon: _jsx(CircleUserRound, { size: 16, "aria-hidden": true }), children: "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: "Settings" }) }), _jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuSub, { open: help, onOpenChange: setHelp, children: [_jsx(DropdownMenuSubTrigger, { children: _jsx(Line, { icon: _jsx(LifeBuoy, { size: 16, "aria-hidden": true }), children: "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: "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: "Log out" }) })) : null] })] }), _jsx(Create, { open: creating, onOpenChange: setCreating }), _jsx(Shortcuts, { open: keys, onOpenChange: setKeys, apple: apple })] }));
188
200
  }
189
201
  /** The app's keys (help.ts `KEYS`), one row each, spelled for this platform. */
190
202
  function Shortcuts({ open, onOpenChange, apple }) {