@hanzo/rooms 0.1.96 → 0.1.98
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 +33 -37
- package/dist/Chrome.js +3 -1
- package/dist/Column.d.ts +5 -3
- package/dist/Column.js +12 -19
- package/dist/Drawer.d.ts +55 -0
- package/dist/Drawer.js +334 -0
- package/dist/Me.d.ts +2 -2
- package/dist/Me.js +12 -5
- package/dist/Orgs.js +1 -1
- package/dist/Palette.js +1 -1
- package/dist/ProviderMark.js +5 -2
- package/dist/Rail.d.ts +3 -3
- package/dist/Rail.js +33 -16
- package/dist/Recents.js +4 -2
- package/dist/Settings.js +3 -1
- package/dist/Top.js +5 -2
- package/dist/dev.js +14 -6
- package/dist/index.js +0 -1
- package/dist/lib/signout.d.ts +5 -0
- package/dist/lib/signout.js +7 -1
- package/dist/slide.d.ts +24 -0
- package/dist/slide.js +48 -0
- package/package.json +5 -2
package/dist/App.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import {
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
// The web app as the whole window, in three modes: Chat, a conversation with
|
|
4
4
|
// the product's AI; Dev, @hanzo/build's `<Builder>` without a rail of its own;
|
|
5
5
|
// and Studio, @hanzo/studio's `<Studio>` the same way, its places (`nav`) in the
|
|
@@ -47,7 +47,9 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
47
47
|
// nothing fills it. So Dev's pane is cut on its own and the tabs are Chat's.
|
|
48
48
|
//
|
|
49
49
|
// EVERY MOVE PUTS THE DRAWER AWAY, the one to where the reader already is
|
|
50
|
-
// included.
|
|
50
|
+
// included. A phone's drawer (Drawer.tsx) is the menu behind the pane, the pane
|
|
51
|
+
// sliding toward the end side over it; a move made from it takes its place in
|
|
52
|
+
// the history (`depart`), so Back never reopens it.
|
|
51
53
|
//
|
|
52
54
|
// STUDIO LOADS WITH ITS MODE. Its module (and three.js behind a 3D tab) is
|
|
53
55
|
// fetched the first time the reader stands in Studio; its places are a few
|
|
@@ -70,6 +72,7 @@ import { Orgs } from './Orgs.js';
|
|
|
70
72
|
import { useRooms } from './host.js';
|
|
71
73
|
import { useKept } from './kept.js';
|
|
72
74
|
import { Mark, Rail, spot, Toggle } from './Rail.js';
|
|
75
|
+
import { Foot, Layer, Lift, RAISED, useDrawer } from './Drawer.js';
|
|
73
76
|
import { empty, openThread, useOpen } from './open.js';
|
|
74
77
|
import { Paned } from './Shell.js';
|
|
75
78
|
import { Palette } from './Palette.js';
|
|
@@ -87,18 +90,6 @@ const Terminals = lazy(() => import('./Terminals.js'));
|
|
|
87
90
|
const HOME = { mode: 'chat', thread: null };
|
|
88
91
|
/** The bar's height, px: the reference window's title bar. */
|
|
89
92
|
const BAR = 40;
|
|
90
|
-
/**
|
|
91
|
-
* The dim under the phone's drawer: design's scrim at two fifths, so the page
|
|
92
|
-
* still reads through the drawer's glass. gui carries a `color-mix()` to the
|
|
93
|
-
* page unchanged; its types only name custom properties.
|
|
94
|
-
*/
|
|
95
|
-
const DIM = 'color-mix(in srgb, var(--surface-scrim) 40%, transparent)';
|
|
96
|
-
/** The phone's drawer stands the gutter off each edge, or the device's inset where that is wider. */
|
|
97
|
-
const INSET = {
|
|
98
|
-
top: 'max(var(--pane-gap), var(--safe-top))',
|
|
99
|
-
bottom: 'max(var(--pane-gap), var(--safe-bottom))',
|
|
100
|
-
left: 'max(var(--pane-gap), var(--safe-left))',
|
|
101
|
-
};
|
|
102
93
|
/** Where each mode was last, so the rail goes back to it. */
|
|
103
94
|
const last = { chat: '', dev: '', studio: '', tabs: '' };
|
|
104
95
|
/** Whether a key press is the reader typing, where the app's keys are the field's. */
|
|
@@ -113,24 +104,26 @@ export function App({ bar } = {}) {
|
|
|
113
104
|
}
|
|
114
105
|
/** The window itself, drawn once the organization is settled. */
|
|
115
106
|
function Window({ bar }) {
|
|
116
|
-
const
|
|
107
|
+
const drawer = useDrawer();
|
|
108
|
+
const { close: shut, depart } = drawer;
|
|
117
109
|
// On a phone the window stands in the visual viewport, so the keyboard never covers the composer (view.ts).
|
|
118
110
|
const view = useView();
|
|
119
|
-
const
|
|
111
|
+
const hold = drawer.window;
|
|
112
|
+
const windowRef = useCallback((node) => {
|
|
113
|
+
view(node);
|
|
114
|
+
hold(node);
|
|
115
|
+
}, [view, hold]);
|
|
120
116
|
// The sidebar shut, kept per browser under the key the builder's rail kept it under.
|
|
121
117
|
const [closed, setClosed] = useKept('hanzo.build.rail', false);
|
|
122
118
|
// The palette is open (the sidebar's search; ⌘K toggles it).
|
|
123
119
|
const [finding, setFinding] = useState(false);
|
|
124
120
|
const found = useCallback(() => setFinding(false), []);
|
|
125
121
|
const dev = useDevHost();
|
|
126
|
-
// The builder's moves put the drawer away
|
|
122
|
+
// The builder's moves put the drawer away, in its place in the history.
|
|
127
123
|
const host = useMemo(() => ({
|
|
128
124
|
...dev,
|
|
129
|
-
go: (to, how) => {
|
|
130
|
-
|
|
131
|
-
dev.go(to, how);
|
|
132
|
-
},
|
|
133
|
-
}), [dev, shut]);
|
|
125
|
+
go: (to, how) => depart((replace) => dev.go(to, replace ? { ...how, replace } : how)),
|
|
126
|
+
}), [dev, depart]);
|
|
134
127
|
const { client } = useAi();
|
|
135
128
|
const { router, path } = useRooms();
|
|
136
129
|
const here = path ?? '/';
|
|
@@ -144,15 +137,12 @@ function Window({ bar }) {
|
|
|
144
137
|
...host,
|
|
145
138
|
name: 'Hanzo Studio',
|
|
146
139
|
path: made,
|
|
147
|
-
go: (to, how) =>
|
|
148
|
-
shut();
|
|
149
|
-
go(studio(to), how?.replace);
|
|
150
|
-
},
|
|
140
|
+
go: (to, how) => depart((replace) => go(studio(to), replace || how?.replace)),
|
|
151
141
|
chooseOrg: (next) => {
|
|
152
142
|
pick(next);
|
|
153
143
|
window.location.assign(studio(''));
|
|
154
144
|
},
|
|
155
|
-
}), [host, made,
|
|
145
|
+
}), [host, made, depart]);
|
|
156
146
|
// A run floats two panes of its own, so the pane it stands in is not cut.
|
|
157
147
|
const run = where !== null && route(where).kind === 'run';
|
|
158
148
|
const asked = at.mode === 'chat' ? at.thread : null;
|
|
@@ -161,8 +151,8 @@ function Window({ bar }) {
|
|
|
161
151
|
// the address does not move (the reader was on that page already).
|
|
162
152
|
useEffect(() => {
|
|
163
153
|
if (settings)
|
|
164
|
-
|
|
165
|
-
}, [settings]);
|
|
154
|
+
shut();
|
|
155
|
+
}, [settings, shut]);
|
|
166
156
|
const plans = where !== null && isPlans(where);
|
|
167
157
|
useEffect(() => {
|
|
168
158
|
if (plans)
|
|
@@ -195,10 +185,12 @@ function Window({ bar }) {
|
|
|
195
185
|
// The selection writes the address only when it moves.
|
|
196
186
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
197
187
|
}, [mode, thread]);
|
|
188
|
+
// The address moved under the drawer: it goes, and the history keeps the move.
|
|
189
|
+
const drop = drawer.drop;
|
|
198
190
|
useEffect(() => {
|
|
199
191
|
last[mode] = `${window.location.pathname}${window.location.search}`;
|
|
200
|
-
|
|
201
|
-
}, [mode, here]);
|
|
192
|
+
drop();
|
|
193
|
+
}, [mode, here, drop]);
|
|
202
194
|
// Where the reader was before a place that takes the sidebar (Library, Scheduled
|
|
203
195
|
// tasks, Customize, Settings): its Back returns there, Chat or Dev alike.
|
|
204
196
|
const away = useRef('');
|
|
@@ -207,11 +199,8 @@ function Window({ bar }) {
|
|
|
207
199
|
if (!inPlace)
|
|
208
200
|
away.current = `${window.location.pathname}${window.location.search}`;
|
|
209
201
|
}, [inPlace, here]);
|
|
210
|
-
/** A move of the app's own: the drawer away,
|
|
211
|
-
const move = useCallback((address) =>
|
|
212
|
-
shut();
|
|
213
|
-
go(address);
|
|
214
|
-
}, [shut]);
|
|
202
|
+
/** A move of the app's own: the drawer away, the address in its place. */
|
|
203
|
+
const move = useCallback((address) => depart((replace) => go(address, replace)), [depart]);
|
|
215
204
|
// The app's keys.
|
|
216
205
|
useEffect(() => {
|
|
217
206
|
const key = (e) => {
|
|
@@ -248,6 +237,11 @@ function Window({ bar }) {
|
|
|
248
237
|
const toDev = () => move(last.dev || app(''));
|
|
249
238
|
const toStudio = () => move(last.studio || studio(''));
|
|
250
239
|
const toTabs = () => move(TERMINALS);
|
|
240
|
+
/** The drawer's New chat. */
|
|
241
|
+
const fresh = () => {
|
|
242
|
+
empty();
|
|
243
|
+
move(talk());
|
|
244
|
+
};
|
|
251
245
|
// Another organization: picked, and the app opened in it at the mode's New.
|
|
252
246
|
const org = (o) => {
|
|
253
247
|
pick(o);
|
|
@@ -259,5 +253,7 @@ function Window({ bar }) {
|
|
|
259
253
|
shut();
|
|
260
254
|
setFinding(true);
|
|
261
255
|
};
|
|
262
|
-
return (_jsx(AiProvider, { client: client ?? undefined, children: _jsx(HostProvider, { host: host, children: _jsx(PrefsProvider, { children: _jsxs(YStack, { ref:
|
|
256
|
+
return (_jsx(AiProvider, { client: client ?? undefined, children: _jsx(HostProvider, { host: host, children: _jsx(PrefsProvider, { children: _jsxs(YStack, { ref: windowRef, "data-slot": "window", ...(bar ? floor : null), flex: 1, minW: 0, height: "100dvh", overflow: "hidden", "$max-md": { position: 'fixed', t: TOP, l: 0, r: 0, height: HEIGHT, ...{ overflow: 'clip' } }, children: [bar ? (_jsxs(XStack, { "data-slot": "title-bar", height: BAR, shrink: 0, items: "center", gap: 2, pl: (bar.inset ?? 0) + 6, pr: "$2", ...bar.drag, children: [_jsx(XStack, { "$max-md": { display: 'none' }, children: _jsx(Toggle, { open: !closed, onPress: flip }) }), _jsx(XStack, { $md: { display: 'none' }, children: _jsx(Toggle, { open: drawer.state === 'open', onPress: drawer.open, label: "Open sidebar" }) }), _jsx(Mark, { label: "Back", "data-slot": "back", onPress: () => router.back(), children: _jsx(ArrowLeft, { size: 18, "aria-hidden": true }) }), _jsx(Mark, { label: "Forward", "data-slot": "forward", onPress: () => router.forward(), children: _jsx(ArrowRight, { size: 18, "aria-hidden": true }) }), _jsx(XStack, { flex: 1, self: "stretch", ...bar.drag })] })) : null, _jsxs(XStack, { ref: drawer.pane, "data-room": "", "data-app": mode, "data-slot": "frame", ...(bar ? null : floor), ...(drawer.slide ? RAISED : null),
|
|
257
|
+
// inline-style: the slide, a transform with no logical form whose sign and distance the drawer writes on the element itself, a drag every frame (Drawer.tsx).
|
|
258
|
+
style: drawer.slide ?? undefined, flex: 1, minW: 0, minH: 0, pr: "var(--pane-gap)", pb: "var(--pane-gap)", pt: bar ? 0 : 'var(--pane-gap)', "$max-md": { p: 0 }, children: [drawer.slide ? _jsx(Lift, {}) : null, _jsx(YStack, { height: "100%", "$max-md": { display: 'none' }, children: _jsx(Rail, { host: host, mode: mode, at: where, open: !closed, toggle: bar ? undefined : flip, onChat: toChat, onDev: toDev, onStudio: toStudio, onTabs: toTabs, go: move, onOrg: org }) }), closed || mode === 'tabs' ? null : (_jsx(YStack, { "$max-md": { display: 'none' }, children: _jsx(Column, { host: host, mode: mode, thread: thread, go: move, onShut: () => setClosed(true), onSeek: seek, onBack: back, places: sidebar }) })), _jsxs(YStack, { "data-slot": "stage", flex: 1, minW: 0, minH: 0, ml: "var(--pane-gap)", "$max-md": { ml: 0, pb: FOOT }, children: [bar ? null : _jsx(Top, { host: host, mode: mode, places: sidebar, go: move, onMenu: drawer.open, onChat: toChat, onDev: toDev }), _jsx(XStack, { "data-slot": "panes", flex: 1, minW: 0, minH: 0, children: mode === 'chat' ? (_jsx(Paned, { framed: true, children: _jsx(Chat, {}) })) : mode === 'tabs' ? (_jsx(YStack, { render: "main", "data-slot": "pane", ...pane(), overflow: "hidden", flex: 1, minW: 0, minH: 0, children: _jsx(Suspense, { fallback: null, children: _jsx(Terminals, {}) }) })) : mode === 'studio' ? (_jsx(YStack, { render: "main", "data-slot": "pane", ...pane(), overflow: "hidden", flex: 1, minW: 0, minH: 0, children: _jsx(YStack, { flex: 1, minH: 0, height: "100%", children: _jsx(Suspense, { fallback: null, children: _jsx(Studio, { host: maker, rail: false }) }) }) })) : (_jsx(YStack, { render: "main", "data-slot": "pane", ...(run ? null : { ...pane(), overflow: 'hidden' }), flex: 1, minW: 0, minH: 0, children: plans ? null : _jsx(Builder, { host: host, rail: false }) })) })] })] }), drawer.state === 'shut' ? null : (_jsx(Layer, { drawer: drawer, top: bar ? BAR : 0, children: _jsx(Column, { host: host, mode: mode, thread: thread, go: move, onShut: shut, onSeek: seek, onBack: back, drawer: true, places: sidebar, rail: _jsx(Rail, { host: host, mode: mode, at: where, open: true, rows: true, onChat: toChat, onDev: toDev, onStudio: toStudio, onTabs: toTabs, go: move, onOrg: org }), foot: _jsx(Foot, { host: host, onChat: fresh, onOrg: org }) }) })), finding ? _jsx(Palette, { go: move, onClose: found, mode: mode, host: host }) : null] }) }) }) }));
|
|
263
259
|
}
|
package/dist/Chrome.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useI18n } from '@hanzo/i18n/react';
|
|
3
4
|
import { useIam } from '@hanzo/iam/react';
|
|
4
5
|
import { Text, View, XStack, YStack } from '@hanzo/gui';
|
|
5
6
|
import { Avatar, Button, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from '@hanzo/ui';
|
|
@@ -13,6 +14,7 @@ const HEIGHT = 60;
|
|
|
13
14
|
/** The notch's inset. gui carries a CSS function to the page unchanged; its types only name custom properties. */
|
|
14
15
|
const NOTCH = 'env(safe-area-inset-top)';
|
|
15
16
|
export function Chrome({ back }) {
|
|
17
|
+
const { t } = useI18n();
|
|
16
18
|
const { user, isAuthenticated } = useIam();
|
|
17
19
|
const signOut = useSignOut();
|
|
18
20
|
const claimed = named();
|
|
@@ -27,5 +29,5 @@ export function Chrome({ back }) {
|
|
|
27
29
|
};
|
|
28
30
|
const out = () => void signOut();
|
|
29
31
|
const signed = isAuthenticated && Boolean(who);
|
|
30
|
-
return (_jsxs(XStack, { render: "header", "data-slot": "masthead", position: "sticky", t: 0, z: "var(--z-header)", shrink: 0, boxSizing: "content-box", height: HEIGHT, pt: NOTCH, px: "var(--page-gutter, 20px)", gap: 12, items: "center", bg: "var(--background)", borderBottomWidth: 1, borderColor: "var(--border)", children: [_jsx(Text, { render: _jsx("a", { href: "/", "aria-label":
|
|
32
|
+
return (_jsxs(XStack, { render: "header", "data-slot": "masthead", position: "sticky", t: 0, z: "var(--z-header)", shrink: 0, boxSizing: "content-box", height: HEIGHT, pt: NOTCH, px: "var(--page-gutter, 20px)", gap: 12, items: "center", bg: "var(--background)", borderBottomWidth: 1, borderColor: "var(--border)", children: [_jsx(Text, { render: _jsx("a", { href: "/", "aria-label": t('{brandName} home', { brandName: brand() }) }), display: "flex", items: "center", shrink: 0, color: "var(--foreground)", textDecorationLine: "none", children: _jsx(HanzoLockup, { label: brand(), size: 22 }) }), back, _jsx(View, { flex: 1 }), signed ? (_jsxs(_Fragment, { children: [_jsxs(XStack, { render: "section", "aria-label": t('Account'), items: "center", gap: 12, minW: 0, "$max-md": { display: 'none' }, children: [_jsx(Avatar, { src: avatar || null, name: who, alt: "" }), _jsxs(YStack, { minW: 0, gap: 2, children: [_jsx(Text, { fontSize: "$3", fontWeight: "500", color: "$ink", numberOfLines: 1, children: who }), name && email ? (_jsx(Text, { fontSize: "$2", color: "$soft", numberOfLines: 1, children: email })) : null] }), _jsx(Button, { variant: "ghost", size: "sm", onClick: other, children: t('Use another account') }), _jsx(Button, { variant: "ghost", size: "sm", onClick: out, children: t('Log out') })] }), _jsx(View, { display: "none", "$max-md": { display: 'flex' }, children: _jsxs(DropdownMenu, { placement: "bottom-end", offset: 8, children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(XStack, { render: "button", "aria-label": t('Account: {name}', { name: who }), width: 40, height: 40, rounded: 999, items: "center", justify: "center", borderWidth: 0, bg: "transparent", hoverStyle: { bg: '$hover' }, focusVisibleStyle: { outlineWidth: 2, outlineStyle: 'solid', outlineColor: '$outlineColor' }, cursor: "pointer", children: _jsx(Avatar, { src: avatar || null, name: who, alt: "" }) }) }), _jsxs(DropdownMenuContent, { minW: 240, maxW: "calc(100vw - 16px)", "aria-labelledby": undefined, "aria-label": t('Account'), children: [_jsx(DropdownMenuLabel, { children: _jsxs(YStack, { minW: 0, gap: 2, children: [_jsx(Text, { fontSize: "$3", fontWeight: "500", color: "$ink", numberOfLines: 1, children: who }), name && email ? (_jsx(Text, { fontSize: "$2", color: "$soft", numberOfLines: 1, children: email })) : null] }) }), _jsx(DropdownMenuSeparator, {}), _jsx(DropdownMenuItem, { onSelect: other, children: _jsx(Text, { fontSize: "$3", color: "$ink", children: t('Use another account') }) }), _jsx(DropdownMenuItem, { onSelect: out, children: _jsx(Text, { fontSize: "$3", color: "$ink", children: t('Log out') }) })] })] }) })] })) : null] }));
|
|
31
33
|
}
|
package/dist/Column.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { type Host } from '@hanzo/build';
|
|
|
2
2
|
import { type RailLink } from '@hanzo/ui/chat';
|
|
3
3
|
import { type ReactNode } from 'react';
|
|
4
4
|
import { type Place } from './lib/host.js';
|
|
5
|
-
export declare function Column({ host, mode, thread, go, onShut, onSeek, onBack, drawer, foot, places, }: {
|
|
5
|
+
export declare function Column({ host, mode, thread, go, onShut, onSeek, onBack, drawer, rail, foot, places, }: {
|
|
6
6
|
host: Host;
|
|
7
7
|
mode: Place['mode'];
|
|
8
8
|
/** The Chat conversation the pane holds, or null. */
|
|
@@ -15,9 +15,11 @@ export declare function Column({ host, mode, thread, go, onShut, onSeek, onBack,
|
|
|
15
15
|
onSeek?: () => void;
|
|
16
16
|
/** Leaves a place that took the sidebar, for where the reader was before it. */
|
|
17
17
|
onBack: () => void;
|
|
18
|
-
/** Drawn in the phone's drawer: no edge,
|
|
18
|
+
/** Drawn in the phone's drawer (Drawer.tsx): on the floor behind the pane, no edge, no handle. */
|
|
19
19
|
drawer?: boolean;
|
|
20
|
-
/** What stands
|
|
20
|
+
/** What stands under the mode's own rows: the drawer's places, the rail's as rows. */
|
|
21
|
+
rail?: ReactNode;
|
|
22
|
+
/** What stands at its foot: the drawer's account and New chat. */
|
|
21
23
|
foot?: ReactNode;
|
|
22
24
|
/** The mode's places, where they are not Dev's (Studio's `nav`). */
|
|
23
25
|
places?: {
|
package/dist/Column.js
CHANGED
|
@@ -3,7 +3,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
// The app's sidebar, beside the rail on the window's floor (App.tsx): the
|
|
4
4
|
// mode's name with the bell and the search at its right, New, Dev's places in
|
|
5
5
|
// Dev, the reader's Recents (Pinned first), and the first-run cards. Which
|
|
6
|
-
// product it lists is the rail's to say; the account is at the rail's foot
|
|
6
|
+
// product it lists is the rail's to say; the account is at the rail's foot, or
|
|
7
|
+
// on a phone at the foot of its menu (Drawer.tsx), where this column is the menu.
|
|
7
8
|
//
|
|
8
9
|
// It is the window, not a panel: no fill, no edge, the floor's own ground. The
|
|
9
10
|
// content beside it is the panel, inset by the gutter.
|
|
@@ -20,6 +21,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
20
21
|
// Its width is the edge's in the gutter to its right: dragged, keyed or
|
|
21
22
|
// double-clicked back (@hanzo/build's `Grip`), kept per browser. Pulled in past
|
|
22
23
|
// its floor it shuts (`onShut`), as the sidebar's toggle shuts it.
|
|
24
|
+
import { useI18n } from '@hanzo/i18n/react';
|
|
23
25
|
import { Text, XStack, YStack } from '@hanzo/gui';
|
|
24
26
|
import { ArrowLeft, Bell, Search, Settings2, SquarePen } from 'lucide-react';
|
|
25
27
|
import { DOTS, Gutter, library, NEW, own, path as written, sections, tabs, useScheduled, useSessions } from '@hanzo/build';
|
|
@@ -34,22 +36,11 @@ import { Setup } from './Setup.js';
|
|
|
34
36
|
import { ago } from './recent.js';
|
|
35
37
|
import { empty } from './open.js';
|
|
36
38
|
import { useSpan } from './span.js';
|
|
37
|
-
import { pane } from './ground.js';
|
|
38
39
|
import { talk } from './lib/host.js';
|
|
39
40
|
import { brand, wordmark } from './where.js';
|
|
40
41
|
/** The sidebar fills the column. Its `width` is typed in px alone, though its frame takes any gui size — the type is
|
|
41
42
|
* @hanzo/ui's to widen. */
|
|
42
43
|
const FILL = { width: '100%' };
|
|
43
|
-
/**
|
|
44
|
-
* The phone's drawer floats over the page under the browser's own glass bar, so
|
|
45
|
-
* it is cut like that bar: the composer's corner, the strong hairline, the lit
|
|
46
|
-
* top edge and the floating drop. Its fill and blur stay the pane's.
|
|
47
|
-
*/
|
|
48
|
-
const FLOAT = {
|
|
49
|
-
borderRadius: 'var(--radius-composer)',
|
|
50
|
-
borderColor: 'var(--border-strong)',
|
|
51
|
-
boxShadow: 'var(--edge-highlight), var(--shadow-floating)',
|
|
52
|
-
};
|
|
53
44
|
/** The sidebar's width, px: its default, and the two a drag holds it between. */
|
|
54
45
|
const WIDTH = 240;
|
|
55
46
|
const FLOOR = 200;
|
|
@@ -65,15 +56,16 @@ const keys = (press) => ({
|
|
|
65
56
|
press();
|
|
66
57
|
},
|
|
67
58
|
});
|
|
68
|
-
/** A row of the sidebar: a pill on a 31px pitch, lit where the reader is. */
|
|
69
|
-
const ROW = { rounded: '$4', minH: 31, py: 0, px: '$2' };
|
|
59
|
+
/** A row of the sidebar: a pill on a 31px pitch, a thumb's 44 on a touch screen, lit where the reader is. */
|
|
60
|
+
const ROW = { rounded: '$4', minH: 31, py: 0, px: '$2', $touchable: { minH: 44 } };
|
|
70
61
|
/** The run activity behind the bell: the runs that moved last, newest first. */
|
|
71
62
|
const RECENT = 6;
|
|
72
63
|
/** What a place that takes the sidebar is called at its head. */
|
|
73
64
|
const TITLE = { library: 'Library', scheduled: 'Scheduled tasks', customize: 'Customize', settings: 'Settings' };
|
|
74
|
-
/** A header mark: a 16px glyph in a 32px target. */
|
|
75
|
-
const HEAD = { width: 32, height: 32, rounded: '$3' };
|
|
65
|
+
/** A header mark: a 16px glyph in a 32px target, a thumb's 44 on a touch screen. */
|
|
66
|
+
const HEAD = { width: 32, height: 32, rounded: '$3', $touchable: { width: 44, height: 44 } };
|
|
76
67
|
function Activity({ host }) {
|
|
68
|
+
const { t } = useI18n();
|
|
77
69
|
const store = useSessions(host);
|
|
78
70
|
const latest = [...store.value].sort((a, b) => (a.updatedAt < b.updatedAt ? 1 : -1)).slice(0, RECENT);
|
|
79
71
|
const items = [
|
|
@@ -90,9 +82,10 @@ function Activity({ host }) {
|
|
|
90
82
|
{ type: 'separator', key: 'line' },
|
|
91
83
|
{ key: 'settings', label: 'Notification settings', icon: _jsx(Settings2, { size: 16, "aria-hidden": true }), onSelect: () => host.go(written({ kind: 'settings', section: 'notifications' })) },
|
|
92
84
|
];
|
|
93
|
-
return (_jsx(DropdownMenu, { minWidth: 280, trigger: _jsx(XStack, { render: "button", "aria-label": "Activity", ...{ title: 'Activity' }, ...HEAD, items: "center", justify: "center", cursor: "pointer", opacity: 0.7, hoverStyle: { bg: '$hover', opacity: 1 }, focusVisibleStyle: { outlineWidth: 2, outlineStyle: 'solid', outlineColor: '$outlineColor' }, children: _jsx(Bell, { size: 16, "aria-hidden": true }) }), items: items }));
|
|
85
|
+
return (_jsx(DropdownMenu, { minWidth: 280, trigger: _jsx(XStack, { render: "button", "aria-label": t("Activity"), ...{ title: 'Activity' }, ...HEAD, items: "center", justify: "center", cursor: "pointer", opacity: 0.7, hoverStyle: { bg: '$hover', opacity: 1 }, focusVisibleStyle: { outlineWidth: 2, outlineStyle: 'solid', outlineColor: '$outlineColor' }, children: _jsx(Bell, { size: 16, "aria-hidden": true }) }), items: items }));
|
|
94
86
|
}
|
|
95
|
-
export function Column({ host, mode, thread, go, onShut, onSeek, onBack, drawer = false, foot, places, }) {
|
|
87
|
+
export function Column({ host, mode, thread, go, onShut, onSeek, onBack, drawer = false, rail, foot, places, }) {
|
|
88
|
+
const { t } = useI18n();
|
|
96
89
|
// The desktop sidebar and a phone's drawer can both be in the page: each names its own group labels.
|
|
97
90
|
const uid = useId();
|
|
98
91
|
const span = useSpan(SPAN, WIDTH, FLOOR, CEIL);
|
|
@@ -118,5 +111,5 @@ export function Column({ host, mode, thread, go, onShut, onSeek, onBack, drawer
|
|
|
118
111
|
};
|
|
119
112
|
return (_jsxs(YStack, { "data-slot": "app-column", ref: drawer ? undefined : span.ref,
|
|
120
113
|
// inline-style: `--span`, the width a drag rewrites on the element itself (span.ts); no prop sets a variable.
|
|
121
|
-
style: drawer ? undefined : span.style, position: "relative", flex: drawer ? 1 : undefined, shrink: drawer ? 1 : 0, minW: 0, height: "100%", width: drawer ? undefined : 'var(--span)', children: [_jsxs(Sidebar, { "data-slot": "app-rail", role: "navigation", "aria-label": brand(),
|
|
114
|
+
style: drawer ? undefined : span.style, position: "relative", flex: drawer ? 1 : undefined, shrink: drawer ? 1 : 0, minW: 0, height: "100%", width: drawer ? undefined : 'var(--span)', children: [_jsxs(Sidebar, { "data-slot": "app-rail", role: "navigation", "aria-label": brand(), bg: "transparent", borderWidth: 0, ...FILL, px: "$2", pt: drawer ? '$2' : 0, pb: "$2", gap: "$0", children: [at ? (_jsxs(XStack, { "data-slot": "app-head", items: "center", gap: "$1", pl: 2, pr: 2, height: 52, shrink: 0, minW: 0, children: [_jsx(SidebarIconButton, { label: t("Back"), "data-slot": "place-back", onPress: onBack, ...HEAD, children: _jsx(ArrowLeft, { size: 16, "aria-hidden": true }) }), _jsx(Text, { flex: 1, minW: 0, fontSize: 18, lineHeight: 24, fontWeight: "600", color: "$ink", numberOfLines: 1, "data-slot": "place-name", children: TITLE[at] })] })) : (_jsxs(XStack, { "data-slot": "app-head", items: "center", gap: "$1", pl: "$2", pr: 2, height: 52, shrink: 0, minW: 0, children: [_jsx(Text, { flex: 1, minW: 0, fontSize: 18, lineHeight: 24, fontWeight: "600", color: "$ink", numberOfLines: 1, children: wordmark(mode) }), _jsx(Activity, { host: host }), _jsx(SidebarIconButton, { label: t("Search"), onPress: onSeek, "aria-haspopup": "dialog", ...HEAD, children: _jsx(Search, { size: 16, "aria-hidden": true }) })] })), mode === 'chat' ? (_jsx(YStack, { role: "list", gap: 2, shrink: 0, children: _jsx(YStack, { role: "listitem", children: _jsx(SidebarItem, { "data-slot": "app-new", ...ROW, active: !thread, bg: "transparent", hoverStyle: { bg: '$hover' }, icon: _jsx(SquarePen, { size: 16, "aria-hidden": true }), onPress: create, ...keys(create), children: _jsx(Text, { fontSize: "$3", color: "$ink", children: t("New chat") }) }) }) })) : (_jsx(YStack, { "data-slot": "app-places", shrink: 0, children: groups.map((g, i) => (_jsxs(YStack, { pt: i ? '$2' : 0, children: [i && g.label ? (_jsx(Text, { id: `${uid}-${g.id}`, pl: "$2", pb: 2, fontSize: "$3", color: "$soft", children: g.label })) : null, _jsx(YStack, { role: "list", "aria-labelledby": i && g.label ? `${uid}-${g.id}` : undefined, "aria-label": i && g.label ? undefined : g.label || (at ? TITLE[at] : g.id), gap: 1, children: g.links.map((link) => (_jsx(YStack, { role: "listitem", children: _jsx(SidebarItem, { ...ROW, ...(link.id === NEW ? { 'data-slot': 'app-new' } : {}), active: link.active, icon: link.id === NEW ? _jsx(SquarePen, { size: 16, "aria-hidden": true }) : link.icon, onPress: link.onPress, ...keys(() => link.onPress?.()), children: _jsx(Text, { fontSize: "$3", color: link.active ? '$ink' : '$quiet', children: link.label }) }) }, link.id))) })] }, g.id))) })), rail, mode === 'studio' || mode === 'tabs' || at ? null : _jsx(Recents, { host: host, mode: mode, thread: thread, onOpen: open, onNew: create }), at ? null : (_jsxs(YStack, { gap: "$2", pt: "$2", shrink: 0, children: [_jsx(Made, {}), _jsx(Setup, {})] })), foot] }), drawer ? null : (_jsx(Gutter, { side: "left", span: span.span, floor: FLOOR, ceil: CEIL, reset: WIDTH, onSpan: span.move, onKeep: span.keep, onShut: onShut, label: t("Resize sidebar") }))] }));
|
|
122
115
|
}
|
package/dist/Drawer.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { type CSSProperties, type ReactNode } from 'react';
|
|
2
|
+
import { type Who } from './Me.js';
|
|
3
|
+
import { type State } from './slide.js';
|
|
4
|
+
/** A press that moves the app: told whether it should take the menu's place in history. */
|
|
5
|
+
export type Move = (replace: boolean) => void;
|
|
6
|
+
export interface Drawer {
|
|
7
|
+
state: State;
|
|
8
|
+
open: () => void;
|
|
9
|
+
close: () => void;
|
|
10
|
+
/** Moves the app from the menu: the move replaces the menu's history entry, and the menu goes. */
|
|
11
|
+
depart: (move: Move) => void;
|
|
12
|
+
/** The address moved under the menu, by a way that was not `depart`: the menu goes, and its entry stays behind the move. */
|
|
13
|
+
drop: () => void;
|
|
14
|
+
/** The window: everything in it but the menu is inert while the menu is out. */
|
|
15
|
+
window: (node: unknown) => void;
|
|
16
|
+
/** The pane that slides. */
|
|
17
|
+
pane: (node: unknown) => void;
|
|
18
|
+
/** The menu's layer. */
|
|
19
|
+
layer: (node: unknown) => void;
|
|
20
|
+
/** The pane's slide, for its `style`: null while shut, so nothing is transformed and nothing is raised. */
|
|
21
|
+
slide: CSSProperties | null;
|
|
22
|
+
}
|
|
23
|
+
export declare function useDrawer(): Drawer;
|
|
24
|
+
/**
|
|
25
|
+
* The pane while the menu is out: raised over the floor, its edge drawn and a
|
|
26
|
+
* drop falling on the menu, clipped to its corners. Spread on the pane beside
|
|
27
|
+
* `slide`; `Lift` is its fill.
|
|
28
|
+
*/
|
|
29
|
+
export declare const RAISED: {
|
|
30
|
+
readonly z: 'var(--z-raised)';
|
|
31
|
+
readonly overflow: 'hidden';
|
|
32
|
+
readonly boxShadow: '0 0 0 1px var(--pane-edge), var(--shadow-floating)';
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* The raised pane's fill: the pane's own material composited opaque, a step
|
|
36
|
+
* brighter than the floor the menu stands on, in both themes. Drawn UNDER the
|
|
37
|
+
* pane's content (`--z-under` inside the pane's own stacking) rather than as the
|
|
38
|
+
* pane's background, which is the frame's and a host may paint it at rest.
|
|
39
|
+
*/
|
|
40
|
+
export declare function Lift(): import("react").JSX.Element;
|
|
41
|
+
/**
|
|
42
|
+
* The menu's layer: the menu at the start on the floor, and over the strip of
|
|
43
|
+
* pane left in sight the press that closes it. Drawn while the drawer is not shut.
|
|
44
|
+
*/
|
|
45
|
+
export declare function Layer({ drawer, top, children }: {
|
|
46
|
+
drawer: Drawer;
|
|
47
|
+
top?: number;
|
|
48
|
+
children: ReactNode;
|
|
49
|
+
}): import("react").JSX.Element;
|
|
50
|
+
/** The menu's foot: the account (its menu opens above it) and the accent New chat. */
|
|
51
|
+
export declare function Foot({ host, onChat, onOrg }: {
|
|
52
|
+
host: Who;
|
|
53
|
+
onChat: () => void;
|
|
54
|
+
onOrg: (org: string) => void;
|
|
55
|
+
}): import("react").JSX.Element;
|