@hanzo/rooms 0.1.57 → 0.1.59
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.d.ts +13 -1
- package/dist/App.js +117 -36
- package/dist/Chat.js +55 -13
- package/dist/Column.d.ts +10 -6
- package/dist/Column.js +77 -54
- package/dist/Home.js +7 -7
- package/dist/Inbox.js +1 -1
- package/dist/Me.d.ts +10 -2
- package/dist/Me.js +36 -9
- package/dist/Meet.js +1 -1
- package/dist/Prose.js +16 -9
- package/dist/Rail.d.ts +39 -0
- package/dist/Rail.js +85 -0
- package/dist/Recents.js +24 -12
- package/dist/RightPane.d.ts +23 -0
- package/dist/RightPane.js +63 -11
- package/dist/Share.js +2 -2
- package/dist/Shell.js +7 -8
- package/dist/Talk.js +3 -3
- package/dist/recent.js +1 -1
- package/dist/rooms.js +2 -2
- package/dist/thread.d.ts +8 -0
- package/dist/thread.js +15 -0
- package/dist/where.d.ts +2 -0
- package/dist/where.js +2 -0
- package/package.json +2 -2
- package/dist/ModeMark.d.ts +0 -8
- package/dist/ModeMark.js +0 -19
package/dist/Column.js
CHANGED
|
@@ -1,38 +1,39 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import {
|
|
3
|
-
// The app's
|
|
4
|
-
//
|
|
5
|
-
//
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
// The app's sidebar, beside the rail on the window's floor (App.tsx): the
|
|
4
|
+
// mode's name with the bell and the search at its right, New, Dev's places in
|
|
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
7
|
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
8
|
+
// It is the window, not a panel: no fill, no edge, the floor's own ground. The
|
|
9
|
+
// content beside it is the panel, inset by the gutter.
|
|
10
|
+
//
|
|
11
|
+
// The bell is the org's run activity (@hanzo/build `useSessions`): the runs
|
|
12
|
+
// that moved last, each one press from its page, and Notifications in Settings.
|
|
13
|
+
// The search opens the filter over Recents (⌘K / Ctrl+K from anywhere, `seek`).
|
|
9
14
|
//
|
|
10
|
-
//
|
|
11
|
-
// row
|
|
15
|
+
// Dev's places are @hanzo/build's `nav`, the one list every rail draws, each a
|
|
16
|
+
// row under a quiet group label.
|
|
12
17
|
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
// `
|
|
16
|
-
// marks, kept under the key the builder's rail kept it under, so a reader's
|
|
17
|
-
// choice outlives the move.
|
|
18
|
+
// Its width is the edge's in the gutter to its right: dragged, keyed or
|
|
19
|
+
// double-clicked back (@hanzo/build's `Grip`), kept per browser. Pulled in past
|
|
20
|
+
// its floor it shuts (`onShut`), as the sidebar's toggle shuts it.
|
|
18
21
|
import { Text, XStack, YStack } from '@hanzo/gui';
|
|
19
|
-
import {
|
|
20
|
-
import { Grip, nav, NEW } from '@hanzo/build';
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import { useKept } from './kept.js';
|
|
26
|
-
import { Made, Me } from './Me.js';
|
|
22
|
+
import { Bell, Search, Settings2, SquarePen, X } from 'lucide-react';
|
|
23
|
+
import { DOTS, Grip, nav, NEW, path as written, useSessions } from '@hanzo/build';
|
|
24
|
+
import { DropdownMenu } from '@hanzo/ui';
|
|
25
|
+
import { Sidebar, SidebarIconButton, SidebarItem, StatusDot } from '@hanzo/ui/chat';
|
|
26
|
+
import { useEffect, useId, useRef, useState } from 'react';
|
|
27
|
+
import { Made } from './Me.js';
|
|
27
28
|
import { Recents } from './Recents.js';
|
|
28
29
|
import { Setup } from './Setup.js';
|
|
30
|
+
import { ago } from './recent.js';
|
|
29
31
|
import { empty } from './open.js';
|
|
30
32
|
import { useSpan } from './span.js';
|
|
31
33
|
import { pane } from './ground.js';
|
|
32
|
-
import {
|
|
33
|
-
import {
|
|
34
|
-
|
|
35
|
-
/** The grip stands in the gutter between the column and the pane. gui carries a `calc()` to the page unchanged; its
|
|
34
|
+
import { talk } from './lib/host.js';
|
|
35
|
+
import { brand, wordmark } from './where.js';
|
|
36
|
+
/** The grip stands in the gutter between the sidebar and the panel. gui carries a `calc()` to the page unchanged; its
|
|
36
37
|
* types only name custom properties. */
|
|
37
38
|
const GUTTER_END = 'calc(-1 * var(--pane-gap))';
|
|
38
39
|
/** The sidebar fills the column. Its `width` is typed in px alone, though its frame takes any gui size — the type is
|
|
@@ -48,12 +49,10 @@ const FLOAT = {
|
|
|
48
49
|
borderColor: 'var(--border-strong)',
|
|
49
50
|
boxShadow: 'var(--edge-highlight), var(--shadow-floating)',
|
|
50
51
|
};
|
|
51
|
-
/** The
|
|
52
|
-
const WIDTH =
|
|
53
|
-
const FLOOR =
|
|
52
|
+
/** The sidebar's width, px: its default, and the two a drag holds it between. */
|
|
53
|
+
const WIDTH = 240;
|
|
54
|
+
const FLOOR = 200;
|
|
54
55
|
const CEIL = 420;
|
|
55
|
-
/** The collapsed rail's width, px. */
|
|
56
|
-
const RAIL = 60;
|
|
57
56
|
/** Per reader, per browser. */
|
|
58
57
|
const SPAN = 'hanzo.app.column';
|
|
59
58
|
/** Enter and Space press a row the way a click does. */
|
|
@@ -65,19 +64,37 @@ const keys = (press) => ({
|
|
|
65
64
|
press();
|
|
66
65
|
},
|
|
67
66
|
});
|
|
68
|
-
/** A row of the
|
|
69
|
-
const ROW = { rounded: '$4', minH:
|
|
70
|
-
/**
|
|
71
|
-
const
|
|
72
|
-
/**
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
const
|
|
78
|
-
|
|
67
|
+
/** A row of the sidebar: a pill on a 31px pitch, lit where the reader is. */
|
|
68
|
+
const ROW = { rounded: '$4', minH: 31, py: 0, px: '$2' };
|
|
69
|
+
/** The run activity behind the bell: the runs that moved last, newest first. */
|
|
70
|
+
const RECENT = 6;
|
|
71
|
+
/** A header mark: a 16px glyph in a 32px target. */
|
|
72
|
+
const HEAD = { width: 32, height: 32, rounded: '$3' };
|
|
73
|
+
function Activity({ host }) {
|
|
74
|
+
const store = useSessions(host);
|
|
75
|
+
const latest = [...store.value].sort((a, b) => (a.updatedAt < b.updatedAt ? 1 : -1)).slice(0, RECENT);
|
|
76
|
+
const items = [
|
|
77
|
+
{ type: 'label', key: 'head', label: 'Activity' },
|
|
78
|
+
...(latest.length
|
|
79
|
+
? latest.map((s) => ({
|
|
80
|
+
key: s.id,
|
|
81
|
+
label: s.title || 'Untitled run',
|
|
82
|
+
description: [s.status, ago(s.updatedAt || s.createdAt)].filter(Boolean).join(' · '),
|
|
83
|
+
icon: _jsx(StatusDot, { status: DOTS[s.status] ?? 'idle' }),
|
|
84
|
+
onSelect: () => host.go(s.id),
|
|
85
|
+
}))
|
|
86
|
+
: [{ key: 'none', label: 'Nothing new', disabled: true, onSelect: () => { } }]),
|
|
87
|
+
{ type: 'separator', key: 'line' },
|
|
88
|
+
{ key: 'settings', label: 'Notification settings', icon: _jsx(Settings2, { size: 16, "aria-hidden": true }), onSelect: () => host.go(written({ kind: 'settings', section: 'notifications' })) },
|
|
89
|
+
];
|
|
90
|
+
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 }));
|
|
91
|
+
}
|
|
92
|
+
export function Column({ host, mode, thread, go, onShut, seek = 0, drawer = false, foot, }) {
|
|
79
93
|
const [words, setWords] = useState('');
|
|
80
|
-
// The
|
|
94
|
+
// The search field shows once asked for, and while it holds words.
|
|
95
|
+
const [seeking, setSeeking] = useState(false);
|
|
96
|
+
const field = useRef(null);
|
|
97
|
+
// The desktop sidebar and a phone's drawer can both be in the page: each names its own group labels.
|
|
81
98
|
const uid = useId();
|
|
82
99
|
const span = useSpan(SPAN, WIDTH, FLOOR, CEIL);
|
|
83
100
|
const dev = (p) => host.go(p);
|
|
@@ -88,21 +105,27 @@ export function Column({ host, mode, thread, onChat, onDev, go, drawer = false,
|
|
|
88
105
|
empty();
|
|
89
106
|
go(talk());
|
|
90
107
|
};
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
108
|
+
const ask = () => {
|
|
109
|
+
setSeeking(true);
|
|
110
|
+
requestAnimationFrame(() => field.current?.focus());
|
|
111
|
+
};
|
|
112
|
+
const done = () => {
|
|
113
|
+
setWords('');
|
|
114
|
+
setSeeking(false);
|
|
95
115
|
};
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
116
|
+
useEffect(() => {
|
|
117
|
+
if (seek)
|
|
118
|
+
ask();
|
|
119
|
+
}, [seek]);
|
|
100
120
|
return (_jsxs(YStack, { "data-slot": "app-column", ref: drawer ? undefined : span.ref,
|
|
101
121
|
// inline-style: `--span`, the width a drag rewrites on the element itself (span.ts); no prop sets a variable.
|
|
102
|
-
style: drawer ? undefined : span.style, position: "relative", shrink: 0, height: "100%", width: drawer ?
|
|
122
|
+
style: drawer ? undefined : span.style, position: "relative", flex: drawer ? 1 : undefined, shrink: drawer ? 1 : 0, minW: 0, height: "100%", width: drawer ? undefined : 'var(--span)', children: [_jsxs(Sidebar, { "data-slot": "app-rail", role: "navigation", "aria-label": brand(), ...(drawer ? { ...pane(), ...FLOAT } : { bg: 'transparent', borderWidth: 0 }), ...FILL, px: "$2", pt: drawer ? '$2' : 0, pb: "$2", gap: "$0", children: [_jsxs(XStack, { "data-slot": "app-head", items: "center", gap: "$1", pl: "$2", pr: 2, height: 52, shrink: 0, minW: 0, children: [_jsx(Text, { flex: 1, minW: 0, fontSize: 18, lineHeight: 24, fontWeight: "600", color: "$ink", numberOfLines: 1, children: wordmark(mode) }), _jsx(Activity, { host: host }), _jsx(SidebarIconButton, { label: mode === 'chat' ? 'Search chats' : 'Search runs', onPress: seeking ? done : ask, "aria-expanded": seeking || !!words, ...HEAD, children: _jsx(Search, { size: 16, "aria-hidden": true }) })] }), seeking || words ? (_jsxs(XStack, { "data-slot": "app-filter", items: "center", gap: "$2", pl: "$3", pr: "$1", height: 36, rounded: 999, borderWidth: 1, borderColor: "$borderColor", bg: "$edge", shrink: 0, mb: "$1.5", children: [_jsx(Search, { size: 15, "aria-hidden": true, color: "var(--soft, var(--muted-foreground))" }), _jsx(Text, { render: _jsx("input", { ref: field, value: words, onChange: (e) => setWords(e.target.value), onKeyDown: (e) => {
|
|
103
123
|
if (e.key === 'Escape')
|
|
104
|
-
|
|
105
|
-
},
|
|
106
|
-
|
|
124
|
+
done();
|
|
125
|
+
}, onBlur: () => {
|
|
126
|
+
if (!words)
|
|
127
|
+
setSeeking(false);
|
|
128
|
+
}, placeholder: mode === 'chat' ? 'Search chats' : 'Search runs', "aria-label": "Filter recents" }), flex: 1, minW: 0, bg: "transparent", borderWidth: 0, outlineStyle: "none", color: "inherit", fontSize: "$3" }), words ? (_jsx(SidebarIconButton, { label: "Clear search", onPress: done, width: 28, height: 28, rounded: 999, children: _jsx(X, { size: 14, "aria-hidden": true }) })) : null] })) : null, mode === 'chat' ? (_jsx(YStack, { role: "list", gap: 2, shrink: 0, children: _jsx(YStack, { role: "listitem", children: _jsx(SidebarItem, { "data-slot": "app-new", ...ROW, active: !thread, bg: "transparent", hoverStyle: { bg: '$hover' }, icon: _jsx(SquarePen, { size: 16, "aria-hidden": true }), onPress: create, ...keys(create), children: _jsx(Text, { fontSize: "$3", color: "$ink", children: "New chat" }) }) }) })) : (_jsx(YStack, { "data-slot": "app-places", shrink: 0, children: groups.map((g, i) => (_jsxs(YStack, { pt: i ? '$2' : 0, children: [i ? (_jsx(Text, { id: `${uid}-${g.id}`, pl: "$2", pb: 2, fontSize: "$3", color: "$soft", children: g.label })) : null, _jsx(YStack, { role: "list", "aria-labelledby": i ? `${uid}-${g.id}` : undefined, "aria-label": i ? undefined : g.label, gap: 1, children: g.links.map((link) => (_jsx(YStack, { role: "listitem", children: _jsx(SidebarItem, { ...ROW, ...(link.id === NEW ? { 'data-slot': 'app-new' } : {}), active: link.active, icon: link.id === NEW ? _jsx(SquarePen, { size: 16, "aria-hidden": true }) : link.icon, onPress: link.onPress, ...keys(() => link.onPress?.()), children: _jsx(Text, { fontSize: "$3", color: link.active ? '$ink' : '$quiet', children: link.label }) }) }, link.id))) })] }, g.id))) })), _jsx(Recents, { host: host, mode: mode, thread: thread, words: words, onOpen: open, onNew: create }), _jsxs(YStack, { gap: "$2", pt: "$2", shrink: 0, children: [_jsx(Made, {}), _jsx(Setup, {})] }), foot] }), drawer ? null : (_jsx(Grip, { side: "left", span: span.span, floor: FLOOR, ceil: CEIL, reset: WIDTH, onSpan: span.move, onKeep: span.keep, onShut: onShut, label: "Resize sidebar",
|
|
129
|
+
// In the gutter between the sidebar and the panel, not over the sidebar's own rows.
|
|
107
130
|
r: GUTTER_END, width: "var(--pane-gap)" }))] }));
|
|
108
131
|
}
|
package/dist/Home.js
CHANGED
|
@@ -110,12 +110,12 @@ function Open({ at, rooms, back, }) {
|
|
|
110
110
|
return _jsx(Talk, { room: found }, teamKey(found));
|
|
111
111
|
const reading = isLoading || (isAuthenticated && rooms.rooms === null && !rooms.wrong);
|
|
112
112
|
const line = reading
|
|
113
|
-
? 'Reading the
|
|
113
|
+
? 'Reading the channel.'
|
|
114
114
|
: !isAuthenticated
|
|
115
|
-
? 'Sign in to read this
|
|
115
|
+
? 'Sign in to read this channel.'
|
|
116
116
|
: rooms.wrong
|
|
117
|
-
? say(rooms.wrong, 'this
|
|
118
|
-
: 'That
|
|
117
|
+
? say(rooms.wrong, 'this channel')
|
|
118
|
+
: 'That channel is not here.';
|
|
119
119
|
return (_jsxs(YStack, { flex: 1, items: "center", justify: "center", p: "$6", gap: "$3", children: [_jsx(Text, { fontSize: "$3", color: SOFT, children: line }), reading ? null : (_jsx(Box, { render: "button", onClick: back, ...tap, children: _jsx(Text, { fontSize: "$2", fontWeight: "500", color: INK, children: "Back to home" }) }))] }));
|
|
120
120
|
}
|
|
121
121
|
/* ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -151,18 +151,18 @@ function Rooms({ rooms, wrong, open }) {
|
|
|
151
151
|
openRoom(teamKey(made));
|
|
152
152
|
}
|
|
153
153
|
catch (e) {
|
|
154
|
-
setRefused(plainly(e, 'this
|
|
154
|
+
setRefused(plainly(e, 'this channel'));
|
|
155
155
|
}
|
|
156
156
|
finally {
|
|
157
157
|
setOpening(false);
|
|
158
158
|
}
|
|
159
159
|
};
|
|
160
|
-
return (_jsxs(Card, { kind: "rooms", icon: _jsx(Hash, { size: 14, "aria-hidden": true }), title: "
|
|
160
|
+
return (_jsxs(Card, { kind: "rooms", icon: _jsx(Hash, { size: 14, "aria-hidden": true }), title: "Channels", children: [rooms === null ? (_jsx(Waiting, { wrong: wrong, subject: "your channels" })) : rooms.length === 0 ? (_jsx(Quiet, { children: "No channels yet." })) : (_jsx(YStack, { gap: "$1", children: rooms.slice(0, 6).map((one) => (_jsx(Row, { icon: one.direct ? _jsx(AtSign, { size: 12, "aria-hidden": true }) : _jsx(Hash, { size: 12, "aria-hidden": true }), onPress: () => openRoom(teamKey(one)), children: one.name || 'Direct message' }, teamKey(one)))) })), !me ? null : naming ? (_jsxs(YStack, { gap: "$1", children: [_jsxs(XStack, { gap: "$1", items: "center", children: [_jsx(Text, { render: _jsx("input", { value: name, onChange: (e) => setName(e.target.value), onKeyDown: (e) => {
|
|
161
161
|
if (e.key === 'Enter')
|
|
162
162
|
void make();
|
|
163
163
|
if (e.key === 'Escape')
|
|
164
164
|
setNaming(false);
|
|
165
|
-
}, placeholder: "Name", "aria-label": "
|
|
165
|
+
}, placeholder: "Name", "aria-label": "Channel name", autoFocus: true }), flex: 1, minW: 0, bg: "transparent", borderWidth: 1, borderColor: "var(--border)", rounded: 6, px: 8, py: 6, color: "inherit", fontSize: "$2" }), _jsx(Box, { render: "button", onClick: () => void make(), "aria-disabled": opening, opacity: opening ? 0.5 : 1, px: "$2", py: "$1.5", rounded: "$2", borderWidth: 1, borderColor: "$borderColor", hoverStyle: { bg: '$hover' }, children: _jsx(Text, { fontSize: "$2", fontWeight: "500", color: INK, children: "Open it" }) })] }), refused ? (_jsx(Text, { fontSize: "$2", color: BAD, children: refused })) : null] })) : (_jsx(Row, { icon: _jsx(Plus, { size: 12, "aria-hidden": true }), onPress: () => setNaming(true), children: "New room" }))] }));
|
|
166
166
|
}
|
|
167
167
|
/**
|
|
168
168
|
* A read the SDK has no hook for.
|
package/dist/Inbox.js
CHANGED
package/dist/Me.d.ts
CHANGED
|
@@ -13,10 +13,18 @@ export declare function Face({ host, size }: {
|
|
|
13
13
|
host: Host;
|
|
14
14
|
size?: number;
|
|
15
15
|
}): import("react").JSX.Element;
|
|
16
|
-
|
|
16
|
+
/**
|
|
17
|
+
* The plans row, named by the plan the organization is on: Enterprise and
|
|
18
|
+
* Agency are plans a person stays on, so the row names theirs; every other
|
|
19
|
+
* plan, Free included, is one to move up from.
|
|
20
|
+
*/
|
|
21
|
+
export declare const upgrade: (family: string | null | undefined) => string;
|
|
22
|
+
export declare function Me({ host, narrow, above, onOrg, }: {
|
|
17
23
|
host: Host;
|
|
18
|
-
/** The
|
|
24
|
+
/** The rail's: the face alone, the menu opening beside it. */
|
|
19
25
|
narrow?: boolean;
|
|
26
|
+
/** The menu opens above the face, not beside it: the rail laid along a phone drawer's foot. */
|
|
27
|
+
above?: boolean;
|
|
20
28
|
/** Work in another organization: picked, then the app opened in it. */
|
|
21
29
|
onOrg: (org: string) => void;
|
|
22
30
|
}): import("react").JSX.Element;
|
package/dist/Me.js
CHANGED
|
@@ -25,7 +25,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
25
25
|
// Orgs.tsx); the next load mints a token that carries it.
|
|
26
26
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
27
27
|
import { Popover, SizableText, usePopperContextSlow, View, XStack, YStack } from '@hanzo/gui';
|
|
28
|
-
import { Building2, ChevronsUpDown, CreditCard, ExternalLink, Gauge, KeyRound, LifeBuoy, LogOut, Plus, Settings, Sparkles, Users, X } from 'lucide-react';
|
|
28
|
+
import { Building2, ChevronLeft, ChevronRight, ChevronsUpDown, CreditCard, ExternalLink, Gauge, KeyRound, LifeBuoy, LogOut, Mail, Plus, Settings, Sparkles, Users, X } from 'lucide-react';
|
|
29
29
|
import { label, Meter, path, said as spoken, useStanding, useWho } from '@hanzo/build';
|
|
30
30
|
import { useOrganizations } from '@hanzo/iam/react';
|
|
31
31
|
import { Button, Dialog, DialogContent, DialogTitle, Input } from '@hanzo/ui';
|
|
@@ -111,7 +111,7 @@ export function Face({ host, size = 28 }) {
|
|
|
111
111
|
/** One row of the menu: a real button, so Enter, Space and focus are the browser's. */
|
|
112
112
|
function Item({ icon, label, sub, checked, onPress, trail, }) {
|
|
113
113
|
const radio = checked !== undefined;
|
|
114
|
-
return (_jsxs(XStack, { render: "button", ...{ role: radio ? 'menuitemradio' : 'menuitem' }, "aria-checked": radio ? checked : undefined, "data-item": "", onPress: onPress, items: "center", gap: "$2.5", px: "$2", minH:
|
|
114
|
+
return (_jsxs(XStack, { render: "button", ...{ role: radio ? 'menuitemradio' : 'menuitem' }, "aria-checked": radio ? checked : undefined, "data-item": "", onPress: onPress, items: "center", gap: "$2.5", px: "$2", minH: 30, py: 4, rounded: "$3", bg: checked ? '$edge' : 'transparent', hoverStyle: { bg: '$hover' }, focusVisibleStyle: { bg: '$hover', outlineWidth: 2, outlineStyle: 'solid', outlineColor: '$outlineColor' }, cursor: "pointer", width: "100%", children: [icon ? (_jsx(XStack, { width: 20, justify: "center", shrink: 0, opacity: 0.8, children: icon })) : null, _jsxs(YStack, { flex: 1, minW: 0, children: [_jsx(SizableText, { size: "$2", color: "$ink", numberOfLines: 1, text: "left", children: label }), sub ? (_jsx(SizableText, { size: "$1", color: "$soft", numberOfLines: 1, text: "left", children: sub })) : null] }), trail] }));
|
|
115
115
|
}
|
|
116
116
|
const Rule = () => _jsx(XStack, { height: 1, bg: "$borderColor", my: "$1", mx: "$1" });
|
|
117
117
|
const Label = ({ children }) => (_jsx(SizableText, { size: "$1", color: "$soft", px: "$2", pt: "$1.5", pb: "$1", children: children }));
|
|
@@ -150,8 +150,21 @@ function Refit({ children }) {
|
|
|
150
150
|
}, [update]);
|
|
151
151
|
return (_jsx(View, { render: _jsx("div", { ref: box }), self: "stretch", minW: 0, children: children }));
|
|
152
152
|
}
|
|
153
|
-
|
|
153
|
+
/**
|
|
154
|
+
* The plans row, named by the plan the organization is on: Enterprise and
|
|
155
|
+
* Agency are plans a person stays on, so the row names theirs; every other
|
|
156
|
+
* plan, Free included, is one to move up from.
|
|
157
|
+
*/
|
|
158
|
+
export const upgrade = (family) => family === 'Enterprise' ? 'Enterprise plan' : family === 'Agency' ? 'Agency plan' : 'Upgrade plan';
|
|
159
|
+
/** The keys a row prints, in this platform's spelling. */
|
|
160
|
+
const mac = () => typeof navigator !== 'undefined' && /Mac|iPhone|iPad/.test(navigator.platform || navigator.userAgent);
|
|
161
|
+
/** A row's keys at its end, quiet. */
|
|
162
|
+
const Keys = ({ children }) => (_jsx(SizableText, { size: "$1", color: "$soft", shrink: 0, children: children }));
|
|
163
|
+
/** A row that opens a page of the menu. */
|
|
164
|
+
const More = () => _jsx(ChevronRight, { size: 14, "aria-hidden": true, opacity: 0.6 });
|
|
165
|
+
export function Me({ host, narrow = false, above = false, onOrg, }) {
|
|
154
166
|
const menu = useWho();
|
|
167
|
+
const [page, setPage] = useState('main');
|
|
155
168
|
const [creating, setCreating] = useState(false);
|
|
156
169
|
const [supporting, setSupporting] = useState(false);
|
|
157
170
|
const row = useRef(null);
|
|
@@ -165,15 +178,24 @@ export function Me({ host, narrow = false, onOrg, }) {
|
|
|
165
178
|
const other = usePlan(personal && personal.name !== current ? personal.name : null, menu.open);
|
|
166
179
|
const planFor = (o) => (o.name === current ? spoken(read.value?.label ?? null) : o.personal ? other : '');
|
|
167
180
|
const who = host.person?.name || host.person?.email || '';
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
//
|
|
181
|
+
const plan = spoken(read.value?.label ?? null);
|
|
182
|
+
const apple = mac();
|
|
183
|
+
// Each page opens on its first row, once it is drawn — after the popover has
|
|
184
|
+
// placed its own focus — so the keys reach the rows at once.
|
|
171
185
|
const land = (node) => {
|
|
172
186
|
content.current = node;
|
|
173
187
|
if (!node)
|
|
174
188
|
return;
|
|
175
189
|
requestAnimationFrame(() => requestAnimationFrame(() => (node.querySelector('[data-item][aria-checked="true"]') ?? node.querySelector('[data-item]'))?.focus()));
|
|
176
190
|
};
|
|
191
|
+
const turn = (to) => {
|
|
192
|
+
setPage(to);
|
|
193
|
+
requestAnimationFrame(() => land(content.current));
|
|
194
|
+
};
|
|
195
|
+
const toggle = () => {
|
|
196
|
+
setPage('main');
|
|
197
|
+
menu.toggle();
|
|
198
|
+
};
|
|
177
199
|
const close = () => {
|
|
178
200
|
menu.onOpenChange(false);
|
|
179
201
|
row.current?.focus();
|
|
@@ -182,15 +204,20 @@ export function Me({ host, narrow = false, onOrg, }) {
|
|
|
182
204
|
menu.onOpenChange(false);
|
|
183
205
|
host.go(p);
|
|
184
206
|
};
|
|
207
|
+
const back = (title) => _jsx(Item, { icon: _jsx(ChevronLeft, { size: 15, "aria-hidden": true }), label: title, onPress: () => turn('main') });
|
|
185
208
|
if (!host.person)
|
|
186
209
|
return null;
|
|
187
|
-
return (_jsxs(_Fragment, { children: [_jsxs(Popover, { open: menu.open, onOpenChange:
|
|
210
|
+
return (_jsxs(_Fragment, { children: [_jsxs(Popover, { open: menu.open, onOpenChange: (open) => {
|
|
211
|
+
if (open)
|
|
212
|
+
setPage('main');
|
|
213
|
+
menu.onOpenChange(open);
|
|
214
|
+
}, placement: narrow && !above ? 'right-end' : above ? 'top-end' : 'top-start', allowFlip: true, stayInFrame: true, offset: 8, children: [_jsx(Popover.Anchor, { width: narrow ? undefined : '100%', children: _jsxs(XStack, { ref: (el) => {
|
|
188
215
|
row.current = el instanceof HTMLElement ? el : null;
|
|
189
|
-
}, render: "button", "data-slot": "me", "aria-label": `Account: ${[who, current].filter(Boolean).join(' · ')}`, "aria-haspopup": "menu", "aria-expanded": menu.open, onPress:
|
|
216
|
+
}, render: "button", "data-slot": "me", "aria-label": `Account: ${[who, current].filter(Boolean).join(' · ')}`, ...{ title: who }, "aria-haspopup": "menu", "aria-expanded": menu.open, onPress: toggle, width: narrow ? 36 : '100%', minH: narrow ? 36 : 48, px: narrow ? 0 : '$2', gap: "$2.5", rounded: narrow ? 999 : '$5', items: "center", justify: narrow ? 'center' : 'flex-start', hoverStyle: { bg: '$hover' }, focusVisibleStyle: { outlineWidth: 2, outlineStyle: 'solid', outlineColor: '$outlineColor' }, cursor: "pointer", children: [_jsx(Face, { host: host, size: narrow ? 26 : 32 }), narrow ? null : (_jsxs(_Fragment, { children: [_jsxs(YStack, { flex: 1, minW: 0, children: [_jsx(SizableText, { size: "$3", fontWeight: "600", color: "$ink", numberOfLines: 1, text: "left", children: who }), plan ? (_jsx(SizableText, { size: "$1", color: "$soft", numberOfLines: 1, text: "left", children: plan })) : null] }), _jsx(ChevronsUpDown, { size: 14, "aria-hidden": true, opacity: 0.6 })] }))] }) }), _jsx(Popover.Content, { ...glass(2), items: "stretch", borderWidth: 1, rounded: "$5", p: "$1", width: 240, maxW: "calc(100vw - 16px)", maxH: "calc(100dvh - 32px)", overflowY: "auto", overflowX: "hidden", children: _jsx(Refit, { children: _jsx(View, { render: _jsx("div", { ref: land, role: "menu", "aria-label": "Account", onKeyDown: (e) => rove(e, close) }), self: "stretch", minW: 0, children: page === 'main' ? (_jsxs(YStack, { gap: 1, children: [_jsxs(XStack, { items: "center", gap: "$2.5", px: "$2", pt: "$1.5", pb: "$2", children: [_jsx(Face, { host: host, size: 28 }), _jsxs(YStack, { flex: 1, minW: 0, children: [_jsx(SizableText, { size: "$2", fontWeight: "600", color: "$ink", numberOfLines: 1, children: who }), _jsx(SizableText, { size: "$1", color: "$soft", numberOfLines: 1, children: plan || host.person.email || '' })] })] }), _jsx(Rule, {}), _jsx(Item, { icon: _jsx(Gauge, { size: 15, "aria-hidden": true }), label: "Usage remaining", trail: _jsx(More, {}), onPress: () => turn('usage') }), _jsx(Item, { icon: _jsx(Building2, { size: 15, "aria-hidden": true }), label: here?.display ?? current ?? 'Organization', sub: "Organization", trail: _jsx(More, {}), onPress: () => turn('orgs') }), _jsx(Item, { icon: _jsx(KeyRound, { size: 15, "aria-hidden": true }), label: "API keys", trail: _jsx(ExternalLink, { size: 13, "aria-hidden": true, opacity: 0.6 }), onPress: () => (menu.onOpenChange(false), window.location.assign(KEYS)) }), _jsx(Item, { icon: _jsx(Settings, { size: 15, "aria-hidden": true }), label: "Settings", trail: _jsx(Keys, { children: apple ? '⌘,' : 'Ctrl+,' }), onPress: () => go(path({ kind: 'settings', section: 'general' })) }), _jsx(Rule, {}), _jsx(Item, { icon: _jsx(LifeBuoy, { size: 15, "aria-hidden": true }), label: "Help", trail: _jsx(More, {}), onPress: () => turn('help') }), host.signOut ? _jsx(Item, { icon: _jsx(LogOut, { size: 15, "aria-hidden": true }), label: "Log out", onPress: () => (menu.onOpenChange(false), host.signOut?.()) }) : null] })) : page === 'usage' ? (_jsxs(YStack, { gap: 1, children: [back('Usage remaining'), _jsx(Rule, {}), _jsx(Meter, { read: read, onPlans: () => go(path({ kind: 'screen', screen: 'plans' })), onBilling: () => go(path({ kind: 'settings', section: 'billing' })) }), _jsx(Rule, {}), _jsx(Item, { icon: _jsx(Gauge, { size: 15, "aria-hidden": true }), label: "Usage", onPress: () => go(path({ kind: 'settings', section: 'usage' })) }), _jsx(Item, { icon: _jsx(CreditCard, { size: 15, "aria-hidden": true }), label: "Billing", onPress: () => go(path({ kind: 'settings', section: 'billing' })) }), _jsx(Item, { icon: _jsx(Sparkles, { size: 15, "aria-hidden": true }), label: upgrade(read.value?.label?.name), onPress: () => go(path({ kind: 'screen', screen: 'plans' })) })] })) : page === 'orgs' ? (_jsxs(YStack, { gap: 1, children: [back('Organization'), _jsx(Rule, {}), _jsx(Label, { children: "Organizations" }), _jsx(YStack, { role: "group", "aria-label": "Organizations", gap: 2, children: all.map((o) => (_jsx(Item, { icon: _jsx(Mark, { org: o }), label: o.display, sub: [o.personal ? 'Personal' : said(o.role), planFor(o)].filter(Boolean).join(' · '), checked: o.name === current, onPress: () => {
|
|
190
217
|
menu.onOpenChange(false);
|
|
191
218
|
if (o.name !== current)
|
|
192
219
|
onOrg(o.name);
|
|
193
|
-
} }, o.name))) }), _jsx(Item, { icon: _jsx(Plus, { size: 15, "aria-hidden": true }), label: "Create organization", onPress: () => (menu.onOpenChange(false), setCreating(true)) }), _jsx(Item, { icon: _jsx(Settings, { size: 15, "aria-hidden": true }), label: "Organization settings", onPress: () => (menu.onOpenChange(false), window.location.assign(site(ORG_SETTINGS))) }), where().team ? null : here && !here.personal ? (_jsx(Item, { icon: _jsx(Users, { size: 15, "aria-hidden": true }), label: "Open in Hanzo Team", trail: _jsx(ExternalLink, { size: 13, "aria-hidden": true, opacity: 0.6 }), onPress: () => (menu.onOpenChange(false), window.location.assign(`${ENTRY}&org=${encodeURIComponent(here.name)}`)) })) : (_jsx(Item, { icon: _jsx(Users, { size: 15, "aria-hidden": true }), label: "Create a team", onPress: () => (menu.onOpenChange(false), setCreating(true)) })), superAdmin() ? (_jsx(Item, { icon: _jsx(Building2, { size: 15, "aria-hidden": true }), label: "All organizations", onPress: () => (menu.onOpenChange(false), setSupporting(true)) })) : null
|
|
220
|
+
} }, o.name))) }), _jsx(Item, { icon: _jsx(Plus, { size: 15, "aria-hidden": true }), label: "Create organization", onPress: () => (menu.onOpenChange(false), setCreating(true)) }), _jsx(Item, { icon: _jsx(Settings, { size: 15, "aria-hidden": true }), label: "Organization settings", onPress: () => (menu.onOpenChange(false), window.location.assign(site(ORG_SETTINGS))) }), where().team ? null : here && !here.personal ? (_jsx(Item, { icon: _jsx(Users, { size: 15, "aria-hidden": true }), label: "Open in Hanzo Team", trail: _jsx(ExternalLink, { size: 13, "aria-hidden": true, opacity: 0.6 }), onPress: () => (menu.onOpenChange(false), window.location.assign(`${ENTRY}&org=${encodeURIComponent(here.name)}`)) })) : (_jsx(Item, { icon: _jsx(Users, { size: 15, "aria-hidden": true }), label: "Create a team", onPress: () => (menu.onOpenChange(false), setCreating(true)) })), superAdmin() ? (_jsx(Item, { icon: _jsx(Building2, { size: 15, "aria-hidden": true }), label: "All organizations", onPress: () => (menu.onOpenChange(false), setSupporting(true)) })) : null] })) : (_jsxs(YStack, { gap: 1, children: [back('Help'), _jsx(Rule, {}), _jsx(Item, { icon: _jsx(LifeBuoy, { size: 15, "aria-hidden": true }), label: "Get help", trail: _jsx(ExternalLink, { size: 13, "aria-hidden": true, opacity: 0.6 }), onPress: () => (menu.onOpenChange(false), window.open(DOCS, '_blank', 'noopener,noreferrer')) }), _jsx(Item, { icon: _jsx(Mail, { size: 15, "aria-hidden": true }), label: "Contact us", onPress: () => (menu.onOpenChange(false), window.location.assign(site('/contact'))) })] })) }) }) })] }), _jsx(Create, { open: creating, onOpenChange: setCreating }), supporting ? _jsx(SupportPicker, { onClose: () => setSupporting(false) }) : null] }));
|
|
194
221
|
}
|
|
195
222
|
/**
|
|
196
223
|
* The sheet that creates an organization: a name, then `POST /v1/account/orgs`,
|
package/dist/Meet.js
CHANGED
|
@@ -273,7 +273,7 @@ export function Meet() {
|
|
|
273
273
|
return (_jsxs(YStack, { width: "100%", height: "100%", bg: "$background", overflowX: "auto", overflowY: "auto", children: [_jsxs(XStack, { minH: 56, px: 20, py: 8, gap: 10, flexWrap: "wrap", bg: "$panel", borderBottomWidth: 1, borderBottomColor: "$borderColor", items: "center", justify: "space-between", children: [_jsxs(XStack, { items: "center", gap: 12, shrink: 1, minW: "auto", children: [_jsx(XStack, { width: 32, height: 32, rounded: 8, bg: "$raised", items: "center", justify: "center", shrink: 1, minW: "auto", children: _jsx(Video, { size: 18, color: "var(--foreground)" }) }), _jsxs(YStack, { shrink: 1, minW: "auto", children: [_jsxs(XStack, { items: "center", gap: 8, children: [_jsx(Text, { fontSize: "$4", fontWeight: "700", color: "$ink", children: "Hanzo Meet" }), _jsx(Text, { fontSize: FLOOR, fontWeight: "700", bg: "$hover", color: "$soft", borderWidth: 1, borderColor: "$borderColor", px: 6, py: 1, rounded: 4, children: "LIVE \u00B7 meet.hanzo.ai" })] }), _jsx(Text, { fontSize: "$1", color: "$soft", children: "Native WebRTC video conferencing with AI meeting bot transcription & voice" })] })] }), activeMeetingRoom ? (_jsxs(XStack, { items: "center", gap: 8, shrink: 1, minW: "auto", flexWrap: "wrap", children: [_jsx(XStack, { bg: "$hover", rounded: 8, p: 2, gap: 2, borderWidth: 1, borderColor: "$borderColor", shrink: 1, minW: "auto", children: [
|
|
274
274
|
['iframe', 'SFU Stage', Tv],
|
|
275
275
|
['grid', 'Agent Grid', LayoutGrid],
|
|
276
|
-
].map(([view, label, Icon]) => (_jsxs(Button, { size: "sm", variant: meetingView === view ? 'primary' : 'ghost', ...(meetingView === view ? null : { color: '$soft' }), "aria-pressed": meetingView === view, onClick: () => setMeetingView(view), children: [_jsx(Icon, { size: 12 }), label] }, view))) }), botAssisting ? (_jsx(View, { display: "none", "$max-md": { display: 'flex' }, children: _jsxs(Button, { size: "sm", variant: copilot ? 'secondary' : 'default', onClick: () => setCopilot((v) => !v), "aria-label": "Co-pilot", "aria-expanded": copilot, "aria-controls": "meet-copilot", children: [_jsx(Bot, { size: 13 }), "Co-pilot"] }) })) : null, _jsxs(Button, { size: "sm", onClick: () => handleCopyLink(activeMeetingRoom), children: [copied ? _jsx(Check, { size: 13 }) : _jsx(Copy, { size: 13 }), copied ? 'Link Copied' : 'Copy
|
|
276
|
+
].map(([view, label, Icon]) => (_jsxs(Button, { size: "sm", variant: meetingView === view ? 'primary' : 'ghost', ...(meetingView === view ? null : { color: '$soft' }), "aria-pressed": meetingView === view, onClick: () => setMeetingView(view), children: [_jsx(Icon, { size: 12 }), label] }, view))) }), botAssisting ? (_jsx(View, { display: "none", "$max-md": { display: 'flex' }, children: _jsxs(Button, { size: "sm", variant: copilot ? 'secondary' : 'default', onClick: () => setCopilot((v) => !v), "aria-label": "Co-pilot", "aria-expanded": copilot, "aria-controls": "meet-copilot", children: [_jsx(Bot, { size: 13 }), "Co-pilot"] }) })) : null, _jsxs(Button, { size: "sm", onClick: () => handleCopyLink(activeMeetingRoom), children: [copied ? _jsx(Check, { size: 13 }) : _jsx(Copy, { size: 13 }), copied ? 'Link Copied' : 'Copy meeting link'] }), _jsxs(Button, { size: "sm", variant: "destructive", onClick: () => {
|
|
277
277
|
stopAgentSpeech();
|
|
278
278
|
// Leaving the call hangs up the hands-free conversation and
|
|
279
279
|
// settles the transcript with it.
|
package/dist/Prose.js
CHANGED
|
@@ -7,8 +7,15 @@ const INK = 'var(--foreground)';
|
|
|
7
7
|
const QUIET = 'var(--muted-foreground)';
|
|
8
8
|
const EDGE = 'var(--border)';
|
|
9
9
|
const GROUND = 'var(--secondary)';
|
|
10
|
-
/**
|
|
11
|
-
|
|
10
|
+
/**
|
|
11
|
+
* The reading type, px: a 16px body on a 24px line, and a heading's size and
|
|
12
|
+
* line by depth, as the reference conversation sets them — the body a reader
|
|
13
|
+
* reads at arm's length, not a sidebar's 14.
|
|
14
|
+
*/
|
|
15
|
+
const BODY = 16;
|
|
16
|
+
const LINE = 24;
|
|
17
|
+
const RUNG = [28, 24, 20, 17, 16, 16];
|
|
18
|
+
const LEAD = [36, 32, 28, 24, 24, 24];
|
|
12
19
|
/** The character references a model writes, as the characters they name. An
|
|
13
20
|
* HTML renderer let the browser decode `&`; a string React sets is shown
|
|
14
21
|
* exactly as written. */
|
|
@@ -21,7 +28,7 @@ const decode = (s) => s.replace(/&(?:#x([0-9a-f]{1,6})|#(\d{1,7})|([a-z]+));/gi,
|
|
|
21
28
|
return NAMED[name.toLowerCase()] ?? all;
|
|
22
29
|
});
|
|
23
30
|
/** One run of inline tokens, in `tone`, at the block's `rung`. */
|
|
24
|
-
function inline(tokens, tone, rung =
|
|
31
|
+
function inline(tokens, tone, rung = BODY) {
|
|
25
32
|
return (tokens ?? []).map((t, i) => {
|
|
26
33
|
switch (t.type) {
|
|
27
34
|
case 'strong':
|
|
@@ -31,7 +38,7 @@ function inline(tokens, tone, rung = '$2') {
|
|
|
31
38
|
case 'del':
|
|
32
39
|
return (_jsx(Text, { render: "del", color: tone, textDecorationLine: "line-through", opacity: 0.7, children: inline(t.tokens, tone, rung) }, i));
|
|
33
40
|
case 'codespan':
|
|
34
|
-
return (_jsx(Text, { render: "code", color: tone, fontFamily: "$mono", fontSize:
|
|
41
|
+
return (_jsx(Text, { render: "code", color: tone, fontFamily: "$mono", fontSize: Math.round(rung * 0.875), px: "$1", rounded: 4, bg: GROUND, children: decode(t.text) }, i));
|
|
35
42
|
case 'br':
|
|
36
43
|
return _jsx("br", {}, i);
|
|
37
44
|
case 'link': {
|
|
@@ -41,7 +48,7 @@ function inline(tokens, tone, rung = '$2') {
|
|
|
41
48
|
// AWAY IS A NEW TAB, HOME IS NOT. Someone else's page opened from a
|
|
42
49
|
// message never gets a handle on this one and never inherits the
|
|
43
50
|
// referrer; a link back into this app is a link.
|
|
44
|
-
return (_jsx(Anchor, { href: to.href, ...(to.away ? { target: '_blank', rel: 'noopener noreferrer nofollow' } : {}), ...(t.title ? { title: t.title } : {}),
|
|
51
|
+
return (_jsx(Anchor, { href: to.href, ...(to.away ? { target: '_blank', rel: 'noopener noreferrer nofollow' } : {}), ...(t.title ? { title: t.title } : {}), fontSize: rung, color: INK, textDecorationLine: "underline", children: inline(t.tokens, INK, rung) }, i));
|
|
45
52
|
}
|
|
46
53
|
// An image is a fetch to wherever the message says, which is a reader's
|
|
47
54
|
// address leaking to whoever wrote it. The alt text is the message.
|
|
@@ -59,11 +66,11 @@ function inline(tokens, tone, rung = '$2') {
|
|
|
59
66
|
}
|
|
60
67
|
/** Running text: a paragraph, or a list item's own words. */
|
|
61
68
|
function Line({ tokens, tone }) {
|
|
62
|
-
return (_jsx(Text, { render: "p", color: tone, fontSize:
|
|
69
|
+
return (_jsx(Text, { render: "p", color: tone, fontSize: BODY, lineHeight: LINE, children: inline(tokens, tone) }));
|
|
63
70
|
}
|
|
64
71
|
function List({ list, tone }) {
|
|
65
72
|
const start = typeof list.start === 'number' ? list.start : 1;
|
|
66
|
-
return (_jsx(YStack, { render: list.ordered ? 'ol' : 'ul', m: 0, p: 0, gap: "$1", children: list.items.map((item, i) => (_jsxs(XStack, { render: "li", gap: "$1", children: [_jsx(Text, { "aria-hidden": true, color: tone, fontSize:
|
|
73
|
+
return (_jsx(YStack, { render: list.ordered ? 'ol' : 'ul', m: 0, p: 0, gap: "$1", children: list.items.map((item, i) => (_jsxs(XStack, { render: "li", gap: "$1", children: [_jsx(Text, { "aria-hidden": true, color: tone, fontSize: BODY, lineHeight: LINE, minW: 16, text: "right", children: item.task ? (item.checked ? '☑' : '☐') : list.ordered ? `${start + i}.` : '•' }), _jsx(YStack, { flex: 1, minW: 0, gap: "$1", children: blocks(item.tokens, tone) })] }, i))) }));
|
|
67
74
|
}
|
|
68
75
|
/** A table is a grid: one track per column, as wide as what the column holds,
|
|
69
76
|
* in a box that scrolls sideways rather than widening the message. Cells
|
|
@@ -71,7 +78,7 @@ function List({ list, tone }) {
|
|
|
71
78
|
* the top and the first column the left. */
|
|
72
79
|
function Table({ table, tone }) {
|
|
73
80
|
const align = (c) => table.align[c] ?? 'left';
|
|
74
|
-
return (_jsx(View, { role: "table", display: "grid", gridTemplateColumns: `repeat(${table.header.length}, max-content)`, self: "flex-start", maxW: "100%", overflowX: "auto", children: [table.header, ...table.rows].map((row, r) => (_jsx(View, { role: "row", display: "contents", children: row.map((cell, c) => (_jsx(Text, { role: r ? 'cell' : 'columnheader', color: tone, fontSize:
|
|
81
|
+
return (_jsx(View, { role: "table", display: "grid", gridTemplateColumns: `repeat(${table.header.length}, max-content)`, self: "flex-start", maxW: "100%", overflowX: "auto", children: [table.header, ...table.rows].map((row, r) => (_jsx(View, { role: "row", display: "contents", children: row.map((cell, c) => (_jsx(Text, { role: r ? 'cell' : 'columnheader', color: tone, fontSize: 14, lineHeight: 20, fontWeight: r ? '400' : '600', text: align(c), px: "$2", py: "$1", bg: r ? undefined : GROUND, borderColor: EDGE, borderStyle: "solid", borderRightWidth: 1, borderBottomWidth: 1, borderTopWidth: r ? 0 : 1, borderLeftWidth: c ? 0 : 1, children: inline(cell.tokens, tone, 14) }, c))) }, r))) }));
|
|
75
82
|
}
|
|
76
83
|
/** A run of block tokens. A tight list item's words arrive as a `text` block. */
|
|
77
84
|
function blocks(tokens, tone) {
|
|
@@ -82,7 +89,7 @@ function blocks(tokens, tone) {
|
|
|
82
89
|
case 'text':
|
|
83
90
|
return _jsx(Line, { tokens: 'tokens' in t && t.tokens ? t.tokens : [t], tone: tone }, i);
|
|
84
91
|
case 'heading':
|
|
85
|
-
return (_jsx(Text, { render: `h${t.depth}`, m: 0, color: tone, fontSize: RUNG[t.depth - 1], lineHeight:
|
|
92
|
+
return (_jsx(Text, { render: `h${t.depth}`, m: 0, mt: t.depth < 3 ? 8 : 4, color: tone, fontSize: RUNG[t.depth - 1], lineHeight: LEAD[t.depth - 1], fontWeight: "600", children: inline(t.tokens, tone, RUNG[t.depth - 1]) }, i));
|
|
86
93
|
case 'list':
|
|
87
94
|
return _jsx(List, { list: t, tone: tone }, i);
|
|
88
95
|
case 'table':
|
package/dist/Rail.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import { type Host } from '@hanzo/build';
|
|
3
|
+
import type { Mode } from './recent.js';
|
|
4
|
+
/** The rail's width, px. */
|
|
5
|
+
export declare const RAIL = 48;
|
|
6
|
+
/** One mark: an icon button named for where it goes, its name its tooltip, lit where the reader is. */
|
|
7
|
+
export declare function Mark({ label, on, onPress, children, ...rest }: {
|
|
8
|
+
label: string;
|
|
9
|
+
/** The reader is there. */
|
|
10
|
+
on?: boolean;
|
|
11
|
+
onPress?: () => void;
|
|
12
|
+
children: ReactNode;
|
|
13
|
+
[attribute: `data-${string}` | `aria-${string}`]: unknown;
|
|
14
|
+
}): import("react").JSX.Element;
|
|
15
|
+
/** The sidebar's toggle: the same mark wherever a host draws it. */
|
|
16
|
+
export declare function Toggle({ open, onPress, label }: {
|
|
17
|
+
open: boolean;
|
|
18
|
+
onPress: () => void;
|
|
19
|
+
label?: string;
|
|
20
|
+
}): import("react").JSX.Element;
|
|
21
|
+
export declare function Rail({ host, mode, at, open, toggle, flat, onChat, onDev, go, onOrg, }: {
|
|
22
|
+
host: Host;
|
|
23
|
+
mode: Mode;
|
|
24
|
+
/** Dev's builder address, or null in Chat. */
|
|
25
|
+
at: string | null;
|
|
26
|
+
/** The sidebar beside the rail is open. */
|
|
27
|
+
open: boolean;
|
|
28
|
+
/** Lead with the sidebar's toggle; absent where the host's bar holds it. */
|
|
29
|
+
toggle?: () => void;
|
|
30
|
+
/** Laid along the foot of the phone's drawer rather than down the window's edge. */
|
|
31
|
+
flat?: boolean;
|
|
32
|
+
/** To Chat and to Dev, each where the reader left it. */
|
|
33
|
+
onChat: () => void;
|
|
34
|
+
onDev: () => void;
|
|
35
|
+
/** Moves the app to an address of this page. */
|
|
36
|
+
go: (address: string) => void;
|
|
37
|
+
/** Work in another organization. */
|
|
38
|
+
onOrg: (org: string) => void;
|
|
39
|
+
}): import("react").JSX.Element;
|