@hanzo/rooms 0.1.72 → 0.1.74
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/assets/personas/ada.md +187 -0
- package/assets/personas/adi_shamir.md +155 -0
- package/assets/personas/al_khwarizmi.md +168 -0
- package/assets/personas/al_kindi.md +172 -0
- package/assets/personas/alan_kay.md +154 -0
- package/assets/personas/analyst.md +138 -0
- package/assets/personas/andrew_yao.md +155 -0
- package/assets/personas/auguste_kerckhoffs.md +168 -0
- package/assets/personas/barbara_liskov.md +154 -0
- package/assets/personas/butler_lampson.md +156 -0
- package/assets/personas/charles_babbage.md +175 -0
- package/assets/personas/creative.md +141 -0
- package/assets/personas/curry.md +169 -0
- package/assets/personas/cynthia_dwork.md +158 -0
- package/assets/personas/david_chaum.md +157 -0
- package/assets/personas/david_parnas.md +153 -0
- package/assets/personas/dennis_ritchie.md +153 -0
- package/assets/personas/dijkstra.md +187 -0
- package/assets/personas/donald_knuth.md +156 -0
- package/assets/personas/douglas_engelbart.md +175 -0
- package/assets/personas/edgar_codd.md +151 -0
- package/assets/personas/engineer.md +140 -0
- package/assets/personas/feynman.md +178 -0
- package/assets/personas/finney.md +187 -0
- package/assets/personas/fred_brooks.md +152 -0
- package/assets/personas/geoffrey_hinton.md +194 -0
- package/assets/personas/herbert_simon.md +190 -0
- package/assets/personas/hickey.md +169 -0
- package/assets/personas/hopper.md +191 -0
- package/assets/personas/hubert_dreyfus.md +184 -0
- package/assets/personas/humberto_maturana.md +156 -0
- package/assets/personas/index.json +1 -0
- package/assets/personas/jcr_licklider.md +178 -0
- package/assets/personas/jobs.md +177 -0
- package/assets/personas/joe_armstrong.md +155 -0
- package/assets/personas/john_backus.md +179 -0
- package/assets/personas/john_mccarthy.md +177 -0
- package/assets/personas/jon_postel.md +149 -0
- package/assets/personas/joseph_weizenbaum.md +185 -0
- package/assets/personas/judea_pearl.md +192 -0
- package/assets/personas/karen_sparck_jones.md +154 -0
- package/assets/personas/konrad_zuse.md +175 -0
- package/assets/personas/kunihiko_fukushima.md +152 -0
- package/assets/personas/leslie_lamport.md +156 -0
- package/assets/personas/markov.md +171 -0
- package/assets/personas/mentor.md +141 -0
- package/assets/personas/nancy_lynch.md +154 -0
- package/assets/personas/niklaus_wirth.md +154 -0
- package/assets/personas/norbert_wiener.md +179 -0
- package/assets/personas/north-star.md +138 -0
- package/assets/personas/peter_naur.md +151 -0
- package/assets/personas/pike.md +165 -0
- package/assets/personas/radia_perlman.md +156 -0
- package/assets/personas/raj_reddy.md +154 -0
- package/assets/personas/ralph_merkle.md +152 -0
- package/assets/personas/richard_sutton.md +156 -0
- package/assets/personas/robin_milner.md +149 -0
- package/assets/personas/shafi_goldwasser.md +156 -0
- package/assets/personas/shannon.md +184 -0
- package/assets/personas/stafford_beer.md +157 -0
- package/assets/personas/taher_elgamal.md +180 -0
- package/assets/personas/tesla.md +181 -0
- package/assets/personas/tony_hoare.md +153 -0
- package/assets/personas/torvalds.md +188 -0
- package/assets/personas/tpope.md +163 -0
- package/assets/personas/turing.md +188 -0
- package/assets/personas/whitfield_diffie.md +159 -0
- package/assets/personas/zeekay.md +151 -0
- package/dist/App.js +26 -22
- package/dist/Chat.js +51 -38
- package/dist/Column.d.ts +3 -3
- package/dist/Column.js +5 -27
- package/dist/Me.d.ts +8 -3
- package/dist/Me.js +79 -108
- package/dist/Palette.d.ts +18 -0
- package/dist/Palette.js +131 -0
- package/dist/Rail.js +3 -15
- package/dist/Recents.d.ts +1 -2
- package/dist/Recents.js +9 -9
- package/dist/Shell.d.ts +7 -0
- package/dist/Shell.js +65 -46
- package/dist/Top.d.ts +17 -0
- package/dist/Top.js +58 -0
- package/dist/agents.d.ts +64 -0
- package/dist/agents.js +62 -0
- package/dist/crew.d.ts +26 -5
- package/dist/crew.js +208 -83
- package/dist/help.d.ts +43 -0
- package/dist/help.js +58 -0
- package/dist/lib/session.d.ts +5 -10
- package/dist/lib/session.js +6 -10
- package/dist/lib/web.d.ts +6 -0
- package/dist/lib/web.js +39 -7
- package/dist/recent.d.ts +2 -2
- package/dist/recent.js +3 -5
- package/dist/seek.d.ts +37 -0
- package/dist/seek.js +55 -0
- package/dist/team.js +3 -11
- package/dist/thread.js +7 -2
- package/dist/view.d.ts +44 -0
- package/dist/view.js +98 -0
- package/package.json +7 -7
- package/dist/Account.d.ts +0 -12
- package/dist/Account.js +0 -65
- package/dist/Find.d.ts +0 -7
- package/dist/Find.js +0 -82
- package/dist/Profile.d.ts +0 -4
- package/dist/Profile.js +0 -67
- package/dist/Standing.d.ts +0 -4
- package/dist/Standing.js +0 -16
package/dist/Recents.js
CHANGED
|
@@ -35,20 +35,20 @@ function items(mode, view, set) {
|
|
|
35
35
|
return parts.flatMap((p, i) => (i ? [{ type: 'separator', key: `sep-${i}` }, ...p] : p));
|
|
36
36
|
}
|
|
37
37
|
const Note = ({ children }) => (_jsx(SizableText, { size: "$1", color: "$soft", px: "$2", py: "$1.5", children: children }));
|
|
38
|
-
export function Recents({ host, mode, thread,
|
|
38
|
+
export function Recents({ host, mode, thread, onOpen, onNew, }) {
|
|
39
39
|
const [kept, keep] = useKept(`hanzo.recents.${mode}`, PLAIN);
|
|
40
40
|
const view = viewOf(mode, kept);
|
|
41
|
-
return mode === 'chat' ? (_jsx(Chats, { thread: thread,
|
|
41
|
+
return mode === 'chat' ? (_jsx(Chats, { thread: thread, view: view, keep: keep, onOpen: onOpen, onNew: onNew })) : (_jsx(Runs, { host: host, view: view, keep: keep, onOpen: onOpen }));
|
|
42
42
|
}
|
|
43
43
|
/** Chat's Recents: the conversation store alone, the archived ones when the view asks for them. */
|
|
44
|
-
function Chats({ thread,
|
|
44
|
+
function Chats({ thread, view, keep, onOpen, onNew, }) {
|
|
45
45
|
const store = useThreadList(view.status === 'archived', thread);
|
|
46
46
|
const rows = useMemo(() => chats(store.threads), [store.threads]);
|
|
47
47
|
const byId = useMemo(() => new Map(store.threads.map((t) => [t.id, t])), [store.threads]);
|
|
48
|
-
return (_jsx(List, { mode: "chat", rows: rows,
|
|
48
|
+
return (_jsx(List, { mode: "chat", rows: rows, view: view, keep: keep, open: (r) => r.id === thread, reading: store.loading && !store.threads.length, error: store.error, onOpen: onOpen, threads: byId, held: thread, onNew: onNew }));
|
|
49
49
|
}
|
|
50
50
|
/** Dev's Recents: the org's coding runs alone. */
|
|
51
|
-
function Runs({ host,
|
|
51
|
+
function Runs({ host, view, keep, onOpen }) {
|
|
52
52
|
const store = useSessions(host);
|
|
53
53
|
const r = route(host.path);
|
|
54
54
|
const run = r.kind === 'run' ? r.id : null;
|
|
@@ -62,11 +62,11 @@ function Runs({ host, words, view, keep, onOpen }) {
|
|
|
62
62
|
reload();
|
|
63
63
|
}, [run, store.loading, store.value, reload]);
|
|
64
64
|
const rows = useMemo(() => runs(store.value), [store.value]);
|
|
65
|
-
return (_jsx(List, { mode: "dev", rows: rows,
|
|
65
|
+
return (_jsx(List, { mode: "dev", rows: rows, view: view, keep: keep, open: (row) => row.id === run, reading: store.loading && !store.value.length, error: store.error, onOpen: onOpen }));
|
|
66
66
|
}
|
|
67
|
-
function List({ mode, rows,
|
|
67
|
+
function List({ mode, rows, view, keep, open, reading, error, onOpen, threads, held = null, onNew, }) {
|
|
68
68
|
const label = useId();
|
|
69
|
-
const groups = arrange(rows, view
|
|
69
|
+
const groups = arrange(rows, view);
|
|
70
70
|
const narrowed = view.status !== PLAIN.status || view.since !== PLAIN.since;
|
|
71
71
|
const running = mode === 'dev' ? rows.filter((r) => r.status === 'running').length : 0;
|
|
72
72
|
// A row is its title alone, as the reference draws it: a conversation's ⋯
|
|
@@ -89,7 +89,7 @@ function List({ mode, rows, words, view, keep, open, reading, error, onOpen, thr
|
|
|
89
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
90
|
const loose = groups.filter((g) => g.title !== PINNED);
|
|
91
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 :
|
|
92
|
+
const note = reading ? (_jsx(Note, { children: mode === 'chat' ? 'Reading your chats…' : 'Reading your runs…' })) : error ? null : 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
93
|
// Each list of conversations holds its own menu (thread.tsx): the view menu
|
|
94
94
|
// in the Recents heading between them is a menu of its own, never inside one.
|
|
95
95
|
const menus = (rows) => threads ? (_jsx(ConversationMenu, { threads: threads, active: held, onGone: onNew, children: _jsx(YStack, { children: rows }) })) : (rows);
|
package/dist/Shell.d.ts
CHANGED
|
@@ -48,6 +48,13 @@ export declare function Beside({ children }: {
|
|
|
48
48
|
* the app at `/` lay a room out with this, so a room's column is one mechanism.
|
|
49
49
|
* `swap` stands in the pane in the room's place (a chosen Settings row).
|
|
50
50
|
*
|
|
51
|
+
* The column is @hanzo/build's (`useSplit`), the one Dev's runs stand their
|
|
52
|
+
* panel in: its edge, the width the person drags (kept in their preferences
|
|
53
|
+
* where a `PrefsProvider` is above, as in the app), the pane hidden while the
|
|
54
|
+
* column is pulled over it until Show chat, and full screen from the panel's
|
|
55
|
+
* strip. The pane is hidden, never unmounted, so the conversation keeps its
|
|
56
|
+
* state.
|
|
57
|
+
*
|
|
51
58
|
* `framed` is the app at `/`: both are panes floating on the frame's ground
|
|
52
59
|
* (app/_web.tsx), filled with `--pane-fill` and cut by `pane()`, and the edge
|
|
53
60
|
* that sizes the column sits in the gutter between them. In a room they lie on
|
package/dist/Shell.js
CHANGED
|
@@ -13,13 +13,13 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
13
13
|
// holds arrangement and words and owns no pixels of its own. That is the whole
|
|
14
14
|
// architecture: a stranger with `@hanzo/ui`, `@hanzo/ai` and `@hanzo/iam` can
|
|
15
15
|
// assemble this room, because nothing in it is private to hanzo.ai.
|
|
16
|
-
import { createContext, lazy, Suspense, useContext, useEffect, useMemo, useRef, useState, } from "react";
|
|
16
|
+
import { createContext, lazy, Suspense, useCallback, useContext, useEffect, useMemo, useRef, useState, } from "react";
|
|
17
17
|
import { createPortal } from "react-dom";
|
|
18
|
-
import { MessageSquare, Code2, Inbox, Bot, ListChecks, HardDrive, Settings2, UserPlus,
|
|
18
|
+
import { MessageSquare, Code2, Inbox, Bot, ListChecks, HardDrive, Settings2, UserPlus, PanelLeftClose, Search, Hash, AtSign, Lock, Workflow, Plus, ArrowLeft, ArrowRight, Send, Minus, Blocks, Star, FolderOpen, Activity, BookOpen, SquareTerminal, Share2, PanelLeft, PanelRight, Users, Globe, Cpu, Boxes, Wallet, Video, Calendar, LayoutGrid, ArrowUpLeft, } from "lucide-react";
|
|
19
19
|
import { AiProvider, useAgents, useInbox, useModels, usePeople, } from "@hanzo/ai/react";
|
|
20
20
|
import { PINNED } from "./recent.js";
|
|
21
21
|
import { ConversationMenu, ThreadRow, useThreadList } from "./thread.js";
|
|
22
|
-
import { Box, XStack, YStack, Text } from "@hanzo/ui";
|
|
22
|
+
import { Box, Shortcut, XStack, YStack, Text } from "@hanzo/ui";
|
|
23
23
|
// THE PAPER LADDER. `sheet(n)` is a fill AND the light on it, published as a
|
|
24
24
|
// pair by @hanzo/design, so a surface asks for a HEIGHT instead of composing a
|
|
25
25
|
// shadow — which is the only way three panels on one screen end up lit from one
|
|
@@ -27,14 +27,17 @@ import { Box, XStack, YStack, Text } from "@hanzo/ui";
|
|
|
27
27
|
import { scrim, sheet } from "@hanzo/ui/glass";
|
|
28
28
|
import { Sidebar, SidebarIconButton, SidebarNewChat, SidebarScroll, SidebarSection, SidebarItem, SidebarFolder, } from "@hanzo/ui/chat";
|
|
29
29
|
import { OrgProjectSwitcher, useIam, useOrganizations, } from "@hanzo/iam/react";
|
|
30
|
-
import {
|
|
30
|
+
import { SideProvider, useSplit } from "@hanzo/build";
|
|
31
|
+
import { Grip } from "@hanzo/ui";
|
|
31
32
|
import { HanzoMark, } from "@hanzogui/shell";
|
|
32
33
|
import { useHydrated } from "./lib/hydrated.js";
|
|
33
34
|
import { useHost } from "./lib/hostname.js";
|
|
34
35
|
import { useAi } from "./lib/ai.js";
|
|
35
36
|
import { useTiers, useHidden } from "./lib/tiers.js";
|
|
36
37
|
import { useAccount } from "./lib/account.js";
|
|
37
|
-
import { hasSession,
|
|
38
|
+
import { bearer, hasSession, org as scoped, pick as choose } from "./lib/session.js";
|
|
39
|
+
import { api } from "./lib/api.js";
|
|
40
|
+
import { useSignOut } from "./lib/signout.js";
|
|
38
41
|
import { enter, LOGIN } from "./lib/destination.js";
|
|
39
42
|
import { address, sharedWithMe } from "./lib/share.js";
|
|
40
43
|
import { list } from "./lib/list.js";
|
|
@@ -60,6 +63,7 @@ const FIELD = {
|
|
|
60
63
|
* custom properties. */
|
|
61
64
|
const GUTTER_START = "calc(-1 * var(--pane-gap, 0px))";
|
|
62
65
|
const GUTTER = "max(8px, var(--pane-gap, 8px))";
|
|
66
|
+
const TOUCH_START = "calc(-22px - var(--pane-gap, 0px) / 2)";
|
|
63
67
|
const HOME_INDICATOR = "env(safe-area-inset-bottom)";
|
|
64
68
|
import { invite } from "./lib/home.js";
|
|
65
69
|
import { reach } from "./lib/reach.js";
|
|
@@ -71,10 +75,9 @@ import { useTeamRooms, teamKey, roomHref } from "./rooms.js";
|
|
|
71
75
|
import { apex, app, dev, ENTRY, room as moves, WORKSPACE } from "./lib/host.js";
|
|
72
76
|
import { netKey } from "./conversations.js";
|
|
73
77
|
import { FIRST, SettingsList, } from "./Settings.js";
|
|
74
|
-
import {
|
|
75
|
-
import {
|
|
76
|
-
import {
|
|
77
|
-
import { Account } from "./Account.js";
|
|
78
|
+
import { Me } from "./Me.js";
|
|
79
|
+
import { Palette } from "./Palette.js";
|
|
80
|
+
import { summons } from "./seek.js";
|
|
78
81
|
import { called } from "./team.js";
|
|
79
82
|
import { checkoutUrl } from './lib/pay.js';
|
|
80
83
|
/** Tabs: the terminals, at their own host. */
|
|
@@ -831,14 +834,11 @@ function Bots({ show, owner, filter = "", onPick, navigate, }) {
|
|
|
831
834
|
}
|
|
832
835
|
/** A phone's tab bar: the rail along the foot, above the home indicator. */
|
|
833
836
|
const TABS_HEIGHT = "calc(56px + env(safe-area-inset-bottom))";
|
|
834
|
-
/**
|
|
837
|
+
/** The sidebar's width, px: the floor a drag holds, the ceiling, and the default a double-click returns to. */
|
|
835
838
|
const FLOOR = 200;
|
|
836
839
|
const CEIL = 480;
|
|
837
840
|
/** Per-reader, per-browser. A width that resets is a width you set every load. */
|
|
838
841
|
const SPAN = "hanzo.sidebar.span";
|
|
839
|
-
const BESIDE = "hanzo.aside.span";
|
|
840
|
-
/** The column beside a room: its default width, px. */
|
|
841
|
-
const BESIDE_WIDTH = 380;
|
|
842
842
|
/** The rooms' sidebar: its default width, px. */
|
|
843
843
|
const SIDEBAR_WIDTH = 244;
|
|
844
844
|
/**
|
|
@@ -875,6 +875,13 @@ export function Beside({ children }) {
|
|
|
875
875
|
* the app at `/` lay a room out with this, so a room's column is one mechanism.
|
|
876
876
|
* `swap` stands in the pane in the room's place (a chosen Settings row).
|
|
877
877
|
*
|
|
878
|
+
* The column is @hanzo/build's (`useSplit`), the one Dev's runs stand their
|
|
879
|
+
* panel in: its edge, the width the person drags (kept in their preferences
|
|
880
|
+
* where a `PrefsProvider` is above, as in the app), the pane hidden while the
|
|
881
|
+
* column is pulled over it until Show chat, and full screen from the panel's
|
|
882
|
+
* strip. The pane is hidden, never unmounted, so the conversation keeps its
|
|
883
|
+
* state.
|
|
884
|
+
*
|
|
878
885
|
* `framed` is the app at `/`: both are panes floating on the frame's ground
|
|
879
886
|
* (app/_web.tsx), filled with `--pane-fill` and cut by `pane()`, and the edge
|
|
880
887
|
* that sizes the column sits in the gutter between them. In a room they lie on
|
|
@@ -884,7 +891,7 @@ export function Beside({ children }) {
|
|
|
884
891
|
*/
|
|
885
892
|
export function Paned({ children, swap, framed = false, bare = false, }) {
|
|
886
893
|
const { aside, showAside } = useOpen();
|
|
887
|
-
const
|
|
894
|
+
const split = useSplit(Boolean(aside), "chat", () => showAside(null));
|
|
888
895
|
// State and not a ref: a ref set during commit does not re-render, so the
|
|
889
896
|
// portal would have nowhere to go on the pass that created its target.
|
|
890
897
|
const [slot, setSlot] = useState(null);
|
|
@@ -896,17 +903,19 @@ export function Paned({ children, swap, framed = false, bare = false, }) {
|
|
|
896
903
|
? bare
|
|
897
904
|
? null
|
|
898
905
|
: { ...pane(), overflow: "hidden" }
|
|
899
|
-
: { ...sheet(0), borderLeftWidth: 1, borderColor: "var(--edge, var(--white-06))" }),
|
|
906
|
+
: { ...sheet(0), borderLeftWidth: 1, borderColor: "var(--edge, var(--white-06))" }), ref: split.main,
|
|
907
|
+
// inline-style: the pane hidden while the column has its room, written by a drag too (useSplit).
|
|
908
|
+
style: split.mainStyle, flex: 1, minW: 0, minH: 0, children: swap ?? (_jsx(Framed.Provider, { value: framed, children: _jsx(SideProvider, { value: split.side, children: _jsx(Slot.Provider, { value: slot, children: children }) }) })) }), aside ? (_jsxs(YStack, { render: "aside", "data-slot": "side", gridArea: "side", ...pane(framed ? undefined : sheet(1).backgroundColor),
|
|
900
909
|
// In the frame the gutter stands between the pane and this column.
|
|
901
|
-
ml: framed ? "var(--pane-gap)" : undefined, ref:
|
|
902
|
-
// inline-style:
|
|
903
|
-
style:
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
// beside an empty strip of the same size.
|
|
907
|
-
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",
|
|
910
|
+
ml: framed ? "var(--pane-gap)" : undefined, ref: split.aside,
|
|
911
|
+
// inline-style: the width a drag rewrites on the element itself, and full screen (useSplit).
|
|
912
|
+
style: split.asideStyle, position: "relative", shrink: 0, minH: 0, display: "none", $lg: { display: "flex" }, children: [split.grip ? (_jsx(Grip, { ...split.grip, label: "Resize side panel", bar: "inline",
|
|
913
|
+
// The handle halfway down the edge, where a hand looks for it.
|
|
914
|
+
justify: "center",
|
|
908
915
|
// In the gutter the frame leaves between the panes; a room has none.
|
|
909
|
-
l: GUTTER_START, width: GUTTER
|
|
916
|
+
l: GUTTER_START, width: GUTTER,
|
|
917
|
+
// A finger's band, centred on the gutter.
|
|
918
|
+
$touchable: { l: TOUCH_START, width: 44 } })) : null, _jsx(YStack, { flex: 1, minH: 0, overflowY: "auto", overflowX: "hidden", rounded: "var(--pane-round)", ref: (el) => setSlot(el instanceof HTMLElement ? el : null) })] })) : null] }));
|
|
910
919
|
}
|
|
911
920
|
/**
|
|
912
921
|
* THE RAIL: where you can go, as marks with their names under them. It is the
|
|
@@ -992,6 +1001,7 @@ function Frame({ mode, children, navigate, back, forward, }) {
|
|
|
992
1001
|
// hook it no longer runs.
|
|
993
1002
|
const hydrated = useHydrated();
|
|
994
1003
|
const [finding, setFinding] = useState(false);
|
|
1004
|
+
const found = useCallback(() => setFinding(false), []);
|
|
995
1005
|
/**
|
|
996
1006
|
* WHICH SETTINGS ROW IS OPEN, and null for none. ONE value, not two.
|
|
997
1007
|
*
|
|
@@ -1001,12 +1011,6 @@ function Frame({ mode, children, navigate, back, forward, }) {
|
|
|
1001
1011
|
* act — which is why there is no second `panel` state beside this one.
|
|
1002
1012
|
*/
|
|
1003
1013
|
const { settings: settingsPane, showSettings } = useOpen();
|
|
1004
|
-
// The profile is a COLUMN, not a modal, so it is not one of the panels above:
|
|
1005
|
-
// those lift off the window and dim it, this one stands in the row beside the
|
|
1006
|
-
// room. It is the slot the thread view will share.
|
|
1007
|
-
const [profiling, setProfiling] = useState(false);
|
|
1008
|
-
// Every organization, for a SuperAdmin to step into (components/workspace/Support.tsx).
|
|
1009
|
-
const [supporting, setSupporting] = useState(false);
|
|
1010
1014
|
/**
|
|
1011
1015
|
* The sidebar is open by default on desktop and stays open.
|
|
1012
1016
|
*
|
|
@@ -1073,7 +1077,7 @@ function Frame({ mode, children, navigate, back, forward, }) {
|
|
|
1073
1077
|
const onPick = typeof window !== "undefined" && window.innerWidth < 1024 ? dismiss : undefined;
|
|
1074
1078
|
useEffect(() => {
|
|
1075
1079
|
const shortcut = (event) => {
|
|
1076
|
-
if ((event
|
|
1080
|
+
if (summons(event)) {
|
|
1077
1081
|
event.preventDefault();
|
|
1078
1082
|
setFinding(true);
|
|
1079
1083
|
}
|
|
@@ -1164,9 +1168,10 @@ function Frame({ mode, children, navigate, back, forward, }) {
|
|
|
1164
1168
|
/**
|
|
1165
1169
|
* WHERE THE PALETTE CAN GO WITHOUT ASKING ANYONE.
|
|
1166
1170
|
*
|
|
1167
|
-
* The rooms this identity may open, and the surfaces above
|
|
1168
|
-
*
|
|
1169
|
-
*
|
|
1171
|
+
* The rooms this identity may open, and the surfaces above, as the palette's
|
|
1172
|
+
* Actions; this Settings and Customize stand in for the palette's own. The
|
|
1173
|
+
* lists that have to be read — conversations, projects, agents, skills — are
|
|
1174
|
+
* the palette's, which is why they are not here.
|
|
1170
1175
|
*/
|
|
1171
1176
|
const onApex = apex(useHost());
|
|
1172
1177
|
const places = useMemo(() => [CHAT, ...PLACES].filter((place) => shows(place, tiers, hidden, onApex)), [tiers, hidden, onApex]);
|
|
@@ -1181,19 +1186,17 @@ function Frame({ mode, children, navigate, back, forward, }) {
|
|
|
1181
1186
|
// Home's is the brand — one row reading "Hanzo" among Chat, Dev and
|
|
1182
1187
|
// Drive names the company rather than the room.
|
|
1183
1188
|
id: place.id,
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
href: place.route,
|
|
1189
|
+
label: place.label,
|
|
1190
|
+
icon: _jsx(place.icon, { size: 16, "aria-hidden": true }),
|
|
1191
|
+
run: () => navigate?.(place.route),
|
|
1188
1192
|
})),
|
|
1189
1193
|
...tools.map((tool) => ({
|
|
1190
1194
|
id: tool.id,
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
action: tool.go,
|
|
1195
|
+
label: tool.label,
|
|
1196
|
+
icon: _jsx(tool.icon, { size: 16, "aria-hidden": true }),
|
|
1197
|
+
run: tool.go,
|
|
1195
1198
|
})),
|
|
1196
|
-
], [tiers, hidden, onApex, tools]);
|
|
1199
|
+
], [tiers, hidden, onApex, tools, navigate]);
|
|
1197
1200
|
// The org IS the workspace. Naming it twice is how the bar and the rail come
|
|
1198
1201
|
// to disagree about which one you are standing in.
|
|
1199
1202
|
const workspace = currentOrg?.displayName || currentOrg?.name || "Hanzo";
|
|
@@ -1208,6 +1211,20 @@ function Frame({ mode, children, navigate, back, forward, }) {
|
|
|
1208
1211
|
const settled = hydrated && !isLoading;
|
|
1209
1212
|
const { user: account } = useAccount();
|
|
1210
1213
|
const signedIn = isAuthenticated || Boolean(user) || Boolean(account) || hasSession();
|
|
1214
|
+
const logout = useSignOut();
|
|
1215
|
+
const face = account?.avatar ?? user?.avatar;
|
|
1216
|
+
// The account menu's reading of this host: who, in which organization, and how they leave.
|
|
1217
|
+
const who = {
|
|
1218
|
+
api: api(),
|
|
1219
|
+
token: bearer,
|
|
1220
|
+
org: scoped(),
|
|
1221
|
+
person: {
|
|
1222
|
+
name: account?.name || user?.displayName || user?.name || user?.email || "",
|
|
1223
|
+
email: user?.email || "",
|
|
1224
|
+
avatar: typeof face === "string" && face.startsWith("https://") ? face : "",
|
|
1225
|
+
},
|
|
1226
|
+
signOut: () => void logout(),
|
|
1227
|
+
};
|
|
1211
1228
|
const list = (_jsxs(Sidebar, { ref: side.ref,
|
|
1212
1229
|
// A phone's sheet is never wider than 280, whatever the laptop kept.
|
|
1213
1230
|
// inline-style: `--span`, the width a drag rewrites on the element itself (span.ts); no prop sets a variable.
|
|
@@ -1226,16 +1243,18 @@ function Frame({ mode, children, navigate, back, forward, }) {
|
|
|
1226
1243
|
// FILTER, not Search. Two controls that both said Search did two
|
|
1227
1244
|
// different things: this one narrows the list beside you, and the
|
|
1228
1245
|
// bar above opens the palette that finds anything anywhere.
|
|
1229
|
-
placeholder: "Filter", "aria-label": "Filter sidebar" }), flex: 1, minW: 0, bg: "transparent", borderWidth: 0, outlineStyle: "none", color: "inherit", fontSize: "$2" })] }) }), settingsPane ? (_jsx(SidebarScroll, { children: _jsx(SettingsList, { active: settingsPane, filter: filter, onPick: pick, onBack: () => showSettings(null) }) })) : (_jsx(_Fragment, { children: _jsx(SidebarScroll, { children: _jsxs(_Fragment, { children: [settled ? (_jsx(Index, { mode: mode, owner: owner, filter: filter, onPick: onPick, navigate: navigate })) : null, products.length ? (_jsx(SidebarFolder, { name: "Cloud & Platform", open: filter ? true : undefined, children: products.map((prod) => (_jsx(SidebarItem, { icon: _jsx(prod.icon, { size: 13, "aria-hidden": true }), onPress: () => window.open(prod.href, "_blank", "noopener"), children: prod.label }, prod.id))) })) : null] }) }) })), settled && onApex ? _jsx(Leave, { href: signedIn ? ENTRY : WORKSPACE, children: "Workspace" }) : null, settled ? (signedIn ? (_jsxs(_Fragment, { children: [
|
|
1246
|
+
placeholder: "Filter", "aria-label": "Filter sidebar" }), flex: 1, minW: 0, bg: "transparent", borderWidth: 0, outlineStyle: "none", color: "inherit", fontSize: "$2" })] }) }), settingsPane ? (_jsx(SidebarScroll, { children: _jsx(SettingsList, { active: settingsPane, filter: filter, onPick: pick, onBack: () => showSettings(null) }) })) : (_jsx(_Fragment, { children: _jsx(SidebarScroll, { children: _jsxs(_Fragment, { children: [settled ? (_jsx(Index, { mode: mode, owner: owner, filter: filter, onPick: onPick, navigate: navigate })) : null, products.length ? (_jsx(SidebarFolder, { name: "Cloud & Platform", open: filter ? true : undefined, children: products.map((prod) => (_jsx(SidebarItem, { icon: _jsx(prod.icon, { size: 13, "aria-hidden": true }), onPress: () => window.open(prod.href, "_blank", "noopener"), children: prod.label }, prod.id))) })) : null] }) }) })), settled && onApex ? _jsx(Leave, { href: signedIn ? ENTRY : WORKSPACE, children: "Workspace" }) : null, settled ? (signedIn ? (_jsxs(_Fragment, { children: [_jsx(SidebarItem, { icon: _jsx(UserPlus, { size: 15, "aria-hidden": true }), onPress: () => window.open(invite(), "_blank", "noopener"), children: "Invite Teammates" }), _jsx(SidebarItem, { icon: _jsx(Share2, { size: 15, "aria-hidden": true }), onPress: () => {
|
|
1230
1247
|
navigate?.(site("/referral"));
|
|
1231
1248
|
dismiss();
|
|
1232
|
-
}, children: "Share Hanzo" }),
|
|
1249
|
+
}, children: "Share Hanzo" }), _jsxs(XStack, { items: "center", gap: "$1", px: "$1.5", py: "$1", borderTopWidth: 1, borderColor: "$borderColor", children: [_jsx(YStack, { flex: 1, minW: 0, children: _jsx(Me, { host: who, onOrg: (o) => (choose(o), window.location.reload()) }) }), _jsx(Box, { render: "button", onClick: toggle, "aria-label": "Collapse sidebar", p: "$2", rounded: "$2", hoverStyle: { bg: "$hover" }, cursor: "pointer", children: _jsx(PanelLeftClose, { size: 16, "aria-hidden": true }) })] })] })) : null) : null] }));
|
|
1233
1250
|
return (_jsxs(YStack, { "data-slot": "workspace", "data-hydrated": settled ? "true" : undefined, "data-sidebar": asked ? "open" : "shut", display: "grid", gridTemplateColumns: `${asked ? 0 : 64}px auto minmax(0, 1fr) auto`, gridTemplateRows: "44px minmax(0, 1fr)", gridTemplateAreas: '"rail bar bar bar" "rail list pane side"', "$max-md": {
|
|
1234
1251
|
gridTemplateColumns: "minmax(0, 1fr)",
|
|
1235
1252
|
gridTemplateRows: `44px minmax(0, 1fr) ${TABS_HEIGHT}`,
|
|
1236
1253
|
gridTemplateAreas: '"bar" "pane" "rail"',
|
|
1237
|
-
}, position: "relative", width: "100%", height: "100dvh", overflow: "hidden", bg: "$background", backgroundImage: "var(--pane-ground)", children: [_jsx(Rail, { mode: mode, places: places, open: asked, team: onApex ? (signedIn ? ENTRY : WORKSPACE) : undefined, navigate: navigate, onSettings: () => showSettings(FIRST), onMore: () => setFinding(true) }), _jsxs(XStack, { render: "header", "data-slot": "bar", gridArea: "bar", ...sheet(0), height: 44, shrink: 0, items: "center", px: "$3", gap: "$3", borderBottomWidth: 1, borderColor: "var(--edge, var(--white-06))", children: [_jsxs(XStack, { items: "center", gap: "$1", shrink: 0, children: [_jsx(SidebarIconButton, { ...TAP, label: shown ? "Close sidebar" : "Open sidebar", onPress: toggle, onMouseEnter: () => setPeeking(true), children: _jsx(PanelLeft, { size: 16, "aria-hidden": true }) }), back || forward ? (_jsxs(XStack, { display: "none", $md: { display: "flex" }, items: "center", gap: "$1", shrink: 0, children: [_jsx(SidebarIconButton, { ...TAP, label: "Back", onPress: () => back?.(), children: _jsx(ArrowLeft, { size: 16, "aria-hidden": true }) }), _jsx(SidebarIconButton, { ...TAP, label: "Forward", onPress: () => forward?.(), children: _jsx(ArrowRight, { size: 16, "aria-hidden": true }) })] })) : null] }), _jsx(XStack, { flex: 1, justify: "center", children: _jsx(YStack, { width: 440, maxW: "100%", position: "relative", children: _jsxs(Box, { render: "button", onClick: () => setFinding(true), display: "flex", flexDirection: "row", "data-slot": "find", items: "center", "$max-md": { justify: "center" }, gap: "$2", width: "100%", height: CONTROL, px: "$2.5", rounded: "$2", borderWidth: 1, borderColor: "$borderColor", hoverStyle: { bg: '$hover' }, children: [_jsx(Search, { size: 13, "aria-hidden": true, color: "var(--soft, var(--muted-foreground))" }), _jsxs(Text, { fontSize: "$1", color: "$soft", "$max-md": { display: "none" }, children: ["Search ", workspace] }), _jsx(
|
|
1254
|
+
}, position: "relative", width: "100%", height: "100dvh", overflow: "hidden", bg: "$background", backgroundImage: "var(--pane-ground)", children: [_jsx(Rail, { mode: mode, places: places, open: asked, team: onApex ? (signedIn ? ENTRY : WORKSPACE) : undefined, navigate: navigate, onSettings: () => showSettings(FIRST), onMore: () => setFinding(true) }), _jsxs(XStack, { render: "header", "data-slot": "bar", gridArea: "bar", ...sheet(0), height: 44, shrink: 0, items: "center", px: "$3", gap: "$3", borderBottomWidth: 1, borderColor: "var(--edge, var(--white-06))", children: [_jsxs(XStack, { items: "center", gap: "$1", shrink: 0, children: [_jsx(SidebarIconButton, { ...TAP, label: shown ? "Close sidebar" : "Open sidebar", onPress: toggle, onMouseEnter: () => setPeeking(true), children: _jsx(PanelLeft, { size: 16, "aria-hidden": true }) }), back || forward ? (_jsxs(XStack, { display: "none", $md: { display: "flex" }, items: "center", gap: "$1", shrink: 0, children: [_jsx(SidebarIconButton, { ...TAP, label: "Back", onPress: () => back?.(), children: _jsx(ArrowLeft, { size: 16, "aria-hidden": true }) }), _jsx(SidebarIconButton, { ...TAP, label: "Forward", onPress: () => forward?.(), children: _jsx(ArrowRight, { size: 16, "aria-hidden": true }) })] })) : null] }), _jsx(XStack, { flex: 1, justify: "center", children: _jsx(YStack, { width: 440, maxW: "100%", position: "relative", children: _jsxs(Box, { render: "button", onClick: () => setFinding(true), display: "flex", flexDirection: "row", "data-slot": "find", items: "center", "$max-md": { justify: "center" }, gap: "$2", width: "100%", height: CONTROL, px: "$2.5", rounded: "$2", borderWidth: 1, borderColor: "$borderColor", hoverStyle: { bg: '$hover' }, children: [_jsx(Search, { size: 13, "aria-hidden": true, color: "var(--soft, var(--muted-foreground))" }), _jsxs(Text, { fontSize: "$1", color: "$soft", "$max-md": { display: "none" }, children: ["Search ", workspace] }), _jsx(Shortcut, { keys: ["Mod", "K"], ml: "auto", "$max-md": { display: "none" } })] }) }) }), _jsxs(XStack, { shrink: 0, minW: 0, justify: "flex-end", items: "center", gap: "$2", children: [settled && !signedIn ? (_jsx(Box, { ...TAP, render: "button", onClick: () => enter(LOGIN), display: "flex", items: "center", justify: "center", cursor: "pointer", children: _jsx(XStack, { items: "center", justify: "center", height: CONTROL, px: "$2.5", rounded: "$2", borderWidth: 1, borderColor: "$borderColor", hoverStyle: { bg: '$hover' }, children: _jsx(Text, { fontSize: "$1", fontWeight: "600", color: "$ink", children: "Sign in" }) }) })) : null, _jsxs(XStack, { display: "none", $lg: { display: "flex" }, items: "center", gap: "$1", children: [_jsx(SidebarIconButton, { ...TAP, label: aside === "roster" ? "Hide directory & roster" : "Show directory & roster", onPress: () => showAside(aside === "roster" ? null : "roster"), children: _jsx(Users, { size: 16, "aria-hidden": true }) }), _jsx(SidebarIconButton, { ...TAP, label: aside === "tasks" ? "Close side panel" : "Open side panel", onPress: () => showAside(aside === "tasks" ? null : "tasks"), children: _jsx(PanelRight, { size: 16, "aria-hidden": true }) })] })] })] }), shown ? (_jsx(Box, { render: "button", "aria-label": "Close sidebar", onClick: dismiss, ...scrim, "data-slot": "scrim", position: "absolute", t: 44, r: 0, b: TABS_HEIGHT, l: 0, z: "var(--z-scrim)", $md: { display: "none" } })) : null, shown ? (_jsx(XStack, { "data-slot": "list", gridArea: "list", position: asked ? "relative" : "absolute", ...(asked ? {} : { top: 44, bottom: 0, left: 64 }), "$max-md": { position: "fixed", t: 44, r: 0, b: TABS_HEIGHT, l: 0, width: "100%" }, z: "var(--z-drawer)", bg: "$background", onMouseEnter: () => setPeeking(true), onMouseLeave: () => {
|
|
1238
1255
|
if (!asked)
|
|
1239
1256
|
setPeeking(false);
|
|
1240
|
-
}, children: list })) : null, _jsx(Paned, { swap: settingsPane ? _jsx(Suspense, { fallback: null, children: _jsx(SettingsPane, { id: settingsPane }) }) : undefined, children: children }),
|
|
1257
|
+
}, children: list })) : null, _jsx(Paned, { swap: settingsPane ? _jsx(Suspense, { fallback: null, children: _jsx(SettingsPane, { id: settingsPane }) }) : undefined, children: children }), finding ? (_jsx(Palette, { actions: doors, onClose: found,
|
|
1258
|
+
// A chat opens in this workspace's Chat, the palette having picked it; the rest is the app's.
|
|
1259
|
+
go: (to) => navigate?.(to.startsWith(CHAT.route) ? CHAT.route : site(to)) })) : null] }));
|
|
1241
1260
|
}
|
package/dist/Top.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type Host } from '@hanzo/build';
|
|
2
|
+
import type { RailLink } from '@hanzo/ui/chat';
|
|
3
|
+
import { type Place } from './lib/host.js';
|
|
4
|
+
export declare function Top({ host, mode, places, go, onMenu, onChat, onDev, }: {
|
|
5
|
+
host: Host;
|
|
6
|
+
mode: Place['mode'];
|
|
7
|
+
/** The mode's places, where they are not Dev's (Studio's `nav`). */
|
|
8
|
+
places?: {
|
|
9
|
+
id: string;
|
|
10
|
+
links: RailLink[];
|
|
11
|
+
}[];
|
|
12
|
+
/** Moves the app, the drawer away first. */
|
|
13
|
+
go: (address: string) => void;
|
|
14
|
+
onMenu: () => void;
|
|
15
|
+
onChat: () => void;
|
|
16
|
+
onDev: () => void;
|
|
17
|
+
}): import("react").JSX.Element;
|
package/dist/Top.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
// A phone's bar, which has neither rail nor sidebar to move the app: the
|
|
4
|
+
// drawer's button at the left, Chat | Dev in the middle, and New at the right
|
|
5
|
+
// (the owner's iPhone reference; LLM.md "The app is `App`").
|
|
6
|
+
//
|
|
7
|
+
// (≡) ( Chat | Dev ) (✎)
|
|
8
|
+
//
|
|
9
|
+
// The three are glass: the pane's material (ground.ts) cut round, its edge one
|
|
10
|
+
// step stronger so a circle on the bare floor reads as one. Measured on the
|
|
11
|
+
// reference at 440×956: the buttons are 44px circles 20px in from each edge,
|
|
12
|
+
// the pill 158×48 with a 40px segment inside a 4px rim, its labels at 15px
|
|
13
|
+
// medium, all centred on one line 48px tall under the device's top inset.
|
|
14
|
+
//
|
|
15
|
+
// Chat | Dev moves the app the way the rail does (`onChat`, `onDev`: where each
|
|
16
|
+
// was last). In Studio neither is lit. New is Chat's New chat, and elsewhere the
|
|
17
|
+
// mode's own New row (@hanzo/build's and @hanzo/studio's `nav`, `NEW`), so the
|
|
18
|
+
// bar names it as the sidebar does.
|
|
19
|
+
import { SquarePen } from 'lucide-react';
|
|
20
|
+
import { Text, XStack } from '@hanzo/gui';
|
|
21
|
+
import { nav, NEW } from '@hanzo/build';
|
|
22
|
+
import { ToggleGroup, ToggleGroupItem } from '@hanzo/ui';
|
|
23
|
+
import { pane } from './ground.js';
|
|
24
|
+
import { empty } from './open.js';
|
|
25
|
+
import { talk } from './lib/host.js';
|
|
26
|
+
/** A button, px: a thumb's 44. */
|
|
27
|
+
const ROUND = 44;
|
|
28
|
+
/** The bar's line under the device's top inset, px: the pill's height. */
|
|
29
|
+
const LINE = 48;
|
|
30
|
+
/** A segment, px: the pill's 150px inside its rim, less the group's 4px gap, halved. */
|
|
31
|
+
const SEGMENT = 73;
|
|
32
|
+
/** The glass the three are cut from. */
|
|
33
|
+
const GLASS = { ...pane(), borderColor: 'var(--border-strong)', borderRadius: 9999 };
|
|
34
|
+
/** A round glass button named for what it does. */
|
|
35
|
+
function Round({ label, onPress, children, ...rest }) {
|
|
36
|
+
return (_jsx(XStack, { render: "button", "aria-label": label, onPress: onPress, ...GLASS, width: ROUND, height: ROUND, items: "center", justify: "center", shrink: 0, cursor: "pointer", pressStyle: { bg: '$raised' }, focusVisibleStyle: { outlineWidth: 2, outlineStyle: 'solid', outlineColor: '$outlineColor' }, ...rest, children: children }));
|
|
37
|
+
}
|
|
38
|
+
/** The drawer's mark: two strokes, the lower one shorter, 19×11 as the reference draws it. */
|
|
39
|
+
function Lines() {
|
|
40
|
+
return (_jsx("svg", { width: 19, height: 11, viewBox: "0 0 19 11", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", "aria-hidden": true, children: _jsx("path", { d: "M1 1h17M1 10h10.7" }) }));
|
|
41
|
+
}
|
|
42
|
+
/** One side of the pill. */
|
|
43
|
+
function Side({ value, children }) {
|
|
44
|
+
return (_jsx(ToggleGroupItem, { value: value, size: "lg", width: SEGMENT, rounded: 9999, bg: "transparent", borderColor: "transparent", hoverStyle: { bg: '$hover' }, activeStyle: { bg: '$raised', borderColor: 'transparent' }, children: _jsx(Text, { fontSize: "$4", fontWeight: "500", color: "$ink", numberOfLines: 1, children: children }) }));
|
|
45
|
+
}
|
|
46
|
+
export function Top({ host, mode, places, go, onMenu, onChat, onDev, }) {
|
|
47
|
+
const made = mode === 'chat' ? null : (places ?? nav(host, (p) => host.go(p))).flatMap((g) => g.links).find((l) => l.id === NEW);
|
|
48
|
+
const fresh = mode === 'chat'
|
|
49
|
+
? {
|
|
50
|
+
label: 'New chat',
|
|
51
|
+
onPress: () => {
|
|
52
|
+
empty();
|
|
53
|
+
go(talk());
|
|
54
|
+
},
|
|
55
|
+
}
|
|
56
|
+
: { label: made?.label ?? 'New', onPress: made?.onPress };
|
|
57
|
+
return (_jsxs(XStack, { "data-slot": "app-bar", height: `calc(${LINE}px + var(--safe-top))`, pt: "var(--safe-top)", px: "$4.5", items: "center", justify: "space-between", shrink: 0, $md: { display: 'none' }, children: [_jsx(Round, { label: "Open sidebar", "data-slot": "bar-menu", onPress: onMenu, children: _jsx(Lines, {}) }), _jsx(XStack, { "data-slot": "bar-mode", ...GLASS, height: LINE, p: "$1", items: "center", children: _jsxs(ToggleGroup, { type: "single", "aria-label": "App", value: mode === 'studio' ? '' : mode, disableDeactivation: true, onValueChange: (v) => (v === 'chat' ? onChat() : v === 'dev' ? onDev() : undefined), children: [_jsx(Side, { value: "chat", children: "Chat" }), _jsx(Side, { value: "dev", children: "Dev" })] }) }), _jsx(Round, { label: fresh.label, "data-slot": "bar-new", onPress: fresh.onPress, children: _jsx(SquarePen, { size: 20, "aria-hidden": true }) })] }));
|
|
58
|
+
}
|
package/dist/agents.d.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
export type { Link, Shelf, ShelfRow as Row } from '@hanzo/build';
|
|
2
|
+
/**
|
|
3
|
+
* The model that draws an agent's face: Hanzo's own image SKU (hanzo-inc/zen
|
|
4
|
+
* catalog.yaml `zen-image`), named on every call, because the endpoint takes
|
|
5
|
+
* none by default.
|
|
6
|
+
*/
|
|
7
|
+
export declare const PAINTER = "zen-image";
|
|
8
|
+
/** The words every drawn face is asked for after its description. */
|
|
9
|
+
export declare const MEMOJI = "memoji style 3D emoji portrait, head and shoulders, centered, plain background";
|
|
10
|
+
/** A core member, drawn for an org that keeps no row of its own for them. */
|
|
11
|
+
export interface House {
|
|
12
|
+
id: string;
|
|
13
|
+
name: string;
|
|
14
|
+
avatar: string;
|
|
15
|
+
}
|
|
16
|
+
/** The default cast: the core team @hanzo/personas publishes, in the crew's order. */
|
|
17
|
+
export declare const CORE: readonly House[];
|
|
18
|
+
/**
|
|
19
|
+
* The org's agents with the default cast filling in any member the org keeps no
|
|
20
|
+
* row for, the cast first in its own order, then the rest as listed. With no
|
|
21
|
+
* list yet (or none at all) it is the default cast.
|
|
22
|
+
*/
|
|
23
|
+
export declare function ordered<T extends {
|
|
24
|
+
name: string;
|
|
25
|
+
}>(agents: readonly T[] | null | undefined): (T | House)[];
|
|
26
|
+
/**
|
|
27
|
+
* One figure of @hanzo/personas' roster, as assets/personas/index.json lists it
|
|
28
|
+
* (scripts/team.mjs). `called`, `about` and `based` are its PERSONA.md's
|
|
29
|
+
* `name`, `description` and `based_on`, present only where one is written.
|
|
30
|
+
*/
|
|
31
|
+
export interface Figure {
|
|
32
|
+
slug: string;
|
|
33
|
+
name: string;
|
|
34
|
+
category: string;
|
|
35
|
+
called?: string;
|
|
36
|
+
about?: string;
|
|
37
|
+
based?: string;
|
|
38
|
+
}
|
|
39
|
+
/** Where a host serves the roster: the list, and each written figure's PERSONA.md. */
|
|
40
|
+
export declare const FIGURES = "/personas";
|
|
41
|
+
/** The name a person reads for a figure: who it is based on, else its own name. */
|
|
42
|
+
export declare const title: (f: Figure) => string;
|
|
43
|
+
/** A figure's one line: its PERSONA.md's description, else what it was. */
|
|
44
|
+
export declare const line: (f: Figure) => string;
|
|
45
|
+
/**
|
|
46
|
+
* A name the agents API takes (cloud apps/agents `nameRE`): letters, digits, dots,
|
|
47
|
+
* dashes and underscores, from a letter or digit, up to 64. Accents are dropped
|
|
48
|
+
* and any other run becomes a dash: "Spärck Jones" is Sparck-Jones.
|
|
49
|
+
*/
|
|
50
|
+
export declare const handle: (name: string) => string;
|
|
51
|
+
/** What the New agent sheet is filled with from a figure. */
|
|
52
|
+
export interface Draft {
|
|
53
|
+
name: string;
|
|
54
|
+
/** The instructions. */
|
|
55
|
+
does: string;
|
|
56
|
+
/** The face to draw. */
|
|
57
|
+
look: string;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* A figure as an agent: its PERSONA.md's name and its prompt word for word
|
|
61
|
+
* (the text under the front matter); a figure with none written is told who it
|
|
62
|
+
* is in one paragraph. The face is described by who it is and its one line.
|
|
63
|
+
*/
|
|
64
|
+
export declare function draft(f: Figure, md: string | null): Draft;
|
package/dist/agents.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// The agents a conversation can hold, and the figures a new one can start from.
|
|
2
|
+
// Pure: the composer's Agents shelf (crew.tsx `useAgentShelf`), the crew strip
|
|
3
|
+
// and the New agent sheet read these, and the tests run them in Node.
|
|
4
|
+
import { TEAM } from './team.js';
|
|
5
|
+
/**
|
|
6
|
+
* The model that draws an agent's face: Hanzo's own image SKU (hanzo-inc/zen
|
|
7
|
+
* catalog.yaml `zen-image`), named on every call, because the endpoint takes
|
|
8
|
+
* none by default.
|
|
9
|
+
*/
|
|
10
|
+
export const PAINTER = 'zen-image';
|
|
11
|
+
/** The words every drawn face is asked for after its description. */
|
|
12
|
+
export const MEMOJI = 'memoji style 3D emoji portrait, head and shoulders, centered, plain background';
|
|
13
|
+
/** The default cast: the core team @hanzo/personas publishes, in the crew's order. */
|
|
14
|
+
export const CORE = TEAM.map((one) => ({ id: `house-${one.id}`, name: one.name, avatar: `/agents/${one.id}.png` }));
|
|
15
|
+
const RANK = new Map(TEAM.map((one, i) => [one.id, i]));
|
|
16
|
+
/**
|
|
17
|
+
* The org's agents with the default cast filling in any member the org keeps no
|
|
18
|
+
* row for, the cast first in its own order, then the rest as listed. With no
|
|
19
|
+
* list yet (or none at all) it is the default cast.
|
|
20
|
+
*/
|
|
21
|
+
export function ordered(agents) {
|
|
22
|
+
const list = Array.isArray(agents) ? [...agents] : [];
|
|
23
|
+
const names = new Set(list.map((one) => one.name?.toLowerCase()));
|
|
24
|
+
for (const one of CORE)
|
|
25
|
+
if (!names.has(one.name.toLowerCase()))
|
|
26
|
+
list.push(one);
|
|
27
|
+
const rank = (one) => RANK.get(one.name?.toLowerCase() ?? '') ?? Infinity;
|
|
28
|
+
return list.sort((a, b) => rank(a) - rank(b));
|
|
29
|
+
}
|
|
30
|
+
/** Where a host serves the roster: the list, and each written figure's PERSONA.md. */
|
|
31
|
+
export const FIGURES = '/personas';
|
|
32
|
+
/** The name a person reads for a figure: who it is based on, else its own name. */
|
|
33
|
+
export const title = (f) => f.based?.replace(/\s*\(.*\)\s*$/, '') ?? f.called ?? f.name;
|
|
34
|
+
/** A figure's one line: its PERSONA.md's description, else what it was. */
|
|
35
|
+
export const line = (f) => f.about ?? f.category.replace(/_/g, ' ').replace(/^./, (c) => c.toUpperCase());
|
|
36
|
+
/**
|
|
37
|
+
* A name the agents API takes (cloud apps/agents `nameRE`): letters, digits, dots,
|
|
38
|
+
* dashes and underscores, from a letter or digit, up to 64. Accents are dropped
|
|
39
|
+
* and any other run becomes a dash: "Spärck Jones" is Sparck-Jones.
|
|
40
|
+
*/
|
|
41
|
+
export const handle = (name) => name
|
|
42
|
+
.normalize('NFD')
|
|
43
|
+
.replace(/[̀-ͯ]/g, '')
|
|
44
|
+
.replace(/[^A-Za-z0-9._-]+/g, '-')
|
|
45
|
+
.replace(/^[^A-Za-z0-9]+/, '')
|
|
46
|
+
.slice(0, 64)
|
|
47
|
+
.replace(/-+$/, '');
|
|
48
|
+
/**
|
|
49
|
+
* A figure as an agent: its PERSONA.md's name and its prompt word for word
|
|
50
|
+
* (the text under the front matter); a figure with none written is told who it
|
|
51
|
+
* is in one paragraph. The face is described by who it is and its one line.
|
|
52
|
+
*/
|
|
53
|
+
export function draft(f, md) {
|
|
54
|
+
const who = title(f);
|
|
55
|
+
const prompt = md?.replace(/^---\n[\s\S]*?\n---\n/, '').trim();
|
|
56
|
+
return {
|
|
57
|
+
name: handle(f.called ?? who),
|
|
58
|
+
does: prompt ||
|
|
59
|
+
`You are ${who}. Answer as ${who} would, in your own voice and from what you wrote and did. When a question is past what you could have known, say so.`,
|
|
60
|
+
look: `${who}, ${line(f).replace(/^./, (c) => c.toLowerCase())}`,
|
|
61
|
+
};
|
|
62
|
+
}
|
package/dist/crew.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useAgents } from '@hanzo/ai/react';
|
|
2
|
+
import { type Shelf } from './agents.js';
|
|
2
3
|
/**
|
|
3
4
|
* The org's agents, read again whenever the sheet makes one. `useAgents` holds
|
|
4
5
|
* one list per caller, so the strip, the chat header and the sheet each hold
|
|
@@ -7,6 +8,19 @@ import { useAgents } from '@hanzo/ai/react';
|
|
|
7
8
|
export declare function useCrew(): ReturnType<typeof useAgents>;
|
|
8
9
|
/** The house crew's names, which a reply may be split by even after one has left the room. */
|
|
9
10
|
export declare const HOUSE: readonly string[];
|
|
11
|
+
/** Opens the New agent sheet, blank or on its persona list; null shuts it. */
|
|
12
|
+
export declare function make(how: 'new' | 'persona' | null): void;
|
|
13
|
+
/**
|
|
14
|
+
* The New agent sheet, wherever the host mounts this once (beside the
|
|
15
|
+
* composer). It is drawn while `make` holds it open, and an agent it makes joins
|
|
16
|
+
* the conversation that is open.
|
|
17
|
+
*/
|
|
18
|
+
export declare function AgentMaker(): import("react").JSX.Element;
|
|
19
|
+
/**
|
|
20
|
+
* The composer's `+` › Agents: the org's agents, the default cast first, each
|
|
21
|
+
* by its face; on is in the open conversation, and a pick toggles it there.
|
|
22
|
+
*/
|
|
23
|
+
export declare function useAgentShelf(): Shelf;
|
|
10
24
|
/**
|
|
11
25
|
* `to` is the chat a press opens, for a strip on a page that is not one. Absent,
|
|
12
26
|
* the strip sits under a chat and a press changes who is in it.
|
|
@@ -20,17 +34,24 @@ export declare function Crew({ to }?: {
|
|
|
20
34
|
* A name, a mind and a spending limit are what the agents API requires; what it
|
|
21
35
|
* does becomes its instructions; the likeness is whichever the maker gives — a
|
|
22
36
|
* photo uploaded onto the round picture (pressed, or dropped on), or a face
|
|
23
|
-
* described and drawn by
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
37
|
+
* described and drawn by Hanzo's image model (`PAINTER`), one call per press of
|
|
38
|
+
* Draw or Regenerate. An agent created with neither a picture nor an emoji has
|
|
39
|
+
* one drawn then, once, from the description or else its name. Every face goes
|
|
40
|
+
* through `mark`, the one crop every saved face takes, and is kept as the row's
|
|
41
|
+
* `avatar`. The row keeps at most one of a picture and an emoji, so choosing one
|
|
42
|
+
* clears the other here too.
|
|
43
|
+
*
|
|
44
|
+
* Start from a persona fills the name, the instructions and the face's
|
|
45
|
+
* description from a figure of @hanzo/personas' roster (agents.ts `draft`);
|
|
46
|
+
* `start` opens the sheet on that list.
|
|
27
47
|
*
|
|
28
48
|
* Create stays off until the required fields are filled in the API's own
|
|
29
49
|
* terms, and a refusal is said in plain words under the field it concerns.
|
|
30
50
|
*
|
|
31
51
|
* EXPORTED, because Contacts adds agents too and there is one way to make one.
|
|
32
52
|
*/
|
|
33
|
-
export declare function Hire({ onClose, onMade }: {
|
|
53
|
+
export declare function Hire({ onClose, onMade, start }: {
|
|
34
54
|
onClose: () => void;
|
|
35
55
|
onMade: (name: string) => void;
|
|
56
|
+
start?: boolean;
|
|
36
57
|
}): import("react").JSX.Element;
|