@hanzo/rooms 0.1.99 → 0.1.101
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 +26 -22
- package/dist/Column.js +17 -20
- package/dist/Drawer.d.ts +6 -0
- package/dist/Drawer.js +10 -1
- package/dist/Me.js +4 -6
- package/dist/OrgSwitcher.js +2 -1
- package/dist/Palette.js +9 -13
- package/dist/Rail.d.ts +1 -5
- package/dist/Rail.js +5 -6
- package/dist/dev.js +16 -35
- 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,15 +71,15 @@ 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
|
-
import { empty, openThread, useOpen } from './open.js';
|
|
76
|
+
import { empty, openThread, showSettings, useOpen } from './open.js';
|
|
77
77
|
import { Paned } from './Shell.js';
|
|
78
78
|
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,15 +97,13 @@ 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
|
}
|
|
105
103
|
/** The window itself, drawn once the organization is settled. */
|
|
106
104
|
function Window({ bar }) {
|
|
107
105
|
const drawer = useDrawer();
|
|
108
|
-
const { close: shut, depart } = drawer;
|
|
106
|
+
const { close: shut, depart, exit } = drawer;
|
|
109
107
|
// On a phone the window stands in the visual viewport, so the keyboard never covers the composer (view.ts).
|
|
110
108
|
const view = useView();
|
|
111
109
|
const hold = drawer.window;
|
|
@@ -119,11 +117,12 @@ function Window({ bar }) {
|
|
|
119
117
|
const [finding, setFinding] = useState(false);
|
|
120
118
|
const found = useCallback(() => setFinding(false), []);
|
|
121
119
|
const dev = useDevHost();
|
|
122
|
-
// The builder's moves put the drawer away, in its place in the history
|
|
120
|
+
// The builder's moves put the drawer away, in its place in the history; one to
|
|
121
|
+
// a page outside the app (Settings) leaves it through the router.
|
|
123
122
|
const host = useMemo(() => ({
|
|
124
123
|
...dev,
|
|
125
|
-
go: (to, how) => depart((replace) => dev.go(to, replace ? { ...how, replace } : how)),
|
|
126
|
-
}), [dev, depart]);
|
|
124
|
+
go: (to, how) => (place(app(to)) ? depart : exit)((replace) => dev.go(to, replace ? { ...how, replace } : how)),
|
|
125
|
+
}), [dev, depart, exit]);
|
|
127
126
|
const { client } = useAi();
|
|
128
127
|
const { router, path } = useRooms();
|
|
129
128
|
const here = path ?? '/';
|
|
@@ -146,13 +145,7 @@ function Window({ bar }) {
|
|
|
146
145
|
// A run floats two panes of its own, so the pane it stands in is not cut.
|
|
147
146
|
const run = where !== null && route(where).kind === 'run';
|
|
148
147
|
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 even when
|
|
151
|
-
// the address does not move (the reader was on that page already).
|
|
152
|
-
useEffect(() => {
|
|
153
|
-
if (settings)
|
|
154
|
-
shut();
|
|
155
|
-
}, [settings, shut]);
|
|
148
|
+
const { thread, settings: wanted } = useOpen();
|
|
156
149
|
const plans = where !== null && isPlans(where);
|
|
157
150
|
useEffect(() => {
|
|
158
151
|
if (plans)
|
|
@@ -192,15 +185,26 @@ function Window({ bar }) {
|
|
|
192
185
|
drop();
|
|
193
186
|
}, [mode, here, drop]);
|
|
194
187
|
// Where the reader was before a place that takes the sidebar (Library, Scheduled
|
|
195
|
-
// tasks, Customize
|
|
188
|
+
// tasks, Customize): its Back returns there, Chat or Dev alike.
|
|
196
189
|
const away = useRef('');
|
|
197
|
-
const inPlace = mode === 'dev' &&
|
|
190
|
+
const inPlace = mode === 'dev' && raised(where) !== null;
|
|
198
191
|
useEffect(() => {
|
|
199
192
|
if (!inPlace)
|
|
200
193
|
away.current = `${window.location.pathname}${window.location.search}`;
|
|
201
194
|
}, [inPlace, here]);
|
|
202
|
-
/**
|
|
203
|
-
|
|
195
|
+
/**
|
|
196
|
+
* A move: the drawer away, the address in its place. One of the app's moves
|
|
197
|
+
* the app; any other page of the site (Settings) is the router's, and leaves it.
|
|
198
|
+
*/
|
|
199
|
+
const move = useCallback((address) => (place(address.split(/[?#]/)[0]) ? depart : exit)((replace) => visit(router, address, replace)), [depart, exit, router]);
|
|
200
|
+
// A pane asks for Settings — the account menu's rows, Chat's See usage: its
|
|
201
|
+
// page (lib/host `settings`), through the router, the drawer away.
|
|
202
|
+
useEffect(() => {
|
|
203
|
+
if (!wanted)
|
|
204
|
+
return;
|
|
205
|
+
showSettings(null);
|
|
206
|
+
move(settings(wanted));
|
|
207
|
+
}, [wanted, move]);
|
|
204
208
|
// The app's keys.
|
|
205
209
|
useEffect(() => {
|
|
206
210
|
const key = (e) => {
|
|
@@ -214,7 +218,7 @@ function Window({ bar }) {
|
|
|
214
218
|
const mod = e.metaKey || e.ctrlKey;
|
|
215
219
|
if (mod && !e.shiftKey && !e.altKey && e.key === ',') {
|
|
216
220
|
e.preventDefault();
|
|
217
|
-
move(
|
|
221
|
+
move(settings());
|
|
218
222
|
return;
|
|
219
223
|
}
|
|
220
224
|
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/Drawer.d.ts
CHANGED
|
@@ -9,6 +9,12 @@ export interface Drawer {
|
|
|
9
9
|
close: () => void;
|
|
10
10
|
/** Moves the app from the menu: the move replaces the menu's history entry, and the menu goes. */
|
|
11
11
|
depart: (move: Move) => void;
|
|
12
|
+
/**
|
|
13
|
+
* Leaves the app from the menu for a page the host's router draws: the page
|
|
14
|
+
* takes the menu's history entry, and the menu goes. Nothing is popped, for
|
|
15
|
+
* the router moves the address after this returns.
|
|
16
|
+
*/
|
|
17
|
+
exit: (move: Move) => void;
|
|
12
18
|
/** The address moved under the menu, by a way that was not `depart`: the menu goes, and its entry stays behind the move. */
|
|
13
19
|
drop: () => void;
|
|
14
20
|
/** The window: everything in it but the menu is inert while the menu is out. */
|
package/dist/Drawer.js
CHANGED
|
@@ -31,7 +31,9 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
31
31
|
// back to what opened it as it shuts; Escape closes it, unless a menu or dialog
|
|
32
32
|
// it opened holds the focus; Back closes it, because opening it pushed an entry
|
|
33
33
|
// of the same address that Back pops. A move made from inside it takes that
|
|
34
|
-
// entry's place (`depart`), so the history holds the move and never the menu
|
|
34
|
+
// entry's place (`depart`), so the history holds the move and never the menu;
|
|
35
|
+
// so does a page the host's router draws outside the app (`exit`: Settings),
|
|
36
|
+
// whose address moves only after the press returns.
|
|
35
37
|
// Its list's scroll stays its own (Recents' `overscrollBehavior`), and the pane,
|
|
36
38
|
// never unmounted, keeps its own.
|
|
37
39
|
import { useI18n } from '@hanzo/i18n/react';
|
|
@@ -112,6 +114,12 @@ export function useDrawer() {
|
|
|
112
114
|
window.history.back();
|
|
113
115
|
home();
|
|
114
116
|
}, [home]);
|
|
117
|
+
const exit = useCallback((move) => {
|
|
118
|
+
const replace = marked.current;
|
|
119
|
+
marked.current = false;
|
|
120
|
+
move(replace);
|
|
121
|
+
home();
|
|
122
|
+
}, [home]);
|
|
115
123
|
const drop = useCallback(() => {
|
|
116
124
|
marked.current = false;
|
|
117
125
|
home();
|
|
@@ -287,6 +295,7 @@ export function useDrawer() {
|
|
|
287
295
|
open,
|
|
288
296
|
close,
|
|
289
297
|
depart,
|
|
298
|
+
exit,
|
|
290
299
|
drop,
|
|
291
300
|
window: useCallback((node) => {
|
|
292
301
|
win.current = element(node);
|
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 (App.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) {
|
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`) and a link it opens (`open`) reach that page through the host's router
|
|
21
|
+
// (`visit`); a pane that asks for Settings (`showSettings`, open.ts) is App's to
|
|
22
|
+
// answer, where the phone's menu is. The rooms' shell draws its own Settings
|
|
23
|
+
// 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,
|
|
@@ -27,7 +29,7 @@
|
|
|
27
29
|
// coming back once paid. A typed or reloaded plans address leaves the same way
|
|
28
30
|
// (App.tsx). A page load, at once, not a move inside the app: the pay site is
|
|
29
31
|
// where a plan is bought.
|
|
30
|
-
import {
|
|
32
|
+
import { useMemo } from 'react';
|
|
31
33
|
import { useLook, useRooms } from './host.js';
|
|
32
34
|
import { useIam } from '@hanzo/iam/react';
|
|
33
35
|
import { useSignOut } from './lib/signout.js';
|
|
@@ -36,15 +38,14 @@ 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
|
-
import { showSettings, useOpen } from './open.js';
|
|
43
44
|
/** Where the builder links out: every one of them an address of this site. */
|
|
44
45
|
const LINKS = {
|
|
45
46
|
github: app('-/sync'),
|
|
46
47
|
customize: app('-/customize'),
|
|
47
|
-
settings:
|
|
48
|
+
settings: settings(),
|
|
48
49
|
home: '/',
|
|
49
50
|
};
|
|
50
51
|
/** The builder's address for the plans. */
|
|
@@ -79,24 +80,6 @@ export function useDevHost() {
|
|
|
79
80
|
const { router, path: pathname } = useRooms();
|
|
80
81
|
const here = path(route(at(pathname)));
|
|
81
82
|
const look = useLook();
|
|
82
|
-
const { settings } = useOpen();
|
|
83
|
-
// Settings is top-level /settings; organization settings are at /settings/organization.
|
|
84
|
-
useEffect(() => {
|
|
85
|
-
if (!settings)
|
|
86
|
-
return;
|
|
87
|
-
showSettings(null);
|
|
88
|
-
const s = settings.toLowerCase();
|
|
89
|
-
if (s.includes('member'))
|
|
90
|
-
router.push('/settings/organization/members');
|
|
91
|
-
else if (s.includes('billing'))
|
|
92
|
-
router.push('/settings/organization/billing');
|
|
93
|
-
else if (s.includes('usage'))
|
|
94
|
-
router.push('/settings/organization/usage');
|
|
95
|
-
else if (s.includes('org'))
|
|
96
|
-
router.push('/settings/organization');
|
|
97
|
-
else
|
|
98
|
-
router.push('/settings');
|
|
99
|
-
}, [settings, router]);
|
|
100
83
|
// The token's claims change only when the token does: a sign-in, a refresh.
|
|
101
84
|
const memberships = useMemo(() => orgs(), [accessToken]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
102
85
|
const scoped = useMemo(() => org(), [accessToken]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
@@ -124,7 +107,7 @@ export function useDevHost() {
|
|
|
124
107
|
person: { name, email, avatar },
|
|
125
108
|
admin: administers(bearer(), scoped),
|
|
126
109
|
path: here,
|
|
127
|
-
go: (to, how) => (isPlans(to) ? leave('here') :
|
|
110
|
+
go: (to, how) => (isPlans(to) ? leave('here') : visit(router, app(to), how?.replace)),
|
|
128
111
|
links: LINKS,
|
|
129
112
|
// The sidebar lists Settings' sections and Customize's tabs (Column.tsx), and Customize › Agents offers the cast.
|
|
130
113
|
sections: true,
|
|
@@ -146,10 +129,8 @@ export function useDevHost() {
|
|
|
146
129
|
const to = `${url.pathname}${url.search}${url.hash}`;
|
|
147
130
|
if (url.origin !== window.location.origin)
|
|
148
131
|
window.location.assign(link(url.href));
|
|
149
|
-
else if (place(url.pathname))
|
|
150
|
-
go(to);
|
|
151
132
|
else
|
|
152
|
-
router
|
|
133
|
+
visit(router, to);
|
|
153
134
|
},
|
|
154
135
|
signIn: () => enter(),
|
|
155
136
|
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.101",
|
|
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",
|