@hanzo/rooms 0.1.1 → 0.1.3
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 +77 -24
- 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/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/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/tiers.js +6 -6
- package/dist/tabs.js +1 -1
- package/package.json +3 -3
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,7 +32,8 @@ 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
38
|
import { SpeakButton, speakAgent } from "./speech.js";
|
|
38
39
|
import { Crew, HOUSE, useCrew } from "./crew.js";
|
|
@@ -47,7 +48,7 @@ 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");
|
|
@@ -588,12 +589,31 @@ function Thread({ placeholder = MESSAGE, initial, anonymous = false, heading, })
|
|
|
588
589
|
// identity changes on every `setMessages` — `useChat` closes over `messages` —
|
|
589
590
|
// so an effect depending on it re-runs after each streamed token.
|
|
590
591
|
const latest = useRef(send);
|
|
591
|
-
|
|
592
|
+
// The images held for a turn ride with it at the moment it is sent, queued or
|
|
593
|
+
// not, and are spent once: a retry is the words again, not the pictures.
|
|
594
|
+
latest.current = (text) => send(text, outgoing.current.splice(0));
|
|
592
595
|
// THE COMPOSER'S DRAFT IS HELD HERE, so an arriving question can be typed into
|
|
593
596
|
// it. `Chat` owns a draft of its own and spreads `composer` after it, so
|
|
594
597
|
// passing value/onChange/onSend takes that ownership over rather than fighting
|
|
595
598
|
// it.
|
|
596
599
|
const [draft, setDraft] = useState("");
|
|
600
|
+
// FILES WITH THE NEXT MESSAGE: pressed in through the paperclip, or dropped
|
|
601
|
+
// anywhere on the room. An image rides as a picture the model reads, a text
|
|
602
|
+
// file as its words (lib/attach); anything else is refused by name here.
|
|
603
|
+
const [files, setFiles] = useState([]);
|
|
604
|
+
const [fileSaid, setFileSaid] = useState(null);
|
|
605
|
+
const [dragging, setDragging] = useState(false);
|
|
606
|
+
const picker = useRef(null);
|
|
607
|
+
const outgoing = useRef([]);
|
|
608
|
+
const depth = useRef(0);
|
|
609
|
+
const take = useCallback(async (list) => {
|
|
610
|
+
if (!list?.length)
|
|
611
|
+
return;
|
|
612
|
+
const out = await Promise.all(Array.from(list).map(hold));
|
|
613
|
+
setFiles((now) => [...now, ...out.filter((one) => typeof one !== "string")]);
|
|
614
|
+
const refused = out.filter((one) => typeof one === "string");
|
|
615
|
+
setFileSaid(refused.length ? refused.join(" ") : null);
|
|
616
|
+
}, []);
|
|
597
617
|
useSeed(initial, setDraft);
|
|
598
618
|
// DICTATION, the same one the site's own composer offers. `speech()` defaults
|
|
599
619
|
// to the Hanzo gateway and `base()` is the one place a host is read from, so
|
|
@@ -771,6 +791,17 @@ function Thread({ placeholder = MESSAGE, initial, anonymous = false, heading, })
|
|
|
771
791
|
}
|
|
772
792
|
speak(text);
|
|
773
793
|
}, [model, speak]);
|
|
794
|
+
/** The draft and the held files, sent as one turn. */
|
|
795
|
+
const submit = () => {
|
|
796
|
+
const text = draft.trim();
|
|
797
|
+
if (!text && !files.length)
|
|
798
|
+
return;
|
|
799
|
+
outgoing.current = images(files);
|
|
800
|
+
setDraft("");
|
|
801
|
+
setFiles([]);
|
|
802
|
+
setFileSaid(null);
|
|
803
|
+
guarded(compose(text, files));
|
|
804
|
+
};
|
|
774
805
|
/*
|
|
775
806
|
WHAT AN UNANSWERED QUESTION LOOKS LIKE: a turn of its own directly under it,
|
|
776
807
|
naming who was asked, saying what went wrong in the reader's words, and
|
|
@@ -803,7 +834,7 @@ function Thread({ placeholder = MESSAGE, initial, anonymous = false, heading, })
|
|
|
803
834
|
}, [said, chat.streaming, queued]);
|
|
804
835
|
const failure = (question) => {
|
|
805
836
|
const failed = failures[question.id];
|
|
806
|
-
const text =
|
|
837
|
+
const text = words(question.content);
|
|
807
838
|
const whom = addressed(text, room);
|
|
808
839
|
const wrong = failed?.error ?? null;
|
|
809
840
|
const how = wrong ? refusalOf(wrong) : null;
|
|
@@ -856,7 +887,31 @@ function Thread({ placeholder = MESSAGE, initial, anonymous = false, heading, })
|
|
|
856
887
|
}, onCancel: () => {
|
|
857
888
|
setAsking(null);
|
|
858
889
|
} }));
|
|
859
|
-
return (_jsxs(YStack, { flex: 1, minH: 0, width: "100%", position: "relative",
|
|
890
|
+
return (_jsxs(YStack, { flex: 1, minH: 0, width: "100%", position: "relative", "data-slot": "chat-room",
|
|
891
|
+
// Entered and left once for every child crossed, so the veil follows a
|
|
892
|
+
// count rather than the last event.
|
|
893
|
+
onDragEnter: (e) => {
|
|
894
|
+
if (!Array.from(e.dataTransfer.types).includes("Files"))
|
|
895
|
+
return;
|
|
896
|
+
depth.current += 1;
|
|
897
|
+
setDragging(true);
|
|
898
|
+
}, onDragOver: (e) => {
|
|
899
|
+
if (!Array.from(e.dataTransfer.types).includes("Files"))
|
|
900
|
+
return;
|
|
901
|
+
e.preventDefault();
|
|
902
|
+
e.dataTransfer.dropEffect = "copy";
|
|
903
|
+
}, onDragLeave: () => {
|
|
904
|
+
depth.current = Math.max(0, depth.current - 1);
|
|
905
|
+
if (depth.current === 0)
|
|
906
|
+
setDragging(false);
|
|
907
|
+
}, onDrop: (e) => {
|
|
908
|
+
depth.current = 0;
|
|
909
|
+
setDragging(false);
|
|
910
|
+
if (!e.dataTransfer.files?.length)
|
|
911
|
+
return;
|
|
912
|
+
e.preventDefault();
|
|
913
|
+
void take(e.dataTransfer.files);
|
|
914
|
+
}, 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
915
|
if (aside === null) {
|
|
861
916
|
showAside("tasks");
|
|
862
917
|
if (!pinned)
|
|
@@ -889,12 +944,15 @@ function Thread({ placeholder = MESSAGE, initial, anonymous = false, heading, })
|
|
|
889
944
|
const question = shown.find((one) => one.id === String(turn.id).slice(FAILED.length + 1));
|
|
890
945
|
return question ? failure(question) : null;
|
|
891
946
|
}
|
|
947
|
+
// A QUESTION THAT CARRIED PICTURES draws them beside its words.
|
|
948
|
+
if (turn.role === "user" && Array.isArray(turn.content))
|
|
949
|
+
return _jsx(Parts, { parts: partsOf(turn.content), prose: (text) => _jsx(Prose, { text: text }) });
|
|
892
950
|
const said = typeof turn.content === "string" ? turn.content : "";
|
|
893
951
|
// WHO IS ANSWERING, while the answer has no words yet: the agents the
|
|
894
952
|
// question addresses, or the whole room when it names nobody.
|
|
895
953
|
if (turn.role === "assistant" && !said && chat.streaming && room.length) {
|
|
896
954
|
const q = [...shown].reverse().find((one) => one.role === "user");
|
|
897
|
-
const whom = addressed(
|
|
955
|
+
const whom = addressed(words(q?.content), room);
|
|
898
956
|
return (_jsxs(Text, { fontSize: "$2", color: "$soft", children: [join(whom), " ", whom.length > 1 ? "are" : "is", " answering\u2026"] }));
|
|
899
957
|
}
|
|
900
958
|
// A ROOM'S ANSWER IS SEVERAL PEOPLE'S. Each opens a line with their
|
|
@@ -907,7 +965,7 @@ function Thread({ placeholder = MESSAGE, initial, anonymous = false, heading, })
|
|
|
907
965
|
const at = shown.findIndex((one) => one.id === turn.id);
|
|
908
966
|
const q = shown.slice(0, at).reverse().find((one) => one.role === "user");
|
|
909
967
|
const silent = room.length > 1 && !(chat.streaming && at === shown.length - 1)
|
|
910
|
-
? addressed(
|
|
968
|
+
? addressed(words(q?.content), room).filter((name) => !parts.some((part) => part.who?.toLowerCase() === name.toLowerCase()))
|
|
911
969
|
: [];
|
|
912
970
|
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
971
|
}
|
|
@@ -926,13 +984,7 @@ function Thread({ placeholder = MESSAGE, initial, anonymous = false, heading, })
|
|
|
926
984
|
column: { maxW: MEASURE }, composer: {
|
|
927
985
|
value: draft,
|
|
928
986
|
onChange: setDraft,
|
|
929
|
-
onSend:
|
|
930
|
-
const text = draft.trim();
|
|
931
|
-
if (!text)
|
|
932
|
-
return;
|
|
933
|
-
setDraft("");
|
|
934
|
-
guarded(text);
|
|
935
|
-
},
|
|
987
|
+
onSend: submit,
|
|
936
988
|
// A CHARACTER IS WHO YOU ARE WRITING TO, so the field says so — and
|
|
937
989
|
// `label` keeps the accessible name constant while it does, which is
|
|
938
990
|
// the reason the component ships both: a placeholder that changes with
|
|
@@ -982,15 +1034,16 @@ function Thread({ placeholder = MESSAGE, initial, anonymous = false, heading, })
|
|
|
982
1034
|
// package's button and `streaming` still has to become stop; it keeps
|
|
983
1035
|
// `data-slot="composer-send"` so a driver finds it, and it is the same
|
|
984
1036
|
// `Control` circle as the mic, filled.
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
1037
|
+
// THE PAPERCLIP opens the file picker; the same files can be dropped on
|
|
1038
|
+
// the room.
|
|
1039
|
+
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) => {
|
|
1040
|
+
void take(e.target.files);
|
|
1041
|
+
e.target.value = "";
|
|
1042
|
+
} })] })),
|
|
1043
|
+
// WHAT GOES WITH THE MESSAGE, above the frame: one chip a file, each
|
|
1044
|
+
// with its own remove, and the reason any file was refused.
|
|
1045
|
+
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,
|
|
1046
|
+
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
1047
|
// WHAT THE PLAN HAS LEFT, under the frame and at its measure, for a
|
|
995
1048
|
// plan holder only: Free keeps the free lane's own notice.
|
|
996
1049
|
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);
|
package/dist/Profile.js
CHANGED
|
@@ -55,7 +55,7 @@ export function Profile({ open, onClose }) {
|
|
|
55
55
|
return (_jsxs(YStack, { ...pane(sheet(2).backgroundColor), width: WIDTH, maxW: "100%", shrink: 0, minH: 0,
|
|
56
56
|
// A column on a laptop. On a phone the room is already the whole screen,
|
|
57
57
|
// so this covers it rather than squeezing it to nothing.
|
|
58
|
-
position: "absolute", t: 0, r: 0, b: 0, l: 0, z: "var(--z-panel)", $lg: { position: 'relative', z: 'var(--z-base)', l: 'auto' }, children: [_jsxs(XStack, { items: "center", px: "$4", py: "$3", gap: "$2", children: [_jsx(Text, { flex: 1, fontSize: "$5", fontWeight: "600", color: "$ink", children: "Profile" }), _jsx(Box, { render: "button", onClick: onClose, "aria-label": "Close profile", p: "$2", rounded: "$2", hoverStyle: { bg: '$hover' }, children: _jsx(X, { size: 16, "aria-hidden": true }) })] }), _jsxs(YStack, { flex: 1, minH: 0,
|
|
58
|
+
position: "absolute", t: 0, r: 0, b: 0, l: 0, z: "var(--z-panel)", $lg: { position: 'relative', z: 'var(--z-base)', l: 'auto' }, children: [_jsxs(XStack, { items: "center", px: "$4", py: "$3", gap: "$2", children: [_jsx(Text, { flex: 1, fontSize: "$5", fontWeight: "600", color: "$ink", children: "Profile" }), _jsx(Box, { render: "button", onClick: onClose, "aria-label": "Close profile", p: "$2", rounded: "$2", hoverStyle: { bg: '$hover' }, children: _jsx(X, { size: 16, "aria-hidden": true }) })] }), _jsxs(YStack, { flex: 1, minH: 0, overflowY: "auto", overflowX: "hidden", px: "$4", pb: "$5", gap: "$4", children: [_jsx("input", { ref: picker, type: "file", accept: "image/png,image/jpeg,image/gif,image/webp", hidden: true, onChange: (e) => {
|
|
59
59
|
const file = e.target.files?.[0];
|
|
60
60
|
e.target.value = '';
|
|
61
61
|
void pick(file);
|
package/dist/RightPane.js
CHANGED
|
@@ -152,7 +152,7 @@ export function RightPane({ said = [], onClose, }) {
|
|
|
152
152
|
// having to tell it that the conversation changed.
|
|
153
153
|
const at = channel(open);
|
|
154
154
|
const pane = usePane(at);
|
|
155
|
-
return (_jsxs(YStack, { flex: 1, minH:
|
|
155
|
+
return (_jsxs(YStack, { flex: 1, minH: 320, bg: "$background", borderLeftWidth: 1, borderColor: "$borderColor", children: [pane.pinned ? _jsx(Summary, { at: at, said: said, held: pane.held }) : null, _jsx(Browser, { at: at, pane: pane, onClose: onClose })] }));
|
|
156
156
|
}
|
|
157
157
|
/** The two cards: out of the conversation, and into it. */
|
|
158
158
|
function Summary({ at, said, held }) {
|
|
@@ -165,7 +165,7 @@ function Summary({ at, said, held }) {
|
|
|
165
165
|
// it would take the browser's half, and scrolls after that — so two rows
|
|
166
166
|
// do not reserve a screen and thirty do not push the browser off the
|
|
167
167
|
// bottom of the column.
|
|
168
|
-
maxH: "45%",
|
|
168
|
+
maxH: "45%", overflowY: "auto", overflowX: "hidden", p: "$3", gap: "$3", borderBottomWidth: 1, borderColor: "$borderColor", children: [_jsx(Marks, { at: at, kind: "outputs", title: "Outputs", icon: _jsx(FileCode, { size: 16, "aria-hidden": true }), marks: made, children: "This conversation has produced nothing yet." }), _jsx(Marks, { at: at, kind: "sources", title: "Sources", icon: _jsx(Paperclip, { size: 16, "aria-hidden": true }), marks: took, children: "Nothing has been put into this conversation yet." })] }));
|
|
169
169
|
}
|
|
170
170
|
/**
|
|
171
171
|
* One card of things, the first {@link FEW} of them, and the honest number of
|
|
@@ -338,7 +338,7 @@ function Browser({ at, pane, onClose, }) {
|
|
|
338
338
|
, {
|
|
339
339
|
// 44 IS THE FLOOR A THUMB NEEDS, and the strip scrolls rather than
|
|
340
340
|
// squeezing its tabs under it — the rule `tabs.tsx` exists to hold.
|
|
341
|
-
minH: 44, shrink: 0, items: "center", gap: "$1", px: "$2",
|
|
341
|
+
minH: 44, shrink: 0, items: "center", gap: "$1", px: "$2", overflowX: "auto", overflowY: "hidden", borderBottomWidth: 1, borderColor: "$borderColor", children: [pane.tabs.map((t) => (_jsx(Sheet, { at: at, leaf: t, on: t.id === pane.at }, t.id))), _jsx(Box, { render: "button", onClick: () => openTab(at, 'https://hanzo.ai'), "aria-label": "New tab", p: "$1.5", rounded: "$2", shrink: 0, hoverStyle: { bg: '$hover' }, children: _jsx(Plus, { size: 16, "aria-hidden": true }) }), _jsx(Box, { flex: 1, minW: "$1" }), leaf ? (_jsxs(_Fragment, { children: [_jsx(Box, { render: "button", onClick: () => reload((n) => n + 1), "aria-label": "Reload this tab", p: "$1.5", rounded: "$2", shrink: 0, hoverStyle: { bg: '$hover' }, children: _jsx(RotateCw, { size: 16, "aria-hidden": true }) }), _jsx(Box, { render: "button", onClick: () => window.open(leaf.url, '_blank', 'noopener,noreferrer'), "aria-label": "Open this tab in a new window", p: "$1.5", rounded: "$2", shrink: 0, hoverStyle: { bg: '$hover' }, children: _jsx(ExternalLink, { size: 16, "aria-hidden": true }) })] })) : null, onClose ? (_jsx(Box, { render: "button", onClick: onClose, "aria-label": "Hide side panel", p: "$1.5", rounded: "$2", shrink: 0, hoverStyle: { bg: '$hover' }, children: _jsx(PanelRightClose, { size: 16, "aria-hidden": true }) })) : null] }), leaf ? _jsx(Where, { at: at, leaf: leaf }) : null, _jsx(YStack, { flex: 1, minH: 0, children: leaf ? (_jsx(Preview, { leaf: leaf, held: pane.held }, `${leaf.id}.${again}`)) : (_jsxs(YStack, { flex: 1, items: "center", justify: "center", p: "$5", gap: "$2", children: [_jsx(Quiet, { children: "Nothing open here." }), _jsx(Quiet, { children: "Press an output or a source to look at it, or open a tab." })] })) })] }));
|
|
342
342
|
}
|
|
343
343
|
/** One tab: the thing you press, and the cross that shuts it. */
|
|
344
344
|
function Sheet({ at, leaf, on }) {
|
|
@@ -385,7 +385,7 @@ function Preview({ leaf, held }) {
|
|
|
385
385
|
const type = mine?.type ?? '';
|
|
386
386
|
const ext = suffix(mine?.name ?? leaf.title);
|
|
387
387
|
if (type.startsWith('image/') || ['png', 'jpg', 'jpeg', 'gif', 'webp', 'avif', 'svg'].includes(ext)) {
|
|
388
|
-
return (_jsx(YStack, { flex: 1, minH: 0, items: "center", justify: "center", p: "$3",
|
|
388
|
+
return (_jsx(YStack, { flex: 1, minH: 0, items: "center", justify: "center", p: "$3", overflowY: "auto", overflowX: "hidden", children: _jsx(View, { render: _jsx("img", { src: leaf.url, alt: leaf.title }), maxW: "100%", maxH: "100%", objectFit: "contain" }) }));
|
|
389
389
|
}
|
|
390
390
|
// THE LIST IS OF WHAT A FRAME CAN RENDER, not of what a file might be. A type
|
|
391
391
|
// absent from it is unsupported, which is the honest default: guessing that
|
package/dist/Settings.js
CHANGED
|
@@ -139,7 +139,7 @@ export function SettingsList({ active, filter, onPick, onBack, }) {
|
|
|
139
139
|
*/
|
|
140
140
|
export function SettingsPane({ id }) {
|
|
141
141
|
const row = ROWS.find((r) => r.id === id);
|
|
142
|
-
return (_jsx(YStack, { flex: 1, minH: 0,
|
|
142
|
+
return (_jsx(YStack, { flex: 1, minH: 0, overflowY: "auto", overflowX: "hidden", children: _jsxs(YStack, { p: "$5", gap: "$4", maxW: 860, children: [_jsx(Text, { fontSize: "$6", fontWeight: "600", color: "$ink", children: row?.label ?? 'Settings' }), _jsx(Pane, { id: id, note: row?.note })] }) }));
|
|
143
143
|
}
|
|
144
144
|
/** One pane. */
|
|
145
145
|
function Pane({ id, note }) {
|
package/dist/Shell.js
CHANGED
|
@@ -887,9 +887,13 @@ export function Paned({ children, swap, framed = false, bare = false, }) {
|
|
|
887
887
|
// In the frame the gutter stands between the pane and this column.
|
|
888
888
|
ml: framed ? "var(--pane-gap)" : undefined, ref: beside.ref,
|
|
889
889
|
// inline-style: `--span`, the width a drag rewrites on the element itself (span.ts); no prop sets a variable.
|
|
890
|
-
style: beside.style, position: "relative", width: "var(--span)",
|
|
890
|
+
style: beside.style, position: "relative", width: "var(--span)",
|
|
891
|
+
// Half the WINDOW. Half of an `auto` grid track is half of this
|
|
892
|
+
// column's own width, so the rooms' shell drew it at half its span
|
|
893
|
+
// beside an empty strip of the same size.
|
|
894
|
+
maxW: "50vw", shrink: 0, minH: 0, display: "none", $lg: { display: "flex" }, children: [_jsx(Grip, { side: "right", span: beside.span, floor: FLOOR, ceil: CEIL, reset: BESIDE_WIDTH, onSpan: beside.move, onKeep: beside.keep, onShut: () => showAside(null), label: "Resize side panel",
|
|
891
895
|
// In the gutter the frame leaves between the panes; a room has none.
|
|
892
|
-
l: GUTTER_START, width: GUTTER }), _jsx(YStack, { flex: 1, minH: 0,
|
|
896
|
+
l: GUTTER_START, width: GUTTER }), _jsx(YStack, { flex: 1, minH: 0, overflowY: "auto", overflowX: "hidden", rounded: "var(--pane-round)", ref: (el) => setSlot(el instanceof HTMLElement ? el : null) })] })) : null] }));
|
|
893
897
|
}
|
|
894
898
|
/**
|
|
895
899
|
* THE RAIL: where you can go, as marks with their names under them. It is the
|
package/dist/Talk.js
CHANGED
|
@@ -77,7 +77,7 @@ export function Talk({ room }) {
|
|
|
77
77
|
}
|
|
78
78
|
};
|
|
79
79
|
const name = (of) => setDraft((held) => mention(held, of));
|
|
80
|
-
return (_jsxs(YStack, { flex: 1, minH: 0, children: [_jsxs(ChannelHeader, { name: titleOf(room), children: [room.topic ? (_jsx(Text, { fontSize: "$2", color: "$soft", numberOfLines: 1, children: room.topic })) : null, _jsx(Box, { render: "button", "aria-label": aside === 'roster' ? 'Hide directory & roster' : 'Show directory & roster', "aria-pressed": aside === 'roster', onClick: () => showAside(aside === 'roster' ? null : 'roster'), ml: "auto", p: "$2", rounded: "$3", hoverStyle: { bg: '$hover' }, children: _jsx(Users, { size: 16, "aria-hidden": true }) })] }), _jsxs(YStack, { flex: 1, minH: 0,
|
|
80
|
+
return (_jsxs(YStack, { flex: 1, minH: 0, children: [_jsxs(ChannelHeader, { name: titleOf(room), children: [room.topic ? (_jsx(Text, { fontSize: "$2", color: "$soft", numberOfLines: 1, children: room.topic })) : null, _jsx(Box, { render: "button", "aria-label": aside === 'roster' ? 'Hide directory & roster' : 'Show directory & roster', "aria-pressed": aside === 'roster', onClick: () => showAside(aside === 'roster' ? null : 'roster'), ml: "auto", p: "$2", rounded: "$3", hoverStyle: { bg: '$hover' }, children: _jsx(Users, { size: 16, "aria-hidden": true }) })] }), _jsxs(YStack, { flex: 1, minH: 0, overflowY: "auto", overflowX: "hidden", p: "$4", gap: "$3", children: [failed ? (_jsx(Text, { fontSize: "$3", color: "$soft", children: say(failed, 'this room') })) : messages === null ? (_jsx(Text, { fontSize: "$3", color: "$soft", children: "Reading the room." })) : messages.length === 0 && pending.length === 0 ? (_jsx(Text, { fontSize: "$3", color: "$soft", children: "Nothing has been said here yet." })) : (messages.map((one) => (_jsx(Line, { member: memberOf(members, one.author), fallback: one.author, stamp: when(one.createdOn), text: one.text }, one.id)))), pending
|
|
81
81
|
.filter((one) => !messages?.some((said) => said.author === me && said.text === one.text))
|
|
82
82
|
.map((one) => (_jsx(Line, { member: mine, fallback: "You", text: one.text, soft: true }, `pending-${one.key}`)))] }), _jsxs(YStack, { p: "$3", gap: "$2", borderTopWidth: 1, borderColor: "$borderColor", children: [_jsx(Composer, { value: draft, onChange: setDraft, onSend: () => void speak(), placeholder: `Message ${titleOf(room)}`, label: `Message ${titleOf(room)}`, hint: agents.length ? 'Mention an agent with @name and it answers.' : undefined, children: agents.length ? (_jsx(XStack, { gap: "$1", flexWrap: "wrap", shrink: 1, minW: 0, children: agents.map((one) => (_jsx(Box, { render: "button", "aria-label": `Mention ${one.name}`, onClick: () => name(one.name), px: "$2", py: "$1", rounded: "$10", hoverStyle: { bg: '$hover' }, children: _jsxs(Text, { fontSize: "$2", color: "$soft", children: ["@", one.name] }) }, one.id))) })) : null }), refused ? (_jsx(Text, { fontSize: "$2", color: BAD, children: refused })) : null] }), _jsx(Beside, { children: aside === 'roster' ? _jsx(Roster, { members: members, wrong: wrong, onMention: name }) : null })] }));
|
|
83
83
|
}
|
package/dist/Vibe.js
CHANGED
|
@@ -166,7 +166,7 @@ function Vibe() {
|
|
|
166
166
|
.catch((e) => setTurns((prev) => prev.map((t) => (t.id === id ? { ...t, refused: plainly(e, 'this run') } : t))))
|
|
167
167
|
.finally(() => setBusy(false));
|
|
168
168
|
};
|
|
169
|
-
return (_jsxs(YStack, { "data-vibe": true, flex: 1, minH: 0, height: "100vh", width: "100%", maxW: "100%", overflowX: "auto", overflowY: "hidden", bg: "$background", pb: 56, children: [_jsxs(XStack, { height: 52, items: "center", justify: "space-between", px: "$3.5", borderBottomWidth: 1, borderColor: "$borderColor", bg: "$background", z: 30, shrink: 0, children: [_jsxs(XStack, { items: "center", gap: "$2.5", children: [_jsx(TopOrgSelector, {}), _jsxs(XStack, { display: "none", $md: { display: 'flex' }, items: "center", gap: "$2", py: 4, px: 10, rounded: 20, bg: "$panel", borderWidth: 1, borderColor: "$borderColor", children: [_jsx(View, { width: 7, height: 7, rounded: 4, bg: busy ? 'var(--state-online)' : '$faint' }), _jsx(Text, { fontSize: 11, color: "$quiet", fontWeight: "500", children: busy ? 'Starting a run' : 'Idle' })] })] }), _jsxs(XStack, { items: "center", gap: "$2", children: [_jsx(XStack, { $md: { display: 'none' }, bg: "$panel", rounded: 8, p: 2, gap: 2, borderWidth: 1, borderColor: "$borderColor", children: ['stream', 'preview', 'team'].map((tab) => (_jsx(Button, { size: "sm", px: "$2", variant: activeTabMobile === tab ? 'secondary' : 'ghost', color: activeTabMobile === tab ? '$ink' : '$soft', "aria-pressed": activeTabMobile === tab, onClick: () => setActiveTabMobile(tab), children: tab === 'stream' ? 'Stream' : tab === 'preview' ? 'Preview' : 'Team' }, tab))) }), _jsx(Button, { asChild: true, variant: "primary", size: "sm", children: _jsxs(Link, { href: invite(), children: [_jsx(UserPlus, { size: 13 }), "Invite"] }) })] })] }), _jsxs(View, { flex: 1, minH: 0, width: "100%", height: "100%", overflow: "hidden", display: "grid", gridTemplateRows: "100%", gridTemplateColumns: "100%", $md: { gridTemplateColumns: 'minmax(320px, 380px) minmax(0, 1fr)' }, $lg: { gridTemplateColumns: 'minmax(340px, 400px) minmax(360px, 1fr) minmax(300px, 340px)' }, $xl: { gridTemplateColumns: 'minmax(380px, 440px) minmax(480px, 1fr) minmax(340px, 380px)' }, children: [_jsxs(YStack, { height: "100%", borderRightWidth: 1, borderColor: "$borderColor", bg: "$background", minW: 0, display: activeTabMobile === 'stream' ? 'flex' : 'none', $md: { display: 'flex' }, children: [_jsxs(XStack, { height: 42, items: "center", gap: "$2", px: "$3.5", borderBottomWidth: 1, borderColor: "$borderColor", bg: "$background", children: [_jsx(Sparkles, { size: 14, color: "var(--foreground)" }), _jsx(Text, { fontSize: 12, fontWeight: "600", color: "$ink", children: "Runs" }), turns.length > 0 && (_jsx(View, { px: 6, py: 2, rounded: 4, bg: "$hover", children: _jsx(Text, { fontSize: 10, color: "$soft", children: turns.length }) }))] }), _jsxs(YStack, { flex: 1,
|
|
169
|
+
return (_jsxs(YStack, { "data-vibe": true, flex: 1, minH: 0, height: "100vh", width: "100%", maxW: "100%", overflowX: "auto", overflowY: "hidden", bg: "$background", pb: 56, children: [_jsxs(XStack, { height: 52, items: "center", justify: "space-between", px: "$3.5", borderBottomWidth: 1, borderColor: "$borderColor", bg: "$background", z: 30, shrink: 0, children: [_jsxs(XStack, { items: "center", gap: "$2.5", children: [_jsx(TopOrgSelector, {}), _jsxs(XStack, { display: "none", $md: { display: 'flex' }, items: "center", gap: "$2", py: 4, px: 10, rounded: 20, bg: "$panel", borderWidth: 1, borderColor: "$borderColor", children: [_jsx(View, { width: 7, height: 7, rounded: 4, bg: busy ? 'var(--state-online)' : '$faint' }), _jsx(Text, { fontSize: 11, color: "$quiet", fontWeight: "500", children: busy ? 'Starting a run' : 'Idle' })] })] }), _jsxs(XStack, { items: "center", gap: "$2", children: [_jsx(XStack, { $md: { display: 'none' }, bg: "$panel", rounded: 8, p: 2, gap: 2, borderWidth: 1, borderColor: "$borderColor", children: ['stream', 'preview', 'team'].map((tab) => (_jsx(Button, { size: "sm", px: "$2", variant: activeTabMobile === tab ? 'secondary' : 'ghost', color: activeTabMobile === tab ? '$ink' : '$soft', "aria-pressed": activeTabMobile === tab, onClick: () => setActiveTabMobile(tab), children: tab === 'stream' ? 'Stream' : tab === 'preview' ? 'Preview' : 'Team' }, tab))) }), _jsx(Button, { asChild: true, variant: "primary", size: "sm", children: _jsxs(Link, { href: invite(), children: [_jsx(UserPlus, { size: 13 }), "Invite"] }) })] })] }), _jsxs(View, { flex: 1, minH: 0, width: "100%", height: "100%", overflow: "hidden", display: "grid", gridTemplateRows: "100%", gridTemplateColumns: "100%", $md: { gridTemplateColumns: 'minmax(320px, 380px) minmax(0, 1fr)' }, $lg: { gridTemplateColumns: 'minmax(340px, 400px) minmax(360px, 1fr) minmax(300px, 340px)' }, $xl: { gridTemplateColumns: 'minmax(380px, 440px) minmax(480px, 1fr) minmax(340px, 380px)' }, children: [_jsxs(YStack, { height: "100%", borderRightWidth: 1, borderColor: "$borderColor", bg: "$background", minW: 0, display: activeTabMobile === 'stream' ? 'flex' : 'none', $md: { display: 'flex' }, children: [_jsxs(XStack, { height: 42, items: "center", gap: "$2", px: "$3.5", borderBottomWidth: 1, borderColor: "$borderColor", bg: "$background", children: [_jsx(Sparkles, { size: 14, color: "var(--foreground)" }), _jsx(Text, { fontSize: 12, fontWeight: "600", color: "$ink", children: "Runs" }), turns.length > 0 && (_jsx(View, { px: 6, py: 2, rounded: 4, bg: "$hover", children: _jsx(Text, { fontSize: 10, color: "$soft", children: turns.length }) }))] }), _jsxs(YStack, { flex: 1, overflowY: "auto", overflowX: "hidden", p: "$3.5", gap: "$4", children: [turns.length === 0 ? (_jsx(Quiet, { children: "Nothing has been sent from this room yet. Describe a task below and the run it opens appears here." })) : null, turns.map((turn) => (_jsxs(YStack, { gap: "$2.5", children: [_jsxs(XStack, { gap: "$2.5", children: [_jsx(View, { width: 24, height: 24, rounded: 12, bg: "$raised", items: "center", justify: "center", shrink: 0, children: _jsx(Text, { fontSize: 10, fontWeight: "700", color: "$ink", children: turn.who.charAt(0).toUpperCase() }) }), _jsxs(YStack, { flex: 1, gap: "$1", children: [_jsxs(XStack, { items: "center", gap: "$2", children: [_jsx(Text, { fontSize: 12, fontWeight: "700", color: "$ink", children: turn.who }), _jsx(Text, { fontSize: 11, color: "$soft", children: turn.time }), _jsx(View, { px: 5, py: 1, rounded: 4, bg: "$hover", children: _jsx(Text, { fontSize: 10, color: "$soft", fontFamily: "$mono", children: turn.model }) })] }), _jsx(Text, { fontSize: 12, color: "$quiet", lineHeight: 18, children: turn.prompt })] })] }), _jsx(YStack, { ml: "$4", gap: "$1.5", children: turn.refused ? (_jsx(Text, { fontSize: 11, color: "$ink", lineHeight: 16, children: turn.refused })) : !turn.run ? (_jsx(Quiet, { children: "Starting." })) : (_jsxs(YStack, { p: "$2", rounded: 8, bg: "$panel", borderWidth: 1, borderColor: "$borderColor", gap: "$1", children: [_jsx(Text, { fontSize: 11, color: "$quiet", fontFamily: "$mono", children: turn.run.sessionId }), _jsxs(Text, { fontSize: 11, color: "$soft", children: [turn.run.routed
|
|
170
170
|
? `Running on ${turn.run.targetId}`
|
|
171
171
|
: 'Running in a sandbox', turn.run.branch ? ` · ${turn.run.branch}` : ''] }), _jsx(Text, { render: _jsx(Link, { href: dev(turn.run.sessionId) }), color: "$ink", fontSize: "$1", children: "Watch it work" })] })) })] }, turn.id))), _jsx("div", { ref: bottom })] }), _jsxs(YStack, { p: "$3", borderTopWidth: 1, borderColor: "$borderColor", bg: "$background", gap: "$2", children: [_jsxs(XStack, { items: "center", justify: "space-between", gap: "$2", children: [_jsxs(Picker, { value: model, onChange: (e) => setWanted(e.target.value), "aria-label": "Model", maxW: 200, children: [models.length === 0 ? _jsx("option", { value: model, children: model }) : null, models.map((m) => (_jsx("option", { value: m.id, children: m.id }, m.id)))] }), _jsx(Text, { fontSize: 10, color: "$faint", children: "Press Enter to send" })] }), _jsxs(YStack, { bg: "$panel", borderWidth: 1, borderColor: "var(--border-control)", rounded: 12, overflow: "hidden", children: [_jsx(Text, { render: _jsx("textarea", { value: agentInput, onChange: (e) => setAgentInput(e.target.value), onKeyDown: (e) => {
|
|
172
172
|
if (e.key === 'Enter' && !e.shiftKey) {
|
|
@@ -177,7 +177,7 @@ function Vibe() {
|
|
|
177
177
|
['desktop', 'Desktop', Monitor],
|
|
178
178
|
['tablet', 'Tablet', Tablet],
|
|
179
179
|
['mobile', 'Mobile', Smartphone],
|
|
180
|
-
].map(([device, label, Glyph]) => (_jsx(Button, { variant: previewDevice === device ? 'secondary' : 'ghost', size: "icon-sm", "aria-label": label, title: label, "aria-pressed": previewDevice === device, onClick: () => setPreviewDevice(device), children: _jsx(Glyph, { size: 12, color: previewDevice === device ? 'var(--foreground)' : 'var(--muted-foreground)' }) }, device))) }), _jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": "Reload preview", title: "Reload preview", onClick: () => setPreviewKey((k) => k + 1), children: _jsx(RefreshCw, { size: 12, color: "var(--muted-foreground)" }) })] })] }), _jsx(YStack, { flex: 1, items: "center", justify: "center", bg: "$background", p: "$2", children: previewUrl.trim() === '' ? (_jsx(Quiet, { children: "Nothing to show. Put the address of what you are building in the bar." })) : (_jsx(View, { width: previewDevice === 'desktop' ? '100%' : previewDevice === 'tablet' ? 768 : 375, maxW: "100%", height: "100%", bg: "$background", rounded: previewDevice === 'desktop' ? 0 : 8, overflow: "hidden", borderWidth: previewDevice === 'desktop' ? 0 : 1, borderColor: "var(--border-control)", boxShadow: "var(--shadow-lg)", children: _jsx(View, { render: _jsx("iframe", { src: previewUrl, sandbox: "allow-scripts allow-forms allow-popups allow-same-origin", title: "Preview" }), width: "100%", height: "100%", borderWidth: 0 }) }, previewKey)) })] }), _jsxs(YStack, { height: "100%", bg: "$background", minW: 0, display: activeTabMobile === 'team' ? 'flex' : 'none', $md: { display: 'none' }, $lg: { display: 'flex' }, children: [_jsxs(XStack, { height: 42, items: "center", gap: "$2", px: "$3", borderBottomWidth: 1, borderColor: "$borderColor", bg: "$background", children: [_jsx(Users, { size: 14, color: "var(--foreground)" }), _jsx(Text, { fontSize: 12, fontWeight: "600", color: "$ink", children: "People" })] }), _jsx(YStack, { flex: 1,
|
|
180
|
+
].map(([device, label, Glyph]) => (_jsx(Button, { variant: previewDevice === device ? 'secondary' : 'ghost', size: "icon-sm", "aria-label": label, title: label, "aria-pressed": previewDevice === device, onClick: () => setPreviewDevice(device), children: _jsx(Glyph, { size: 12, color: previewDevice === device ? 'var(--foreground)' : 'var(--muted-foreground)' }) }, device))) }), _jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": "Reload preview", title: "Reload preview", onClick: () => setPreviewKey((k) => k + 1), children: _jsx(RefreshCw, { size: 12, color: "var(--muted-foreground)" }) })] })] }), _jsx(YStack, { flex: 1, items: "center", justify: "center", bg: "$background", p: "$2", children: previewUrl.trim() === '' ? (_jsx(Quiet, { children: "Nothing to show. Put the address of what you are building in the bar." })) : (_jsx(View, { width: previewDevice === 'desktop' ? '100%' : previewDevice === 'tablet' ? 768 : 375, maxW: "100%", height: "100%", bg: "$background", rounded: previewDevice === 'desktop' ? 0 : 8, overflow: "hidden", borderWidth: previewDevice === 'desktop' ? 0 : 1, borderColor: "var(--border-control)", boxShadow: "var(--shadow-lg)", children: _jsx(View, { render: _jsx("iframe", { src: previewUrl, sandbox: "allow-scripts allow-forms allow-popups allow-same-origin", title: "Preview" }), width: "100%", height: "100%", borderWidth: 0 }) }, previewKey)) })] }), _jsxs(YStack, { height: "100%", bg: "$background", minW: 0, display: activeTabMobile === 'team' ? 'flex' : 'none', $md: { display: 'none' }, $lg: { display: 'flex' }, children: [_jsxs(XStack, { height: 42, items: "center", gap: "$2", px: "$3", borderBottomWidth: 1, borderColor: "$borderColor", bg: "$background", children: [_jsx(Users, { size: 14, color: "var(--foreground)" }), _jsx(Text, { fontSize: 12, fontWeight: "600", color: "$ink", children: "People" })] }), _jsx(YStack, { flex: 1, overflowY: "auto", overflowX: "hidden", p: "$3", gap: "$3", children: _jsx(Team, {}) })] })] })] }));
|
|
181
181
|
}
|
|
182
182
|
// THE GATE IS NOT A ROOM'S TO REMEMBER. This room draws its own shell rather
|
|
183
183
|
// than `Room`, which is where every other room picks the organization gate up —
|
package/dist/Work.js
CHANGED
|
@@ -157,7 +157,7 @@ function Watching({ idle }) {
|
|
|
157
157
|
*/
|
|
158
158
|
function Idle({ runs }) {
|
|
159
159
|
if (runs === 0) {
|
|
160
|
-
return (_jsx(YStack, { flex: 1, minH: 0,
|
|
160
|
+
return (_jsx(YStack, { flex: 1, minH: 0, overflowY: "auto", overflowX: "hidden", children: _jsxs(YStack, { width: "100%", maxW: 640, mx: "auto", px: "$5", pt: "$10", gap: "$4", children: [_jsxs(YStack, { gap: "$2", children: [_jsx(Text, { render: "h1", fontSize: "$7", fontWeight: "500", color: "$ink", children: "Start a run" }), _jsx(Text, { fontSize: "$3", color: "$soft", maxW: 520, children: "Give an agent a task. Its plan, progress, and output will appear here." })] }), _jsx(Start, {}), _jsx(Text, { fontSize: "$1", color: "$soft", children: "Runs stay visible in Working so you can inspect or steer them at any time." })] }) }));
|
|
161
161
|
}
|
|
162
162
|
return (_jsx(YStack, { flex: 1, items: "center", justify: "center", p: "$6", children: _jsx(Text, { fontSize: "$3", color: "$soft", children: "Pick a run to watch it work." }) }));
|
|
163
163
|
}
|
|
@@ -230,7 +230,7 @@ function Watch({ id }) {
|
|
|
230
230
|
setNote('Could not reach this run');
|
|
231
231
|
}
|
|
232
232
|
}, [ai, draft, id]);
|
|
233
|
-
return (_jsxs(XStack, { flex: 1, minH: 0, flexWrap: "wrap", children: [_jsxs(YStack, { flex: 1, minH: 0, minW: 0, children: [_jsx(ChannelHeader, { name: session?.title || session?.agent || id, artifacts: 0 }), _jsx(XStack, { px: "$4", pb: "$2", children: _jsx(Bar, { run: session }) }), _jsx(YStack, { flex: 1, minH: 0,
|
|
233
|
+
return (_jsxs(XStack, { flex: 1, minH: 0, flexWrap: "wrap", children: [_jsxs(YStack, { flex: 1, minH: 0, minW: 0, children: [_jsx(ChannelHeader, { name: session?.title || session?.agent || id, artifacts: 0 }), _jsx(XStack, { px: "$4", pb: "$2", children: _jsx(Bar, { run: session }) }), _jsx(YStack, { flex: 1, minH: 0, overflowY: "auto", overflowX: "hidden", p: "$4", gap: "$4", children: _jsx(ProgressBlock, { steps: shown, done: finished, children: message ? (_jsx(Text, { fontSize: "$4", color: "$ink", children: message })) : null }) }), _jsxs(YStack, { p: "$3", gap: "$2", borderTopWidth: 1, borderColor: "$borderColor", children: [_jsx(Steer, { onCommand: (c) => void steer(c), withhold: finished ? ['pause', 'resume', 'stop'] : [], note: note }), _jsx(Composer, { value: draft, onChange: setDraft, onSend: () => void send(), placeholder: "Steer this run\u2026", label: "Steer this run" })] })] }), _jsx(YStack, { width: "100%", p: "$3", borderTopWidth: 1, $sm: { width: 420, shrink: 0, borderTopWidth: 0, borderLeftWidth: 1 }, borderColor: "$borderColor", children: _jsx(Pane, { tabs: [
|
|
234
234
|
{ id: 'code', label: 'Code', kind: 'code', content: _jsx(Nothing, { what: "changes" }) },
|
|
235
235
|
{ id: 'view', label: 'View', kind: 'view', content: _jsx(Nothing, { what: "preview" }) },
|
|
236
236
|
{ id: 'canvas', label: 'Canvas', kind: 'canvas', content: _jsx(Nothing, { what: "summary" }) },
|
package/dist/crew.js
CHANGED
|
@@ -98,11 +98,11 @@ export function Crew({ to } = {}) {
|
|
|
98
98
|
if (here !== to)
|
|
99
99
|
router.push(to);
|
|
100
100
|
};
|
|
101
|
-
return (_jsxs(_Fragment, { children: [_jsxs(XStack, { items: "flex-start", flexWrap: "nowrap", overflowX: "auto", gap: "$2.5", px: "$4", py: "$3", width: "100%", maxW: 820, mx: "auto", "data-slot": "crew", ...bare, role: "group", "aria-label": "Agents", children: [
|
|
101
|
+
return (_jsxs(_Fragment, { children: [_jsxs(XStack, { items: "flex-start", flexWrap: "nowrap", overflowX: "auto", overflowY: "hidden", gap: "$2.5", px: "$4", py: "$3", width: "100%", maxW: 820, mx: "auto", "data-slot": "crew", ...bare, role: "group", "aria-label": "Agents", children: [ordered(agents).map((one) => {
|
|
102
102
|
const name = called(one.name);
|
|
103
103
|
const on = room.some((there) => there.toLowerCase() === one.name.toLowerCase());
|
|
104
104
|
return (_jsx(Box, { render: "button", onClick: () => talk(name), "aria-pressed": on, "aria-label": `Chat with ${name}`, title: to ? `Chat with ${name}` : on ? `Remove ${name} from this chat` : `Add ${name} to this chat`, shrink: 0, children: _jsxs(YStack, { items: "center", gap: "$1.5", width: 68, children: [_jsxs(YStack, { position: "relative", rounded: DISC / 2 + 2, p: 2, borderWidth: 1.5, borderColor: on ? '$ink' : 'transparent', hoverStyle: { borderColor: on ? '$ink' : '$borderColor' }, children: [_jsx(Face, { src: one.avatar, emoji: one.emoji, name: one.name, size: DISC }), on ? (_jsx(YStack, { "aria-hidden": true, position: "absolute", b: -2, r: -2, width: 18, height: 18, rounded: 9, bg: "$ink", borderWidth: 2, borderColor: "$background", items: "center", justify: "center", children: _jsx(Check, { size: 10, strokeWidth: 3, color: "var(--background)" }) })) : null] }), _jsx(Text, { fontSize: "$1", color: on ? '$ink' : '$soft', numberOfLines: 1, maxW: 68, children: name })] }) }, one.id || one.name));
|
|
105
|
-
})] }), hiring ? _jsx(Hire, { onClose: () => setHiring(false), onMade: talk }) : null] }));
|
|
105
|
+
}), _jsx(Box, { render: "button", onClick: () => setHiring(true), "aria-label": "New agent", "aria-haspopup": "dialog", shrink: 0, children: _jsxs(YStack, { items: "center", gap: "$1.5", width: 68, children: [_jsx(YStack, { width: DISC, height: DISC, rounded: DISC / 2, borderWidth: 1.5, borderStyle: "dashed", borderColor: "$soft", items: "center", justify: "center", hoverStyle: { borderColor: '$ink', bg: '$raised' }, children: _jsx(Plus, { size: 18, "aria-hidden": true }) }), _jsx(Text, { fontSize: "$1", color: "$soft", numberOfLines: 1, children: "New" })] }) })] }), hiring ? _jsx(Hire, { onClose: () => setHiring(false), onMade: talk }) : null] }));
|
|
106
106
|
}
|
|
107
107
|
/** The agents API's name rule (cloud apps/agents `nameRE`), checked before the request. */
|
|
108
108
|
const NAME = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
package/dist/ground.d.ts
CHANGED
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
* The ground the workspace's cards stand on, and how each card is cut.
|
|
3
3
|
*
|
|
4
4
|
* A PANE is a card floating on the ground, cut by the `--pane-*` tokens
|
|
5
|
-
* (
|
|
6
|
-
* paper rung's lit edge and drop
|
|
5
|
+
* (@hanzo/design elevation.css): a corner, an edge, the lens behind it, and the
|
|
6
|
+
* first paper rung's lit edge and drop — the second's 15px drop and the floor's
|
|
7
|
+
* strong glass read as a gradient through a translucent pane, so both stay one
|
|
8
|
+
* rung down. The fill is the caller's rung: `--pane-fill`
|
|
7
9
|
* in the app's frame, `sheet(1)`/`sheet(2)` where a pane lies in a room.
|
|
8
10
|
*
|
|
9
11
|
* The drop is a prop and not `sheet(2)`'s `elevation-2` class, whose shadow is
|
|
@@ -16,7 +18,7 @@ export declare const pane: (fill?: string) => {
|
|
|
16
18
|
readonly borderWidth: 1;
|
|
17
19
|
readonly borderColor: 'var(--pane-edge)';
|
|
18
20
|
readonly backdropFilter: 'var(--pane-blur)';
|
|
19
|
-
readonly boxShadow: 'var(--shadow-sheet-
|
|
21
|
+
readonly boxShadow: 'var(--shadow-sheet-1)';
|
|
20
22
|
};
|
|
21
23
|
/**
|
|
22
24
|
* THE FLOOR under the app's frame (app/_web.tsx, components/settings/Frame.tsx):
|
|
@@ -26,7 +28,7 @@ export declare const pane: (fill?: string) => {
|
|
|
26
28
|
*/
|
|
27
29
|
export declare const floor: {
|
|
28
30
|
readonly backgroundColor: 'var(--pane-floor)';
|
|
29
|
-
readonly backgroundImage: 'linear-gradient(to bottom, var(--glass
|
|
31
|
+
readonly backgroundImage: 'linear-gradient(to bottom, var(--glass), transparent), var(--pane-ground)';
|
|
30
32
|
readonly backgroundSize: '100% 70%, auto';
|
|
31
33
|
readonly backgroundRepeat: 'no-repeat';
|
|
32
34
|
};
|
package/dist/ground.js
CHANGED
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
* The ground the workspace's cards stand on, and how each card is cut.
|
|
3
3
|
*
|
|
4
4
|
* A PANE is a card floating on the ground, cut by the `--pane-*` tokens
|
|
5
|
-
* (
|
|
6
|
-
* paper rung's lit edge and drop
|
|
5
|
+
* (@hanzo/design elevation.css): a corner, an edge, the lens behind it, and the
|
|
6
|
+
* first paper rung's lit edge and drop — the second's 15px drop and the floor's
|
|
7
|
+
* strong glass read as a gradient through a translucent pane, so both stay one
|
|
8
|
+
* rung down. The fill is the caller's rung: `--pane-fill`
|
|
7
9
|
* in the app's frame, `sheet(1)`/`sheet(2)` where a pane lies in a room.
|
|
8
10
|
*
|
|
9
11
|
* The drop is a prop and not `sheet(2)`'s `elevation-2` class, whose shadow is
|
|
@@ -16,7 +18,7 @@ export const pane = (fill = 'var(--pane-fill)') => ({
|
|
|
16
18
|
borderWidth: 1,
|
|
17
19
|
borderColor: 'var(--pane-edge)',
|
|
18
20
|
backdropFilter: 'var(--pane-blur)',
|
|
19
|
-
boxShadow: 'var(--shadow-sheet-
|
|
21
|
+
boxShadow: 'var(--shadow-sheet-1)',
|
|
20
22
|
});
|
|
21
23
|
/**
|
|
22
24
|
* THE FLOOR under the app's frame (app/_web.tsx, components/settings/Frame.tsx):
|
|
@@ -26,7 +28,7 @@ export const pane = (fill = 'var(--pane-fill)') => ({
|
|
|
26
28
|
*/
|
|
27
29
|
export const floor = {
|
|
28
30
|
backgroundColor: 'var(--pane-floor)',
|
|
29
|
-
backgroundImage: 'linear-gradient(to bottom, var(--glass
|
|
31
|
+
backgroundImage: 'linear-gradient(to bottom, var(--glass), transparent), var(--pane-ground)',
|
|
30
32
|
backgroundSize: '100% 70%, auto',
|
|
31
33
|
backgroundRepeat: 'no-repeat',
|
|
32
34
|
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Files that go with a chat message.
|
|
3
|
+
*
|
|
4
|
+
* @hanzo/ai's `send(text, images)` carries a picture as an `image_url` part the
|
|
5
|
+
* model reads, and nothing else rides that wire, so a file goes one of two
|
|
6
|
+
* ways: an image as a `data:` URI beside the words, a text file as its own
|
|
7
|
+
* words after the message, fenced under its name. Any other kind is refused by
|
|
8
|
+
* name, before anything is sent.
|
|
9
|
+
*/
|
|
10
|
+
import type { MessagePart } from '@hanzo/ui/chat';
|
|
11
|
+
/** A file held for the next message. */
|
|
12
|
+
export interface Held {
|
|
13
|
+
id: string;
|
|
14
|
+
name: string;
|
|
15
|
+
kind: 'image' | 'text';
|
|
16
|
+
/** A `data:` URI for an image, the words for a text file. */
|
|
17
|
+
data: string;
|
|
18
|
+
size: number;
|
|
19
|
+
}
|
|
20
|
+
export declare const IMAGE_MAX: number;
|
|
21
|
+
export declare const TEXT_MAX: number;
|
|
22
|
+
/** Which way a file goes with a message, or null when it cannot go. */
|
|
23
|
+
export declare function kindOf(file: {
|
|
24
|
+
name: string;
|
|
25
|
+
type: string;
|
|
26
|
+
}): Held['kind'] | null;
|
|
27
|
+
/** A file read and held, or the sentence that says why it cannot go. */
|
|
28
|
+
export declare function hold(file: File): Promise<Held | string>;
|
|
29
|
+
/**
|
|
30
|
+
* The message as sent: the words, then each text file fenced under its name.
|
|
31
|
+
* With no words and no text file, the images' names, so the turn says what it
|
|
32
|
+
* carries.
|
|
33
|
+
*/
|
|
34
|
+
export declare function compose(text: string, files: readonly Held[]): string;
|
|
35
|
+
/** The images of a message, as the URIs @hanzo/ai's `send` takes. */
|
|
36
|
+
export declare const images: (files: readonly Held[]) => string[];
|
|
37
|
+
/** A wire turn's parts — text and `image_url` — as the parts `@hanzo/ui/chat` draws. */
|
|
38
|
+
export declare function partsOf(content: readonly unknown[]): MessagePart[];
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Files that go with a chat message.
|
|
3
|
+
*
|
|
4
|
+
* @hanzo/ai's `send(text, images)` carries a picture as an `image_url` part the
|
|
5
|
+
* model reads, and nothing else rides that wire, so a file goes one of two
|
|
6
|
+
* ways: an image as a `data:` URI beside the words, a text file as its own
|
|
7
|
+
* words after the message, fenced under its name. Any other kind is refused by
|
|
8
|
+
* name, before anything is sent.
|
|
9
|
+
*/
|
|
10
|
+
export const IMAGE_MAX = 8 * 1024 * 1024;
|
|
11
|
+
export const TEXT_MAX = 256 * 1024;
|
|
12
|
+
const TEXT_TYPE = /^(text\/|application\/(json|ld\+json|xml|x-yaml|yaml|toml|javascript|typescript|x-sh|sql|x-tex))/;
|
|
13
|
+
const TEXT_NAME = /\.(md|mdx|txt|csv|tsv|json|jsonl|ya?ml|toml|xml|html?|css|scss|[cm]?[jt]sx?|py|go|rs|rb|java|kt|swift|c|cc|cpp|h|hpp|cs|php|sh|bash|zsh|sql|ini|env|log|tex|proto|graphql)$/i;
|
|
14
|
+
/** Which way a file goes with a message, or null when it cannot go. */
|
|
15
|
+
export function kindOf(file) {
|
|
16
|
+
if (file.type.startsWith('image/'))
|
|
17
|
+
return 'image';
|
|
18
|
+
if (TEXT_TYPE.test(file.type) || TEXT_NAME.test(file.name))
|
|
19
|
+
return 'text';
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
function read(file, as) {
|
|
23
|
+
return new Promise((resolve, reject) => {
|
|
24
|
+
const reader = new FileReader();
|
|
25
|
+
reader.onload = () => resolve(String(reader.result ?? ''));
|
|
26
|
+
reader.onerror = () => reject(reader.error ?? new Error(`${file.name} could not be read.`));
|
|
27
|
+
if (as === 'url')
|
|
28
|
+
reader.readAsDataURL(file);
|
|
29
|
+
else
|
|
30
|
+
reader.readAsText(file);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
/** A file read and held, or the sentence that says why it cannot go. */
|
|
34
|
+
export async function hold(file) {
|
|
35
|
+
const kind = kindOf(file);
|
|
36
|
+
if (!kind)
|
|
37
|
+
return `${file.name}: only images and text files go with a message.`;
|
|
38
|
+
const max = kind === 'image' ? IMAGE_MAX : TEXT_MAX;
|
|
39
|
+
if (file.size > max)
|
|
40
|
+
return `${file.name} is larger than ${kind === 'image' ? '8 MB' : '256 KB'}.`;
|
|
41
|
+
try {
|
|
42
|
+
const data = await read(file, kind === 'image' ? 'url' : 'text');
|
|
43
|
+
return { id: crypto.randomUUID(), name: file.name, kind, data, size: file.size };
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return `${file.name} could not be read.`;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/** A fence longer than any run of backticks inside the words, so they cannot close it. */
|
|
50
|
+
function fence(words) {
|
|
51
|
+
const longest = Math.max(0, ...(words.match(/`+/g) ?? []).map((run) => run.length));
|
|
52
|
+
return '`'.repeat(Math.max(3, longest + 1));
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The message as sent: the words, then each text file fenced under its name.
|
|
56
|
+
* With no words and no text file, the images' names, so the turn says what it
|
|
57
|
+
* carries.
|
|
58
|
+
*/
|
|
59
|
+
export function compose(text, files) {
|
|
60
|
+
const blocks = files
|
|
61
|
+
.filter((f) => f.kind === 'text')
|
|
62
|
+
.map((f) => {
|
|
63
|
+
const f3 = fence(f.data);
|
|
64
|
+
return `${f.name}\n${f3}\n${f.data.replace(/\n$/, '')}\n${f3}`;
|
|
65
|
+
});
|
|
66
|
+
const out = [text.trim(), ...blocks].filter(Boolean).join('\n\n');
|
|
67
|
+
return out || files.map((f) => f.name).join(', ');
|
|
68
|
+
}
|
|
69
|
+
/** The images of a message, as the URIs @hanzo/ai's `send` takes. */
|
|
70
|
+
export const images = (files) => files.filter((f) => f.kind === 'image').map((f) => f.data);
|
|
71
|
+
/** A wire turn's parts — text and `image_url` — as the parts `@hanzo/ui/chat` draws. */
|
|
72
|
+
export function partsOf(content) {
|
|
73
|
+
const out = [];
|
|
74
|
+
for (const one of content) {
|
|
75
|
+
const part = one;
|
|
76
|
+
if (part?.type === 'text' && part.text)
|
|
77
|
+
out.push({ type: 'text', text: part.text });
|
|
78
|
+
else if (part?.type === 'image_url' && part.image_url?.url)
|
|
79
|
+
out.push({ type: 'image', url: part.image_url.url, alt: 'Attached image' });
|
|
80
|
+
}
|
|
81
|
+
return out;
|
|
82
|
+
}
|
package/dist/lib/tiers.js
CHANGED
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
import { useEffect, useState } from 'react';
|
|
34
34
|
import { useIam, useOrganizations } from '@hanzo/iam/react';
|
|
35
35
|
import { api } from './api.js';
|
|
36
|
-
import { superAdmin as isSuperAdmin } from './session.js';
|
|
36
|
+
import { scope, superAdmin as isSuperAdmin } from './session.js';
|
|
37
37
|
/** Every tier, for the callers that are entitled to all of them. */
|
|
38
38
|
const ALL = ['alpha', 'beta'];
|
|
39
39
|
/**
|
|
@@ -81,13 +81,13 @@ export function useTiers() {
|
|
|
81
81
|
}
|
|
82
82
|
const ac = new AbortController();
|
|
83
83
|
// The PostHog-shaped evaluate: a distinct_id and the person's org, answered
|
|
84
|
-
// with `{ featureFlags: { … } }`. The
|
|
85
|
-
//
|
|
86
|
-
// the
|
|
84
|
+
// with `{ featureFlags: { … } }`. The bearer carries the principal, as on
|
|
85
|
+
// every other read: no host the rooms are drawn on holds a session cookie
|
|
86
|
+
// for the API, so a cookie alone was answered 403 on every page. A refusal
|
|
87
|
+
// still falls to the empty set, the signed-out room — the safe direction.
|
|
87
88
|
fetch(`${api()}/v1/flags`, {
|
|
88
89
|
method: 'POST',
|
|
89
|
-
|
|
90
|
-
headers: { 'content-type': 'application/json' },
|
|
90
|
+
headers: { 'content-type': 'application/json', ...scope() },
|
|
91
91
|
body: JSON.stringify({ distinct_id: id, person_properties: { org: organizations[0]?.name } }),
|
|
92
92
|
signal: ac.signal,
|
|
93
93
|
})
|
package/dist/tabs.js
CHANGED
|
@@ -13,7 +13,7 @@ import { Box, Text, XStack } from '@hanzo/ui';
|
|
|
13
13
|
* `after` is what sits past the last tab: the plus that would add one.
|
|
14
14
|
*/
|
|
15
15
|
export function Tabs({ tabs, chosen, onPick, after, }) {
|
|
16
|
-
return (_jsxs(XStack, { minH: 48, shrink: 0, items: "center", px: "$3", gap: "$3",
|
|
16
|
+
return (_jsxs(XStack, { minH: 48, shrink: 0, items: "center", px: "$3", gap: "$3", overflowX: "auto", overflowY: "hidden", borderBottomWidth: 1, borderColor: "$borderColor", children: [tabs.map((one) => {
|
|
17
17
|
const on = one.id === chosen;
|
|
18
18
|
const Icon = one.icon;
|
|
19
19
|
return (_jsx(Box, { ...(onPick ? { render: 'button', onClick: () => onPick(one.id) } : {}), "aria-current": on,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzo/rooms",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "The workspace rooms — Home, Chat, Inbox, Contacts, Meet, Cal, Drive, Board, Work, Bots — and the frame that holds them, once, for every Hanzo surface.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"simple-icons": "16.30.0"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
|
-
"@hanzo/ai": ">=0.6.
|
|
48
|
+
"@hanzo/ai": ">=0.6.20",
|
|
49
49
|
"@hanzo/appearance": ">=0.2.6",
|
|
50
50
|
"@hanzo/build": ">=0.2.34",
|
|
51
51
|
"@hanzo/composer": ">=0.2.0",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"react-dom": ">=19.2.8"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
|
-
"@hanzo/ai": "0.6.
|
|
66
|
+
"@hanzo/ai": "0.6.20",
|
|
67
67
|
"@hanzo/appearance": "0.2.6",
|
|
68
68
|
"@hanzo/build": "0.2.34",
|
|
69
69
|
"@hanzo/composer": "0.2.0",
|