@hanzo/rooms 0.1.57 → 0.1.59
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/App.d.ts +13 -1
- package/dist/App.js +117 -36
- package/dist/Chat.js +55 -13
- package/dist/Column.d.ts +10 -6
- package/dist/Column.js +77 -54
- package/dist/Home.js +7 -7
- package/dist/Inbox.js +1 -1
- package/dist/Me.d.ts +10 -2
- package/dist/Me.js +36 -9
- package/dist/Meet.js +1 -1
- package/dist/Prose.js +16 -9
- package/dist/Rail.d.ts +39 -0
- package/dist/Rail.js +85 -0
- package/dist/Recents.js +24 -12
- package/dist/RightPane.d.ts +23 -0
- package/dist/RightPane.js +63 -11
- package/dist/Share.js +2 -2
- package/dist/Shell.js +7 -8
- package/dist/Talk.js +3 -3
- package/dist/recent.js +1 -1
- package/dist/rooms.js +2 -2
- package/dist/thread.d.ts +8 -0
- package/dist/thread.js +15 -0
- package/dist/where.d.ts +2 -0
- package/dist/where.js +2 -0
- package/package.json +2 -2
- package/dist/ModeMark.d.ts +0 -8
- package/dist/ModeMark.js +0 -19
package/dist/Rail.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
// The app's rail: its products as marks down the window's left edge, in one
|
|
4
|
+
// order — Chat, Dev, Library, Scheduled tasks, Customize — then More, a menu of
|
|
5
|
+
// the rest, and the account at the foot. It is how the app moves between Chat
|
|
6
|
+
// and Dev; there is no switch anywhere else.
|
|
7
|
+
//
|
|
8
|
+
// Library, Scheduled tasks and Customize are Dev's own places (@hanzo/build's
|
|
9
|
+
// Artifacts, Automations and Customize), raised to the rail; each is lit while
|
|
10
|
+
// the reader stands on it, and Dev is lit for the rest of Dev.
|
|
11
|
+
//
|
|
12
|
+
// It stands on the frame's floor, never cut as a pane (ground.ts): the rail and
|
|
13
|
+
// the sidebar beside it are the window, and the content is the panel inset in
|
|
14
|
+
// it.
|
|
15
|
+
//
|
|
16
|
+
// The sidebar's toggle leads it (`toggle`) where the host has no bar of its own:
|
|
17
|
+
// a browser tab. A host that is a window draws the toggle in its bar beside Back
|
|
18
|
+
// and Forward (App.tsx `bar`), and its rail starts with Chat. On a phone the
|
|
19
|
+
// rail lies along the foot of the drawer (`flat`), the account at its end.
|
|
20
|
+
import { Boxes, Clock, Code2, Ellipsis, FolderGit2, LayoutTemplate, Library, ListTodo, MessageSquare, PanelLeft, Puzzle, Server, SquareTerminal, Users } from 'lucide-react';
|
|
21
|
+
import { XStack, YStack } from '@hanzo/gui';
|
|
22
|
+
import { route } from '@hanzo/build';
|
|
23
|
+
import { DropdownMenu } from '@hanzo/ui';
|
|
24
|
+
import { Me } from './Me.js';
|
|
25
|
+
import { app, TABS, WORKSPACE } from './lib/host.js';
|
|
26
|
+
/** The rail's width, px. */
|
|
27
|
+
export const RAIL = 48;
|
|
28
|
+
/** A mark, px: a 36px square on a 44px pitch. */
|
|
29
|
+
const MARK = 36;
|
|
30
|
+
/**
|
|
31
|
+
* Opens an address of another product in a tab of its own, through an anchor,
|
|
32
|
+
* so a host that sends links out of its window (the desktop's system browser)
|
|
33
|
+
* hears it as the link it is.
|
|
34
|
+
*/
|
|
35
|
+
function out(href) {
|
|
36
|
+
const a = document.createElement('a');
|
|
37
|
+
a.href = href;
|
|
38
|
+
a.target = '_blank';
|
|
39
|
+
a.rel = 'noopener noreferrer';
|
|
40
|
+
document.body.appendChild(a);
|
|
41
|
+
a.click();
|
|
42
|
+
a.remove();
|
|
43
|
+
}
|
|
44
|
+
/** The places the rail raises out of Dev, by the builder address each opens. */
|
|
45
|
+
const RAISED = { library: '-/artifacts', scheduled: '-/automations', customize: '-/customize' };
|
|
46
|
+
/** Which raised place a builder address stands on, if any. */
|
|
47
|
+
function raised(at) {
|
|
48
|
+
if (at === null)
|
|
49
|
+
return null;
|
|
50
|
+
const r = route(at);
|
|
51
|
+
if (r.kind === 'screen' && r.screen === 'artifacts')
|
|
52
|
+
return 'library';
|
|
53
|
+
if ((r.kind === 'screen' && r.screen === 'automations') || r.kind === 'automation')
|
|
54
|
+
return 'scheduled';
|
|
55
|
+
if (r.kind === 'customize')
|
|
56
|
+
return 'customize';
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
/** One mark: an icon button named for where it goes, its name its tooltip, lit where the reader is. */
|
|
60
|
+
export function Mark({ label, on = false, onPress, children, ...rest }) {
|
|
61
|
+
return (_jsx(XStack, { render: "button", "aria-label": label, ...{ title: label }, "aria-current": on ? 'page' : undefined, onPress: onPress, width: MARK, height: MARK, rounded: "$4", items: "center", justify: "center", shrink: 0, cursor: "pointer", opacity: on ? 1 : 0.62, bg: on ? '$edge' : 'transparent', hoverStyle: { bg: on ? '$edge' : '$hover', opacity: 1 }, pressStyle: { bg: '$raised' }, focusVisibleStyle: { outlineWidth: 2, outlineStyle: 'solid', outlineColor: '$outlineColor' },
|
|
62
|
+
// A thumb's 44px on a touch screen.
|
|
63
|
+
$touchable: { width: 44, height: 44 }, ...rest, children: children }));
|
|
64
|
+
}
|
|
65
|
+
/** The sidebar's toggle: the same mark wherever a host draws it. */
|
|
66
|
+
export function Toggle({ open, onPress, label }) {
|
|
67
|
+
return (_jsx(Mark, { label: label ?? (open ? 'Close sidebar' : 'Open sidebar'), "data-slot": "sidebar-toggle", "aria-expanded": open, onPress: onPress, children: _jsx(PanelLeft, { size: 18, "aria-hidden": true }) }));
|
|
68
|
+
}
|
|
69
|
+
export function Rail({ host, mode, at, open, toggle, flat = false, onChat, onDev, go, onOrg, }) {
|
|
70
|
+
const here = mode === 'dev' ? raised(at) : null;
|
|
71
|
+
const to = (p) => () => go(app(p));
|
|
72
|
+
// Everything else the app reaches, behind More: Dev's other places, then the products on hosts of their own.
|
|
73
|
+
const rest = [
|
|
74
|
+
{ key: 'projects', label: 'Projects', icon: _jsx(FolderGit2, { size: 16, "aria-hidden": true }), onSelect: to('-/projects') },
|
|
75
|
+
{ key: 'templates', label: 'Templates', icon: _jsx(LayoutTemplate, { size: 16, "aria-hidden": true }), onSelect: to('-/templates') },
|
|
76
|
+
{ key: 'issues', label: 'Issues', icon: _jsx(ListTodo, { size: 16, "aria-hidden": true }), onSelect: to('-/issues') },
|
|
77
|
+
{ key: 'machines', label: 'Machines', icon: _jsx(Server, { size: 16, "aria-hidden": true }), onSelect: to('-/settings/machines') },
|
|
78
|
+
{ key: 'environments', label: 'Environments', icon: _jsx(Boxes, { size: 16, "aria-hidden": true }), onSelect: to('-/settings/environments') },
|
|
79
|
+
{ type: 'separator', key: 'away' },
|
|
80
|
+
{ key: 'tabs', label: 'Tabs', icon: _jsx(SquareTerminal, { size: 16, "aria-hidden": true }), onSelect: () => out(TABS) },
|
|
81
|
+
{ key: 'team', label: 'Team', icon: _jsx(Users, { size: 16, "aria-hidden": true }), onSelect: () => out(WORKSPACE) },
|
|
82
|
+
];
|
|
83
|
+
const Stack = flat ? XStack : YStack;
|
|
84
|
+
return (_jsxs(Stack, { "data-slot": "rail", render: "nav", "aria-label": "Products", ...(flat ? { pt: '$2', mt: '$2', borderTopWidth: 1, borderColor: '$borderColor', gap: '$1' } : { width: RAIL, flex: 1, pt: '$2', pb: '$2', gap: 8 }), shrink: 0, items: "center", children: [toggle ? (_jsx(YStack, { pb: 8, children: _jsx(Toggle, { open: open, onPress: toggle }) })) : null, _jsx(Mark, { label: "Chat", on: mode === 'chat', onPress: onChat, "data-slot": "rail-chat", children: _jsx(MessageSquare, { size: 18, "aria-hidden": true }) }), _jsx(Mark, { label: "Dev", on: mode === 'dev' && !here, onPress: onDev, "data-slot": "rail-dev", children: _jsx(Code2, { size: 18, "aria-hidden": true }) }), _jsx(Mark, { label: "Library", on: here === 'library', onPress: to(RAISED.library), "data-slot": "rail-library", children: _jsx(Library, { size: 18, "aria-hidden": true }) }), _jsx(Mark, { label: "Scheduled tasks", on: here === 'scheduled', onPress: to(RAISED.scheduled), "data-slot": "rail-scheduled", children: _jsx(Clock, { size: 18, "aria-hidden": true }) }), _jsx(Mark, { label: "Customize", on: here === 'customize', onPress: to(RAISED.customize), "data-slot": "rail-customize", children: _jsx(Puzzle, { size: 18, "aria-hidden": true }) }), _jsx(DropdownMenu, { placement: flat ? 'top-start' : 'right-start', minWidth: 220, trigger: _jsx(XStack, { render: "button", "aria-label": "More", ...{ title: 'More' }, "data-slot": "rail-more", width: MARK, height: MARK, rounded: "$4", items: "center", justify: "center", cursor: "pointer", opacity: 0.62, hoverStyle: { bg: '$hover', opacity: 1 }, pressStyle: { bg: '$raised' }, focusVisibleStyle: { outlineWidth: 2, outlineStyle: 'solid', outlineColor: '$outlineColor' }, $touchable: { width: 44, height: 44 }, children: _jsx(Ellipsis, { size: 18, "aria-hidden": true }) }), items: rest }), _jsx(YStack, { flex: 1 }), _jsx(Me, { host: host, narrow: true, above: flat, onOrg: onOrg })] }));
|
|
85
|
+
}
|
package/dist/Recents.js
CHANGED
|
@@ -9,12 +9,12 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
9
9
|
// and Chat's Status shows the archived ones apart.
|
|
10
10
|
import { useEffect, useId, useMemo, useRef } from 'react';
|
|
11
11
|
import { ScrollView, SizableText, XStack, YStack } from '@hanzo/gui';
|
|
12
|
-
import { ListFilter
|
|
12
|
+
import { ListFilter } from 'lucide-react';
|
|
13
13
|
import { DOTS, route, useSessions } from '@hanzo/build';
|
|
14
14
|
import { DropdownMenu } from '@hanzo/ui';
|
|
15
15
|
import { SidebarItem, StatusDot } from '@hanzo/ui/chat';
|
|
16
16
|
import { useKept } from './kept.js';
|
|
17
|
-
import {
|
|
17
|
+
import { arrange, chats, MENUS, PINNED, PLAIN, runs, SINCE, viewOf } from './recent.js';
|
|
18
18
|
import { ConversationMenu, ThreadRow, useThreadList } from './thread.js';
|
|
19
19
|
/** The menu's items: each section a heading and its options, the one in force checked. */
|
|
20
20
|
function items(mode, view, set) {
|
|
@@ -68,23 +68,35 @@ function List({ mode, rows, words, view, keep, open, reading, error, onOpen, thr
|
|
|
68
68
|
const label = useId();
|
|
69
69
|
const groups = arrange(rows, view, words);
|
|
70
70
|
const narrowed = view.status !== PLAIN.status || view.since !== PLAIN.since;
|
|
71
|
-
const now = new Date();
|
|
72
71
|
const running = mode === 'dev' ? rows.filter((r) => r.status === 'running').length : 0;
|
|
72
|
+
// A row is its title alone, as the reference draws it: a conversation's ⋯
|
|
73
|
+
// shows on hover and focus, a run's state is a dot at its end.
|
|
73
74
|
const row = (r) => {
|
|
74
|
-
const when = ago(r.at, now);
|
|
75
75
|
const t = threads?.get(r.id);
|
|
76
|
-
const item = (_jsx(SidebarItem, { "data-kind": r.kind, active: open(r), rounded: "$4", minH:
|
|
76
|
+
const item = (_jsx(SidebarItem, { "data-kind": r.kind, active: open(r), rounded: "$4", minH: 31, px: "$2", pr: t || r.kind === 'dev' ? '$7' : '$2', ...(t ? { $touchable: { pr: 52 } } : null), onKeyDown: (e) => {
|
|
77
77
|
if (e.key !== 'Enter' && e.key !== ' ')
|
|
78
78
|
return;
|
|
79
79
|
e.preventDefault?.();
|
|
80
80
|
onOpen(r);
|
|
81
|
-
},
|
|
82
|
-
// How long ago, at the row's end; the full time is its title.
|
|
83
|
-
const age = when ? (_jsx(SizableText, { size: "$1", color: "$soft", pointerEvents: "none", display: "flex", items: "center", fontVariant: ['tabular-nums'], children: _jsx("time", { dateTime: r.at, title: new Date(r.at).toLocaleString(), children: when }) })) : null;
|
|
81
|
+
}, onPress: () => onOpen(r), children: _jsx(SizableText, { size: "$3", color: open(r) ? '$ink' : '$quiet', numberOfLines: 1, display: "block", children: r.title }) }));
|
|
84
82
|
if (t)
|
|
85
|
-
return (_jsx(ThreadRow, { t: t, active: open(r),
|
|
86
|
-
return (_jsxs(YStack, { role: "listitem", position: "relative", children: [item,
|
|
83
|
+
return (_jsx(ThreadRow, { t: t, active: open(r), children: item }, r.id));
|
|
84
|
+
return (_jsxs(YStack, { role: "listitem", position: "relative", children: [item, r.kind === 'dev' ? (_jsx(XStack, { position: "absolute", r: "$2.5", t: 0, b: 0, items: "center", pointerEvents: "none", children: _jsx(StatusDot, { status: DOTS[r.status] ?? 'idle' }) })) : null] }, r.id));
|
|
87
85
|
};
|
|
88
|
-
|
|
89
|
-
|
|
86
|
+
// The view menu stands at the end of the Recents heading: the target's own, quiet until the pointer is near.
|
|
87
|
+
const menu = (_jsx(DropdownMenu, { minWidth: 200, trigger: _jsx(XStack, { render: "button", "aria-label": "Recents view", width: 26, height: 26, rounded: "$2", items: "center", justify: "center", cursor: "pointer", opacity: narrowed ? 1 : 0, "$group-hover": { opacity: 1 }, focusStyle: { opacity: 1 }, $touchable: { opacity: 1 }, hoverStyle: { bg: '$hover' }, bg: narrowed ? '$hover' : 'transparent', children: _jsx(ListFilter, { size: 14, "aria-hidden": true }) }), items: items(mode, view, keep) }));
|
|
88
|
+
/** A section's heading: the reference's quiet 14px label, the Recents one carrying the count and the menu. */
|
|
89
|
+
const heading = (title, id, lead = false) => (_jsxs(XStack, { group: true, items: "center", justify: "space-between", height: 31, pl: "$2", pr: "$1", mt: "$3", shrink: 0, children: [_jsxs(XStack, { items: "center", gap: "$2", children: [_jsx(SizableText, { id: id, size: "$3", color: "$soft", children: title }), lead && running ? (_jsx(XStack, { "aria-label": `${running} running`, minW: 18, height: 18, px: "$1.5", rounded: 999, items: "center", justify: "center", bg: "$edge", borderWidth: 1, borderColor: "$borderColor", children: _jsx(SizableText, { size: "$1", fontWeight: "600", color: "$ink", "aria-hidden": true, children: String(running) }) })) : null] }), lead ? menu : null] }));
|
|
90
|
+
const loose = groups.filter((g) => g.title !== PINNED);
|
|
91
|
+
const pinned = groups.find((g) => g.title === PINNED);
|
|
92
|
+
const note = reading ? (_jsx(Note, { children: mode === 'chat' ? 'Reading your chats…' : 'Reading your runs…' })) : error ? null : words.trim() ? (_jsx(Note, { children: `Nothing matches “${words.trim()}”.` })) : view.status === 'archived' ? (_jsx(Note, { children: "No archived chats." })) : narrowed ? (_jsx(Note, { children: "Nothing in this view." })) : (_jsx(Note, { children: mode === 'chat' ? 'No chats yet.' : 'No runs yet.' }));
|
|
93
|
+
// Each list of conversations holds its own menu (thread.tsx): the view menu
|
|
94
|
+
// in the Recents heading between them is a menu of its own, never inside one.
|
|
95
|
+
const menus = (rows) => threads ? (_jsx(ConversationMenu, { threads: threads, active: held, onGone: onNew, children: _jsx(YStack, { children: rows }) })) : (rows);
|
|
96
|
+
const rest = loose.length
|
|
97
|
+
? loose.map((g) => g.title ? (_jsxs(YStack, { gap: 1, pt: "$1", children: [_jsx(SizableText, { size: "$1", color: "$soft", px: "$2", pb: "$1", children: g.title }), _jsx(YStack, { role: "list", "aria-label": g.title, gap: 1, children: g.rows.map(row) })] }, g.title)) : (_jsx(YStack, { role: "list", "aria-label": "All", gap: 1, children: g.rows.map(row) }, "all")))
|
|
98
|
+
: pinned
|
|
99
|
+
? null
|
|
100
|
+
: note;
|
|
101
|
+
return (_jsx(YStack, { "data-slot": "recents", flex: 1, minH: 0, children: _jsxs(ScrollView, { flex: 1, showsVerticalScrollIndicator: false, children: [error ? _jsx(Note, { children: error.message }) : null, _jsxs(YStack, { role: "list", "aria-labelledby": label, gap: 1, children: [pinned ? (_jsxs(YStack, { role: "listitem", gap: 1, children: [heading(PINNED), menus(_jsx(YStack, { role: "list", "aria-label": PINNED, gap: 1, children: pinned.rows.map(row) }))] })) : null, _jsxs(YStack, { role: "listitem", gap: 1, children: [heading('Recents', label, true), loose.length ? menus(rest) : rest] })] })] }) }));
|
|
90
102
|
}
|
package/dist/RightPane.d.ts
CHANGED
|
@@ -38,3 +38,26 @@ export declare function useKinds({ at, said, held, deck }: {
|
|
|
38
38
|
held: Held[];
|
|
39
39
|
deck: Deck;
|
|
40
40
|
}): Kind[];
|
|
41
|
+
/**
|
|
42
|
+
* The conversation's summary, from the outline mark in its header: what it
|
|
43
|
+
* produced (Outputs, led by Create file or site, which starts a Dev run) and
|
|
44
|
+
* what went into it (Sources, with the `+` that brings more). Each row opens in
|
|
45
|
+
* a tab beside the conversation. A section with nothing in it keeps its heading
|
|
46
|
+
* and one quiet line.
|
|
47
|
+
*/
|
|
48
|
+
export declare function Summary({ at, said, held, deck, onOpen, onCreate }: {
|
|
49
|
+
at: string;
|
|
50
|
+
said: ChatMessage[];
|
|
51
|
+
held: Held[];
|
|
52
|
+
deck: Deck;
|
|
53
|
+
onOpen: () => void;
|
|
54
|
+
onCreate: () => void;
|
|
55
|
+
}): import("react").JSX.Element;
|
|
56
|
+
/**
|
|
57
|
+
* The organization's sites, as an empty tab suggests them: `GET /v1/projects`,
|
|
58
|
+
* every row with a live address. Read once a conversation; nothing on a refusal.
|
|
59
|
+
*/
|
|
60
|
+
export declare function useSites(): {
|
|
61
|
+
title: string;
|
|
62
|
+
url: string;
|
|
63
|
+
}[];
|
package/dist/RightPane.js
CHANGED
|
@@ -16,26 +16,22 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
16
16
|
// The panel's tabs, their order and the open one are kept per conversation by
|
|
17
17
|
// @hanzo/build (`useDeck`, scope `chat:<channel>`); the frame owns the column
|
|
18
18
|
// it stands in (`Beside` in `Shell.tsx`).
|
|
19
|
-
import { useMemo, useState } from 'react';
|
|
20
|
-
import { FileCode, Image as ImageMark, Paperclip, Plus, Search } from 'lucide-react';
|
|
21
|
-
import { Box, DropdownMenu, DropdownMenuContent, DropdownMenuCheckboxItem, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, Text, YStack, } from '@hanzo/ui';
|
|
19
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
20
|
+
import { FileCode, Globe, Image as ImageMark, ListTodo, Paperclip, Plus, Search } from 'lucide-react';
|
|
21
|
+
import { Box, DropdownMenu, DropdownMenuContent, DropdownMenuCheckboxItem, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, Popover, PopoverContent, PopoverTrigger, Text, XStack, YStack, } from '@hanzo/ui';
|
|
22
22
|
import { useAi } from '@hanzo/ai/react';
|
|
23
23
|
import { artifacts, name as titleOf, PAGE } from '@hanzo/build';
|
|
24
24
|
import { Card, Quiet, Row } from './card.js';
|
|
25
25
|
import { said as told } from './lib/attach.js';
|
|
26
26
|
import { download, fileOf } from './lib/files.js';
|
|
27
|
-
import { org } from './lib/session.js';
|
|
27
|
+
import { org, scope } from './lib/session.js';
|
|
28
|
+
import { api } from './lib/api.js';
|
|
29
|
+
import { list } from './lib/list.js';
|
|
28
30
|
import { localOf, take } from './pane.js';
|
|
29
31
|
import { useProviders, useSkills } from './tools.js';
|
|
30
32
|
import { activate, startDevice } from './connect.js';
|
|
31
33
|
/** A conversation's panel before anyone has arranged it. */
|
|
32
|
-
export const SEED = {
|
|
33
|
-
tabs: [
|
|
34
|
-
{ id: 'artifacts', kind: 'artifacts', title: 'Artifacts' },
|
|
35
|
-
{ id: 'sources', kind: 'sources', title: 'Sources' },
|
|
36
|
-
],
|
|
37
|
-
at: 'artifacts',
|
|
38
|
-
};
|
|
34
|
+
export const SEED = { tabs: [], at: null };
|
|
39
35
|
/** The text of a turn, whichever shape the wire used. */
|
|
40
36
|
const words = (m) => typeof m.content === 'string'
|
|
41
37
|
? m.content
|
|
@@ -222,3 +218,59 @@ function Services() {
|
|
|
222
218
|
.catch((e) => setStep(String(e?.message ?? 'Could not start')));
|
|
223
219
|
}, children: _jsx(Word, { children: p.name }) }, p.id)))] }));
|
|
224
220
|
}
|
|
221
|
+
/** A row of the summary: its mark and its name, one press from a tab. */
|
|
222
|
+
function Line({ icon, children, onPress, quiet = false }) {
|
|
223
|
+
return (_jsxs(XStack, { render: onPress ? 'button' : undefined, role: onPress ? 'button' : undefined, onPress: onPress, items: "center", gap: "$2.5", px: "$2", minH: 30, width: "100%", rounded: "$3", cursor: onPress ? 'pointer' : undefined, hoverStyle: onPress ? { bg: '$hover' } : undefined, focusVisibleStyle: { outlineWidth: 2, outlineStyle: 'solid', outlineColor: '$outlineColor' }, children: [icon ? (_jsx(XStack, { width: 16, justify: "center", shrink: 0, opacity: 0.75, children: icon })) : null, _jsx(Text, { flex: 1, minW: 0, fontSize: "$2", color: quiet ? '$soft' : '$ink', numberOfLines: 1, children: children })] }));
|
|
224
|
+
}
|
|
225
|
+
/** A heading of the summary, with what it adds at its end. */
|
|
226
|
+
const Head = ({ children, action }) => (_jsxs(XStack, { items: "center", justify: "space-between", px: "$2", pt: "$1.5", pb: "$1", minH: 28, children: [_jsx(Text, { fontSize: "$2", color: "$soft", children: children }), action] }));
|
|
227
|
+
/**
|
|
228
|
+
* The conversation's summary, from the outline mark in its header: what it
|
|
229
|
+
* produced (Outputs, led by Create file or site, which starts a Dev run) and
|
|
230
|
+
* what went into it (Sources, with the `+` that brings more). Each row opens in
|
|
231
|
+
* a tab beside the conversation. A section with nothing in it keeps its heading
|
|
232
|
+
* and one quiet line.
|
|
233
|
+
*/
|
|
234
|
+
export function Summary({ at, said, held, deck, onOpen, onCreate }) {
|
|
235
|
+
const ai = useAi();
|
|
236
|
+
const [open, setOpen] = useState(false);
|
|
237
|
+
const made = useMemo(() => outputs(said), [said]);
|
|
238
|
+
const took = useMemo(() => sources(said, held), [said, held]);
|
|
239
|
+
const done = useMemo(() => lookups(said), [said]);
|
|
240
|
+
const show = (m) => {
|
|
241
|
+
setOpen(false);
|
|
242
|
+
void look(deck, m, ai);
|
|
243
|
+
onOpen();
|
|
244
|
+
};
|
|
245
|
+
return (_jsxs(Popover, { open: open, onOpenChange: setOpen, allowFlip: true, children: [_jsx(PopoverTrigger, { children: _jsx(XStack, { render: "button", "data-slot": "summary-toggle", "aria-label": "Summary", "aria-haspopup": "dialog", "aria-expanded": open, ...{ title: 'Summary' }, width: 32, height: 32, rounded: "$3", items: "center", justify: "center", cursor: "pointer", bg: open ? '$hover' : 'transparent', hoverStyle: { bg: '$hover' }, focusVisibleStyle: { outlineWidth: 2, outlineStyle: 'solid', outlineColor: '$outlineColor' }, children: _jsx(ListTodo, { size: 16, "aria-hidden": true }) }) }), _jsx(PopoverContent, { solid: true, align: "end", p: 6, width: 300, children: _jsxs(YStack, { "data-slot": "summary", role: "dialog", "aria-label": "Summary", gap: 1, self: "stretch", width: "100%", maxH: "min(70dvh, 520px)", overflowY: "auto", overflowX: "hidden", children: [_jsx(Head, { children: "Outputs" }), _jsx(Line, { quiet: true, onPress: () => {
|
|
246
|
+
setOpen(false);
|
|
247
|
+
onCreate();
|
|
248
|
+
}, children: "Create file or site" }), made.map((m) => {
|
|
249
|
+
const Glyph = GLYPH[m.kind];
|
|
250
|
+
return (_jsx(Line, { icon: _jsx(Glyph, { size: 15, "aria-hidden": true }), onPress: () => show(m), children: m.name }, m.id));
|
|
251
|
+
}), _jsx(XStack, { height: 1, bg: "$borderColor", my: "$1.5", mx: "$1" }), _jsx(Head, { action: _jsx(Add, { at: at }), children: "Sources" }), done.map((call) => (_jsx(Line, { icon: _jsx(Globe, { size: 15, "aria-hidden": true }), onPress: () => show({ id: call.id, name: call.name, kind: 'file', body: [call.asked, call.got ?? ''].filter(Boolean).join('\n\n'), mime: 'text/plain' }), children: call.name === 'web_search' || call.name === 'search' ? 'Web search' : call.name }, call.id))), took.map((m) => {
|
|
252
|
+
const Glyph = GLYPH[m.kind];
|
|
253
|
+
return (_jsx(Line, { icon: _jsx(Glyph, { size: 15, "aria-hidden": true }), onPress: () => show(m), children: m.name }, m.id));
|
|
254
|
+
}), !done.length && !took.length ? _jsx(Line, { quiet: true, children: "Nothing yet" }) : null] }) })] }));
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* The organization's sites, as an empty tab suggests them: `GET /v1/projects`,
|
|
258
|
+
* every row with a live address. Read once a conversation; nothing on a refusal.
|
|
259
|
+
*/
|
|
260
|
+
export function useSites() {
|
|
261
|
+
const [rows, setRows] = useState([]);
|
|
262
|
+
useEffect(() => {
|
|
263
|
+
const stop = new AbortController();
|
|
264
|
+
fetch(`${api()}/v1/projects`, { headers: scope(), signal: stop.signal })
|
|
265
|
+
.then((r) => (r.ok ? r.json() : null))
|
|
266
|
+
.then((body) => {
|
|
267
|
+
if (stop.signal.aborted)
|
|
268
|
+
return;
|
|
269
|
+
const all = list(Array.isArray(body) ? body : body?.data);
|
|
270
|
+
setRows(all.filter((p) => typeof p.liveUrl === 'string' && /^https:\/\//.test(p.liveUrl)).map((p) => ({ title: p.name || p.slug || 'Site', url: p.liveUrl })));
|
|
271
|
+
})
|
|
272
|
+
.catch(() => { });
|
|
273
|
+
return () => stop.abort();
|
|
274
|
+
}, []);
|
|
275
|
+
return rows;
|
|
276
|
+
}
|
package/dist/Share.js
CHANGED
|
@@ -10,7 +10,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
10
10
|
// them, and revokes a link, which ends it for all of them. Recipients read and
|
|
11
11
|
// do not reply: a conversation belongs to the member who started it.
|
|
12
12
|
import { useCallback, useEffect, useId, useRef, useState } from 'react';
|
|
13
|
-
import { Check, Link2,
|
|
13
|
+
import { Check, Link2, Upload } from 'lucide-react';
|
|
14
14
|
import { Box, Button, Dialog, DialogContent, DialogTitle, Text, XStack, YStack } from '@hanzo/ui';
|
|
15
15
|
import { hold, heldToken, link, Refused, shares } from './lib/share.js';
|
|
16
16
|
import { BAD } from './lib/mix.js';
|
|
@@ -29,7 +29,7 @@ export function Share({ thread }) {
|
|
|
29
29
|
setOpen(false);
|
|
30
30
|
button.current?.focus();
|
|
31
31
|
}, []);
|
|
32
|
-
return (_jsxs(Box, { position: "relative", children: [
|
|
32
|
+
return (_jsxs(Box, { position: "relative", children: [_jsxs(Box, { ref: button, render: "button", onClick: () => setOpen((o) => !o), "aria-haspopup": "dialog", "aria-expanded": open, "aria-label": "Share this conversation", "data-slot": "share", display: "flex", flexDirection: "row", gap: "$1.5", height: 32, px: "$2.5", items: "center", justify: "center", borderWidth: 0, bg: open ? '$hover' : 'transparent', rounded: "$3", cursor: "pointer", hoverStyle: { bg: '$hover' }, children: [_jsx(Upload, { size: 16, "aria-hidden": true }), _jsx(Text, { fontSize: "$3", color: "$ink", children: "Share" })] }), open ? _jsx(Panel, { thread: thread, onClose: close }) : null] }));
|
|
33
33
|
}
|
|
34
34
|
/** The header's panel: hung under its button, shut by Escape or a press outside, focus starting inside. */
|
|
35
35
|
function Panel({ thread, onClose }) {
|
package/dist/Shell.js
CHANGED
|
@@ -75,7 +75,6 @@ import { Profile } from "./Profile.js";
|
|
|
75
75
|
import { SupportPicker } from "./Support.js";
|
|
76
76
|
import { Find } from "./Find.js";
|
|
77
77
|
import { Account } from "./Account.js";
|
|
78
|
-
import { ModeMark } from "./ModeMark.js";
|
|
79
78
|
import { called } from "./team.js";
|
|
80
79
|
import { checkoutUrl } from './lib/pay.js';
|
|
81
80
|
/** Tabs: the terminals, at their own host. */
|
|
@@ -116,7 +115,7 @@ const CONTROL = 28;
|
|
|
116
115
|
import { getStoredPeople, getStoredAgents, getCustomThreads, createDirectChat, } from "./contacts-store.js";
|
|
117
116
|
import { bare } from './lib/bare.js';
|
|
118
117
|
import { BAD } from './lib/mix.js';
|
|
119
|
-
import { brand, site, where } from './where.js';
|
|
118
|
+
import { brand, site, where, wordmark } from './where.js';
|
|
120
119
|
/**
|
|
121
120
|
* The destinations, in rail order.
|
|
122
121
|
*
|
|
@@ -570,13 +569,13 @@ function Team({ filter = "", onPick, navigate, }) {
|
|
|
570
569
|
// this app climbs (see Home's `Waiting`). "Reading." never resolves for a
|
|
571
570
|
// reader nobody is going to answer, so the session is asked first.
|
|
572
571
|
if (!signedIn)
|
|
573
|
-
return _jsx(Note, { children: "Sign in to see your org's
|
|
572
|
+
return _jsx(Note, { children: "Sign in to see your org's channels." });
|
|
574
573
|
if (wrong)
|
|
575
|
-
return _jsx(Note, { children: say(wrong, "your
|
|
574
|
+
return _jsx(Note, { children: say(wrong, "your channels") });
|
|
576
575
|
if (rooms === null)
|
|
577
|
-
return _jsx(Note, { children: "Reading your
|
|
576
|
+
return _jsx(Note, { children: "Reading your channels." });
|
|
578
577
|
if (rooms.length === 0)
|
|
579
|
-
return _jsx(Note, { children: "No
|
|
578
|
+
return _jsx(Note, { children: "No channels yet." });
|
|
580
579
|
if (shown.length === 0)
|
|
581
580
|
return _jsxs(Note, { children: ["No room matches \u201C", filter, "\u201D."] });
|
|
582
581
|
return (_jsxs(_Fragment, { children: [favourites.length > 0 ? (_jsx(SidebarFolder, { name: "Favorites", defaultOpen: true, children: favourites.map((one) => (_jsx(SidebarItem, { active: teamKey(one) === room, icon: _jsx(Star, { size: 13, "aria-hidden": true }), onPress: () => {
|
|
@@ -1181,7 +1180,7 @@ function Frame({ mode, children, navigate, back, forward, }) {
|
|
|
1181
1180
|
// Drive names the company rather than the room.
|
|
1182
1181
|
id: place.id,
|
|
1183
1182
|
title: place.label,
|
|
1184
|
-
category: "
|
|
1183
|
+
category: "Apps",
|
|
1185
1184
|
icon: _jsx(place.icon, { size: 15, "aria-hidden": true }),
|
|
1186
1185
|
href: place.route,
|
|
1187
1186
|
})),
|
|
@@ -1210,7 +1209,7 @@ function Frame({ mode, children, navigate, back, forward, }) {
|
|
|
1210
1209
|
const list = (_jsxs(Sidebar, { ref: side.ref,
|
|
1211
1210
|
// A phone's sheet is never wider than 280, whatever the laptop kept.
|
|
1212
1211
|
// inline-style: `--span`, the width a drag rewrites on the element itself (span.ts); no prop sets a variable.
|
|
1213
|
-
style: side.style, $md: { width: "var(--span)" }, "$max-md": { width: "100%", maxW: 280 }, ...sheet(0), borderRightWidth: 1, borderColor: "var(--edge, var(--white-06))", position: "relative", children: [_jsx(Grip, { side: "left", span: side.span, floor: FLOOR, ceil: CEIL, reset: SIDEBAR_WIDTH, onSpan: side.move, onKeep: side.keep, onShut: () => setAsked(false), label: "Resize sidebar" }), _jsx(XStack, { "data-slot": "mode-
|
|
1212
|
+
style: side.style, $md: { width: "var(--span)" }, "$max-md": { width: "100%", maxW: 280 }, ...sheet(0), borderRightWidth: 1, borderColor: "var(--edge, var(--white-06))", position: "relative", children: [_jsx(Grip, { side: "left", span: side.span, floor: FLOOR, ceil: CEIL, reset: SIDEBAR_WIDTH, onSpan: side.move, onKeep: side.keep, onShut: () => setAsked(false), label: "Resize sidebar" }), _jsx(XStack, { "data-slot": "mode-name", items: "center", px: "$2", pt: "$2", minW: 0, children: _jsx(Text, { fontSize: "$4", fontWeight: "600", color: "$ink", numberOfLines: 1, children: wordmark(mode === "dev" ? "dev" : "chat") }) }), _jsxs(XStack, { "data-slot": "workspace-switcher", items: "center", gap: "$2", px: "$2", pt: "$1", pb: "$1", minW: 0, children: [_jsx(Text, { render: _jsx("a", { href: "/", onClick: (e) => {
|
|
1214
1213
|
if (!navigate || e.metaKey || e.ctrlKey || e.shiftKey || e.button !== 0)
|
|
1215
1214
|
return;
|
|
1216
1215
|
e.preventDefault();
|
package/dist/Talk.js
CHANGED
|
@@ -43,7 +43,7 @@ function when(ms) {
|
|
|
43
43
|
* an empty header. The sigil is how a client DRAWS a room and is not stored, so
|
|
44
44
|
* it is added here and only where there is a name to put it on.
|
|
45
45
|
*/
|
|
46
|
-
const titleOf = (room) => room.name ? `#${room.name}` : room.direct ? 'Direct message' : '
|
|
46
|
+
const titleOf = (room) => room.name ? `#${room.name}` : room.direct ? 'Direct message' : 'Channel';
|
|
47
47
|
/** Adds `@name ` to a draft, after a space where the draft needs one. */
|
|
48
48
|
const mention = (draft, name) => `${draft && !/\s$/.test(draft) ? `${draft} ` : draft}@${name} `;
|
|
49
49
|
export function Talk({ room }) {
|
|
@@ -70,14 +70,14 @@ export function Talk({ room }) {
|
|
|
70
70
|
}
|
|
71
71
|
catch (e) {
|
|
72
72
|
setDraft(text);
|
|
73
|
-
setRefused(say(e, 'this
|
|
73
|
+
setRefused(say(e, 'this channel', 'save'));
|
|
74
74
|
}
|
|
75
75
|
finally {
|
|
76
76
|
setPending((held) => held.filter((one) => one.key !== key));
|
|
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, overflowY: "auto", overflowX: "hidden", p: "$4", gap: "$3", children: [failed ? (_jsx(Text, { fontSize: "$3", color: "$soft", children: say(failed, 'this
|
|
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 channel') })) : 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/recent.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// The Recents list as data: each mode's rows, the views a menu offers, and the
|
|
2
2
|
// list a view makes of them. No React, so a spec reads it as it is.
|
|
3
|
-
export const PLAIN = { status: 'all', since: 'any', group: '
|
|
3
|
+
export const PLAIN = { status: 'all', since: 'any', group: 'none', sort: 'activity' };
|
|
4
4
|
const STATUSES = [
|
|
5
5
|
{ id: 'all', label: 'All' },
|
|
6
6
|
{ id: 'running', label: 'Running' },
|
package/dist/rooms.js
CHANGED
|
@@ -87,7 +87,7 @@ export function useTeamRooms() {
|
|
|
87
87
|
}, [client, user, now]);
|
|
88
88
|
const open = useCallback(async (room) => {
|
|
89
89
|
if (!client)
|
|
90
|
-
throw Object.assign(new Error('Sign in to open a
|
|
90
|
+
throw Object.assign(new Error('Sign in to open a channel'), { status: 401 });
|
|
91
91
|
const made = await client.http.json({
|
|
92
92
|
method: 'POST',
|
|
93
93
|
path: '/v1/team/rooms',
|
|
@@ -180,7 +180,7 @@ export function useRoomMessages(room, space) {
|
|
|
180
180
|
/** Says it and answers the stored row, or throws with the platform's status. */
|
|
181
181
|
const send = useCallback(async (text) => {
|
|
182
182
|
if (!client || !room || !space) {
|
|
183
|
-
throw Object.assign(new Error('This
|
|
183
|
+
throw Object.assign(new Error('This channel has no address to send to'), { status: 503 });
|
|
184
184
|
}
|
|
185
185
|
const row = await client.http.json({
|
|
186
186
|
method: 'POST',
|
package/dist/thread.d.ts
CHANGED
|
@@ -55,3 +55,11 @@ export declare function ThreadRow({ t, active, aside, listed, children, }: {
|
|
|
55
55
|
listed?: boolean;
|
|
56
56
|
children: ReactElement;
|
|
57
57
|
}): import("react").JSX.Element;
|
|
58
|
+
/**
|
|
59
|
+
* The ⋯ of the conversation a pane holds, in its header: the trigger of the
|
|
60
|
+
* menu its row opens, drawn inside a `ConversationMenu` of that one
|
|
61
|
+
* conversation, so the header and the row offer one menu.
|
|
62
|
+
*/
|
|
63
|
+
export declare function ThreadMore({ t }: {
|
|
64
|
+
t: Thread;
|
|
65
|
+
}): import("react").JSX.Element;
|
package/dist/thread.js
CHANGED
|
@@ -373,6 +373,21 @@ export function ThreadRow({ t, active = false, aside, listed = true, children, }
|
|
|
373
373
|
// Before gui's own press handler, which opens the menu and stops there.
|
|
374
374
|
onPointerDownCapture: aim, onKeyDownCapture: aim, onFocus: () => setFocused(true), onBlur: () => setFocused(false), children: _jsx(MoreHorizontal, { size: 15, "aria-hidden": true }) }) }) })] }));
|
|
375
375
|
}
|
|
376
|
+
/**
|
|
377
|
+
* The ⋯ of the conversation a pane holds, in its header: the trigger of the
|
|
378
|
+
* menu its row opens, drawn inside a `ConversationMenu` of that one
|
|
379
|
+
* conversation, so the header and the row offer one menu.
|
|
380
|
+
*/
|
|
381
|
+
export function ThreadMore({ t }) {
|
|
382
|
+
const menus = useContext(Rows);
|
|
383
|
+
if (!menus)
|
|
384
|
+
throw new Error('ThreadMore is drawn inside a ConversationMenu, which holds its menu.');
|
|
385
|
+
// gui types these handlers by React Native's event; on the web they carry the DOM's.
|
|
386
|
+
const aim = (e) => menus.aim(t, e.currentTarget);
|
|
387
|
+
return (_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(XStack, { render: "button", "aria-label": "Conversation options", ...{ title: 'More' }, "data-slot": "conversation-options", width: 32, height: 32, rounded: "$3", items: "center", justify: "center", cursor: "pointer", bg: menus.open === t.id ? '$hover' : 'transparent', hoverStyle: { bg: '$hover' }, focusVisibleStyle: { outlineWidth: 2, outlineStyle: 'solid', outlineColor: '$outlineColor' },
|
|
388
|
+
// Before gui's own press handler, which opens the menu and stops there.
|
|
389
|
+
onPointerDownCapture: aim, onKeyDownCapture: aim, children: _jsx(MoreHorizontal, { size: 16, "aria-hidden": true }) }) }));
|
|
390
|
+
}
|
|
376
391
|
/** A conversation's title, written again: one line, never empty. */
|
|
377
392
|
function Rename({ t, open, onOpenChange, onCloseAutoFocus, save, }) {
|
|
378
393
|
const [words, setWords] = useState('');
|
package/dist/where.d.ts
CHANGED
|
@@ -67,5 +67,7 @@ export declare function configure(given: Partial<Addresses>): void;
|
|
|
67
67
|
export declare const where: () => Addresses;
|
|
68
68
|
/** The product's name, as the host states it. Read at call time, like every address. */
|
|
69
69
|
export declare const brand: () => string;
|
|
70
|
+
/** The product's name in each mode of the app: the host's brand in Chat (Hanzo AI, Hanzo Team), Hanzo Dev in Dev. */
|
|
71
|
+
export declare const wordmark: (mode: 'chat' | 'dev') => string;
|
|
70
72
|
/** A page of the app's own, from wherever the rooms are mounted. */
|
|
71
73
|
export declare const site: (path: string) => string;
|
package/dist/where.js
CHANGED
|
@@ -11,5 +11,7 @@ export function configure(given) {
|
|
|
11
11
|
export const where = () => addresses;
|
|
12
12
|
/** The product's name, as the host states it. Read at call time, like every address. */
|
|
13
13
|
export const brand = () => addresses.brand ?? 'Hanzo AI';
|
|
14
|
+
/** The product's name in each mode of the app: the host's brand in Chat (Hanzo AI, Hanzo Team), Hanzo Dev in Dev. */
|
|
15
|
+
export const wordmark = (mode) => (mode === 'dev' ? 'Hanzo Dev' : brand());
|
|
14
16
|
/** A page of the app's own, from wherever the rooms are mounted. */
|
|
15
17
|
export const site = (path) => `${addresses.site}${path}`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzo/rooms",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.59",
|
|
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",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"devDependencies": {
|
|
67
67
|
"@hanzo/ai": "0.6.23",
|
|
68
68
|
"@hanzo/appearance": "0.2.8",
|
|
69
|
-
"@hanzo/build": "0.2.
|
|
69
|
+
"@hanzo/build": "0.2.55",
|
|
70
70
|
"@hanzo/composer": "0.2.0",
|
|
71
71
|
"@hanzo/design": "0.5.34",
|
|
72
72
|
"@hanzo/event": "0.3.56",
|
package/dist/ModeMark.d.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
/** The product's name in each mode: the host's brand in Chat (Hanzo AI, Hanzo Team), Hanzo Dev in Dev. */
|
|
2
|
-
export declare const wordmark: (mode: 'chat' | 'dev') => string;
|
|
3
|
-
export declare function ModeMark({ mode, onChat, onDev, stacked, }: {
|
|
4
|
-
mode: 'chat' | 'dev';
|
|
5
|
-
onChat: () => void;
|
|
6
|
-
onDev: () => void;
|
|
7
|
-
stacked?: boolean;
|
|
8
|
-
}): import("react").JSX.Element;
|
package/dist/ModeMark.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { MessageSquare, Code2 } from 'lucide-react';
|
|
4
|
-
import { XStack, YStack, Text, Tooltip, TooltipContent, TooltipTrigger } from '@hanzo/ui';
|
|
5
|
-
import { brand } from './where.js';
|
|
6
|
-
/** The product's name in each mode: the host's brand in Chat (Hanzo AI, Hanzo Team), Hanzo Dev in Dev. */
|
|
7
|
-
export const wordmark = (mode) => (mode === 'dev' ? 'Hanzo Dev' : brand());
|
|
8
|
-
/** One side of the switch: an icon button named for its mode, with the name as its tooltip. */
|
|
9
|
-
function Side({ name, on, onPress, children }) {
|
|
10
|
-
return (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { children: _jsx(XStack, { render: "button", "aria-label": name, "aria-pressed": on, onPress: onPress, width: 28, height: 24, rounded: 999, items: "center", justify: "center", bg: on ? '$hover' : 'transparent', hoverStyle: { bg: '$hover' }, cursor: "pointer", children: children }) }), _jsx(TooltipContent, { children: _jsx(Text, { fontSize: "$2", children: name }) })] }));
|
|
11
|
-
}
|
|
12
|
-
export function ModeMark({ mode, onChat, onDev, stacked = false, }) {
|
|
13
|
-
const dev = mode === 'dev';
|
|
14
|
-
const Group = stacked ? YStack : XStack;
|
|
15
|
-
const toggle = (_jsxs(Group, { role: "group", "aria-label": "Switch Hanzo", items: "center", gap: 2, p: 2, rounded: 999, bg: "$edge", shrink: 0, children: [_jsx(Side, { name: "Chat", on: !dev, onPress: onChat, children: _jsx(MessageSquare, { size: 14, "aria-hidden": true }) }), _jsx(Side, { name: "Dev", on: dev, onPress: onDev, children: _jsx(Code2, { size: 14, "aria-hidden": true }) })] }));
|
|
16
|
-
if (stacked)
|
|
17
|
-
return toggle;
|
|
18
|
-
return (_jsxs(XStack, { flex: 1, items: "center", justify: "space-between", gap: "$2", minW: 0, children: [_jsx(Text, { fontSize: "$4", fontWeight: "600", color: "$ink", numberOfLines: 1, children: wordmark(mode) }), toggle] }));
|
|
19
|
-
}
|