@hanzo/rooms 0.1.2 → 0.1.4
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/Beluga.js +1 -1
- package/dist/Board.js +5 -5
- package/dist/Boxes.js +1 -1
- package/dist/Chat.js +101 -46
- package/dist/Contacts.js +1 -1
- package/dist/Directory.js +1 -1
- package/dist/Drive.js +1 -1
- package/dist/Home.js +1 -1
- package/dist/Inbox.js +2 -2
- package/dist/Market.js +1 -1
- package/dist/Me.js +1 -1
- package/dist/Meet.js +91 -148
- package/dist/Profile.js +1 -1
- package/dist/RightPane.js +4 -4
- package/dist/Settings.js +1 -1
- package/dist/Shell.js +6 -2
- package/dist/Talk.js +1 -1
- package/dist/Vibe.js +2 -2
- package/dist/Work.js +2 -2
- package/dist/cast.d.ts +17 -14
- package/dist/cast.js +39 -49
- package/dist/crew.js +2 -2
- package/dist/ground.d.ts +6 -4
- package/dist/ground.js +6 -4
- package/dist/lib/attach.d.ts +38 -0
- package/dist/lib/attach.js +82 -0
- package/dist/lib/limits.d.ts +9 -0
- package/dist/lib/limits.js +6 -0
- package/dist/speech.d.ts +23 -1
- package/dist/speech.js +70 -79
- package/dist/tabs.js +1 -1
- package/package.json +8 -8
package/dist/Beluga.js
CHANGED
|
@@ -141,7 +141,7 @@ function BelugaStudioInner() {
|
|
|
141
141
|
setMobileDrawer('none');
|
|
142
142
|
}
|
|
143
143
|
};
|
|
144
|
-
return (_jsxs(YStack, { "data-beluga": true, flex: 1, minH: 0, height: "100vh", width: "100%", maxW: "100%", overflowX: "auto", overflowY: "hidden", bg: "var(--pure-black)", pb: 56, children: [_jsxs(XStack, { height: 50, items: "center", justify: "space-between", px: "$3.5", borderBottomWidth: 1, borderColor: "var(--white-08)", bg: "var(--neutral-950)", z: 30, shrink: 0, children: [_jsxs(XStack, { items: "center", gap: "$2.5", children: [_jsx(Link, { href: "/vibe", children: _jsx(XStack, { items: "center", justify: "center", width: 28, height: 28, rounded: 8, bg: "var(--neutral-900)", borderWidth: 1, borderColor: "var(--white-10)", hoverStyle: { bg: 'var(--neutral-800)' }, children: _jsx(ArrowLeft, { size: 14, color: "var(--neutral-400)" }) }) }), _jsxs(XStack, { items: "center", gap: "$2", children: [_jsx(Bot, { size: 15, color: "$ink" }), _jsx(Text, { render: "h1", fontSize: "$2", fontWeight: "700", color: "var(--pure-white)", children: "Blue the Beluga" }), _jsx(Text, { fontSize: 11, color: "var(--neutral-600)", children: "|" }), _jsx(Text, { fontSize: 11, color: "var(--neutral-400)", children: "ZenLM Neural Studio" })] })] }), _jsxs(XStack, { items: "center", gap: "$2", children: [_jsxs(XStack, { $md: { display: 'none' }, gap: "$1.5", children: [_jsx(Button, { size: "$2", height: 28, px: "$2", rounded: 6, bg: mobileDrawer === 'prompts' ? 'var(--neutral-800)' : 'var(--neutral-900)', onPress: () => setMobileDrawer(mobileDrawer === 'prompts' ? 'none' : 'prompts'), children: _jsx(Sparkles, { size: 12, color: "$ink" }) }), _jsx(Button, { size: "$2", height: 28, px: "$2", rounded: 6, bg: mobileDrawer === 'telemetry' ? 'var(--neutral-800)' : 'var(--neutral-900)', onPress: () => setMobileDrawer(mobileDrawer === 'telemetry' ? 'none' : 'telemetry'), children: _jsx(Activity, { size: 12, color: "$ink" }) })] }), _jsx(View, { cursor: "pointer", onPress: () => setMuted(!muted), p: 6, rounded: 8, bg: "var(--neutral-900)", borderWidth: 1, borderColor: "var(--white-08)", hoverStyle: { bg: 'var(--neutral-800)' }, children: muted ? _jsx(VolumeX, { size: 14, color: "var(--neutral-400)" }) : _jsx(Volume2, { size: 14, color: "var(--neutral-400)" }) }), _jsx(Button, { display: "none", $md: { display: 'flex' }, size: "$2", height: 28, px: "$3", rounded: 8, bg: "var(--neutral-900)", borderColor: "var(--white-10)", borderWidth: 1, onPress: () => setRightOpen(!rightOpen), children: _jsxs(XStack, { items: "center", gap: "$1.5", children: [_jsx(Activity, { size: 12, color: "$ink" }), _jsx(Text, { fontSize: 11, fontWeight: "600", color: "var(--neutral-200)", children: "Telemetry" })] }) })] })] }), _jsxs(View, { flex: 1, minH: 0, width: "100%", height: "100%", overflow: "hidden", display: "grid", gridTemplateRows: "100%", gridTemplateColumns: "100%", $md: { gridTemplateColumns: 'minmax(0, 1fr) 280px' }, $lg: { gridTemplateColumns: '280px minmax(0, 1fr) 280px' }, children: [_jsxs(YStack, { height: "100%", bg: "var(--neutral-950)", borderRightWidth: 1, borderColor: "var(--white-08)", minW: 0, display: mobileDrawer === 'prompts' ? 'flex' : 'none', "$max-md": { position: 'absolute', t: 0, r: 0, b: 0, l: 0, z: 'var(--z-dock)' }, $md: { display: 'none' }, $lg: { display: 'flex' }, children: [_jsx(XStack, { height: 40, items: "center", justify: "space-between", px: "$3.5", borderBottomWidth: 1, borderColor: "var(--white-06)", children: _jsx(Text, { fontSize: 11, fontWeight: "700", color: "var(--neutral-500)", textTransform: "uppercase", children: "Quick Ocean Prompts" }) }), _jsxs(YStack, { flex: 1,
|
|
144
|
+
return (_jsxs(YStack, { "data-beluga": true, flex: 1, minH: 0, height: "100vh", width: "100%", maxW: "100%", overflowX: "auto", overflowY: "hidden", bg: "var(--pure-black)", pb: 56, children: [_jsxs(XStack, { height: 50, items: "center", justify: "space-between", px: "$3.5", borderBottomWidth: 1, borderColor: "var(--white-08)", bg: "var(--neutral-950)", z: 30, shrink: 0, children: [_jsxs(XStack, { items: "center", gap: "$2.5", children: [_jsx(Link, { href: "/vibe", children: _jsx(XStack, { items: "center", justify: "center", width: 28, height: 28, rounded: 8, bg: "var(--neutral-900)", borderWidth: 1, borderColor: "var(--white-10)", hoverStyle: { bg: 'var(--neutral-800)' }, children: _jsx(ArrowLeft, { size: 14, color: "var(--neutral-400)" }) }) }), _jsxs(XStack, { items: "center", gap: "$2", children: [_jsx(Bot, { size: 15, color: "$ink" }), _jsx(Text, { render: "h1", fontSize: "$2", fontWeight: "700", color: "var(--pure-white)", children: "Blue the Beluga" }), _jsx(Text, { fontSize: 11, color: "var(--neutral-600)", children: "|" }), _jsx(Text, { fontSize: 11, color: "var(--neutral-400)", children: "ZenLM Neural Studio" })] })] }), _jsxs(XStack, { items: "center", gap: "$2", children: [_jsxs(XStack, { $md: { display: 'none' }, gap: "$1.5", children: [_jsx(Button, { size: "$2", height: 28, px: "$2", rounded: 6, bg: mobileDrawer === 'prompts' ? 'var(--neutral-800)' : 'var(--neutral-900)', onPress: () => setMobileDrawer(mobileDrawer === 'prompts' ? 'none' : 'prompts'), children: _jsx(Sparkles, { size: 12, color: "$ink" }) }), _jsx(Button, { size: "$2", height: 28, px: "$2", rounded: 6, bg: mobileDrawer === 'telemetry' ? 'var(--neutral-800)' : 'var(--neutral-900)', onPress: () => setMobileDrawer(mobileDrawer === 'telemetry' ? 'none' : 'telemetry'), children: _jsx(Activity, { size: 12, color: "$ink" }) })] }), _jsx(View, { cursor: "pointer", onPress: () => setMuted(!muted), p: 6, rounded: 8, bg: "var(--neutral-900)", borderWidth: 1, borderColor: "var(--white-08)", hoverStyle: { bg: 'var(--neutral-800)' }, children: muted ? _jsx(VolumeX, { size: 14, color: "var(--neutral-400)" }) : _jsx(Volume2, { size: 14, color: "var(--neutral-400)" }) }), _jsx(Button, { display: "none", $md: { display: 'flex' }, size: "$2", height: 28, px: "$3", rounded: 8, bg: "var(--neutral-900)", borderColor: "var(--white-10)", borderWidth: 1, onPress: () => setRightOpen(!rightOpen), children: _jsxs(XStack, { items: "center", gap: "$1.5", children: [_jsx(Activity, { size: 12, color: "$ink" }), _jsx(Text, { fontSize: 11, fontWeight: "600", color: "var(--neutral-200)", children: "Telemetry" })] }) })] })] }), _jsxs(View, { flex: 1, minH: 0, width: "100%", height: "100%", overflow: "hidden", display: "grid", gridTemplateRows: "100%", gridTemplateColumns: "100%", $md: { gridTemplateColumns: 'minmax(0, 1fr) 280px' }, $lg: { gridTemplateColumns: '280px minmax(0, 1fr) 280px' }, children: [_jsxs(YStack, { height: "100%", bg: "var(--neutral-950)", borderRightWidth: 1, borderColor: "var(--white-08)", minW: 0, display: mobileDrawer === 'prompts' ? 'flex' : 'none', "$max-md": { position: 'absolute', t: 0, r: 0, b: 0, l: 0, z: 'var(--z-dock)' }, $md: { display: 'none' }, $lg: { display: 'flex' }, children: [_jsx(XStack, { height: 40, items: "center", justify: "space-between", px: "$3.5", borderBottomWidth: 1, borderColor: "var(--white-06)", children: _jsx(Text, { fontSize: 11, fontWeight: "700", color: "var(--neutral-500)", textTransform: "uppercase", children: "Quick Ocean Prompts" }) }), _jsxs(YStack, { flex: 1, overflowY: "auto", overflowX: "hidden", p: "$3.5", gap: "$4", children: [_jsx(YStack, { gap: "$1.5", children: QUICK_PROMPTS.map((p, i) => (_jsx(View, { cursor: "pointer", onPress: () => handleSend(p), p: "$2.5", rounded: 8, bg: "var(--neutral-900)", borderWidth: 1, borderColor: "var(--white-06)", hoverStyle: { bg: 'var(--neutral-800)' }, children: _jsx(Text, { fontSize: 12, color: "var(--neutral-300)", children: p }) }, i))) }), _jsxs(YStack, { p: "$3", rounded: 12, bg: "var(--white-08)", borderWidth: 1, borderColor: "var(--white-20)", gap: "$1.5", children: [_jsxs(XStack, { items: "center", gap: "$1.5", children: [_jsx(Sparkles, { size: 13, color: "$ink" }), _jsx(Text, { fontSize: 11, fontWeight: "700", color: "$ink", children: "Ocean Thought" })] }), _jsx(Text, { fontSize: 11, color: "var(--neutral-300)", lineHeight: 16, children: idleThought })] })] })] }), _jsxs(YStack, { height: "100%", bg: "var(--pure-black)", position: "relative", items: "center", justify: "space-between", p: "$4", minW: 0, children: [_jsxs(YStack, { flex: 1, items: "center", justify: "center", width: "100%", children: [_jsx(YStack, { width: 220, height: 220, rounded: 110, borderWidth: 1, borderColor: "var(--white-30)", items: "center", justify: "center", shadowColor: "var(--white-20)", shadowRadius: 50, children: _jsx(Text, { fontSize: 72, children: "\uD83D\uDC2C" }) }), _jsx(View, { ...LENS, mt: "$4", maxW: 520, width: "100%", px: "$3.5", py: "$2.5", rounded: 14, bg: glass(85), borderWidth: 1, borderColor: "var(--white-10)", children: _jsx(Text, { fontSize: 12, color: "var(--neutral-200)", text: "center", fontFamily: "$mono", lineHeight: 18, children: messages[messages.length - 1]?.content }) })] }), _jsx(YStack, { width: "100%", maxW: 600, z: 20, children: _jsxs(XStack, { ...LENS, items: "center", bg: glass(90), borderWidth: 1, borderColor: "var(--white-14)", rounded: 14, p: "$2", children: [_jsx(Text, { render: _jsx("input", { type: "text", value: input, onChange: (e) => setInput(e.target.value), onKeyDown: (e) => e.key === 'Enter' && handleSend(), placeholder: "Ask Blue the Beluga anything or send acoustic signal..." }), flex: 1, bg: "transparent", borderWidth: 0, color: "var(--pure-white)", fontSize: "$2", outlineStyle: "none", px: 10, py: 4 }), _jsx(Button, { size: "$2", bg: "var(--neutral-50)", rounded: 10, height: 30, px: "$2.5", onPress: () => handleSend(), disabled: !input.trim() || busy, opacity: !input.trim() || busy ? 0.3 : 1, hoverStyle: { bg: 'var(--neutral-200)' }, children: _jsx(ArrowUp, { size: 14, color: "var(--neutral-950)" }) })] }) })] }), _jsxs(YStack, { height: "100%", bg: "var(--neutral-950)", borderLeftWidth: 1, borderColor: "var(--white-08)", minW: 0, display: mobileDrawer === 'telemetry' ? 'flex' : 'none', "$max-md": { position: 'absolute', t: 0, r: 0, b: 0, l: 0, z: 'var(--z-dock)' }, $md: { display: 'flex' }, children: [_jsxs(XStack, { height: 40, items: "center", justify: "space-between", px: "$3.5", borderBottomWidth: 1, borderColor: "var(--white-06)", children: [_jsxs(XStack, { items: "center", gap: "$2", children: [_jsx(Activity, { size: 14, color: "$ink" }), _jsx(Text, { fontSize: 12, fontWeight: "600", color: "var(--neutral-200)", children: "Bioacoustic Telemetry" })] }), _jsx(View, { px: 6, py: 2, rounded: 4, bg: "var(--white-15)", children: _jsx(Text, { fontSize: 10, color: "$ink", fontFamily: "$mono", fontWeight: "600", children: "120 kHz" }) })] }), _jsxs(YStack, { flex: 1, overflowY: "auto", overflowX: "hidden", p: "$3.5", gap: "$4", children: [_jsxs(YStack, { gap: "$2", children: [_jsx(Text, { fontSize: 10, fontWeight: "700", color: "var(--neutral-500)", textTransform: "uppercase", children: "Active Emotion State" }), _jsxs(YStack, { p: "$3", rounded: 12, bg: "var(--neutral-900)", borderWidth: 1, borderColor: "var(--white-06)", gap: "$1", children: [_jsxs(XStack, { items: "center", gap: "$2", children: [_jsx(Text, { fontSize: 15, children: EMOTIONS[currentEmotion]?.emoji }), _jsx(Text, { fontSize: 13, fontWeight: "700", color: "var(--pure-white)", children: EMOTIONS[currentEmotion]?.name })] }), _jsx(Text, { fontSize: 11, color: "var(--neutral-400)", lineHeight: 16, children: EMOTIONS[currentEmotion]?.desc })] })] }), _jsxs(YStack, { gap: "$2", children: [_jsx(Text, { fontSize: 10, fontWeight: "700", color: "var(--neutral-500)", textTransform: "uppercase", children: "Emotion State Matrix" }), _jsx(YStack, { gap: "$1.5", children: Object.entries(EMOTIONS).map(([key, item]) => (_jsx(View, { cursor: "pointer", onPress: () => setCurrentEmotion(key), px: "$2.5", py: "$2", rounded: 8, borderWidth: 1, borderColor: currentEmotion === key ? 'var(--white-40)' : 'var(--white-06)', bg: currentEmotion === key ? 'var(--white-14)' : 'var(--neutral-900)', children: _jsxs(XStack, { items: "center", justify: "space-between", children: [_jsxs(XStack, { items: "center", gap: "$2", children: [_jsx(Text, { fontSize: 13, children: item.emoji }), _jsx(Text, { fontSize: 12, fontWeight: currentEmotion === key ? '700' : '400', color: currentEmotion === key ? 'var(--pure-white)' : 'var(--neutral-300)', children: item.name })] }), currentEmotion === key && (_jsx(View, { width: 6, height: 6, rounded: 3, bg: "var(--pure-white)" }))] }) }, key))) })] })] })] })] })] }));
|
|
145
145
|
}
|
|
146
146
|
// THE GATE IS NOT A ROOM'S TO REMEMBER. This room draws its own shell rather
|
|
147
147
|
// than `Room`, which is where every other room picks the organization gate up —
|
package/dist/Board.js
CHANGED
|
@@ -179,7 +179,7 @@ function Detail({ card, people, agents, onClose, onSaved, }) {
|
|
|
179
179
|
setBody(written);
|
|
180
180
|
void save({ description: written });
|
|
181
181
|
};
|
|
182
|
-
return (_jsxs(YStack, { position: "absolute", t: 0, r: 0, b: 0, width: 560, maxW: "100%", bg: "$panel", borderLeftWidth: 1, borderColor: "$borderColor", z: "var(--z-sheet)", children: [_jsxs(XStack, { items: "center", gap: "$2", px: "$4", py: "$3", borderBottomWidth: 1, borderColor: "$borderColor", children: [_jsx(Text, { fontSize: "$1", color: "$soft", flex: 1, numberOfLines: 1, children: card.identifier }), _jsx(Box, { render: "button", onClick: onClose, p: "$1", rounded: "$2", "aria-label": "Close", children: _jsx(X, { size: 16, "aria-hidden": true }) })] }), _jsxs(YStack, { flex: 1, minH: 0,
|
|
182
|
+
return (_jsxs(YStack, { position: "absolute", t: 0, r: 0, b: 0, width: 560, maxW: "100%", bg: "$panel", borderLeftWidth: 1, borderColor: "$borderColor", z: "var(--z-sheet)", children: [_jsxs(XStack, { items: "center", gap: "$2", px: "$4", py: "$3", borderBottomWidth: 1, borderColor: "$borderColor", children: [_jsx(Text, { fontSize: "$1", color: "$soft", flex: 1, numberOfLines: 1, children: card.identifier }), _jsx(Box, { render: "button", onClick: onClose, p: "$1", rounded: "$2", "aria-label": "Close", children: _jsx(X, { size: 16, "aria-hidden": true }) })] }), _jsxs(YStack, { flex: 1, minH: 0, overflowY: "auto", overflowX: "hidden", p: "$4", gap: "$4", children: [_jsxs(XStack, { items: "center", gap: "$2", children: [_jsx(Box, { render: "button", onClick: () => void save({ status: finished ? 'todo' : 'done' }), "aria-label": finished ? 'Reopen this card' : 'Mark this card done', "aria-pressed": finished, width: 22, height: 22, shrink: 0, rounded: 9999, borderWidth: finished ? 0 : 1.5, borderColor: "$soft", bg: finished ? '$ink' : 'transparent', items: "center", justify: "center", hoverStyle: { borderColor: '$ink' }, children: finished ? _jsx(Check, { size: 13, color: "var(--background)", "aria-hidden": true }) : null }), _jsx(Text, { render: _jsx("input", { value: title, onChange: (e) => setTitle(e.target.value), onBlur: () => title.trim() && title !== card.title && void save({ title: title.trim() }), "aria-label": "Card title" }), flex: 1, minW: 0, bg: "transparent", borderWidth: 0, outlineStyle: "none", color: "inherit", fontSize: "$7", fontWeight: "600", textDecorationLine: finished ? 'line-through' : 'none' })] }), _jsxs(XStack, { gap: "$1.5", flexWrap: "wrap", children: [_jsx(Act, { icon: Tag, label: "Labels", on: panel === 'labels', onPress: () => setPanel(panel === 'labels' ? null : 'labels') }), _jsx(Act, { icon: Calendar, label: "Dates", on: panel === 'dates', onPress: () => setPanel(panel === 'dates' ? null : 'dates') }), _jsx(Act, { icon: UserPlus, label: "Members", on: panel === 'members', onPress: () => setPanel(panel === 'members' ? null : 'members') })] }), panel === 'labels' ? (_jsxs(YStack, { gap: "$2", p: "$3", rounded: "$3", borderWidth: 1, borderColor: "$borderColor", children: [_jsx(Text, { fontSize: "$2", fontWeight: "600", color: "$ink", children: "Labels" }), card.labels.length > 0 ? (_jsx(XStack, { gap: "$1", flexWrap: "wrap", children: card.labels.map((l) => (_jsx(Label, { name: l }, l))) })) : (_jsx(Text, { fontSize: "$1", color: "$soft", children: "None yet." })), _jsx(Text, { fontSize: "$1", color: "$soft", children: "A label is a word on the forge issue, and its colour comes from the word \u2014 so it reads the same here and there. Labels are added on the forge; this board does not write them yet." })] })) : null, panel === 'dates' ? (_jsxs(YStack, { gap: "$2", p: "$3", rounded: "$3", borderWidth: 1, borderColor: "$borderColor", children: [_jsx(Text, { fontSize: "$2", fontWeight: "600", color: "$ink", children: "Dates" }), _jsxs(XStack, { gap: "$3", flexWrap: "wrap", children: [_jsxs(YStack, { gap: "$1", children: [_jsx(Text, { fontSize: "$1", color: "$soft", children: "Start" }), _jsx(Text, { render: _jsx("input", { type: "date", value: asInput(card.startAt), readOnly: true }), ...DATE })] }), _jsxs(YStack, { gap: "$1", children: [_jsx(Text, { fontSize: "$1", color: "$soft", children: "Due" }), _jsx(Text, { render: _jsx("input", { type: "date", value: asInput(card.dueAt), readOnly: true }), ...DATE })] })] }), _jsx(Text, { fontSize: "$1", color: "$soft", children: "A card's dates come from its forge milestone, which is why they read here and cannot yet be set here." })] })) : null, panel === 'members' ? (_jsxs(YStack, { gap: "$2", p: "$3", rounded: "$3", borderWidth: 1, borderColor: "$borderColor", children: [_jsx(Text, { fontSize: "$2", fontWeight: "600", color: "$ink", children: "Members" }), people.length === 0 ? (_jsx(Text, { fontSize: "$1", color: "$soft", children: "Nobody to assign yet." })) : (people.map((p) => (_jsx(Box, { render: "button", onClick: () => void save({ assignee: card.assignee === p.name ? '' : p.name }), width: "100%", p: "$2", rounded: "$2", hoverStyle: { bg: '$hover' }, children: _jsxs(XStack, { items: "center", gap: "$2", children: [_jsx(Face, { src: p.avatar, emoji: p.emoji, name: p.label, size: 22 }), _jsx(Text, { fontSize: "$2", color: "$ink", flex: 1, numberOfLines: 1, children: p.label }), p.agent ? (_jsx(Text, { fontSize: "$1", color: "$soft", children: "agent" })) : null, card.assignee === p.name ? _jsx(Check, { size: 14, "aria-hidden": true }) : null] }) }, p.id)))), _jsx(Box, { render: "button", onClick: () => {
|
|
183
183
|
if (!client || busy)
|
|
184
184
|
return;
|
|
185
185
|
setBusy(true);
|
|
@@ -242,7 +242,7 @@ function Planner({ cards, onOpen }) {
|
|
|
242
242
|
}, [offset]);
|
|
243
243
|
const start = Math.floor(day.getTime() / 1000);
|
|
244
244
|
const due = cards.filter((c) => c.dueAt && c.dueAt >= start && c.dueAt < start + DAY);
|
|
245
|
-
return (_jsxs(YStack, { flex: 1, minW: 0, minH: 0, children: [_jsxs(XStack, { items: "center", gap: "$2", px: "$3", py: "$3", borderBottomWidth: 1, borderColor: "$borderColor", children: [_jsx(Calendar, { size: 14, "aria-hidden": true }), _jsx(Text, { fontSize: "$2", fontWeight: "600", color: "$ink", flex: 1, children: day.toLocaleDateString(undefined, { month: 'long' }) }), _jsx(Box, { render: "button", onClick: () => setOffset((n) => n - 1), p: "$1", rounded: "$2", "aria-label": "Previous day", children: _jsx(ChevronLeft, { size: 14, "aria-hidden": true }) }), _jsx(Box, { render: "button", onClick: () => setOffset(0), px: "$2", py: "$1", rounded: "$2", borderWidth: 1, borderColor: "$borderColor", children: _jsx(Text, { fontSize: "$1", color: "$ink", children: "Today" }) }), _jsx(Box, { render: "button", onClick: () => setOffset((n) => n + 1), p: "$1", rounded: "$2", "aria-label": "Next day", children: _jsx(ChevronRight, { size: 14, "aria-hidden": true }) })] }), _jsxs(YStack, { items: "center", py: "$2", gap: "$1", children: [_jsx(Text, { fontSize: "$1", color: "$soft", children: dayName(day) }), _jsx(Text, { fontSize: "$4", fontWeight: "600", color: "$ink", children: day.getDate() })] }), _jsxs(YStack, { flex: 1, minH: 0,
|
|
245
|
+
return (_jsxs(YStack, { flex: 1, minW: 0, minH: 0, children: [_jsxs(XStack, { items: "center", gap: "$2", px: "$3", py: "$3", borderBottomWidth: 1, borderColor: "$borderColor", children: [_jsx(Calendar, { size: 14, "aria-hidden": true }), _jsx(Text, { fontSize: "$2", fontWeight: "600", color: "$ink", flex: 1, children: day.toLocaleDateString(undefined, { month: 'long' }) }), _jsx(Box, { render: "button", onClick: () => setOffset((n) => n - 1), p: "$1", rounded: "$2", "aria-label": "Previous day", children: _jsx(ChevronLeft, { size: 14, "aria-hidden": true }) }), _jsx(Box, { render: "button", onClick: () => setOffset(0), px: "$2", py: "$1", rounded: "$2", borderWidth: 1, borderColor: "$borderColor", children: _jsx(Text, { fontSize: "$1", color: "$ink", children: "Today" }) }), _jsx(Box, { render: "button", onClick: () => setOffset((n) => n + 1), p: "$1", rounded: "$2", "aria-label": "Next day", children: _jsx(ChevronRight, { size: 14, "aria-hidden": true }) })] }), _jsxs(YStack, { items: "center", py: "$2", gap: "$1", children: [_jsx(Text, { fontSize: "$1", color: "$soft", children: dayName(day) }), _jsx(Text, { fontSize: "$4", fontWeight: "600", color: "$ink", children: day.getDate() })] }), _jsxs(YStack, { flex: 1, minH: 0, overflowY: "auto", overflowX: "hidden", children: [due.length > 0 ? (_jsxs(YStack, { p: "$3", gap: "$2", children: [_jsx(Text, { fontSize: "$1", color: "$soft", children: "Due today" }), due.map((c) => (_jsx(Box, { render: "button", onClick: () => onOpen(c), width: "100%", p: "$2.5", rounded: "$3", borderWidth: 1, borderColor: "$borderColor", bg: "$raised", hoverStyle: { borderColor: '$ink' }, children: _jsx(Text, { fontSize: "$2", color: "$ink", numberOfLines: 2, text: "left", children: c.title }) }, c.id)))] })) : null, Array.from({ length: 12 }, (_, i) => i + 8).map((hour) => (_jsx(XStack, { height: 56, borderTopWidth: 1, borderColor: "$borderColor", children: _jsx(YStack, { width: 54, pt: "$1", pl: "$3", children: _jsxs(Text, { fontSize: "$1", color: "$soft", children: [hour % 12 === 0 ? 12 : hour % 12, " ", hour < 12 ? 'am' : 'pm'] }) }) }, hour)))] })] }));
|
|
246
246
|
}
|
|
247
247
|
// ── the board ───────────────────────────────────────────────────────────────
|
|
248
248
|
function Column({ column, cards, people, onDrop, onOpen, onAdd, loading, }) {
|
|
@@ -258,7 +258,7 @@ function Column({ column, cards, people, onDrop, onOpen, onAdd, loading, }) {
|
|
|
258
258
|
const ref = e.dataTransfer.getData('text/plain');
|
|
259
259
|
if (ref)
|
|
260
260
|
onDrop(ref, column.id);
|
|
261
|
-
}, children: _jsxs(YStack, { width: 300, gap: "$2", p: "$2", rounded: "$3", borderWidth: 1, borderColor: over ? '$ink' : '$borderColor', bg: "$hover", maxH: "100%", children: [_jsxs(XStack, { items: "baseline", gap: "$2", px: "$2", pt: "$1", children: [_jsx(Text, { fontSize: "$2", fontWeight: "600", color: "$ink", flex: 1, children: column.label }), _jsx(Text, { fontSize: "$1", color: "$soft", children: loading ? '' : cards.length })] }), _jsx(YStack, { gap: "$2",
|
|
261
|
+
}, children: _jsxs(YStack, { width: 300, gap: "$2", p: "$2", rounded: "$3", borderWidth: 1, borderColor: over ? '$ink' : '$borderColor', bg: "$hover", maxH: "100%", children: [_jsxs(XStack, { items: "baseline", gap: "$2", px: "$2", pt: "$1", children: [_jsx(Text, { fontSize: "$2", fontWeight: "600", color: "$ink", flex: 1, children: column.label }), _jsx(Text, { fontSize: "$1", color: "$soft", children: loading ? '' : cards.length })] }), _jsx(YStack, { gap: "$2", overflowY: "auto", overflowX: "hidden", shrink: 1, children: cards.map((c) => (_jsx(Tile, { card: c, who: people.find((p) => p.name === c.assignee), onOpen: () => onOpen(c) }, c.id))) }), adding ? (_jsx(YStack, { gap: "$2", children: _jsx(Text, { render: _jsx("textarea", { value: title, autoFocus: true, onChange: (e) => setTitle(e.target.value), onKeyDown: (e) => {
|
|
262
262
|
if (e.key === 'Escape') {
|
|
263
263
|
setAdding(false);
|
|
264
264
|
setTitle('');
|
|
@@ -307,7 +307,7 @@ function Filter({ want, labels, onWant, onClose, }) {
|
|
|
307
307
|
set({ labels: held.includes(l) ? held.filter((x) => x !== l) : [...held, l] });
|
|
308
308
|
};
|
|
309
309
|
const Head = ({ children }) => (_jsx(Text, { fontSize: "$1", color: "$soft", px: "$2", pt: "$2", children: children }));
|
|
310
|
-
return (_jsxs(YStack, { position: "absolute", t: 52, r: 12, width: 300, maxH: 560,
|
|
310
|
+
return (_jsxs(YStack, { position: "absolute", t: 52, r: 12, width: 300, maxH: 560, overflowY: "auto", overflowX: "hidden", p: "$2", gap: "$0.5", rounded: "$4", borderWidth: 1, borderColor: "$borderColor", bg: "$panel", z: "var(--z-dock)", children: [_jsxs(XStack, { items: "center", px: "$2", py: "$1", children: [_jsx(Text, { fontSize: "$2", fontWeight: "600", color: "$ink", flex: 1, children: "Filter" }), _jsx(Box, { render: "button", onClick: onClose, p: "$1", rounded: "$2", "aria-label": "Close filter", children: _jsx(X, { size: 14, "aria-hidden": true }) })] }), _jsx(Tick, { on: want.holder === 'none', label: "Nobody has it", onPress: () => set({ holder: want.holder === 'none' ? undefined : 'none' }) }), _jsx(Tick, { on: want.holder === 'me', label: "Mine", onPress: () => set({ holder: want.holder === 'me' ? undefined : 'me' }) }), _jsx(Head, { children: "Who did the work" }), _jsx(Tick, { on: want.by === 'person', label: "A person", onPress: () => set({ by: want.by === 'person' ? undefined : 'person' }) }), _jsx(Tick, { on: want.by === 'agent', label: "An agent", onPress: () => set({ by: want.by === 'agent' ? undefined : 'agent' }) }), _jsx(Head, { children: "Card status" }), _jsx(Tick, { on: want.state === 'done', label: "Finished", onPress: () => set({ state: want.state === 'done' ? undefined : 'done' }) }), _jsx(Tick, { on: want.state === 'open', label: "Still open", onPress: () => set({ state: want.state === 'open' ? undefined : 'open' }) }), _jsx(Head, { children: "Due date" }), [['none', 'No dates'], ['late', 'Overdue'], ['day', 'Due in the next day'], ['week', 'Due in the next week'], ['month', 'Due in the next month']].map(([id, label]) => (_jsx(Tick, { on: want.due === id, label: label, onPress: () => set({ due: want.due === id ? undefined : id }) }, id))), labels.length > 0 ? (_jsxs(_Fragment, { children: [_jsx(Head, { children: "Labels" }), labels.map((l) => (_jsx(Tick, { on: (want.labels ?? []).includes(l), label: _jsx(Label, { name: l }), onPress: () => flipLabel(l) }, l)))] })) : null, _jsx(Head, { children: "Activity" }), [[7, 'Moved in the last week'], [14, 'Moved in the last two weeks'], [28, 'Moved in the last four weeks']].map(([days, label]) => (_jsx(Tick, { on: want.active === days, label: label, onPress: () => set({ active: want.active === days ? undefined : days }) }, days))), _jsx(Box, { render: "button", onClick: () => onWant({}), width: "100%", mt: "$2", p: "$2", rounded: "$2", borderWidth: 1, borderColor: "$borderColor", hoverStyle: { bg: '$hover' }, children: _jsx(Text, { fontSize: "$1", color: "$ink", children: "Clear" }) })] }));
|
|
311
311
|
}
|
|
312
312
|
// ── the room ────────────────────────────────────────────────────────────────
|
|
313
313
|
/** Said when a write has no board to write to. It is the same event whichever
|
|
@@ -432,7 +432,7 @@ function Cards() {
|
|
|
432
432
|
}, [client, key, keys]);
|
|
433
433
|
return (_jsxs(YStack, { flex: 1, minH: 0, position: "relative", children: [_jsxs(XStack, { height: 52, shrink: 0, items: "center", gap: "$3", px: "$3", borderBottomWidth: 1, borderColor: "$borderColor", children: [_jsx(Text, { render: "h1", fontSize: "$4", fontWeight: "600", color: "$ink", numberOfLines: 1, children: "Board" }), _jsxs(Select, { value: key || 'all', onValueChange: setKey, children: [_jsx(SelectTrigger, { "aria-label": "Board", width: 140, $sm: { width: 100 }, children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "all", children: "All Boards" }, "all"), keys.map((k) => (_jsx(SelectItem, { value: k, children: k.charAt(0).toUpperCase() + k.slice(1) }, k)))] })] }), _jsx(YStack, { flex: 1 }), cards ? (_jsx(Text, { fontSize: "$1", color: "$soft", display: "none", $md: { display: 'flex' }, children: narrowed ? `${shown.length} of ${cards.length}` : `${cards.length} cards` })) : null, _jsx(Act, { icon: ListFilter, label: "Filter", on: filtering || narrowed, onPress: () => setFiltering((v) => !v) }), _jsx(XStack, { display: "none", $md: { display: 'flex' }, children: _jsx(Act, { icon: SquareTerminal, label: "Tabs & Shell", on: Boolean(aside), onPress: () => showAside(aside ? null : "tasks") }) })] }), _jsxs(XStack, { flex: 1, minH: 0, children: [view === 'planner' ? _jsx(Planner, { cards: cards ?? [], onOpen: setOpened }) : null, _jsxs(YStack, { flex: 1, minW: 0, minH: 0,
|
|
434
434
|
// One view at a time: the planner is the other reading of these cards.
|
|
435
|
-
display: view === 'planner' ? "none" : "flex", children: [refused ? (_jsxs(XStack, { items: "center", gap: "$2", mx: "$4", mt: "$3", px: "$3", py: "$2", rounded: "$3", borderWidth: 1, borderColor: "$borderColor", bg: "$hover", children: [_jsxs(Text, { fontSize: "$1", color: "$soft", flex: 1, minW: 0, children: [refused, " Cards will appear here when it answers."] }), _jsx(Box, { render: "button", onClick: () => bump((x) => x + 1), shrink: 0, px: "$2", py: "$1", rounded: "$2", borderWidth: 1, borderColor: "$borderColor", hoverStyle: { bg: '$hover' }, children: _jsx(Text, { fontSize: "$1", color: "$ink", children: "Try again" }) })] })) : null, _jsx(XStack, { flex: 1, minH: 0,
|
|
435
|
+
display: view === 'planner' ? "none" : "flex", children: [refused ? (_jsxs(XStack, { items: "center", gap: "$2", mx: "$4", mt: "$3", px: "$3", py: "$2", rounded: "$3", borderWidth: 1, borderColor: "$borderColor", bg: "$hover", children: [_jsxs(Text, { fontSize: "$1", color: "$soft", flex: 1, minW: 0, children: [refused, " Cards will appear here when it answers."] }), _jsx(Box, { render: "button", onClick: () => bump((x) => x + 1), shrink: 0, px: "$2", py: "$1", rounded: "$2", borderWidth: 1, borderColor: "$borderColor", hoverStyle: { bg: '$hover' }, children: _jsx(Text, { fontSize: "$1", color: "$ink", children: "Try again" }) })] })) : null, _jsx(XStack, { flex: 1, minH: 0, overflowX: "auto", overflowY: "auto", p: "$4", gap: "$3", items: "flex-start", children: COLUMNS.map((c) => (_jsx(Column, { column: c, cards: filed.get(c.id) ?? [], people: roster, onDrop: move, onOpen: setOpened, onAdd: add, loading: cards === null && !refused }, c.id))) })] })] }), _jsxs(XStack, { shrink: 0, items: "center", gap: "$2", px: "$3", py: "$2", borderTopWidth: 1, borderColor: "$borderColor", children: [_jsx(Act, { icon: Columns3, label: "Board", on: view === 'board', onPress: () => setView('board') }), _jsx(Act, { icon: Calendar, label: "Planner", on: view === 'planner', onPress: () => setView('planner') }), _jsx(YStack, { flex: 1 }), _jsx(Text, { fontSize: "$1", color: "$soft", numberOfLines: 1, display: "none", $md: { display: 'flex' }, children: "A card is a forge issue \u2014 moving it here moves it there." })] }), filtering ? (_jsx(Filter, { want: want, labels: labels, onWant: setWant, onClose: () => setFiltering(false) })) : null, opened ? (_jsx(Detail, { card: cards?.find((c) => c.id === opened.id) ?? opened, people: roster, agents: agents, onClose: () => setOpened(null), onSaved: () => bump((x) => x + 1) })) : null] }));
|
|
436
436
|
}
|
|
437
437
|
/**
|
|
438
438
|
* The pane, and the session its reads need.
|
package/dist/Boxes.js
CHANGED
|
@@ -169,5 +169,5 @@ export function Boxes() {
|
|
|
169
169
|
return _jsx(Empty, { children: say(failed, 'this list') });
|
|
170
170
|
if (!boxes)
|
|
171
171
|
return _jsx(Empty, { children: "Reading your leases." });
|
|
172
|
-
return (_jsxs(YStack, { flex: 1, minH: 0, children: [_jsx(ChannelHeader, { name: "Sandboxes", children: _jsxs(Text, { fontSize: "$2", color: "$soft", children: ["from ", usd(HOURLY.exec), "/hr"] }) }), _jsx(Start, { onDone: reload }), boxes.length === 0 ? (_jsx(Empty, { children: "Nothing is running. A sandbox is leased when an agent needs somewhere to work, and billed for the hours it is held." })) : (_jsx(YStack, { flex: 1, minH: 0,
|
|
172
|
+
return (_jsxs(YStack, { flex: 1, minH: 0, children: [_jsx(ChannelHeader, { name: "Sandboxes", children: _jsxs(Text, { fontSize: "$2", color: "$soft", children: ["from ", usd(HOURLY.exec), "/hr"] }) }), _jsx(Start, { onDone: reload }), boxes.length === 0 ? (_jsx(Empty, { children: "Nothing is running. A sandbox is leased when an agent needs somewhere to work, and billed for the hours it is held." })) : (_jsx(YStack, { flex: 1, minH: 0, overflowY: "auto", overflowX: "hidden", p: "$4", gap: "$3", children: boxes.map((box) => (_jsx(Row, { box: box, busy: busy[box.id], onResume: () => void act(box.id, 'resume'), onStop: () => void act(box.id, 'stop'), onEnd: () => void act(box.id, 'end') }, box.id))) }))] }));
|
|
173
173
|
}
|
package/dist/Chat.js
CHANGED
|
@@ -32,22 +32,23 @@ import { capped, detail, familyOf, planRequired, plainly, refused } from "./fail
|
|
|
32
32
|
import { Prose } from "./Prose.js";
|
|
33
33
|
import { useAgent, useAi, useChat, useModels, } from "@hanzo/ai/react";
|
|
34
34
|
import { freeCopy, grantConsent, hasConsent, isFree, } from "@hanzo/ai";
|
|
35
|
-
import { ASK, Chat as ChatSurface, Code, Failure } from "@hanzo/ui/chat";
|
|
35
|
+
import { ASK, Chat as ChatSurface, Code, Failure, Parts, words } from "@hanzo/ui/chat";
|
|
36
|
+
import { compose, hold, images, partsOf } from "./lib/attach.js";
|
|
36
37
|
import { Face, brief as roomBrief, join, roleOf, roomTurn, roster as rosterLine, rosterOf, speakers, speaksOf, voiceOf } from "./cast.js";
|
|
37
|
-
import { SpeakButton,
|
|
38
|
+
import { SpeakButton, cleanForSpeech } from "./speech.js";
|
|
38
39
|
import { Crew, HOUSE, useCrew } from "./crew.js";
|
|
39
40
|
import { called } from "./team.js";
|
|
40
41
|
import { Anchor, Box, Button, Text, View, XStack, YStack } from "@hanzo/ui";
|
|
41
42
|
import { Control } from "@hanzo/composer";
|
|
42
43
|
import { useIam, useIamToken } from "@hanzo/iam/react";
|
|
43
44
|
import { hasSession, org } from "./lib/session.js";
|
|
44
|
-
import { clock, sku, useLimits } from "./lib/limits.js";
|
|
45
|
+
import { chats, clock, sku, useLimits } from "./lib/limits.js";
|
|
45
46
|
import { Meters } from "./meters.js";
|
|
46
47
|
import { useHydrated } from "./lib/hydrated.js";
|
|
47
48
|
import { base, served } from "./lib/ai.js";
|
|
48
49
|
import { onServed } from "./lib/served.js";
|
|
49
50
|
import { speech, useVoice, Voice } from "@hanzo/voice";
|
|
50
|
-
import { ArrowUp, Mic, Square, Star, PanelRight, X } from "lucide-react";
|
|
51
|
+
import { ArrowUp, Mic, Paperclip, Square, Star, PanelRight, X } from "lucide-react";
|
|
51
52
|
import { ENSO, FREE } from "./lib/ai.js";
|
|
52
53
|
import { openThread, useOpen } from "./open.js";
|
|
53
54
|
import { Beside, Framed } from "./Shell.js";
|
|
@@ -62,7 +63,7 @@ import { checkoutUrl, onFree, planName } from './lib/plans.js';
|
|
|
62
63
|
import { Upgrade, useFree } from './Upgrade.js';
|
|
63
64
|
import { enter, LOGIN } from './lib/destination.js';
|
|
64
65
|
import { first } from './lib/first.js';
|
|
65
|
-
import { mix } from "./lib/mix.js";
|
|
66
|
+
import { BAD, mix } from "./lib/mix.js";
|
|
66
67
|
import { site } from './where.js';
|
|
67
68
|
/** The veil under the free lane's consent sheet. */
|
|
68
69
|
const SCRIM = mix("var(--pure-black)", 72, "srgb");
|
|
@@ -430,7 +431,11 @@ function Thread({ placeholder = MESSAGE, initial, anonymous = false, heading, })
|
|
|
430
431
|
// bare "not found", after it has already recorded the question. `served`
|
|
431
432
|
// keeps the house name where it is served and otherwise falls to the first
|
|
432
433
|
// model that is, so a room on any catalogue can be spoken to.
|
|
433
|
-
|
|
434
|
+
// A SPEECH MODEL NEVER ANSWERS A CHAT TURN. The catalog lists the
|
|
435
|
+
// transcriber and the voice beside the chat models; one that names its
|
|
436
|
+
// outputs and names no text (`chats`) is neither offered nor fallen back to.
|
|
437
|
+
const { models: catalog } = useModels();
|
|
438
|
+
const models = useMemo(() => catalog.filter(chats), [catalog]);
|
|
434
439
|
// HANZO MODELS ALONE are offered here, to everyone: an Enso or Zen id
|
|
435
440
|
// (`sku`). A preference kept from before for any other model is not one this
|
|
436
441
|
// room offers, so the house router is asked for instead, and `served` keeps
|
|
@@ -588,49 +593,66 @@ function Thread({ placeholder = MESSAGE, initial, anonymous = false, heading, })
|
|
|
588
593
|
// identity changes on every `setMessages` — `useChat` closes over `messages` —
|
|
589
594
|
// so an effect depending on it re-runs after each streamed token.
|
|
590
595
|
const latest = useRef(send);
|
|
591
|
-
|
|
596
|
+
// The images held for a turn ride with it at the moment it is sent, queued or
|
|
597
|
+
// not, and are spent once: a retry is the words again, not the pictures.
|
|
598
|
+
latest.current = (text) => send(text, outgoing.current.splice(0));
|
|
592
599
|
// THE COMPOSER'S DRAFT IS HELD HERE, so an arriving question can be typed into
|
|
593
600
|
// it. `Chat` owns a draft of its own and spreads `composer` after it, so
|
|
594
601
|
// passing value/onChange/onSend takes that ownership over rather than fighting
|
|
595
602
|
// it.
|
|
596
603
|
const [draft, setDraft] = useState("");
|
|
604
|
+
// FILES WITH THE NEXT MESSAGE: pressed in through the paperclip, or dropped
|
|
605
|
+
// anywhere on the room. An image rides as a picture the model reads, a text
|
|
606
|
+
// file as its words (lib/attach); anything else is refused by name here.
|
|
607
|
+
const [files, setFiles] = useState([]);
|
|
608
|
+
const [fileSaid, setFileSaid] = useState(null);
|
|
609
|
+
const [dragging, setDragging] = useState(false);
|
|
610
|
+
const picker = useRef(null);
|
|
611
|
+
const outgoing = useRef([]);
|
|
612
|
+
const depth = useRef(0);
|
|
613
|
+
const take = useCallback(async (list) => {
|
|
614
|
+
if (!list?.length)
|
|
615
|
+
return;
|
|
616
|
+
const out = await Promise.all(Array.from(list).map(hold));
|
|
617
|
+
setFiles((now) => [...now, ...out.filter((one) => typeof one !== "string")]);
|
|
618
|
+
const refused = out.filter((one) => typeof one === "string");
|
|
619
|
+
setFileSaid(refused.length ? refused.join(" ") : null);
|
|
620
|
+
}, []);
|
|
597
621
|
useSeed(initial, setDraft);
|
|
598
|
-
// DICTATION
|
|
599
|
-
// to
|
|
622
|
+
// DICTATION through Hanzo's own ear: the mic records and each pause is
|
|
623
|
+
// posted to /v1/audio/transcriptions (`zen-scribe`). `speech()` defaults to
|
|
624
|
+
// the Hanzo gateway and `base()` is the one place a host is read from, so
|
|
600
625
|
// pointing a local cloud at one points both. The token is the reader's own —
|
|
601
|
-
// the package never invents a credential — and
|
|
602
|
-
//
|
|
626
|
+
// the package never invents a credential — and a refusal is worn by
|
|
627
|
+
// `<Voice/>`, never passed off as a transcript.
|
|
603
628
|
const { token } = useIamToken();
|
|
604
|
-
const
|
|
605
|
-
const ear = useMemo(() => speech({
|
|
606
|
-
...base(),
|
|
607
|
-
...(token ? { token } : {}),
|
|
608
|
-
voice: { name: agentVoice },
|
|
609
|
-
}), [token, agentVoice]);
|
|
629
|
+
const ear = useMemo(() => speech({ ...base(), ...(token ? { token } : {}) }), [token]);
|
|
610
630
|
const voice = useVoice({
|
|
611
631
|
speech: ear,
|
|
612
632
|
onPartial: setDraft,
|
|
613
633
|
onUtterance: setDraft,
|
|
614
634
|
});
|
|
615
|
-
//
|
|
635
|
+
// WHILE THE MIC IS OPEN, A FINISHED REPLY IS READ ALOUD by the platform's
|
|
636
|
+
// voice, through the conversation's own mouth so speaking over it stops it.
|
|
637
|
+
// A room's reply is read part by part, each in its speaker's cast voice.
|
|
616
638
|
const wasStreaming = useRef(false);
|
|
617
639
|
useEffect(() => {
|
|
618
640
|
if (wasStreaming.current && !chat.streaming && voice.open) {
|
|
619
641
|
const msgs = chat.messages;
|
|
620
642
|
const lastMsg = msgs[msgs.length - 1];
|
|
621
643
|
if (lastMsg && lastMsg.role === "assistant" && typeof lastMsg.content === "string") {
|
|
622
|
-
// A room's reply is read part by part, each in its speaker's voice.
|
|
623
644
|
const parts = room.length > 1 ? speakers(lastMsg.content, room) : [{ who: withWhom, text: lastMsg.content }];
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
645
|
+
void (async () => {
|
|
646
|
+
for (const part of parts) {
|
|
647
|
+
const said = cleanForSpeech(part.text);
|
|
648
|
+
if (said)
|
|
649
|
+
await voice.say(said, voiceOf(part.who ?? withWhom ?? undefined));
|
|
650
|
+
}
|
|
651
|
+
})();
|
|
630
652
|
}
|
|
631
653
|
}
|
|
632
654
|
wasStreaming.current = chat.streaming;
|
|
633
|
-
}, [chat.streaming, voice.open, chat.messages, withWhom, room]);
|
|
655
|
+
}, [chat.streaming, voice.open, voice.say, chat.messages, withWhom, room]);
|
|
634
656
|
// A SEND THAT WAITS ONE RENDER. `send` closes over the turns it was rendered
|
|
635
657
|
// with, so a send that first tidies the thread is held here and made from the
|
|
636
658
|
// next render, where the tidied turns are the ones it sends.
|
|
@@ -771,6 +793,17 @@ function Thread({ placeholder = MESSAGE, initial, anonymous = false, heading, })
|
|
|
771
793
|
}
|
|
772
794
|
speak(text);
|
|
773
795
|
}, [model, speak]);
|
|
796
|
+
/** The draft and the held files, sent as one turn. */
|
|
797
|
+
const submit = () => {
|
|
798
|
+
const text = draft.trim();
|
|
799
|
+
if (!text && !files.length)
|
|
800
|
+
return;
|
|
801
|
+
outgoing.current = images(files);
|
|
802
|
+
setDraft("");
|
|
803
|
+
setFiles([]);
|
|
804
|
+
setFileSaid(null);
|
|
805
|
+
guarded(compose(text, files));
|
|
806
|
+
};
|
|
774
807
|
/*
|
|
775
808
|
WHAT AN UNANSWERED QUESTION LOOKS LIKE: a turn of its own directly under it,
|
|
776
809
|
naming who was asked, saying what went wrong in the reader's words, and
|
|
@@ -803,7 +836,7 @@ function Thread({ placeholder = MESSAGE, initial, anonymous = false, heading, })
|
|
|
803
836
|
}, [said, chat.streaming, queued]);
|
|
804
837
|
const failure = (question) => {
|
|
805
838
|
const failed = failures[question.id];
|
|
806
|
-
const text =
|
|
839
|
+
const text = words(question.content);
|
|
807
840
|
const whom = addressed(text, room);
|
|
808
841
|
const wrong = failed?.error ?? null;
|
|
809
842
|
const how = wrong ? refusalOf(wrong) : null;
|
|
@@ -856,7 +889,31 @@ function Thread({ placeholder = MESSAGE, initial, anonymous = false, heading, })
|
|
|
856
889
|
}, onCancel: () => {
|
|
857
890
|
setAsking(null);
|
|
858
891
|
} }));
|
|
859
|
-
return (_jsxs(YStack, { flex: 1, minH: 0, width: "100%", position: "relative",
|
|
892
|
+
return (_jsxs(YStack, { flex: 1, minH: 0, width: "100%", position: "relative", "data-slot": "chat-room",
|
|
893
|
+
// Entered and left once for every child crossed, so the veil follows a
|
|
894
|
+
// count rather than the last event.
|
|
895
|
+
onDragEnter: (e) => {
|
|
896
|
+
if (!Array.from(e.dataTransfer.types).includes("Files"))
|
|
897
|
+
return;
|
|
898
|
+
depth.current += 1;
|
|
899
|
+
setDragging(true);
|
|
900
|
+
}, onDragOver: (e) => {
|
|
901
|
+
if (!Array.from(e.dataTransfer.types).includes("Files"))
|
|
902
|
+
return;
|
|
903
|
+
e.preventDefault();
|
|
904
|
+
e.dataTransfer.dropEffect = "copy";
|
|
905
|
+
}, onDragLeave: () => {
|
|
906
|
+
depth.current = Math.max(0, depth.current - 1);
|
|
907
|
+
if (depth.current === 0)
|
|
908
|
+
setDragging(false);
|
|
909
|
+
}, onDrop: (e) => {
|
|
910
|
+
depth.current = 0;
|
|
911
|
+
setDragging(false);
|
|
912
|
+
if (!e.dataTransfer.files?.length)
|
|
913
|
+
return;
|
|
914
|
+
e.preventDefault();
|
|
915
|
+
void take(e.dataTransfer.files);
|
|
916
|
+
}, children: [dragging ? (_jsx(YStack, { "aria-hidden": true, position: "absolute", t: 0, r: 0, b: 0, l: 0, z: 10, items: "center", justify: "center", bg: "var(--surface-scrim)", borderWidth: 2, borderStyle: "dashed", borderColor: "$borderColor", rounded: "var(--pane-round)", pointerEvents: "none", children: _jsx(Text, { fontSize: "$4", color: "$ink", children: "Drop to attach" }) })) : null, _jsxs(XStack, { height: 48, items: "center", justify: "space-between", px: "$3", borderBottomWidth: 1, borderColor: "$borderColor", children: [_jsxs(XStack, { items: "center", gap: "$2", minW: 0, children: [_jsx(Box, { render: "button", onClick: () => open && star(open), "aria-label": open && starred.has(open) ? "Unstar this conversation" : "Star this conversation", "aria-pressed": !!open && starred.has(open), p: "$1.5", rounded: "$2", hoverStyle: { bg: '$hover' }, children: _jsx(Star, { size: 15, "aria-hidden": true, color: open && starred.has(open) ? "var(--foreground)" : "var(--muted-foreground)", fill: open && starred.has(open) ? "currentColor" : "none" }) }), room.length ? (_jsx(XStack, { role: "list", "aria-label": "Agents in this chat", items: "center", gap: "$1.5", minW: 0, overflowX: "auto", "$platform-web": { scrollbarWidth: "none" }, children: room.map((name) => (_jsxs(XStack, { role: "listitem", items: "center", gap: "$1.5", pl: "$1", pr: "$0.5", py: "$0.5", rounded: 999, borderWidth: 1, borderColor: "$borderColor", shrink: 0, children: [_jsx(Face, { src: faces[name]?.avatar ?? (name === withWhom ? portrait : undefined), emoji: faces[name]?.emoji ?? (name === withWhom ? persona?.emoji : undefined), name: name, size: 22 }), _jsx(Text, { fontSize: "$2", fontWeight: "600", color: "$color", numberOfLines: 1, display: "none", $sm: { display: "flex" }, children: name }), _jsx(Box, { render: "button", onClick: () => openAgent(name), "aria-label": `Remove ${name} from this chat`, title: `Remove ${name}`, width: 24, height: 24, rounded: 999, items: "center", justify: "center", hoverStyle: { bg: "$hover" }, children: _jsx(X, { size: 13, "aria-hidden": true, color: "var(--soft, var(--muted-foreground))" }) })] }, name))) })) : null] }), _jsxs(XStack, { items: "center", gap: "$1", children: [_jsx(Box, { render: "button", onClick: () => {
|
|
860
917
|
if (aside === null) {
|
|
861
918
|
showAside("tasks");
|
|
862
919
|
if (!pinned)
|
|
@@ -889,12 +946,15 @@ function Thread({ placeholder = MESSAGE, initial, anonymous = false, heading, })
|
|
|
889
946
|
const question = shown.find((one) => one.id === String(turn.id).slice(FAILED.length + 1));
|
|
890
947
|
return question ? failure(question) : null;
|
|
891
948
|
}
|
|
949
|
+
// A QUESTION THAT CARRIED PICTURES draws them beside its words.
|
|
950
|
+
if (turn.role === "user" && Array.isArray(turn.content))
|
|
951
|
+
return _jsx(Parts, { parts: partsOf(turn.content), prose: (text) => _jsx(Prose, { text: text }) });
|
|
892
952
|
const said = typeof turn.content === "string" ? turn.content : "";
|
|
893
953
|
// WHO IS ANSWERING, while the answer has no words yet: the agents the
|
|
894
954
|
// question addresses, or the whole room when it names nobody.
|
|
895
955
|
if (turn.role === "assistant" && !said && chat.streaming && room.length) {
|
|
896
956
|
const q = [...shown].reverse().find((one) => one.role === "user");
|
|
897
|
-
const whom = addressed(
|
|
957
|
+
const whom = addressed(words(q?.content), room);
|
|
898
958
|
return (_jsxs(Text, { fontSize: "$2", color: "$soft", children: [join(whom), " ", whom.length > 1 ? "are" : "is", " answering\u2026"] }));
|
|
899
959
|
}
|
|
900
960
|
// A ROOM'S ANSWER IS SEVERAL PEOPLE'S. Each opens a line with their
|
|
@@ -907,7 +967,7 @@ function Thread({ placeholder = MESSAGE, initial, anonymous = false, heading, })
|
|
|
907
967
|
const at = shown.findIndex((one) => one.id === turn.id);
|
|
908
968
|
const q = shown.slice(0, at).reverse().find((one) => one.role === "user");
|
|
909
969
|
const silent = room.length > 1 && !(chat.streaming && at === shown.length - 1)
|
|
910
|
-
? addressed(
|
|
970
|
+
? addressed(words(q?.content), room).filter((name) => !parts.some((part) => part.who?.toLowerCase() === name.toLowerCase()))
|
|
911
971
|
: [];
|
|
912
972
|
return (_jsxs(YStack, { gap: "$3", children: [parts.map((part, i) => (_jsxs(YStack, { gap: "$1", children: [part.who ? (_jsxs(XStack, { items: "center", gap: "$2", children: [_jsx(Face, { src: faces[part.who]?.avatar, emoji: faces[part.who]?.emoji, name: part.who, size: 20 }), _jsx(Text, { fontSize: "$2", fontWeight: "600", color: "$ink", children: part.who })] })) : null, _jsx(Prose, { text: part.text }), part.text ? (_jsxs(XStack, { ml: "$-1", items: "center", gap: "$2", children: [_jsx(Take, { text: part.text, says: "this answer" }), _jsx(SpeakButton, { text: part.text, agentName: part.who ?? withWhom ?? undefined })] })) : null] }, i))), silent.length ? (_jsxs(Text, { fontSize: "$2", color: "$soft", children: ["No answer from ", join(silent), " in this reply."] })) : null, byline(turn.id)] }));
|
|
913
973
|
}
|
|
@@ -926,13 +986,7 @@ function Thread({ placeholder = MESSAGE, initial, anonymous = false, heading, })
|
|
|
926
986
|
column: { maxW: MEASURE }, composer: {
|
|
927
987
|
value: draft,
|
|
928
988
|
onChange: setDraft,
|
|
929
|
-
onSend:
|
|
930
|
-
const text = draft.trim();
|
|
931
|
-
if (!text)
|
|
932
|
-
return;
|
|
933
|
-
setDraft("");
|
|
934
|
-
guarded(text);
|
|
935
|
-
},
|
|
989
|
+
onSend: submit,
|
|
936
990
|
// A CHARACTER IS WHO YOU ARE WRITING TO, so the field says so — and
|
|
937
991
|
// `label` keeps the accessible name constant while it does, which is
|
|
938
992
|
// the reason the component ships both: a placeholder that changes with
|
|
@@ -982,15 +1036,16 @@ function Thread({ placeholder = MESSAGE, initial, anonymous = false, heading, })
|
|
|
982
1036
|
// package's button and `streaming` still has to become stop; it keeps
|
|
983
1037
|
// `data-slot="composer-send"` so a driver finds it, and it is the same
|
|
984
1038
|
// `Control` circle as the mic, filled.
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
1039
|
+
// THE PAPERCLIP opens the file picker; the same files can be dropped on
|
|
1040
|
+
// the room.
|
|
1041
|
+
children: (_jsxs(_Fragment, { children: [_jsx(Control, { type: "button", size: ROUND, "aria-label": "Attach files", onClick: () => picker.current?.click(), children: _jsx(Paperclip, { size: 15, "aria-hidden": true }) }), _jsx("input", { ref: picker, type: "file", multiple: true, hidden: true, "data-slot": "composer-files", onChange: (e) => {
|
|
1042
|
+
void take(e.target.files);
|
|
1043
|
+
e.target.value = "";
|
|
1044
|
+
} })] })),
|
|
1045
|
+
// WHAT GOES WITH THE MESSAGE, above the frame: one chip a file, each
|
|
1046
|
+
// with its own remove, and the reason any file was refused.
|
|
1047
|
+
head: files.length || fileSaid ? (_jsxs(YStack, { width: "calc(100% - 32px)", maxW: `calc(${MEASURE} - 3rem)`, mx: "auto", gap: "$1.5", children: [files.length ? (_jsx(XStack, { gap: "$1.5", flexWrap: "wrap", role: "list", "aria-label": "Attached files", "data-slot": "composer-attached", children: files.map((one) => (_jsxs(XStack, { role: "listitem", items: "center", gap: "$1.5", pl: "$2.5", pr: "$1", py: "$1", rounded: "$10", borderWidth: 1, borderColor: "$borderColor", bg: "$hover", maxW: 260, children: [_jsx(Paperclip, { size: 12, "aria-hidden": true, color: "var(--muted-foreground)" }), _jsx(Text, { fontSize: "$2", color: "$ink", numberOfLines: 1, children: one.name }), _jsx(Box, { render: "button", "aria-label": `Remove ${one.name}`, onClick: () => setFiles((now) => now.filter((f) => f.id !== one.id)), p: "$1", rounded: "$10", hoverStyle: { bg: "$raised" }, children: _jsx(X, { size: 12, "aria-hidden": true }) })] }, one.id))) })) : null, fileSaid ? (_jsx(Text, { role: "alert", fontSize: "$2", color: BAD, children: fileSaid })) : null] })) : undefined,
|
|
1048
|
+
send: (_jsxs(XStack, { items: "center", gap: "$2", children: [_jsx(Enso, { effort: effort, onEffort: setEffort, model: wanted, onModel: choose, models: offered }), _jsx(Control, { asChild: true, size: ROUND, children: _jsx(Voice, { voice: voice, children: (state) => (_jsx(Mic, { size: 15, fill: state === "idle" ? "none" : "currentColor" })) }) }), _jsx(Control, { type: "button", size: ROUND, fill: true, "data-slot": "composer-send", "aria-label": chat.streaming ? "Stop" : "Send", opacity: chat.streaming || draft.trim() || files.length ? 1 : 0.4, onClick: () => (chat.streaming ? chat.stop() : submit()), children: chat.streaming ? (_jsx(Square, { size: 13, fill: "currentColor" })) : (_jsx(ArrowUp, { size: 15, strokeWidth: 2.5 })) })] })),
|
|
994
1049
|
// WHAT THE PLAN HAS LEFT, under the frame and at its measure, for a
|
|
995
1050
|
// plan holder only: Free keeps the free lane's own notice.
|
|
996
1051
|
foot: held && limits && (limits.session || limits.day || limits.month) ? (_jsx(YStack, { width: "calc(100% - 32px)", maxW: `calc(${MEASURE} - 3rem)`, mx: "auto", children: _jsx(Meters, { limits: limits }) })) : undefined,
|
package/dist/Contacts.js
CHANGED
|
@@ -226,7 +226,7 @@ function Roster({ owner }) {
|
|
|
226
226
|
}
|
|
227
227
|
}, [ai, busy, entries, picked, router]);
|
|
228
228
|
const waiting = (reading && people.length === 0) || (gathering && agents.length === 0);
|
|
229
|
-
return (_jsxs(YStack, { flex: 1, minH: 0, children: [_jsxs(XStack, { height: 48, shrink: 0, items: "center", gap: "$2", px: "$3", borderBottomWidth: 1, borderColor: "$borderColor", children: [_jsx(Text, { render: "h1", flex: 1, fontSize: "$3", fontWeight: "600", color: "$ink", numberOfLines: 1, children: "Contacts" }), _jsx(Control, { onPress: () => setInviting(true), icon: _jsx(UserPlus, { size: 13, "aria-hidden": true }), children: "Invite" }), _jsx(Control, { onPress: () => setHiring(true), icon: _jsx(Plus, { size: 13, "aria-hidden": true }), children: "New agent" })] }), _jsxs(XStack, { shrink: 0, items: "center", gap: "$2", px: "$3", py: "$2", borderBottomWidth: 1, borderColor: "$borderColor", flexWrap: "wrap", children: [_jsxs(XStack, { items: "center", gap: "$1.5", flex: 1, minW: 160, px: "$2", borderWidth: 1, borderColor: "$borderColor", rounded: "$3", children: [_jsx(Search, { size: 13, "aria-hidden": true }), _jsx(Text, { render: _jsx("input", { value: look, onChange: (e) => setLook(e.target.value), placeholder: "Search", "aria-label": "Search contacts" }), flex: 1, minW: 0, bg: "transparent", borderWidth: 0, outlineStyle: "none", color: "inherit", fontSize: "$2", px: 0, py: 6 })] }), TABS.map((one) => (_jsx(Pill, { on: tab === one.id, onPress: () => setTab(one.id), children: one.label }, one.id))), _jsx(Box, { width: 1, height: 18, bg: "$borderColor" }), _jsx(Pill, { on: order === 'name', onPress: () => setOrder('name'), children: "A\u2013Z" }), _jsx(Pill, { on: order === 'recent', onPress: () => setOrder('recent'), children: "Recent" })] }), unread ? _jsx(Note, { children: say(unread, 'your organization') }) : null, ungathered ? _jsx(Note, { children: say(ungathered, "this org's agents") }) : null, unlisted ? _jsx(Note, { children: say(unlisted, 'the agent presets') }) : null, wrong ? _jsx(Note, { children: wrong }) : null, _jsx(YStack, { flex: 1, minH: 0,
|
|
229
|
+
return (_jsxs(YStack, { flex: 1, minH: 0, children: [_jsxs(XStack, { height: 48, shrink: 0, items: "center", gap: "$2", px: "$3", borderBottomWidth: 1, borderColor: "$borderColor", children: [_jsx(Text, { render: "h1", flex: 1, fontSize: "$3", fontWeight: "600", color: "$ink", numberOfLines: 1, children: "Contacts" }), _jsx(Control, { onPress: () => setInviting(true), icon: _jsx(UserPlus, { size: 13, "aria-hidden": true }), children: "Invite" }), _jsx(Control, { onPress: () => setHiring(true), icon: _jsx(Plus, { size: 13, "aria-hidden": true }), children: "New agent" })] }), _jsxs(XStack, { shrink: 0, items: "center", gap: "$2", px: "$3", py: "$2", borderBottomWidth: 1, borderColor: "$borderColor", flexWrap: "wrap", children: [_jsxs(XStack, { items: "center", gap: "$1.5", flex: 1, minW: 160, px: "$2", borderWidth: 1, borderColor: "$borderColor", rounded: "$3", children: [_jsx(Search, { size: 13, "aria-hidden": true }), _jsx(Text, { render: _jsx("input", { value: look, onChange: (e) => setLook(e.target.value), placeholder: "Search", "aria-label": "Search contacts" }), flex: 1, minW: 0, bg: "transparent", borderWidth: 0, outlineStyle: "none", color: "inherit", fontSize: "$2", px: 0, py: 6 })] }), TABS.map((one) => (_jsx(Pill, { on: tab === one.id, onPress: () => setTab(one.id), children: one.label }, one.id))), _jsx(Box, { width: 1, height: 18, bg: "$borderColor" }), _jsx(Pill, { on: order === 'name', onPress: () => setOrder('name'), children: "A\u2013Z" }), _jsx(Pill, { on: order === 'recent', onPress: () => setOrder('recent'), children: "Recent" })] }), unread ? _jsx(Note, { children: say(unread, 'your organization') }) : null, ungathered ? _jsx(Note, { children: say(ungathered, "this org's agents") }) : null, unlisted ? _jsx(Note, { children: say(unlisted, 'the agent presets') }) : null, wrong ? _jsx(Note, { children: wrong }) : null, _jsx(YStack, { flex: 1, minH: 0, overflowY: "auto", overflowX: "hidden", children: waiting && shown.length === 0 ? (_jsx(Note, { children: "Reading your directory." })) : shown.length === 0 ? (_jsx(Note, { children: look.trim()
|
|
230
230
|
? `Nobody here matches “${look.trim()}”.`
|
|
231
231
|
: tab === 'agent'
|
|
232
232
|
? 'No agents yet. New agent writes one.'
|
package/dist/Directory.js
CHANGED
|
@@ -297,7 +297,7 @@ export function Catalog({ tab }) {
|
|
|
297
297
|
return r.name.toLowerCase().includes(q) || r.description.toLowerCase().includes(q);
|
|
298
298
|
});
|
|
299
299
|
}, [rows, query]);
|
|
300
|
-
return (_jsxs(YStack, { gap: "$4", children: [_jsx(XStack, { items: "center", gap: "$2", flexWrap: "wrap", children: _jsxs(XStack, { items: "center", gap: "$2", flex: 1, minW: 180, children: [_jsx(Search, { size: 14, "aria-hidden": true }), _jsx(Input, { value: query, onChangeText: setQuery, placeholder: `Search ${tab}`, "aria-label": `Search ${tab}`, flex: 1 })] }) }), shown.length > 0 ? (_jsxs(_Fragment, { children: [_jsxs(Text, { fontSize: "$3", color: "$soft", children: [shown.length, " ", shown.length === 1 ? 'result' : 'results'] }), _jsx(YStack, { maxH: 380,
|
|
300
|
+
return (_jsxs(YStack, { gap: "$4", children: [_jsx(XStack, { items: "center", gap: "$2", flexWrap: "wrap", children: _jsxs(XStack, { items: "center", gap: "$2", flex: 1, minW: 180, children: [_jsx(Search, { size: 14, "aria-hidden": true }), _jsx(Input, { value: query, onChangeText: setQuery, placeholder: `Search ${tab}`, "aria-label": `Search ${tab}`, flex: 1 })] }) }), shown.length > 0 ? (_jsxs(_Fragment, { children: [_jsxs(Text, { fontSize: "$3", color: "$soft", children: [shown.length, " ", shown.length === 1 ? 'result' : 'results'] }), _jsx(YStack, { maxH: 380, overflowY: "auto", overflowX: "hidden", pr: "$1", width: "100%", children: _jsx(Grid, { columns: tab === 'channels' ? { min: 168, max: 4 } : { min: 240, max: 2 }, gap: 12, children: shown.map((r) => tab === 'channels' ? (_jsx(Channel, { row: r, onChange: () => setTurn((n) => n + 1) }, r.id)) : (_jsx(RowCard, { row: r, tab: tab }, r.id))) }) })] })) : (_jsxs(YStack, { gap: "$3", py: "$4", children: [_jsx(Text, { fontSize: "$3", color: "$soft", children: query ? `Nothing matches “${query}”.` : `No ${label.toLowerCase()} here yet.` }), !query ? (_jsx(Action, { href: `${CONSOLE}/${tab}`, target: "_blank", rel: "noreferrer", $touchable: { minH: 44, minW: 44 }, children: "Add in console" })) : null] }))] }));
|
|
301
301
|
}
|
|
302
302
|
function RowCard({ row, tab }) {
|
|
303
303
|
const { isAuthenticated } = useIam();
|
package/dist/Drive.js
CHANGED
|
@@ -423,7 +423,7 @@ export function Drive() {
|
|
|
423
423
|
setPrefix('');
|
|
424
424
|
setMenu(false);
|
|
425
425
|
}, children: [_jsx(Icon, { size: 16 }), item.label] }, item.id));
|
|
426
|
-
}) }), _jsxs(YStack, { gap: "$2", mt: "$2", px: "$2", children: [_jsx(Text, { fontSize: 11, fontWeight: "600", color: "$faint", letterSpacing: 0.6, children: "Storage" }), _jsx(Text, { fontSize: 12, color: "$quiet", children: usage ? `${usage.capped ? 'over ' : ''}${weigh(usage.bytes)} in ${usage.files.toLocaleString()} file${usage.files === 1 ? '' : 's'}` : 'Measuring…' }), _jsx(Button, { asChild: true, size: "sm", mt: 8, children: _jsx("a", { href: checkoutUrl(), target: "_blank", rel: "noreferrer", children: "Manage storage" }) })] })] }), _jsxs(YStack, { px: 14, py: 12, rounded: 10, borderWidth: 1, borderColor: "$borderColor", bg: "$panel", gap: 6, children: [_jsxs(XStack, { items: "center", gap: "$2", children: [_jsx(Cloud, { size: 16, color: "var(--foreground)" }), _jsx(Text, { fontSize: "$2", fontWeight: "600", color: "$ink", children: "Hanzo Drive" })] }), _jsx(Text, { fontSize: "$1", color: "$soft", children: [userEmail, activeBucket].filter(Boolean).join(' · ') }), _jsx(Button, { asChild: true, variant: "link", size: "sm", px: 0, mt: 4, justify: "flex-start", children: _jsxs("a", { href: "https://docs.hanzo.ai/docs/openapi/s3/", target: "_blank", rel: "noreferrer", children: ["Documentation", _jsx(ArrowRight, { size: 12 })] }) })] })] }), _jsxs(YStack, { flex: 1, minH: 0, px: "$6", py: "$5",
|
|
426
|
+
}) }), _jsxs(YStack, { gap: "$2", mt: "$2", px: "$2", children: [_jsx(Text, { fontSize: 11, fontWeight: "600", color: "$faint", letterSpacing: 0.6, children: "Storage" }), _jsx(Text, { fontSize: 12, color: "$quiet", children: usage ? `${usage.capped ? 'over ' : ''}${weigh(usage.bytes)} in ${usage.files.toLocaleString()} file${usage.files === 1 ? '' : 's'}` : 'Measuring…' }), _jsx(Button, { asChild: true, size: "sm", mt: 8, children: _jsx("a", { href: checkoutUrl(), target: "_blank", rel: "noreferrer", children: "Manage storage" }) })] })] }), _jsxs(YStack, { px: 14, py: 12, rounded: 10, borderWidth: 1, borderColor: "$borderColor", bg: "$panel", gap: 6, children: [_jsxs(XStack, { items: "center", gap: "$2", children: [_jsx(Cloud, { size: 16, color: "var(--foreground)" }), _jsx(Text, { fontSize: "$2", fontWeight: "600", color: "$ink", children: "Hanzo Drive" })] }), _jsx(Text, { fontSize: "$1", color: "$soft", children: [userEmail, activeBucket].filter(Boolean).join(' · ') }), _jsx(Button, { asChild: true, variant: "link", size: "sm", px: 0, mt: 4, justify: "flex-start", children: _jsxs("a", { href: "https://docs.hanzo.ai/docs/openapi/s3/", target: "_blank", rel: "noreferrer", children: ["Documentation", _jsx(ArrowRight, { size: 12 })] }) })] })] }), _jsxs(YStack, { flex: 1, minH: 0, px: "$6", py: "$5", overflowY: "auto", overflowX: "hidden", gap: "$5", children: [bucket || prefix ? (_jsxs(XStack, { items: "center", gap: "$2", pb: "$2", borderBottomWidth: 1, borderColor: "$borderColor", children: [_jsx(Button, { ...CRUMB, color: "$soft", onClick: () => {
|
|
427
427
|
openBucket(null);
|
|
428
428
|
setPrefix('');
|
|
429
429
|
}, children: "Drive" }), _jsx(ChevronRight, { size: 13, color: "var(--muted-foreground)" }), _jsx(Button, { ...CRUMB, color: crumbs.length === 0 ? '$ink' : '$soft', onClick: () => setPrefix(''), children: bucket || 'hanzo' }), crumbs.map((c, i) => (_jsxs(React.Fragment, { children: [_jsx(ChevronRight, { size: 13, color: "var(--muted-foreground)" }), _jsx(Button, { ...CRUMB, color: i === crumbs.length - 1 ? '$ink' : '$soft', onClick: () => setPrefix(crumbs.slice(0, i + 1).map((x) => `${x}/`).join('')), children: c })] }, `${i}-${c}`)))] })) : null, _jsxs(XStack, { items: "center", justify: "space-between", children: [_jsxs(YStack, { gap: "$1", children: [_jsx(Text, { render: "h1", fontSize: 26, fontWeight: "700", color: "$ink", children: "Drive" }), _jsx(Text, { fontSize: 13, color: "$soft", children: "Secure. Private. Built for builders." })] }), _jsx(XStack, { items: "center", bg: "$panel", rounded: 8, borderWidth: 1, borderColor: "$borderColor", p: 2, gap: 2, children: [
|
package/dist/Home.js
CHANGED
|
@@ -90,7 +90,7 @@ export function Home({ ask }) {
|
|
|
90
90
|
const q = hints.length ? `${question} ${hints.map((s) => `@${s}`).join(' ')}` : question;
|
|
91
91
|
window.location.href = `/chat?q=${encodeURIComponent(q)}`;
|
|
92
92
|
};
|
|
93
|
-
return (_jsxs(YStack, { "data-home": true, flex: 1, minH: 0, width: "100%",
|
|
93
|
+
return (_jsxs(YStack, { "data-home": true, flex: 1, minH: 0, width: "100%", overflowY: "auto", overflowX: "hidden", children: [_jsxs(YStack, { width: "100%", maxW: 720, mx: "auto", px: "$4", pt: "$8", gap: "$4", $sm: { pt: '$10' }, children: [_jsx(Text, { render: "h1", fontSize: "$8", fontWeight: "500", color: INK, text: "center", $sm: { fontSize: '$9' }, children: "What are we building?" }), _jsx(XStack, { justify: "center", gap: "$1", flexWrap: "wrap", children: MODES.map((one) => (_jsx(Box, { "data-tab": one.id, render: "button", onClick: () => setMode(one.id), "aria-current": mode === one.id, px: "$3", py: "$2", rounded: "$10", bg: mode === one.id ? CHOSEN : 'transparent', hoverStyle: { bg: '$hover' }, children: _jsx(Text, { fontSize: "$3", fontWeight: "500", color: mode === one.id ? INK : SOFT, children: one.label }) }, one.id))) }), _jsx(Composer, { value: draft, onChange: setDraft, onSend: send, placeholder: here.asks, label: here.asks, rows: 2, maxHeight: 240, children: grounded ? (_jsx(XStack, { gap: "$1", flexWrap: "wrap", shrink: 1, minW: 0, children: SOURCES.map((s) => {
|
|
94
94
|
const on = picked.includes(s);
|
|
95
95
|
return (_jsx(Box, { "data-chip": s, render: "button", "aria-pressed": on, onClick: () => setPicked((held) => (on ? held.filter((h) => h !== s) : [...held, s])), px: "$2", py: "$1", rounded: "$10", bg: on ? CHOSEN : 'transparent', hoverStyle: { bg: '$hover' }, children: _jsxs(Text, { fontSize: "$2", color: on ? INK : SOFT, children: ["@", s] }) }, s));
|
|
96
96
|
}) })) : null }), _jsx(Crew, { to: "/chat" })] }), _jsx(YStack, { width: "100%", maxW: 880, mx: "auto", px: "$4", py: "$8", children: _jsxs(Grid, { columns: { min: 260, max: 3 }, gap: 12, children: [_jsx(Rooms, { ...held }), _jsx(Projects, {}), _jsx(Chats, {}), _jsx(Next, { onConnect: () => setDirectory(true) }), _jsx(Apps, { onConnect: () => setDirectory(true) }), _jsx(Team, {}), _jsx(Build, {})] }) }), _jsx(Directory, { open: directory, onClose: () => setDirectory(false) })] }));
|
package/dist/Inbox.js
CHANGED
|
@@ -86,7 +86,7 @@ function Empty({ children, offer }) {
|
|
|
86
86
|
}
|
|
87
87
|
/** The list, which stays on screen while you read down it. */
|
|
88
88
|
function Arrivals({ rooms, open, onPick, }) {
|
|
89
|
-
return (_jsxs(YStack, { flex: 1, minH: 0, children: [_jsxs(XStack, { height: 48, shrink: 0, items: "center", px: "$3", gap: "$2", borderBottomWidth: 1, borderColor: "$borderColor", children: [_jsx(Text, { flex: 1, fontSize: "$3", fontWeight: "600", color: "$ink", children: "Inbox" }), _jsx(Connect, {})] }), _jsx(YStack, { flex: 1, minH: 0,
|
|
89
|
+
return (_jsxs(YStack, { flex: 1, minH: 0, children: [_jsxs(XStack, { height: 48, shrink: 0, items: "center", px: "$3", gap: "$2", borderBottomWidth: 1, borderColor: "$borderColor", children: [_jsx(Text, { flex: 1, fontSize: "$3", fontWeight: "600", color: "$ink", children: "Inbox" }), _jsx(Connect, {})] }), _jsx(YStack, { flex: 1, minH: 0, overflowY: "auto", overflowX: "hidden", children: rooms.map((one) => (_jsx(Box, { render: "button", onClick: () => onPick(one.key), "aria-current": one.key === open, display: "block", width: "100%", cursor: "pointer", borderBottomWidth: 1, borderColor: "var(--borderColor, var(--white-08))", bg: one.key === open ? 'var(--raised, var(--white-06))' : 'transparent', hoverStyle: { bg: '$hover' }, children: _jsxs(XStack, { gap: "$3", items: "center", p: "$3", children: [_jsx(Sender, { channel: one.channel }), _jsxs(YStack, { flex: 1, minW: 0, gap: "$1", items: "flex-start", children: [_jsxs(XStack, { width: "100%", items: "baseline", gap: "$2", children: [_jsx(Text, { fontSize: "$3", fontWeight: "600", color: "$ink", numberOfLines: 1, flex: 1, text: "left", children: one.title }), one.lastAt ? (_jsx(Text, { fontSize: "$1", color: "$soft", children: stamp(one.lastAt / 1000) })) : null] }), one.preview ? (_jsx(Text, { fontSize: "$2", color: "$soft", numberOfLines: 1, children: one.preview })) : null] })] }) }, one.key))) })] }));
|
|
90
90
|
}
|
|
91
91
|
/** What is open, beside the list rather than instead of it. */
|
|
92
92
|
function Reading({ room, reload, }) {
|
|
@@ -133,7 +133,7 @@ function Reading({ room, reload, }) {
|
|
|
133
133
|
setSending(false);
|
|
134
134
|
}
|
|
135
135
|
}, [client, draft, sending, room.channel, room.roomId, reload]);
|
|
136
|
-
return (_jsxs(YStack, { flex: 1, minH: 0, children: [_jsx(ChannelHeader, { name: roomName(room), children: _jsxs(XStack, { items: "center", gap: "$1.5", children: [_jsx(Network, { channel: room.channel, size: 14 }), _jsx(Text, { fontSize: "$2", color: "$soft", children: of(room.channel).name })] }) }), _jsx(YStack, { flex: 1, minH: 0,
|
|
136
|
+
return (_jsxs(YStack, { flex: 1, minH: 0, children: [_jsx(ChannelHeader, { name: roomName(room), children: _jsxs(XStack, { items: "center", gap: "$1.5", children: [_jsx(Network, { channel: room.channel, size: 14 }), _jsx(Text, { fontSize: "$2", color: "$soft", children: of(room.channel).name })] }) }), _jsx(YStack, { flex: 1, minH: 0, overflowY: "auto", overflowX: "hidden", p: "$4", gap: "$3", children: [...room.messages]
|
|
137
137
|
.sort((a, b) => a.createdAt - b.createdAt)
|
|
138
138
|
.map((message) => (_jsxs(XStack, { gap: "$3", items: "flex-start", children: [_jsx(Sender, { channel: room.channel, size: 32 }), _jsxs(YStack, { flex: 1, minW: 0, gap: "$1", children: [_jsxs(XStack, { gap: "$2", items: "baseline", children: [_jsx(Text, { fontSize: "$3", fontWeight: "500", color: "$ink", children: speaker(room.channel, message.sender) }), _jsx(Text, { fontSize: "$1", color: "$soft", children: when(message.createdAt) })] }), _jsx(Text, { fontSize: "$3", color: "$ink", children: message.text })] })] }, message.id))) }), _jsx(YStack, { p: "$3", gap: "$2", borderTopWidth: 1, borderColor: "$borderColor", children: room.roomId ? (_jsxs(_Fragment, { children: [_jsx(Composer, { value: draft, onChange: setDraft, onSend: () => void send(), busy: sending, placeholder: `Reply on ${of(room.channel).name}`, label: `Reply on ${of(room.channel).name}` }), failed ? (_jsx(Text, { fontSize: "$2", color: BAD, children: failed })) : null] })) : (_jsx(Text, { fontSize: "$2", color: "$soft", children: "This message names no room to answer in." })) })] }));
|
|
139
139
|
}
|
package/dist/Market.js
CHANGED
|
@@ -141,7 +141,7 @@ export function Market({ scope }) {
|
|
|
141
141
|
? 'No agents installed yet.'
|
|
142
142
|
: ask
|
|
143
143
|
? 'Nothing in the registry matches that.'
|
|
144
|
-
: 'The shelf is empty — the catalog has not been synced yet.' })) : (_jsxs(YStack, { flex: 1, minH: 0,
|
|
144
|
+
: 'The shelf is empty — the catalog has not been synced yet.' })) : (_jsxs(YStack, { flex: 1, minH: 0, overflowY: "auto", overflowX: "hidden", p: "$5", gap: "$3", children: [_jsx(Text, { fontSize: "$1", color: "$soft", children: scope === 'installed'
|
|
145
145
|
? `${shown.length} installed`
|
|
146
146
|
: ask
|
|
147
147
|
? `${shown.length} of ${total} matching`
|
package/dist/Me.js
CHANGED
|
@@ -160,7 +160,7 @@ export function Me({ host, narrow = false, onOrg, }) {
|
|
|
160
160
|
return null;
|
|
161
161
|
return (_jsxs(_Fragment, { children: [_jsxs(Popover, { open: menu.open, onOpenChange: menu.onOpenChange, placement: narrow ? 'right-end' : 'top-start', allowFlip: true, stayInFrame: true, offset: 8, children: [_jsx(Popover.Anchor, { width: "100%", children: _jsxs(XStack, { ref: (el) => {
|
|
162
162
|
row.current = el instanceof HTMLElement ? el : null;
|
|
163
|
-
}, render: "button", "data-slot": "me", "aria-label": `Account: ${[who, current].filter(Boolean).join(' · ')}`, "aria-haspopup": "menu", "aria-expanded": menu.open, onPress: menu.toggle, width: narrow ? 40 : '100%', minH: narrow ? 40 : 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 ? 30 : 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 }), where ? (_jsx(SizableText, { size: "$1", color: "$soft", numberOfLines: 1, text: "left", children: where })) : null] }), _jsx(ChevronsUpDown, { size: 14, "aria-hidden": true, opacity: 0.6 })] }))] }) }), _jsx(Popover.Content, { ...glass(2), items: "stretch", borderWidth: 1, rounded: "$6", p: "$1.5", width: 288, maxW: "calc(100vw - 16px)", maxH: "calc(100dvh - 32px)",
|
|
163
|
+
}, render: "button", "data-slot": "me", "aria-label": `Account: ${[who, current].filter(Boolean).join(' · ')}`, "aria-haspopup": "menu", "aria-expanded": menu.open, onPress: menu.toggle, width: narrow ? 40 : '100%', minH: narrow ? 40 : 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 ? 30 : 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 }), where ? (_jsx(SizableText, { size: "$1", color: "$soft", numberOfLines: 1, text: "left", children: where })) : null] }), _jsx(ChevronsUpDown, { size: 14, "aria-hidden": true, opacity: 0.6 })] }))] }) }), _jsx(Popover.Content, { ...glass(2), items: "stretch", borderWidth: 1, rounded: "$6", p: "$1.5", width: 288, maxW: "calc(100vw - 16px)", maxH: "calc(100dvh - 32px)", overflowY: "auto", overflowX: "hidden", children: _jsx(View, { render: _jsx("div", { ref: land, role: "menu", "aria-label": "Account", onKeyDown: (e) => rove(e, close) }), self: "stretch", minW: 0, children: _jsxs(YStack, { gap: 2, children: [_jsx(SizableText, { size: "$2", color: "$soft", px: "$2", py: "$1.5", numberOfLines: 1, children: host.person.email || who }), _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: () => {
|
|
164
164
|
menu.onOpenChange(false);
|
|
165
165
|
if (o.name !== current)
|
|
166
166
|
onOrg(o.name);
|