@hanzo/rooms 0.1.98 → 0.1.100
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 +13 -15
- package/dist/Column.js +17 -20
- package/dist/Me.js +4 -6
- package/dist/OrgSwitcher.js +2 -1
- package/dist/Palette.js +16 -14
- package/dist/Rail.d.ts +1 -5
- package/dist/Rail.js +5 -6
- package/dist/dev.js +20 -30
- package/dist/lib/home.js +2 -1
- package/dist/lib/host.d.ts +37 -6
- package/dist/lib/host.js +53 -8
- package/package.json +1 -1
package/dist/App.js
CHANGED
|
@@ -62,7 +62,7 @@ import { lazy, Suspense, useCallback, useEffect, useMemo, useRef, useState } fro
|
|
|
62
62
|
import { ArrowLeft, ArrowRight } from 'lucide-react';
|
|
63
63
|
import { AiProvider } from '@hanzo/ai/react';
|
|
64
64
|
import { XStack, YStack } from '@hanzo/gui';
|
|
65
|
-
import { Builder, HostProvider, PrefsProvider,
|
|
65
|
+
import { Builder, HostProvider, PrefsProvider, route } from '@hanzo/build';
|
|
66
66
|
import { nav as places } from '@hanzo/studio';
|
|
67
67
|
import { useAi } from './lib/ai.js';
|
|
68
68
|
import { Chat } from './Chat.js';
|
|
@@ -71,7 +71,7 @@ import { isPlans, leave, useDevHost } from './dev.js';
|
|
|
71
71
|
import { Orgs } from './Orgs.js';
|
|
72
72
|
import { useRooms } from './host.js';
|
|
73
73
|
import { useKept } from './kept.js';
|
|
74
|
-
import { Mark,
|
|
74
|
+
import { Mark, raised, Rail, Toggle } from './Rail.js';
|
|
75
75
|
import { Foot, Layer, Lift, RAISED, useDrawer } from './Drawer.js';
|
|
76
76
|
import { empty, openThread, useOpen } from './open.js';
|
|
77
77
|
import { Paned } from './Shell.js';
|
|
@@ -79,7 +79,7 @@ import { Palette } from './Palette.js';
|
|
|
79
79
|
import { summons } from './seek.js';
|
|
80
80
|
import { floor, pane } from './ground.js';
|
|
81
81
|
import { pick } from './lib/session.js';
|
|
82
|
-
import { app, go, place, studio, talk, TERMINALS } from './lib/host.js';
|
|
82
|
+
import { app, go, place, settings, studio, talk, TERMINALS, visit } from './lib/host.js';
|
|
83
83
|
import { Top } from './Top.js';
|
|
84
84
|
import { FOOT, HEIGHT, TOP, useView } from './view.js';
|
|
85
85
|
/** Studio, fetched the first time the reader stands in it. */
|
|
@@ -97,8 +97,6 @@ const typing = (e) => {
|
|
|
97
97
|
const el = e.target;
|
|
98
98
|
return !!el && (el.isContentEditable || el.tagName === 'INPUT' || el.tagName === 'TEXTAREA' || el.tagName === 'SELECT');
|
|
99
99
|
};
|
|
100
|
-
/** The builder's Settings, general section. */
|
|
101
|
-
const SETTINGS = written({ kind: 'settings', section: 'general' });
|
|
102
100
|
export function App({ bar } = {}) {
|
|
103
101
|
return (_jsx(Orgs, { children: _jsx(Window, { bar: bar }) }));
|
|
104
102
|
}
|
|
@@ -146,13 +144,13 @@ function Window({ bar }) {
|
|
|
146
144
|
// A run floats two panes of its own, so the pane it stands in is not cut.
|
|
147
145
|
const run = where !== null && route(where).kind === 'run';
|
|
148
146
|
const asked = at.mode === 'chat' ? at.thread : null;
|
|
149
|
-
const { thread, settings } = useOpen();
|
|
150
|
-
// A row of the account menu asks for Settings: the drawer goes away
|
|
151
|
-
//
|
|
147
|
+
const { thread, settings: wanted } = useOpen();
|
|
148
|
+
// A row of the account menu asks for Settings: the drawer goes away as the
|
|
149
|
+
// router leaves for the page (dev.tsx).
|
|
152
150
|
useEffect(() => {
|
|
153
|
-
if (
|
|
151
|
+
if (wanted)
|
|
154
152
|
shut();
|
|
155
|
-
}, [
|
|
153
|
+
}, [wanted, shut]);
|
|
156
154
|
const plans = where !== null && isPlans(where);
|
|
157
155
|
useEffect(() => {
|
|
158
156
|
if (plans)
|
|
@@ -192,15 +190,15 @@ function Window({ bar }) {
|
|
|
192
190
|
drop();
|
|
193
191
|
}, [mode, here, drop]);
|
|
194
192
|
// Where the reader was before a place that takes the sidebar (Library, Scheduled
|
|
195
|
-
// tasks, Customize
|
|
193
|
+
// tasks, Customize): its Back returns there, Chat or Dev alike.
|
|
196
194
|
const away = useRef('');
|
|
197
|
-
const inPlace = mode === 'dev' &&
|
|
195
|
+
const inPlace = mode === 'dev' && raised(where) !== null;
|
|
198
196
|
useEffect(() => {
|
|
199
197
|
if (!inPlace)
|
|
200
198
|
away.current = `${window.location.pathname}${window.location.search}`;
|
|
201
199
|
}, [inPlace, here]);
|
|
202
|
-
/** A move
|
|
203
|
-
const move = useCallback((address) => depart((replace) =>
|
|
200
|
+
/** A move: the drawer away, the address in its place; a page that is not the app's (Settings) through the router. */
|
|
201
|
+
const move = useCallback((address) => depart((replace) => visit(router, address, replace)), [depart, router]);
|
|
204
202
|
// The app's keys.
|
|
205
203
|
useEffect(() => {
|
|
206
204
|
const key = (e) => {
|
|
@@ -214,7 +212,7 @@ function Window({ bar }) {
|
|
|
214
212
|
const mod = e.metaKey || e.ctrlKey;
|
|
215
213
|
if (mod && !e.shiftKey && !e.altKey && e.key === ',') {
|
|
216
214
|
e.preventDefault();
|
|
217
|
-
move(
|
|
215
|
+
move(settings());
|
|
218
216
|
return;
|
|
219
217
|
}
|
|
220
218
|
if (!bar)
|
package/dist/Column.js
CHANGED
|
@@ -10,28 +10,29 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
10
10
|
// content beside it is the panel, inset by the gutter.
|
|
11
11
|
//
|
|
12
12
|
// The bell is the org's run activity (@hanzo/build `useSessions`): the runs
|
|
13
|
-
// that moved last, each one press from its page
|
|
13
|
+
// that moved last, each one press from its page.
|
|
14
14
|
// The search opens the palette (Palette.tsx; ⌘K / Ctrl+K from anywhere).
|
|
15
15
|
//
|
|
16
16
|
// Dev's places are @hanzo/build's `own`: New run, Projects, Issues, Templates.
|
|
17
|
-
// A place the rail holds (Library, Scheduled tasks, Customize)
|
|
18
|
-
//
|
|
19
|
-
//
|
|
17
|
+
// A place the rail holds (Library, Scheduled tasks, Customize) takes the sidebar
|
|
18
|
+
// instead (Rail.tsx `raised`): Back, the place's name, and its own rows
|
|
19
|
+
// (Customize's tabs, the Library's kinds), never Dev's beside them. Settings is
|
|
20
|
+
// the site's pages at /settings, with a column of their own.
|
|
20
21
|
//
|
|
21
22
|
// Its width is the edge's in the gutter to its right: dragged, keyed or
|
|
22
23
|
// double-clicked back (@hanzo/build's `Grip`), kept per browser. Pulled in past
|
|
23
24
|
// its floor it shuts (`onShut`), as the sidebar's toggle shuts it.
|
|
24
25
|
import { useI18n } from '@hanzo/i18n/react';
|
|
25
26
|
import { Text, XStack, YStack } from '@hanzo/gui';
|
|
26
|
-
import { ArrowLeft, Bell, Search,
|
|
27
|
-
import { DOTS, Gutter, library, NEW, own,
|
|
27
|
+
import { ArrowLeft, Bell, Search, SquarePen } from 'lucide-react';
|
|
28
|
+
import { DOTS, Gutter, library, NEW, own, tabs, useScheduled, useSessions } from '@hanzo/build';
|
|
28
29
|
import { tidy } from './lib/title.js';
|
|
29
30
|
import { DropdownMenu } from '@hanzo/ui';
|
|
30
31
|
import { Sidebar, SidebarIconButton, SidebarItem, StatusDot } from '@hanzo/ui/chat';
|
|
31
32
|
import { useId } from 'react';
|
|
32
33
|
import { Made } from './Me.js';
|
|
33
34
|
import { Recents } from './Recents.js';
|
|
34
|
-
import {
|
|
35
|
+
import { raised } from './Rail.js';
|
|
35
36
|
import { Setup } from './Setup.js';
|
|
36
37
|
import { ago } from './recent.js';
|
|
37
38
|
import { empty } from './open.js';
|
|
@@ -61,7 +62,7 @@ const ROW = { rounded: '$4', minH: 31, py: 0, px: '$2', $touchable: { minH: 44 }
|
|
|
61
62
|
/** The run activity behind the bell: the runs that moved last, newest first. */
|
|
62
63
|
const RECENT = 6;
|
|
63
64
|
/** What a place that takes the sidebar is called at its head. */
|
|
64
|
-
const TITLE = { library: 'Library', scheduled: 'Scheduled tasks', customize: 'Customize'
|
|
65
|
+
const TITLE = { library: 'Library', scheduled: 'Scheduled tasks', customize: 'Customize' };
|
|
65
66
|
/** A header mark: a 16px glyph in a 32px target, a thumb's 44 on a touch screen. */
|
|
66
67
|
const HEAD = { width: 32, height: 32, rounded: '$3', $touchable: { width: 44, height: 44 } };
|
|
67
68
|
function Activity({ host }) {
|
|
@@ -79,8 +80,6 @@ function Activity({ host }) {
|
|
|
79
80
|
onSelect: () => host.go(s.id),
|
|
80
81
|
}))
|
|
81
82
|
: [{ key: 'none', label: 'Nothing new', disabled: true, onSelect: () => { } }]),
|
|
82
|
-
{ type: 'separator', key: 'line' },
|
|
83
|
-
{ key: 'settings', label: 'Notification settings', icon: _jsx(Settings2, { size: 16, "aria-hidden": true }), onSelect: () => host.go(written({ kind: 'settings', section: 'notifications' })) },
|
|
84
83
|
];
|
|
85
84
|
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 }));
|
|
86
85
|
}
|
|
@@ -90,19 +89,17 @@ export function Column({ host, mode, thread, go, onShut, onSeek, onBack, drawer
|
|
|
90
89
|
const uid = useId();
|
|
91
90
|
const span = useSpan(SPAN, WIDTH, FLOOR, CEIL);
|
|
92
91
|
const dev = (p) => host.go(p);
|
|
93
|
-
const at = mode === 'dev' ?
|
|
92
|
+
const at = mode === 'dev' ? raised(host.path) : null;
|
|
94
93
|
// Each place the rail holds lists its own rows: Library its kinds and Upload, Scheduled tasks its tasks and New task.
|
|
95
94
|
const scheduled = useScheduled(host, dev, at === 'scheduled');
|
|
96
95
|
const groups = places ??
|
|
97
|
-
(at === '
|
|
98
|
-
?
|
|
99
|
-
: at === '
|
|
100
|
-
?
|
|
101
|
-
: at === '
|
|
102
|
-
?
|
|
103
|
-
:
|
|
104
|
-
? scheduled
|
|
105
|
-
: own(host, dev));
|
|
96
|
+
(at === 'customize'
|
|
97
|
+
? tabs(host, dev)
|
|
98
|
+
: at === 'library'
|
|
99
|
+
? library(host, dev)
|
|
100
|
+
: at === 'scheduled'
|
|
101
|
+
? scheduled
|
|
102
|
+
: own(host, dev));
|
|
106
103
|
const open = (row) => (row.kind === 'chat' ? go(talk(row.id)) : dev(row.id));
|
|
107
104
|
// Chat's New; Dev's is the first of its places.
|
|
108
105
|
const create = () => {
|
package/dist/Me.js
CHANGED
|
@@ -9,8 +9,8 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
9
9
|
// the person's place on the beta waitlist (the site's /beta/status); then
|
|
10
10
|
// Help ›, the site's own pages and the keyboard shortcuts (⌘/ / Ctrl+/ from
|
|
11
11
|
// anywhere); and Log out. Every row opens a real destination: Settings'
|
|
12
|
-
//
|
|
13
|
-
//
|
|
12
|
+
// pages through `showSettings` (open.ts), which the app answers with the
|
|
13
|
+
// site's Settings at /settings (dev.tsx) and the shell with its own pane; the plans on the
|
|
14
14
|
// pay site (`leave`); the site's pages through `site()`, so hanzo.team names
|
|
15
15
|
// hanzo.ai (help.ts).
|
|
16
16
|
//
|
|
@@ -39,15 +39,13 @@ import { api } from './lib/api.js';
|
|
|
39
39
|
import { createOrg, say } from './lib/org.js';
|
|
40
40
|
import { list } from './lib/list.js';
|
|
41
41
|
import { choose, org as current, orgs, pick, renew, scope, superAdmin } from './lib/session.js';
|
|
42
|
-
import { ENTRY } from './lib/host.js';
|
|
42
|
+
import { ENTRY, settings } from './lib/host.js';
|
|
43
43
|
import { leave } from './dev.js';
|
|
44
44
|
import { payUrl } from './lib/pay.js';
|
|
45
45
|
import { asked, KEYS, legal, out, pages } from './help.js';
|
|
46
46
|
import { showSettings } from './open.js';
|
|
47
47
|
import { site, where } from './where.js';
|
|
48
48
|
import { BAD } from './lib/mix.js';
|
|
49
|
-
/** Where an organization's own settings live: a page of the site (`site()`), not of the app. */
|
|
50
|
-
const ORG_SETTINGS = '/settings/organization';
|
|
51
49
|
/** An organization this tab created, until the token carries it. */
|
|
52
50
|
const MADE = 'hanzo:org:made';
|
|
53
51
|
/** Each Help page's icon, by its key in help.ts. */
|
|
@@ -201,7 +199,7 @@ export function Me({ host, narrow = false, above = false, onOrg, }) {
|
|
|
201
199
|
row.current = el instanceof HTMLElement ? el : null;
|
|
202
200
|
}, render: "button", "data-slot": "me", "aria-label": `Account: ${[who, current].filter(Boolean).join(' · ')}`, ...{ title: who }, width: narrow ? 36 : '100%', minH: narrow ? 36 : 48,
|
|
203
201
|
// A thumb's 44 on a touch screen.
|
|
204
|
-
$touchable: narrow ? { width: 44, minH: 44 } : undefined, 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(
|
|
202
|
+
$touchable: narrow ? { width: 44, minH: 44 } : undefined, 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(settings('organization'))), 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 })] }));
|
|
205
203
|
}
|
|
206
204
|
/** The app's keys (help.ts `KEYS`), one row each, spelled for this platform. */
|
|
207
205
|
function Shortcuts({ open, onOpenChange, apple }) {
|
package/dist/OrgSwitcher.js
CHANGED
|
@@ -10,6 +10,7 @@ import { iam } from './lib/api.js';
|
|
|
10
10
|
import { pick } from './lib/session.js';
|
|
11
11
|
import { Text, View, XStack, YStack } from '@hanzo/gui';
|
|
12
12
|
import { site } from './where.js';
|
|
13
|
+
import { settings } from './lib/host.js';
|
|
13
14
|
/** design's smallest rung (`--text-floor`, 10px), below gui's `$1`; gui's font size takes a rung or a number. */
|
|
14
15
|
const FLOOR = 10;
|
|
15
16
|
/** A row at the foot of the menu that leaves for another page: a ghost Button, label to the left. */
|
|
@@ -95,7 +96,7 @@ export function OrgSwitcherDropdown({ open, onClose, activeOrgId, onSelect, alig
|
|
|
95
96
|
onSelect(o.id);
|
|
96
97
|
onClose();
|
|
97
98
|
}, width: "100%", justify: "space-between", px: 10, py: 6, bg: isSelected ? '$hover' : 'transparent', children: [_jsxs(XStack, { items: "center", gap: 10, minW: 0, shrink: 1, children: [_jsx(Text, { width: 24, height: 24, rounded: 6, bg: "$raised", display: "flex", items: "center", justify: "center", fontSize: "$2", fontWeight: "700", color: "$ink", shrink: 0, overflow: "hidden", children: o.logo ? (_jsx(View, { render: _jsx("img", { src: o.logo, alt: "" }), width: "100%", height: "100%", objectFit: "cover" })) : (o.displayName.toLowerCase().includes('hanzo') || o.name.toLowerCase().includes('hanzo')) ? (_jsx(HanzoMark, { size: 14 })) : (o.name.slice(0, 3).toUpperCase()) }), _jsxs(YStack, { minW: 0, shrink: 1, items: "flex-start", children: [_jsx(Text, { fontSize: "$2", fontWeight: isSelected ? '600' : '500', color: "$ink", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", children: o.displayName }), o.role ? (_jsx(Text, { fontSize: FLOOR, color: "$faint", children: o.role })) : null] })] }), isSelected ? _jsx(Check, { size: 14, color: "currentColor" }) : null] }, o.id));
|
|
98
|
-
}), _jsx(View, { height: 1, bg: "$borderColor", my: 4 }), _jsx(Button, { asChild: true, ...LINK, children: _jsxs("a", { href: iam(), target: "_blank", rel: "noreferrer", children: [_jsx(Plus, { size: 14 }), "Create Organization"] }) }), _jsx(Button, { asChild: true, ...LINK, children: _jsxs("a", { href: site("
|
|
99
|
+
}), _jsx(View, { height: 1, bg: "$borderColor", my: 4 }), _jsx(Button, { asChild: true, ...LINK, children: _jsxs("a", { href: iam(), target: "_blank", rel: "noreferrer", children: [_jsx(Plus, { size: 14 }), "Create Organization"] }) }), _jsx(Button, { asChild: true, ...LINK, children: _jsxs("a", { href: site(settings("organization")), children: [_jsx(Settings, { size: 14 }), "Organization settings"] }) })] }));
|
|
99
100
|
}
|
|
100
101
|
/**
|
|
101
102
|
* THE WORKSPACE YOU ARE STANDING IN, and the way to another one.
|
package/dist/Palette.js
CHANGED
|
@@ -5,14 +5,15 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
5
5
|
//
|
|
6
6
|
// An empty field lists Recent (the reader's chats and Dev tasks, newest first,
|
|
7
7
|
// each with its kind's icon), Actions, and Go to: the products, the places of
|
|
8
|
-
// the app and the apps in beta the reader may open. A query searches all of it, deeper places too (Settings'
|
|
8
|
+
// the app and the apps in beta the reader may open. A query searches all of it, deeper places too (Settings' pages,
|
|
9
9
|
// Customize's tabs, the Library's kinds, Studio's places, Help), and the
|
|
10
10
|
// reader's chats, Dev tasks, projects, agents, skills and connectors by name;
|
|
11
11
|
// each group keeps its best five and the group with the best answer leads
|
|
12
12
|
// (seek.ts `find`). A row opens where it lives; an action runs.
|
|
13
13
|
//
|
|
14
14
|
// GO TO IS DERIVED, never listed here: the places are @hanzo/build's own
|
|
15
|
-
// registries (`nav` for Dev's places, `sections` for Settings
|
|
15
|
+
// registries (`nav` for Dev's places, `sections` for Settings — the sections
|
|
16
|
+
// with a page at /settings (lib/host.ts `paged`), each opened there — `tabs` for
|
|
16
17
|
// Customize, `library` for the Library's kinds), @hanzo/studio's `nav`, and
|
|
17
18
|
// help.ts's pages, lib/betas.ts's granted apps: the same lists the sidebars and
|
|
18
19
|
// menus draw. A page added to
|
|
@@ -40,7 +41,7 @@ import { empty, openThread, withAgents } from './open.js';
|
|
|
40
41
|
import { useThreadList } from './thread.js';
|
|
41
42
|
import { useSignOut } from './lib/signout.js';
|
|
42
43
|
import { choose } from './lib/session.js';
|
|
43
|
-
import { app, studio, talk, TERMINALS, WORKSPACE } from './lib/host.js';
|
|
44
|
+
import { app, paged, settings, studio, talk, TERMINALS, WORKSPACE } from './lib/host.js';
|
|
44
45
|
import { BETAS } from './lib/betas.js';
|
|
45
46
|
import { useBetas } from './Betas.js';
|
|
46
47
|
import { tidy } from './lib/title.js';
|
|
@@ -48,15 +49,11 @@ import { KEYS, legal, out, pages } from './help.js';
|
|
|
48
49
|
import { site } from './where.js';
|
|
49
50
|
import { find } from './seek.js';
|
|
50
51
|
const ICON = { size: 16, 'aria-hidden': true };
|
|
51
|
-
/** The builder's Settings, its first section. */
|
|
52
|
-
const SETTINGS = written({ kind: 'settings', section: 'general' });
|
|
53
52
|
const SKILLS = written({ kind: 'customize', tab: 'skills' });
|
|
54
53
|
const CONNECTORS = written({ kind: 'customize', tab: 'connectors' });
|
|
55
|
-
/** The words a Settings
|
|
54
|
+
/** The words a Settings page is also asked for by: the account menu's names for General, and the obvious ones. */
|
|
56
55
|
const ALSO = {
|
|
57
|
-
general: ['personalization', 'appearance', 'theme'],
|
|
58
|
-
account: ['profile'],
|
|
59
|
-
privacy: ['security'],
|
|
56
|
+
general: ['personalization', 'appearance', 'theme', 'profile', 'account'],
|
|
60
57
|
billing: ['plans', 'subscription'],
|
|
61
58
|
members: ['organization', 'team'],
|
|
62
59
|
};
|
|
@@ -79,7 +76,7 @@ export function places(host, go) {
|
|
|
79
76
|
{ id: 'tabs', label: 'Tabs', icon: _jsx(SquareTerminal, { ...ICON }), run: () => go(TERMINALS) },
|
|
80
77
|
...(host ? [{ id: 'team', label: 'Team', icon: _jsx(Users, { ...ICON }), keywords: ['workspace'], run: () => out(WORKSPACE) }] : []),
|
|
81
78
|
...(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(
|
|
79
|
+
{ id: 'settings', label: 'Settings', icon: _jsx(Settings, { ...ICON }), shortcut: ['Mod', ','], run: () => go(settings()) },
|
|
83
80
|
// The site's own pages, outside the app: the browser loads them.
|
|
84
81
|
{ id: 'referral', label: 'Referral', icon: _jsx(Gift, { ...ICON }), keywords: ['invite', 'share'], run: () => window.location.assign(site('/referral')) },
|
|
85
82
|
{ id: 'beta', label: 'Beta status', icon: _jsx(FlaskConical, { ...ICON }), keywords: ['waitlist'], run: () => window.location.assign(site('/beta/status')) },
|
|
@@ -90,7 +87,7 @@ export function places(host, go) {
|
|
|
90
87
|
const deep = [
|
|
91
88
|
...(host
|
|
92
89
|
? [
|
|
93
|
-
...rows(sections(host, builder), 'Settings', 'settings'),
|
|
90
|
+
...rows(sections(host, builder).map((g) => ({ links: g.links.filter((l) => paged(l.id)) })), 'Settings', 'settings'),
|
|
94
91
|
...rows(tabs(host, builder), 'Customize', 'customize'),
|
|
95
92
|
...rows(library(host, builder).slice(1), 'Library', 'library'),
|
|
96
93
|
...rows(made(host, (p) => go(studio(p))), 'Studio', 'studio').filter((a) => a.id !== `studio-${NEW}`),
|
|
@@ -100,8 +97,7 @@ export function places(host, go) {
|
|
|
100
97
|
.filter((p) => p.key !== 'help')
|
|
101
98
|
.map((p) => ({ id: `help-${p.key}`, label: p.label, where: 'Help', keywords: ['Help'], run: () => out(p.href) })),
|
|
102
99
|
];
|
|
103
|
-
|
|
104
|
-
return { top, deep: deep.filter((a) => !top.some((t) => t.id === a.id.replace(/^settings-/, 'dev-'))) };
|
|
100
|
+
return { top, deep };
|
|
105
101
|
}
|
|
106
102
|
/** The composer's model menu, opened as a press would: a page without a composer opens New chat's. */
|
|
107
103
|
function tune(go) {
|
|
@@ -164,6 +160,12 @@ const obj = (v) => (v && typeof v === 'object' ? v : {});
|
|
|
164
160
|
const str = (v) => (typeof v === 'string' ? v : '');
|
|
165
161
|
const list = (v) => (Array.isArray(v) ? v : []);
|
|
166
162
|
/** `/v1/projects`: a project by its slug, found by its name, slug and repository. */
|
|
163
|
+
/**
|
|
164
|
+
* The builder as a frame without it holds it (hanzo.team's Shell): nobody signed
|
|
165
|
+
* in to it, so `useSessions` reads no runs. The hook reads `api`, `token`, `org`
|
|
166
|
+
* and `person` off whatever it is handed, and handed nothing it throws.
|
|
167
|
+
*/
|
|
168
|
+
const NOBODY = { api: '', token: () => null, org: null, person: null };
|
|
167
169
|
const projects = (body) => (Array.isArray(body) ? body : list(obj(body).data))
|
|
168
170
|
.map(obj)
|
|
169
171
|
.filter((p) => str(p.slug))
|
|
@@ -187,7 +189,7 @@ export function Palette({ onClose, go, actions: frame = [], mode = 'chat', host,
|
|
|
187
189
|
const known = useRead('/v1/tool/skills?activated=true', skills);
|
|
188
190
|
const plugged = useRead('/v1/tool/mcp/servers', connectors);
|
|
189
191
|
const signOut = useSignOut();
|
|
190
|
-
const runs = useSessions(host ??
|
|
192
|
+
const runs = useSessions(host ?? NOBODY);
|
|
191
193
|
const { open: opened } = useBetas();
|
|
192
194
|
const groups = useMemo(() => {
|
|
193
195
|
const { top, deep } = places(host, go);
|
package/dist/Rail.d.ts
CHANGED
|
@@ -10,11 +10,7 @@ export declare const RAISED: {
|
|
|
10
10
|
readonly customize: '-/customize';
|
|
11
11
|
};
|
|
12
12
|
export type Raised = keyof typeof RAISED;
|
|
13
|
-
/**
|
|
14
|
-
export type Spot = Raised | 'settings';
|
|
15
|
-
/** The place a builder address stands on, if it is one that takes the sidebar. */
|
|
16
|
-
export declare const spot: (at: string | null) => Spot | null;
|
|
17
|
-
/** Which of them a builder address stands on, if any: an agent's page is Customize's, an automation Scheduled tasks'. */
|
|
13
|
+
/** Which of them a builder address stands on, if any, and so takes the sidebar: an agent's page is Customize's, an automation Scheduled tasks'. */
|
|
18
14
|
export declare function raised(at: string | null): Raised | null;
|
|
19
15
|
/** One mark: an icon button named for where it goes, its name its tooltip, lit where the reader is. */
|
|
20
16
|
export declare function Mark({ label, on, onPress, children, ...rest }: {
|
package/dist/Rail.js
CHANGED
|
@@ -15,8 +15,9 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
15
15
|
// conversations, Dev its own places (@hanzo/build `own`: New run, Projects,
|
|
16
16
|
// Issues, Templates) and its runs, Studio its own; Library, Scheduled tasks and
|
|
17
17
|
// Customize (@hanzo/build's Artifacts, Automations and Customize) each their
|
|
18
|
-
// own
|
|
19
|
-
//
|
|
18
|
+
// own (Column.tsx); Settings is the site's pages at /settings, beside the app
|
|
19
|
+
// (lib/host.ts `settings`). Nothing on the rail is listed inside Dev: one level
|
|
20
|
+
// of the app a place, each lit while the reader is on it.
|
|
20
21
|
//
|
|
21
22
|
// It stands on the frame's floor, never cut as a pane (ground.ts): the rail and
|
|
22
23
|
// the sidebar beside it are the window, and the content is the panel inset in
|
|
@@ -47,9 +48,7 @@ export const RAIL = 48;
|
|
|
47
48
|
const MARK = 36;
|
|
48
49
|
/** The places the rail holds beside the products, by the builder address each opens. */
|
|
49
50
|
export const RAISED = { library: '-/artifacts', scheduled: '-/automations', customize: '-/customize' };
|
|
50
|
-
/**
|
|
51
|
-
export const spot = (at) => (at !== null && route(at).kind === 'settings' ? 'settings' : raised(at));
|
|
52
|
-
/** Which of them a builder address stands on, if any: an agent's page is Customize's, an automation Scheduled tasks'. */
|
|
51
|
+
/** Which of them a builder address stands on, if any, and so takes the sidebar: an agent's page is Customize's, an automation Scheduled tasks'. */
|
|
53
52
|
export function raised(at) {
|
|
54
53
|
if (at === null)
|
|
55
54
|
return null;
|
|
@@ -85,7 +84,7 @@ export function Toggle({ open, onPress, label }) {
|
|
|
85
84
|
}
|
|
86
85
|
export function Rail({ host, mode, at, open, toggle, rows = false, onChat, onDev, onStudio, onTabs, go, onOrg, }) {
|
|
87
86
|
const { t } = useI18n();
|
|
88
|
-
const here = mode === 'dev' ?
|
|
87
|
+
const here = mode === 'dev' ? raised(at) : null;
|
|
89
88
|
const to = (p) => () => go(app(p));
|
|
90
89
|
// The products on hosts of their own, behind More; on a phone the raised places lead it.
|
|
91
90
|
// Studio offers no Team, so on a laptop it draws no More.
|
package/dist/dev.js
CHANGED
|
@@ -9,16 +9,18 @@
|
|
|
9
9
|
//
|
|
10
10
|
// THE ADDRESS IS A PATH. Dev is /dev, and every place the builder shows is a
|
|
11
11
|
// path under it in the builder's real-path grammar (lib/host.ts `app`, `place`;
|
|
12
|
-
// @hanzo/build/route `href`, `parse`): a run, a screen, a
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
12
|
+
// @hanzo/build/route `href`, `parse`): a run, a screen, a Customize tab, the
|
|
13
|
+
// plans and a project all survive a reload, and Back steps through them. The
|
|
14
|
+
// host hands the rooms the path (`<Rooms path>`); a move is a native history
|
|
15
|
+
// entry, which the Next router folds into `usePathname`, so reading the address
|
|
16
|
+
// and moving it are one path.
|
|
17
17
|
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
18
|
+
// SETTINGS IS THE SITE'S, never Dev's: one tree at /settings, the host's own
|
|
19
|
+
// pages (lib/host.ts `settings`). A builder section the builder moves to
|
|
20
|
+
// (`go`), a link it opens (`open`) and a pane that asks for Settings
|
|
21
|
+
// (`showSettings`, open.ts: the account menu's rows, Chat's "See usage") all
|
|
22
|
+
// reach that page through the host's router (`visit`). The rooms' shell draws
|
|
23
|
+
// its own Settings pane; the app has none.
|
|
22
24
|
//
|
|
23
25
|
// THE PLANS ARE THE PAY SITE'S. Every way the app reaches the plans — the
|
|
24
26
|
// account menu's View all plans and its plan's Upgrade, Billing's Adjust plan,
|
|
@@ -36,7 +38,7 @@ import { bearer, named, org, orgs, pick } from './lib/session.js';
|
|
|
36
38
|
import { enter } from './lib/destination.js';
|
|
37
39
|
import { api } from './lib/api.js';
|
|
38
40
|
import { CAST } from './agents.js';
|
|
39
|
-
import { app,
|
|
41
|
+
import { app, place, settings, visit } from './lib/host.js';
|
|
40
42
|
import { link } from './lib/tags.js';
|
|
41
43
|
import { payUrl } from './lib/pay.js';
|
|
42
44
|
import { showSettings, useOpen } from './open.js';
|
|
@@ -44,7 +46,7 @@ import { showSettings, useOpen } from './open.js';
|
|
|
44
46
|
const LINKS = {
|
|
45
47
|
github: app('-/sync'),
|
|
46
48
|
customize: app('-/customize'),
|
|
47
|
-
settings:
|
|
49
|
+
settings: settings(),
|
|
48
50
|
home: '/',
|
|
49
51
|
};
|
|
50
52
|
/** The builder's address for the plans. */
|
|
@@ -79,24 +81,14 @@ export function useDevHost() {
|
|
|
79
81
|
const { router, path: pathname } = useRooms();
|
|
80
82
|
const here = path(route(at(pathname)));
|
|
81
83
|
const look = useLook();
|
|
82
|
-
const { settings } = useOpen();
|
|
83
|
-
//
|
|
84
|
+
const { settings: asked } = useOpen();
|
|
85
|
+
// A pane asked for Settings: its page, through the router.
|
|
84
86
|
useEffect(() => {
|
|
85
|
-
if (!
|
|
87
|
+
if (!asked)
|
|
86
88
|
return;
|
|
87
89
|
showSettings(null);
|
|
88
|
-
|
|
89
|
-
|
|
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]);
|
|
90
|
+
router.push(settings(asked));
|
|
91
|
+
}, [asked, router]);
|
|
100
92
|
// The token's claims change only when the token does: a sign-in, a refresh.
|
|
101
93
|
const memberships = useMemo(() => orgs(), [accessToken]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
102
94
|
const scoped = useMemo(() => org(), [accessToken]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
@@ -124,7 +116,7 @@ export function useDevHost() {
|
|
|
124
116
|
person: { name, email, avatar },
|
|
125
117
|
admin: administers(bearer(), scoped),
|
|
126
118
|
path: here,
|
|
127
|
-
go: (to, how) => (isPlans(to) ? leave('here') :
|
|
119
|
+
go: (to, how) => (isPlans(to) ? leave('here') : visit(router, app(to), how?.replace)),
|
|
128
120
|
links: LINKS,
|
|
129
121
|
// The sidebar lists Settings' sections and Customize's tabs (Column.tsx), and Customize › Agents offers the cast.
|
|
130
122
|
sections: true,
|
|
@@ -146,10 +138,8 @@ export function useDevHost() {
|
|
|
146
138
|
const to = `${url.pathname}${url.search}${url.hash}`;
|
|
147
139
|
if (url.origin !== window.location.origin)
|
|
148
140
|
window.location.assign(link(url.href));
|
|
149
|
-
else if (place(url.pathname))
|
|
150
|
-
go(to);
|
|
151
141
|
else
|
|
152
|
-
router
|
|
142
|
+
visit(router, to);
|
|
153
143
|
},
|
|
154
144
|
signIn: () => enter(),
|
|
155
145
|
signOut: () => void logout(),
|
package/dist/lib/home.js
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* two is how a card comes to say "0 projects" about a question nobody asked.
|
|
13
13
|
*/
|
|
14
14
|
import { site } from '../where.js';
|
|
15
|
+
import { settings } from './host.js';
|
|
15
16
|
/**
|
|
16
17
|
* Where a teammate is invited.
|
|
17
18
|
*
|
|
@@ -34,7 +35,7 @@ import { site } from '../where.js';
|
|
|
34
35
|
* other page of this app is, and the three doors that offer it (the empty
|
|
35
36
|
* room's tile, the Create menu, the sidebar's row) all read this one line.
|
|
36
37
|
*/
|
|
37
|
-
export const invite = () => site('
|
|
38
|
+
export const invite = () => site(settings('members'));
|
|
38
39
|
/** The org's projects, newest first. */
|
|
39
40
|
export async function projects(client, signal) {
|
|
40
41
|
const rows = await client.http.json({ path: '/v1/projects', signal });
|
package/dist/lib/host.d.ts
CHANGED
|
@@ -39,10 +39,12 @@ export declare const TABS = "https://tabs.hanzo.ai";
|
|
|
39
39
|
* read by `href`/`parse` (@hanzo/build/route): /dev/run/<id>, /dev/projects,
|
|
40
40
|
* /dev/projects/<org>/<repo> a repository, /dev/projects/<slug> a deployed
|
|
41
41
|
* site, /dev/issues, /dev/artifacts, /dev/templates, /dev/automations[/<id>],
|
|
42
|
-
* /dev/machines, /dev/environments, /dev/customize[/<tab>],
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
42
|
+
* /dev/machines, /dev/environments, /dev/customize[/<tab>], /dev/plans. The
|
|
43
|
+
* export holds one page per root, /chat and /dev, and the edge serves it for
|
|
44
|
+
* every path under it (hanzo.ai's lib/edge.ts `APP`), so every address reloads.
|
|
45
|
+
*
|
|
46
|
+
* Settings is none of them: it is the host's own pages, one tree at /settings
|
|
47
|
+
* (`settings`), and no builder section is written under /dev.
|
|
46
48
|
*/
|
|
47
49
|
export declare const CHAT = "/chat";
|
|
48
50
|
export declare const DEV = "/dev";
|
|
@@ -60,8 +62,25 @@ export declare const TERMINALS = "/tabs";
|
|
|
60
62
|
export declare const studio: (at: string) => string;
|
|
61
63
|
/** Chat on a conversation, or /chat itself for a new one. */
|
|
62
64
|
export declare const talk: (id?: string | null) => string;
|
|
63
|
-
/**
|
|
64
|
-
|
|
65
|
+
/**
|
|
66
|
+
* Settings, and every page under it: the person's and the organization's, the
|
|
67
|
+
* host's own pages beside the app (hanzo.ai's app/(app)/settings), served
|
|
68
|
+
* wherever the app is.
|
|
69
|
+
*/
|
|
70
|
+
export declare const SETTINGS = "/settings";
|
|
71
|
+
/** Whether a section of Settings (a builder section, or what `showSettings` asks for) has a page of its own. */
|
|
72
|
+
export declare const paged: (section: string) => boolean;
|
|
73
|
+
/** The Settings page a section names; one with no page of its own opens General, /settings. */
|
|
74
|
+
export declare const settings: (section?: string | null) => string;
|
|
75
|
+
/**
|
|
76
|
+
* The app's address for a builder address (`route` in @hanzo/build): Dev's
|
|
77
|
+
* path, '' New at /dev, and what is not an address New too. A section of the
|
|
78
|
+
* builder's Settings is the Settings page that answers for it (`settings`),
|
|
79
|
+
* never a path under /dev. Machines and Environments, which the builder files
|
|
80
|
+
* under Settings and writes as places of their own (/machines, /environments),
|
|
81
|
+
* stay Dev's.
|
|
82
|
+
*/
|
|
83
|
+
export declare function app(at: string): string;
|
|
65
84
|
/** A place in the app: Chat on a conversation or none, or Dev at a builder address. */
|
|
66
85
|
export type Place = {
|
|
67
86
|
mode: 'chat';
|
|
@@ -95,6 +114,17 @@ export declare const dev: (ref?: string | null) => string;
|
|
|
95
114
|
* history entry into `usePathname`, so the app follows the path it reads.
|
|
96
115
|
*/
|
|
97
116
|
export declare function go(address: string, replace?: boolean): void;
|
|
117
|
+
/** The host's router, as far as moving to a page goes (`useRooms().router`). */
|
|
118
|
+
interface Router {
|
|
119
|
+
push(href: string): void;
|
|
120
|
+
replace(href: string): void;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Move to an address of this site: one of the app's (`place`) by `go`, which
|
|
124
|
+
* the app follows; any other page — Settings — through the host's router, which
|
|
125
|
+
* draws that page. A history entry, or in place of this one.
|
|
126
|
+
*/
|
|
127
|
+
export declare function visit(router: Router, address: string, replace?: boolean): void;
|
|
98
128
|
/** Keep the mode the reader is in, for `resume`. */
|
|
99
129
|
export declare function remember(mode: Place['mode']): void;
|
|
100
130
|
/** Where `/` takes a member: the mode they were last in, Chat when none is kept. */
|
|
@@ -162,3 +192,4 @@ export declare const visits: (hostname: string) => boolean;
|
|
|
162
192
|
* cloud.yaml).
|
|
163
193
|
*/
|
|
164
194
|
export declare const answers: (hostname: string) => boolean;
|
|
195
|
+
export {};
|
package/dist/lib/host.js
CHANGED
|
@@ -42,10 +42,12 @@ export const TABS = 'https://tabs.hanzo.ai';
|
|
|
42
42
|
* read by `href`/`parse` (@hanzo/build/route): /dev/run/<id>, /dev/projects,
|
|
43
43
|
* /dev/projects/<org>/<repo> a repository, /dev/projects/<slug> a deployed
|
|
44
44
|
* site, /dev/issues, /dev/artifacts, /dev/templates, /dev/automations[/<id>],
|
|
45
|
-
* /dev/machines, /dev/environments, /dev/customize[/<tab>],
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
45
|
+
* /dev/machines, /dev/environments, /dev/customize[/<tab>], /dev/plans. The
|
|
46
|
+
* export holds one page per root, /chat and /dev, and the edge serves it for
|
|
47
|
+
* every path under it (hanzo.ai's lib/edge.ts `APP`), so every address reloads.
|
|
48
|
+
*
|
|
49
|
+
* Settings is none of them: it is the host's own pages, one tree at /settings
|
|
50
|
+
* (`settings`), and no builder section is written under /dev.
|
|
49
51
|
*/
|
|
50
52
|
export const CHAT = '/chat';
|
|
51
53
|
export const DEV = '/dev';
|
|
@@ -63,8 +65,40 @@ export const TERMINALS = '/tabs';
|
|
|
63
65
|
export const studio = (at) => (at ? `${STUDIO}/${at.replace(/^\/+/, '')}` : STUDIO);
|
|
64
66
|
/** Chat on a conversation, or /chat itself for a new one. */
|
|
65
67
|
export const talk = (id) => (id ? `${CHAT}/${encodeURIComponent(id)}` : CHAT);
|
|
66
|
-
/**
|
|
67
|
-
|
|
68
|
+
/**
|
|
69
|
+
* Settings, and every page under it: the person's and the organization's, the
|
|
70
|
+
* host's own pages beside the app (hanzo.ai's app/(app)/settings), served
|
|
71
|
+
* wherever the app is.
|
|
72
|
+
*/
|
|
73
|
+
export const SETTINGS = '/settings';
|
|
74
|
+
/** The organization's settings. */
|
|
75
|
+
const ORGANIZATION = `${SETTINGS}/organization`;
|
|
76
|
+
/** Settings' pages, by the name a surface asks for each: General at /settings, the organization's under it. */
|
|
77
|
+
const PAGES = {
|
|
78
|
+
general: SETTINGS,
|
|
79
|
+
organization: ORGANIZATION,
|
|
80
|
+
members: `${ORGANIZATION}/members`,
|
|
81
|
+
billing: `${ORGANIZATION}/billing`,
|
|
82
|
+
usage: `${ORGANIZATION}/usage`,
|
|
83
|
+
keys: `${ORGANIZATION}/keys`,
|
|
84
|
+
};
|
|
85
|
+
/** Whether a section of Settings (a builder section, or what `showSettings` asks for) has a page of its own. */
|
|
86
|
+
export const paged = (section) => Object.hasOwn(PAGES, section);
|
|
87
|
+
/** The Settings page a section names; one with no page of its own opens General, /settings. */
|
|
88
|
+
export const settings = (section) => (section && paged(section) ? PAGES[section] : SETTINGS);
|
|
89
|
+
/**
|
|
90
|
+
* The app's address for a builder address (`route` in @hanzo/build): Dev's
|
|
91
|
+
* path, '' New at /dev, and what is not an address New too. A section of the
|
|
92
|
+
* builder's Settings is the Settings page that answers for it (`settings`),
|
|
93
|
+
* never a path under /dev. Machines and Environments, which the builder files
|
|
94
|
+
* under Settings and writes as places of their own (/machines, /environments),
|
|
95
|
+
* stay Dev's.
|
|
96
|
+
*/
|
|
97
|
+
export function app(at) {
|
|
98
|
+
const r = route(at);
|
|
99
|
+
const to = href(r);
|
|
100
|
+
return r.kind === 'settings' && (to === SETTINGS || to.startsWith(`${SETTINGS}/`)) ? settings(r.section) : DEV + to;
|
|
101
|
+
}
|
|
68
102
|
/** /chat/shared is a page of its own, where a chat shared by link is read (lib/share.ts). */
|
|
69
103
|
const SHARED = 'shared';
|
|
70
104
|
/** The place a path names, or null when it names none of the app's. */
|
|
@@ -137,6 +171,19 @@ export function go(address, replace = false) {
|
|
|
137
171
|
else
|
|
138
172
|
window.history.pushState(null, '', address);
|
|
139
173
|
}
|
|
174
|
+
/**
|
|
175
|
+
* Move to an address of this site: one of the app's (`place`) by `go`, which
|
|
176
|
+
* the app follows; any other page — Settings — through the host's router, which
|
|
177
|
+
* draws that page. A history entry, or in place of this one.
|
|
178
|
+
*/
|
|
179
|
+
export function visit(router, address, replace = false) {
|
|
180
|
+
if (place(address.split(/[?#]/)[0]))
|
|
181
|
+
go(address, replace);
|
|
182
|
+
else if (replace)
|
|
183
|
+
router.replace(address);
|
|
184
|
+
else
|
|
185
|
+
router.push(address);
|
|
186
|
+
}
|
|
140
187
|
/** Where this browser keeps the mode its reader was last in. */
|
|
141
188
|
const MODE = 'hanzo.app.mode';
|
|
142
189
|
/** Keep the mode the reader is in, for `resume`. */
|
|
@@ -160,8 +207,6 @@ export function resume() {
|
|
|
160
207
|
}
|
|
161
208
|
/** The routes under app/(app) the apex keeps: the app — /chat, a chat shared by link, and /dev. */
|
|
162
209
|
const KEPT = new Set(['/chat', '/chat/shared', '/dev']);
|
|
163
|
-
/** Settings, and every page under it: the person's and the organization's, served wherever the app is. */
|
|
164
|
-
const SETTINGS = '/settings';
|
|
165
210
|
/**
|
|
166
211
|
* The workspace's rooms: every route under app/(app) the apex does not keep.
|
|
167
212
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzo/rooms",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.100",
|
|
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",
|