@hanzo/ui 8.13.3 → 8.14.1
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/agents/Board.cjs +119 -0
- package/dist/agents/Board.d.ts +98 -0
- package/dist/agents/Board.d.ts.map +1 -0
- package/dist/agents/Board.js +115 -0
- package/dist/agents/Board.js.map +1 -0
- package/dist/agents/Screen.cjs +91 -0
- package/dist/agents/Screen.d.ts +53 -0
- package/dist/agents/Screen.d.ts.map +1 -0
- package/dist/agents/Screen.js +89 -0
- package/dist/agents/Screen.js.map +1 -0
- package/dist/agents/Steer.cjs +42 -0
- package/dist/agents/Steer.d.ts +54 -0
- package/dist/agents/Steer.d.ts.map +1 -0
- package/dist/agents/Steer.js +39 -0
- package/dist/agents/Steer.js.map +1 -0
- package/dist/agents/Transcript.cjs +59 -0
- package/dist/agents/Transcript.d.ts +50 -0
- package/dist/agents/Transcript.d.ts.map +1 -0
- package/dist/agents/Transcript.js +56 -0
- package/dist/agents/Transcript.js.map +1 -0
- package/dist/agents/index.cjs +36 -0
- package/dist/agents/index.d.ts +26 -0
- package/dist/agents/index.d.ts.map +1 -0
- package/dist/agents/index.js +26 -0
- package/dist/agents/index.js.map +1 -0
- package/dist/backends/gui/drawer.cjs +20 -1
- package/dist/backends/gui/drawer.d.ts +11 -1
- package/dist/backends/gui/drawer.d.ts.map +1 -1
- package/dist/backends/gui/drawer.js +18 -0
- package/dist/backends/gui/drawer.js.map +1 -1
- package/dist/backends/gui/index.cjs +2 -1
- package/dist/backends/gui/index.d.ts +1 -1
- package/dist/backends/gui/index.d.ts.map +1 -1
- package/dist/backends/gui/index.js +1 -1
- package/dist/backends/gui/index.js.map +1 -1
- package/dist/box.cjs +19 -0
- package/dist/box.d.ts.map +1 -1
- package/dist/box.js +19 -0
- package/dist/box.js.map +1 -1
- package/dist/chat/Sidebar.cjs +26 -19
- package/dist/chat/Sidebar.d.ts +46 -19
- package/dist/chat/Sidebar.d.ts.map +1 -1
- package/dist/chat/Sidebar.js +26 -19
- package/dist/chat/Sidebar.js.map +1 -1
- package/dist/gallery.html +1 -1
- package/dist/index.cjs +3 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.node.js +1 -1
- package/dist/primitives/DrawerTrigger.cjs +8 -0
- package/dist/primitives/DrawerTrigger.d.ts +2 -0
- package/dist/primitives/DrawerTrigger.d.ts.map +1 -0
- package/dist/primitives/DrawerTrigger.js +5 -0
- package/dist/primitives/DrawerTrigger.js.map +1 -0
- package/package.json +13 -7
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
'use client';"use strict";
|
|
2
|
+
'use client';
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.RunRow = RunRow;
|
|
5
|
+
exports.AgentBoard = AgentBoard;
|
|
6
|
+
exports.nest = nest;
|
|
7
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
8
|
+
/**
|
|
9
|
+
* AgentBoard — the agents working view: who is running, on what, and where.
|
|
10
|
+
*
|
|
11
|
+
* A run is a SESSION, and a session that spawned others is the root of a tree.
|
|
12
|
+
* A flat list draws a fan-out as N unrelated rows and loses the one fact worth
|
|
13
|
+
* seeing — that they are one piece of work — so the board nests children under
|
|
14
|
+
* their root and a collapsed root says how many it is hiding.
|
|
15
|
+
*
|
|
16
|
+
* Grouped by PROJECT, because that is the question a person opens this with:
|
|
17
|
+
* not "what is running" but "what is running on the thing I am working on".
|
|
18
|
+
* A session with no project groups under `ungrouped`, named rather than hidden.
|
|
19
|
+
*
|
|
20
|
+
* Presentational. It takes rows and raises `onOpen`; it does no fetching, no
|
|
21
|
+
* grouping policy beyond the field it is given, and no status derivation. The
|
|
22
|
+
* surface holds the data — `@hanzo/ai`'s `sessions.list` / `sessions.tree` and
|
|
23
|
+
* the stream — so the same board serves hanzo.ai, a channel workspace, or
|
|
24
|
+
* anything else that can answer with rows.
|
|
25
|
+
*/
|
|
26
|
+
const gui_1 = require("@hanzo/gui");
|
|
27
|
+
const lucide_icons_2_1 = require("@hanzogui/lucide-icons-2");
|
|
28
|
+
const react_1 = require("react");
|
|
29
|
+
const slot_1 = require("../backends/gui/slot.cjs");
|
|
30
|
+
const TONE = {
|
|
31
|
+
running: '$green10',
|
|
32
|
+
paused: '$yellow10',
|
|
33
|
+
done: '$color10',
|
|
34
|
+
error: '$red10',
|
|
35
|
+
unknown: '$color8',
|
|
36
|
+
};
|
|
37
|
+
/** The label a status wears. Separate from the colour so neither is guessed. */
|
|
38
|
+
const SAYS = {
|
|
39
|
+
running: 'running',
|
|
40
|
+
paused: 'paused',
|
|
41
|
+
done: 'done',
|
|
42
|
+
error: 'error',
|
|
43
|
+
unknown: '—',
|
|
44
|
+
};
|
|
45
|
+
function Dot({ status }) {
|
|
46
|
+
return ((0, jsx_runtime_1.jsx)(gui_1.YStack, { width: 7, height: 7, rounded: 9999, bg: TONE[status], shrink: 0, "aria-label": SAYS[status] }));
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* One row, and its children under it.
|
|
50
|
+
*
|
|
51
|
+
* Expansion is per-row local state: a board is read top-down and a caller that
|
|
52
|
+
* wanted to persist which roots are open would be holding UI state for a list
|
|
53
|
+
* it does not own. A root with children opens by default, because the fan-out
|
|
54
|
+
* is the thing worth seeing.
|
|
55
|
+
*/
|
|
56
|
+
function RunRow({ run, depth = 0, active = false, onOpen, trailing, ...rest }) {
|
|
57
|
+
const kids = run.children ?? [];
|
|
58
|
+
const [open, setOpen] = (0, react_1.useState)(true);
|
|
59
|
+
return ((0, jsx_runtime_1.jsxs)(gui_1.YStack, { ...(0, slot_1.slot)('run-row'), ...rest, children: [(0, jsx_runtime_1.jsxs)(gui_1.XStack, { items: "center", gap: "$2", px: "$2", py: "$1.5", rounded: "$3", pl: 8 + depth * 16, cursor: "pointer", role: "button", tabIndex: 0, "aria-current": active ? 'true' : undefined, bg: active ? '$edge' : undefined, hoverStyle: { bg: active ? '$edge' : '$hover' }, pressStyle: { bg: '$raised' }, onPress: () => onOpen?.(run.id), children: [kids.length ? ((0, jsx_runtime_1.jsx)(gui_1.XStack, { width: 16, height: 16, items: "center", justify: "center", shrink: 0, role: "button", tabIndex: 0, "aria-expanded": open, "aria-label": open ? 'Hide sub-agents' : 'Show sub-agents', onPress: (e) => {
|
|
60
|
+
e.stopPropagation?.();
|
|
61
|
+
setOpen((v) => !v);
|
|
62
|
+
}, children: open ? (0, jsx_runtime_1.jsx)(lucide_icons_2_1.ChevronDown, { size: 13, opacity: 0.6 }) : (0, jsx_runtime_1.jsx)(lucide_icons_2_1.ChevronRight, { size: 13, opacity: 0.6 }) })) : ((0, jsx_runtime_1.jsx)(gui_1.YStack, { width: 16, shrink: 0 })), (0, jsx_runtime_1.jsx)(Dot, { status: run.status }), (0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$2", numberOfLines: 1, shrink: 1, color: active ? '$color' : '$quiet', children: run.agent }), run.doing ? ((0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$1", numberOfLines: 1, flex: 1, color: "$soft", children: run.doing })) : ((0, jsx_runtime_1.jsx)(gui_1.XStack, { flex: 1 })), kids.length && !open ? ((0, jsx_runtime_1.jsxs)(gui_1.SizableText, { size: "$1", color: "$soft", children: [kids.length, " sub-agent", kids.length === 1 ? '' : 's'] })) : null, run.target ? ((0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$1", color: "$soft", numberOfLines: 1, children: run.target })) : null, trailing] }), open && kids.length
|
|
63
|
+
? kids.map((kid) => ((0, jsx_runtime_1.jsx)(RunRow, { run: kid, depth: depth + 1, onOpen: onOpen }, kid.id)))
|
|
64
|
+
: null] }));
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The board.
|
|
68
|
+
*
|
|
69
|
+
* Groups are ordered by name with the un-projected group LAST, so the answer to
|
|
70
|
+
* "what is running on my project" is never below a pile of stray runs.
|
|
71
|
+
*/
|
|
72
|
+
function AgentBoard({ runs, active, onOpen, empty, otherLabel = 'Other work', ...rest }) {
|
|
73
|
+
const groups = (0, react_1.useMemo)(() => group(runs, otherLabel), [runs, otherLabel]);
|
|
74
|
+
if (!runs.length) {
|
|
75
|
+
return ((0, jsx_runtime_1.jsx)(gui_1.YStack, { ...(0, slot_1.slot)('agent-board'), p: "$4", ...rest, children: empty ?? ((0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$2", color: "$soft", children: "Nothing running." })) }));
|
|
76
|
+
}
|
|
77
|
+
return ((0, jsx_runtime_1.jsx)(gui_1.ScrollView, { ...(0, slot_1.slot)('agent-board'), flex: 1, showsVerticalScrollIndicator: false, ...rest, children: (0, jsx_runtime_1.jsx)(gui_1.YStack, { gap: "$1", p: "$2", children: groups.map(([project, rows]) => ((0, jsx_runtime_1.jsxs)(gui_1.YStack, { mt: "$2", gap: "$0.5", children: [(0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$1", px: "$2", py: "$1", color: "$soft", fontWeight: "500", children: project }), rows.map((run) => ((0, jsx_runtime_1.jsx)(RunRow, { run: run, active: run.id === active, onOpen: onOpen }, run.id)))] }, project))) }) }));
|
|
78
|
+
}
|
|
79
|
+
/** Group roots by project, un-projected last. */
|
|
80
|
+
function group(runs, otherLabel) {
|
|
81
|
+
const by = new Map();
|
|
82
|
+
for (const run of runs) {
|
|
83
|
+
const key = run.project?.trim() || otherLabel;
|
|
84
|
+
const rows = by.get(key);
|
|
85
|
+
if (rows)
|
|
86
|
+
rows.push(run);
|
|
87
|
+
else
|
|
88
|
+
by.set(key, [run]);
|
|
89
|
+
}
|
|
90
|
+
return [...by.entries()].sort(([a], [b]) => a === otherLabel ? 1 : b === otherLabel ? -1 : a.localeCompare(b));
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Build the tree a board draws from the flat list an API answers with.
|
|
94
|
+
*
|
|
95
|
+
* A session names its root rather than its parent, so a fan-out is TWO levels
|
|
96
|
+
* and not N: every non-root hangs off its root. A row whose root is not in the
|
|
97
|
+
* list is treated as a root itself — otherwise a filtered page silently drops
|
|
98
|
+
* work, which is the one thing a board must not do.
|
|
99
|
+
*/
|
|
100
|
+
function nest(rows) {
|
|
101
|
+
const roots = new Map();
|
|
102
|
+
const orphans = [];
|
|
103
|
+
for (const row of rows) {
|
|
104
|
+
if (!row.rootId || row.rootId === row.id)
|
|
105
|
+
roots.set(row.id, { ...row, children: [] });
|
|
106
|
+
}
|
|
107
|
+
for (const row of rows) {
|
|
108
|
+
if (!row.rootId || row.rootId === row.id)
|
|
109
|
+
continue;
|
|
110
|
+
const root = roots.get(row.rootId);
|
|
111
|
+
if (root)
|
|
112
|
+
root.children.push({ ...row, children: [] });
|
|
113
|
+
else
|
|
114
|
+
orphans.push(row);
|
|
115
|
+
}
|
|
116
|
+
for (const row of orphans)
|
|
117
|
+
roots.set(row.id, { ...row, children: [] });
|
|
118
|
+
return [...roots.values()];
|
|
119
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentBoard — the agents working view: who is running, on what, and where.
|
|
3
|
+
*
|
|
4
|
+
* A run is a SESSION, and a session that spawned others is the root of a tree.
|
|
5
|
+
* A flat list draws a fan-out as N unrelated rows and loses the one fact worth
|
|
6
|
+
* seeing — that they are one piece of work — so the board nests children under
|
|
7
|
+
* their root and a collapsed root says how many it is hiding.
|
|
8
|
+
*
|
|
9
|
+
* Grouped by PROJECT, because that is the question a person opens this with:
|
|
10
|
+
* not "what is running" but "what is running on the thing I am working on".
|
|
11
|
+
* A session with no project groups under `ungrouped`, named rather than hidden.
|
|
12
|
+
*
|
|
13
|
+
* Presentational. It takes rows and raises `onOpen`; it does no fetching, no
|
|
14
|
+
* grouping policy beyond the field it is given, and no status derivation. The
|
|
15
|
+
* surface holds the data — `@hanzo/ai`'s `sessions.list` / `sessions.tree` and
|
|
16
|
+
* the stream — so the same board serves hanzo.ai, a channel workspace, or
|
|
17
|
+
* anything else that can answer with rows.
|
|
18
|
+
*/
|
|
19
|
+
import { ScrollView, YStack } from '@hanzo/gui';
|
|
20
|
+
import { type ComponentProps, type ReactNode } from 'react';
|
|
21
|
+
type Col = Omit<ComponentProps<typeof YStack>, 'children'>;
|
|
22
|
+
type Scroll = Omit<ComponentProps<typeof ScrollView>, 'children'>;
|
|
23
|
+
/** The states a run can be in. `unknown` is honest about a value we do not model. */
|
|
24
|
+
export type RunStatus = 'running' | 'paused' | 'done' | 'error' | 'unknown';
|
|
25
|
+
/**
|
|
26
|
+
* One run.
|
|
27
|
+
*
|
|
28
|
+
* `children` is the fan-out. It is the SHAPE the board draws, so a caller with
|
|
29
|
+
* a flat list plus parent ids builds the tree before handing it over — `nest()`
|
|
30
|
+
* below does exactly that and is exported for the purpose.
|
|
31
|
+
*/
|
|
32
|
+
export interface Run {
|
|
33
|
+
id: string;
|
|
34
|
+
/** The agent doing the work. */
|
|
35
|
+
agent: string;
|
|
36
|
+
status: RunStatus;
|
|
37
|
+
/** What it is doing right now — one line, already trimmed by the caller. */
|
|
38
|
+
doing?: string;
|
|
39
|
+
/** Where it is running: a machine, a sandbox, a registered target. */
|
|
40
|
+
target?: string;
|
|
41
|
+
/** The project this run belongs to. Absent groups under "Other work". */
|
|
42
|
+
project?: string;
|
|
43
|
+
/** Sub-agents this run spawned. */
|
|
44
|
+
children?: Run[];
|
|
45
|
+
}
|
|
46
|
+
export interface RunRowProps extends Col {
|
|
47
|
+
run: Run;
|
|
48
|
+
depth?: number;
|
|
49
|
+
active?: boolean;
|
|
50
|
+
onOpen?: (id: string) => void;
|
|
51
|
+
/** Rendered at the end of the row — a watch affordance, a menu, a count. */
|
|
52
|
+
trailing?: ReactNode;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* One row, and its children under it.
|
|
56
|
+
*
|
|
57
|
+
* Expansion is per-row local state: a board is read top-down and a caller that
|
|
58
|
+
* wanted to persist which roots are open would be holding UI state for a list
|
|
59
|
+
* it does not own. A root with children opens by default, because the fan-out
|
|
60
|
+
* is the thing worth seeing.
|
|
61
|
+
*/
|
|
62
|
+
export declare function RunRow({ run, depth, active, onOpen, trailing, ...rest }: RunRowProps): import("react/jsx-runtime").JSX.Element;
|
|
63
|
+
export interface AgentBoardProps extends Scroll {
|
|
64
|
+
/** Roots. Children ride on each root's `children`. */
|
|
65
|
+
runs: Run[];
|
|
66
|
+
/** The run currently open, so the board can mark it. */
|
|
67
|
+
active?: string;
|
|
68
|
+
onOpen?: (id: string) => void;
|
|
69
|
+
/** What to draw when there is nothing running. */
|
|
70
|
+
empty?: ReactNode;
|
|
71
|
+
/** Heading for runs carrying no project. */
|
|
72
|
+
otherLabel?: string;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The board.
|
|
76
|
+
*
|
|
77
|
+
* Groups are ordered by name with the un-projected group LAST, so the answer to
|
|
78
|
+
* "what is running on my project" is never below a pile of stray runs.
|
|
79
|
+
*/
|
|
80
|
+
export declare function AgentBoard({ runs, active, onOpen, empty, otherLabel, ...rest }: AgentBoardProps): import("react/jsx-runtime").JSX.Element;
|
|
81
|
+
/**
|
|
82
|
+
* Build the tree a board draws from the flat list an API answers with.
|
|
83
|
+
*
|
|
84
|
+
* A session names its root rather than its parent, so a fan-out is TWO levels
|
|
85
|
+
* and not N: every non-root hangs off its root. A row whose root is not in the
|
|
86
|
+
* list is treated as a root itself — otherwise a filtered page silently drops
|
|
87
|
+
* work, which is the one thing a board must not do.
|
|
88
|
+
*/
|
|
89
|
+
export declare function nest<T extends {
|
|
90
|
+
id: string;
|
|
91
|
+
rootId?: string;
|
|
92
|
+
}>(rows: T[]): (T & {
|
|
93
|
+
children: (T & {
|
|
94
|
+
children: never[];
|
|
95
|
+
})[];
|
|
96
|
+
})[];
|
|
97
|
+
export {};
|
|
98
|
+
//# sourceMappingURL=Board.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Board.d.ts","sourceRoot":"","sources":["../../src/agents/Board.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAAE,UAAU,EAAuB,MAAM,EAAE,MAAM,YAAY,CAAA;AAEpE,OAAO,EAAqB,KAAK,cAAc,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAI9E,KAAK,GAAG,GAAG,IAAI,CAAC,cAAc,CAAC,OAAO,MAAM,CAAC,EAAE,UAAU,CAAC,CAAA;AAC1D,KAAK,MAAM,GAAG,IAAI,CAAC,cAAc,CAAC,OAAO,UAAU,CAAC,EAAE,UAAU,CAAC,CAAA;AAEjE,qFAAqF;AACrF,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAA;AAE3E;;;;;;GAMG;AACH,MAAM,WAAW,GAAG;IAClB,EAAE,EAAE,MAAM,CAAA;IACV,gCAAgC;IAChC,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,SAAS,CAAA;IACjB,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,sEAAsE;IACtE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,yEAAyE;IACzE,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,mCAAmC;IACnC,QAAQ,CAAC,EAAE,GAAG,EAAE,CAAA;CACjB;AAkCD,MAAM,WAAW,WAAY,SAAQ,GAAG;IACtC,GAAG,EAAE,GAAG,CAAA;IACR,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC7B,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,SAAS,CAAA;CACrB;AAED;;;;;;;GAOG;AACH,wBAAgB,MAAM,CAAC,EAAE,GAAG,EAAE,KAAS,EAAE,MAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EAAE,WAAW,2CAiFhG;AAED,MAAM,WAAW,eAAgB,SAAQ,MAAM;IAC7C,sDAAsD;IACtD,IAAI,EAAE,GAAG,EAAE,CAAA;IACX,wDAAwD;IACxD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC7B,kDAAkD;IAClD,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,4CAA4C;IAC5C,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,EACzB,IAAI,EACJ,MAAM,EACN,MAAM,EACN,KAAK,EACL,UAAyB,EACzB,GAAG,IAAI,EACR,EAAE,eAAe,2CA+BjB;AAgBD;;;;;;;GAOG;AACH,wBAAgB,IAAI,CAAC,CAAC,SAAS;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,EAC5D,IAAI,EAAE,CAAC,EAAE,GACR,CAAC,CAAC,GAAG;IAAE,QAAQ,EAAE,CAAC,CAAC,GAAG;QAAE,QAAQ,EAAE,KAAK,EAAE,CAAA;KAAE,CAAC,EAAE,CAAA;CAAE,CAAC,EAAE,CAgBrD"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* AgentBoard — the agents working view: who is running, on what, and where.
|
|
5
|
+
*
|
|
6
|
+
* A run is a SESSION, and a session that spawned others is the root of a tree.
|
|
7
|
+
* A flat list draws a fan-out as N unrelated rows and loses the one fact worth
|
|
8
|
+
* seeing — that they are one piece of work — so the board nests children under
|
|
9
|
+
* their root and a collapsed root says how many it is hiding.
|
|
10
|
+
*
|
|
11
|
+
* Grouped by PROJECT, because that is the question a person opens this with:
|
|
12
|
+
* not "what is running" but "what is running on the thing I am working on".
|
|
13
|
+
* A session with no project groups under `ungrouped`, named rather than hidden.
|
|
14
|
+
*
|
|
15
|
+
* Presentational. It takes rows and raises `onOpen`; it does no fetching, no
|
|
16
|
+
* grouping policy beyond the field it is given, and no status derivation. The
|
|
17
|
+
* surface holds the data — `@hanzo/ai`'s `sessions.list` / `sessions.tree` and
|
|
18
|
+
* the stream — so the same board serves hanzo.ai, a channel workspace, or
|
|
19
|
+
* anything else that can answer with rows.
|
|
20
|
+
*/
|
|
21
|
+
import { ScrollView, SizableText, XStack, YStack } from '@hanzo/gui';
|
|
22
|
+
import { ChevronDown, ChevronRight } from '@hanzogui/lucide-icons-2';
|
|
23
|
+
import { useMemo, useState } from 'react';
|
|
24
|
+
import { slot } from '../backends/gui/slot.js';
|
|
25
|
+
const TONE = {
|
|
26
|
+
running: '$green10',
|
|
27
|
+
paused: '$yellow10',
|
|
28
|
+
done: '$color10',
|
|
29
|
+
error: '$red10',
|
|
30
|
+
unknown: '$color8',
|
|
31
|
+
};
|
|
32
|
+
/** The label a status wears. Separate from the colour so neither is guessed. */
|
|
33
|
+
const SAYS = {
|
|
34
|
+
running: 'running',
|
|
35
|
+
paused: 'paused',
|
|
36
|
+
done: 'done',
|
|
37
|
+
error: 'error',
|
|
38
|
+
unknown: '—',
|
|
39
|
+
};
|
|
40
|
+
function Dot({ status }) {
|
|
41
|
+
return (_jsx(YStack, { width: 7, height: 7, rounded: 9999, bg: TONE[status], shrink: 0, "aria-label": SAYS[status] }));
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* One row, and its children under it.
|
|
45
|
+
*
|
|
46
|
+
* Expansion is per-row local state: a board is read top-down and a caller that
|
|
47
|
+
* wanted to persist which roots are open would be holding UI state for a list
|
|
48
|
+
* it does not own. A root with children opens by default, because the fan-out
|
|
49
|
+
* is the thing worth seeing.
|
|
50
|
+
*/
|
|
51
|
+
export function RunRow({ run, depth = 0, active = false, onOpen, trailing, ...rest }) {
|
|
52
|
+
const kids = run.children ?? [];
|
|
53
|
+
const [open, setOpen] = useState(true);
|
|
54
|
+
return (_jsxs(YStack, { ...slot('run-row'), ...rest, children: [_jsxs(XStack, { items: "center", gap: "$2", px: "$2", py: "$1.5", rounded: "$3", pl: 8 + depth * 16, cursor: "pointer", role: "button", tabIndex: 0, "aria-current": active ? 'true' : undefined, bg: active ? '$edge' : undefined, hoverStyle: { bg: active ? '$edge' : '$hover' }, pressStyle: { bg: '$raised' }, onPress: () => onOpen?.(run.id), children: [kids.length ? (_jsx(XStack, { width: 16, height: 16, items: "center", justify: "center", shrink: 0, role: "button", tabIndex: 0, "aria-expanded": open, "aria-label": open ? 'Hide sub-agents' : 'Show sub-agents', onPress: (e) => {
|
|
55
|
+
e.stopPropagation?.();
|
|
56
|
+
setOpen((v) => !v);
|
|
57
|
+
}, children: open ? _jsx(ChevronDown, { size: 13, opacity: 0.6 }) : _jsx(ChevronRight, { size: 13, opacity: 0.6 }) })) : (_jsx(YStack, { width: 16, shrink: 0 })), _jsx(Dot, { status: run.status }), _jsx(SizableText, { size: "$2", numberOfLines: 1, shrink: 1, color: active ? '$color' : '$quiet', children: run.agent }), run.doing ? (_jsx(SizableText, { size: "$1", numberOfLines: 1, flex: 1, color: "$soft", children: run.doing })) : (_jsx(XStack, { flex: 1 })), kids.length && !open ? (_jsxs(SizableText, { size: "$1", color: "$soft", children: [kids.length, " sub-agent", kids.length === 1 ? '' : 's'] })) : null, run.target ? (_jsx(SizableText, { size: "$1", color: "$soft", numberOfLines: 1, children: run.target })) : null, trailing] }), open && kids.length
|
|
58
|
+
? kids.map((kid) => (_jsx(RunRow, { run: kid, depth: depth + 1, onOpen: onOpen }, kid.id)))
|
|
59
|
+
: null] }));
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The board.
|
|
63
|
+
*
|
|
64
|
+
* Groups are ordered by name with the un-projected group LAST, so the answer to
|
|
65
|
+
* "what is running on my project" is never below a pile of stray runs.
|
|
66
|
+
*/
|
|
67
|
+
export function AgentBoard({ runs, active, onOpen, empty, otherLabel = 'Other work', ...rest }) {
|
|
68
|
+
const groups = useMemo(() => group(runs, otherLabel), [runs, otherLabel]);
|
|
69
|
+
if (!runs.length) {
|
|
70
|
+
return (_jsx(YStack, { ...slot('agent-board'), p: "$4", ...rest, children: empty ?? (_jsx(SizableText, { size: "$2", color: "$soft", children: "Nothing running." })) }));
|
|
71
|
+
}
|
|
72
|
+
return (_jsx(ScrollView, { ...slot('agent-board'), flex: 1, showsVerticalScrollIndicator: false, ...rest, children: _jsx(YStack, { gap: "$1", p: "$2", children: groups.map(([project, rows]) => (_jsxs(YStack, { mt: "$2", gap: "$0.5", children: [_jsx(SizableText, { size: "$1", px: "$2", py: "$1", color: "$soft", fontWeight: "500", children: project }), rows.map((run) => (_jsx(RunRow, { run: run, active: run.id === active, onOpen: onOpen }, run.id)))] }, project))) }) }));
|
|
73
|
+
}
|
|
74
|
+
/** Group roots by project, un-projected last. */
|
|
75
|
+
function group(runs, otherLabel) {
|
|
76
|
+
const by = new Map();
|
|
77
|
+
for (const run of runs) {
|
|
78
|
+
const key = run.project?.trim() || otherLabel;
|
|
79
|
+
const rows = by.get(key);
|
|
80
|
+
if (rows)
|
|
81
|
+
rows.push(run);
|
|
82
|
+
else
|
|
83
|
+
by.set(key, [run]);
|
|
84
|
+
}
|
|
85
|
+
return [...by.entries()].sort(([a], [b]) => a === otherLabel ? 1 : b === otherLabel ? -1 : a.localeCompare(b));
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Build the tree a board draws from the flat list an API answers with.
|
|
89
|
+
*
|
|
90
|
+
* A session names its root rather than its parent, so a fan-out is TWO levels
|
|
91
|
+
* and not N: every non-root hangs off its root. A row whose root is not in the
|
|
92
|
+
* list is treated as a root itself — otherwise a filtered page silently drops
|
|
93
|
+
* work, which is the one thing a board must not do.
|
|
94
|
+
*/
|
|
95
|
+
export function nest(rows) {
|
|
96
|
+
const roots = new Map();
|
|
97
|
+
const orphans = [];
|
|
98
|
+
for (const row of rows) {
|
|
99
|
+
if (!row.rootId || row.rootId === row.id)
|
|
100
|
+
roots.set(row.id, { ...row, children: [] });
|
|
101
|
+
}
|
|
102
|
+
for (const row of rows) {
|
|
103
|
+
if (!row.rootId || row.rootId === row.id)
|
|
104
|
+
continue;
|
|
105
|
+
const root = roots.get(row.rootId);
|
|
106
|
+
if (root)
|
|
107
|
+
root.children.push({ ...row, children: [] });
|
|
108
|
+
else
|
|
109
|
+
orphans.push(row);
|
|
110
|
+
}
|
|
111
|
+
for (const row of orphans)
|
|
112
|
+
roots.set(row.id, { ...row, children: [] });
|
|
113
|
+
return [...roots.values()];
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=Board.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Board.js","sourceRoot":"","sources":["../../src/agents/Board.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
'use client';"use strict";
|
|
2
|
+
'use client';
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.Screen = Screen;
|
|
5
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
|
+
/**
|
|
7
|
+
* Screen — watching a run while it works.
|
|
8
|
+
*
|
|
9
|
+
* Some runs have something to look at: a sandbox serving an interactive page, a
|
|
10
|
+
* headful browser a person can watch over VNC. Most have nothing, and this
|
|
11
|
+
* component's first job is to say which it is, honestly — a viewer that draws a
|
|
12
|
+
* black rectangle for a run with no screen has told the reader the run is
|
|
13
|
+
* broken.
|
|
14
|
+
*
|
|
15
|
+
* THE CREDENTIAL NEVER OUTLIVES THE VIEW. A watchable surface is authorized by
|
|
16
|
+
* a short-lived TICKET the caller mints per view; this component asks for one
|
|
17
|
+
* when it mounts, embeds the page the ticket names, and asks for another if the
|
|
18
|
+
* viewer stays past its life. It does NOT take a URL with a long-lived token in
|
|
19
|
+
* it, and there is no prop to pass one — an embed URL ends up in browser
|
|
20
|
+
* history, in a screenshot, and in whatever the parent frame can read.
|
|
21
|
+
*
|
|
22
|
+
* The mint is a PROP, not an import, because who may watch is the surface's
|
|
23
|
+
* question and the answer lives in `@hanzo/ai` on one deployment and behind a
|
|
24
|
+
* BFF on another. This file owns the frame, the states and the refresh.
|
|
25
|
+
*/
|
|
26
|
+
const gui_1 = require("@hanzo/gui");
|
|
27
|
+
const react_1 = require("react");
|
|
28
|
+
const slot_1 = require("../backends/gui/slot.cjs");
|
|
29
|
+
const NAMES = {
|
|
30
|
+
sandbox: 'sandbox',
|
|
31
|
+
browser: 'browser',
|
|
32
|
+
};
|
|
33
|
+
/** Re-mint at 80% of a ticket's life — early enough that the frame never blinks. */
|
|
34
|
+
const EARLY = 0.8;
|
|
35
|
+
function Screen({ sessionId, kind, mint, empty, title, ...rest }) {
|
|
36
|
+
const [state, setState] = (0, react_1.useState)({ phase: 'idle' });
|
|
37
|
+
const timer = (0, react_1.useRef)(null);
|
|
38
|
+
const live = (0, react_1.useRef)(true);
|
|
39
|
+
const open = (0, react_1.useCallback)(async () => {
|
|
40
|
+
if (!mint || !kind)
|
|
41
|
+
return;
|
|
42
|
+
setState({ phase: 'minting' });
|
|
43
|
+
try {
|
|
44
|
+
const ticket = await mint(sessionId);
|
|
45
|
+
if (!live.current)
|
|
46
|
+
return;
|
|
47
|
+
setState({ phase: 'watching', url: ticket.url });
|
|
48
|
+
if (ticket.expiresIn && ticket.expiresIn > 0) {
|
|
49
|
+
if (timer.current)
|
|
50
|
+
clearTimeout(timer.current);
|
|
51
|
+
timer.current = setTimeout(() => void open(), ticket.expiresIn * 1000 * EARLY);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
if (!live.current)
|
|
56
|
+
return;
|
|
57
|
+
setState({
|
|
58
|
+
phase: 'failed',
|
|
59
|
+
message: err instanceof Error ? err.message : 'Could not open this screen.',
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}, [mint, kind, sessionId]);
|
|
63
|
+
(0, react_1.useEffect)(() => {
|
|
64
|
+
live.current = true;
|
|
65
|
+
void open();
|
|
66
|
+
return () => {
|
|
67
|
+
live.current = false;
|
|
68
|
+
if (timer.current)
|
|
69
|
+
clearTimeout(timer.current);
|
|
70
|
+
};
|
|
71
|
+
}, [open]);
|
|
72
|
+
if (!kind) {
|
|
73
|
+
return ((0, jsx_runtime_1.jsx)(Frame, { title: title, ...rest, children: empty ?? ((0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$2", color: "$soft", children: "This run has no screen." })) }));
|
|
74
|
+
}
|
|
75
|
+
if (!mint) {
|
|
76
|
+
return ((0, jsx_runtime_1.jsx)(Frame, { title: title, ...rest, children: (0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$2", color: "$soft", children: "Watching is not wired up on this surface." }) }));
|
|
77
|
+
}
|
|
78
|
+
if (state.phase === 'failed') {
|
|
79
|
+
return ((0, jsx_runtime_1.jsx)(Frame, { title: title, ...rest, children: (0, jsx_runtime_1.jsxs)(gui_1.YStack, { gap: "$2", items: "center", children: [(0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$2", color: "$soft", children: state.message }), (0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$2", color: "$color", cursor: "pointer", role: "button", tabIndex: 0, onPress: () => void open(), children: "Try again" })] }) }));
|
|
80
|
+
}
|
|
81
|
+
if (state.phase !== 'watching') {
|
|
82
|
+
return ((0, jsx_runtime_1.jsx)(Frame, { title: title, ...rest, children: (0, jsx_runtime_1.jsxs)(gui_1.SizableText, { size: "$2", color: "$soft", children: ["Opening the ", NAMES[kind], "\u2026"] }) }));
|
|
83
|
+
}
|
|
84
|
+
return ((0, jsx_runtime_1.jsxs)(gui_1.YStack, { ...(0, slot_1.slot)('screen'), flex: 1, minH: 0, bg: "$panel", rounded: "$4", overflow: "hidden", ...rest, children: [title ? (0, jsx_runtime_1.jsx)(Caption, { children: title }) : null, (0, jsx_runtime_1.jsx)("iframe", { src: state.url, title: title ?? `Live ${NAMES[kind]}`, sandbox: "allow-scripts allow-same-origin allow-forms", referrerPolicy: "no-referrer", allow: "clipboard-read; clipboard-write", style: { border: 0, width: '100%', height: '100%', flex: 1, display: 'block' } })] }));
|
|
85
|
+
}
|
|
86
|
+
function Caption({ children }) {
|
|
87
|
+
return ((0, jsx_runtime_1.jsx)(gui_1.XStack, { px: "$3", py: "$2", borderBottomWidth: 1, borderColor: "$borderColor", children: (0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$1", color: "$soft", numberOfLines: 1, children: children }) }));
|
|
88
|
+
}
|
|
89
|
+
function Frame({ title, children, ...rest }) {
|
|
90
|
+
return ((0, jsx_runtime_1.jsxs)(gui_1.YStack, { ...(0, slot_1.slot)('screen'), flex: 1, minH: 0, bg: "$panel", rounded: "$4", overflow: "hidden", ...rest, children: [title ? (0, jsx_runtime_1.jsx)(Caption, { children: title }) : null, (0, jsx_runtime_1.jsx)(gui_1.YStack, { flex: 1, items: "center", justify: "center", p: "$4", children: children })] }));
|
|
91
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Screen — watching a run while it works.
|
|
3
|
+
*
|
|
4
|
+
* Some runs have something to look at: a sandbox serving an interactive page, a
|
|
5
|
+
* headful browser a person can watch over VNC. Most have nothing, and this
|
|
6
|
+
* component's first job is to say which it is, honestly — a viewer that draws a
|
|
7
|
+
* black rectangle for a run with no screen has told the reader the run is
|
|
8
|
+
* broken.
|
|
9
|
+
*
|
|
10
|
+
* THE CREDENTIAL NEVER OUTLIVES THE VIEW. A watchable surface is authorized by
|
|
11
|
+
* a short-lived TICKET the caller mints per view; this component asks for one
|
|
12
|
+
* when it mounts, embeds the page the ticket names, and asks for another if the
|
|
13
|
+
* viewer stays past its life. It does NOT take a URL with a long-lived token in
|
|
14
|
+
* it, and there is no prop to pass one — an embed URL ends up in browser
|
|
15
|
+
* history, in a screenshot, and in whatever the parent frame can read.
|
|
16
|
+
*
|
|
17
|
+
* The mint is a PROP, not an import, because who may watch is the surface's
|
|
18
|
+
* question and the answer lives in `@hanzo/ai` on one deployment and behind a
|
|
19
|
+
* BFF on another. This file owns the frame, the states and the refresh.
|
|
20
|
+
*/
|
|
21
|
+
import { YStack } from '@hanzo/gui';
|
|
22
|
+
import { type ComponentProps, type ReactNode } from 'react';
|
|
23
|
+
type Col = Omit<ComponentProps<typeof YStack>, 'children'>;
|
|
24
|
+
/** What a mint answers with: where to look, and for how long. */
|
|
25
|
+
export interface Ticket {
|
|
26
|
+
/** The page to embed. Already carries whatever the grant put in it. */
|
|
27
|
+
url: string;
|
|
28
|
+
/** Seconds until it stops working. Absent, the view is not refreshed. */
|
|
29
|
+
expiresIn?: number;
|
|
30
|
+
}
|
|
31
|
+
/** How a run's screen is served, for the label a viewer reads. */
|
|
32
|
+
export type ScreenKind = 'sandbox' | 'browser';
|
|
33
|
+
export interface ScreenProps extends Col {
|
|
34
|
+
/** The run being watched. Changing it re-mints. */
|
|
35
|
+
sessionId: string;
|
|
36
|
+
/**
|
|
37
|
+
* What this run has to look at. `null` means nothing does — the honest empty
|
|
38
|
+
* state, and the common case. The caller decides; this component never guesses
|
|
39
|
+
* from a session's shape.
|
|
40
|
+
*/
|
|
41
|
+
kind?: ScreenKind | null;
|
|
42
|
+
/**
|
|
43
|
+
* Mint a view. Called on mount, on `sessionId` change, and shortly before the
|
|
44
|
+
* previous ticket expires. Throwing renders the failure with a retry.
|
|
45
|
+
*/
|
|
46
|
+
mint?: (sessionId: string) => Promise<Ticket>;
|
|
47
|
+
/** Shown when `kind` is null. */
|
|
48
|
+
empty?: ReactNode;
|
|
49
|
+
title?: string;
|
|
50
|
+
}
|
|
51
|
+
export declare function Screen({ sessionId, kind, mint, empty, title, ...rest }: ScreenProps): import("react/jsx-runtime").JSX.Element;
|
|
52
|
+
export {};
|
|
53
|
+
//# sourceMappingURL=Screen.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Screen.d.ts","sourceRoot":"","sources":["../../src/agents/Screen.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAuB,MAAM,EAAE,MAAM,YAAY,CAAA;AACxD,OAAO,EAA4C,KAAK,cAAc,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAIrG,KAAK,GAAG,GAAG,IAAI,CAAC,cAAc,CAAC,OAAO,MAAM,CAAC,EAAE,UAAU,CAAC,CAAA;AAE1D,iEAAiE;AACjE,MAAM,WAAW,MAAM;IACrB,uEAAuE;IACvE,GAAG,EAAE,MAAM,CAAA;IACX,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,kEAAkE;AAClE,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,SAAS,CAAA;AAO9C,MAAM,WAAW,WAAY,SAAQ,GAAG;IACtC,mDAAmD;IACnD,SAAS,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,IAAI,CAAC,EAAE,UAAU,GAAG,IAAI,CAAA;IACxB;;;OAGG;IACH,IAAI,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IAC7C,iCAAiC;IACjC,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAWD,wBAAgB,MAAM,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,IAAI,EAAE,EAAE,WAAW,2CA2GnF"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* Screen — watching a run while it works.
|
|
5
|
+
*
|
|
6
|
+
* Some runs have something to look at: a sandbox serving an interactive page, a
|
|
7
|
+
* headful browser a person can watch over VNC. Most have nothing, and this
|
|
8
|
+
* component's first job is to say which it is, honestly — a viewer that draws a
|
|
9
|
+
* black rectangle for a run with no screen has told the reader the run is
|
|
10
|
+
* broken.
|
|
11
|
+
*
|
|
12
|
+
* THE CREDENTIAL NEVER OUTLIVES THE VIEW. A watchable surface is authorized by
|
|
13
|
+
* a short-lived TICKET the caller mints per view; this component asks for one
|
|
14
|
+
* when it mounts, embeds the page the ticket names, and asks for another if the
|
|
15
|
+
* viewer stays past its life. It does NOT take a URL with a long-lived token in
|
|
16
|
+
* it, and there is no prop to pass one — an embed URL ends up in browser
|
|
17
|
+
* history, in a screenshot, and in whatever the parent frame can read.
|
|
18
|
+
*
|
|
19
|
+
* The mint is a PROP, not an import, because who may watch is the surface's
|
|
20
|
+
* question and the answer lives in `@hanzo/ai` on one deployment and behind a
|
|
21
|
+
* BFF on another. This file owns the frame, the states and the refresh.
|
|
22
|
+
*/
|
|
23
|
+
import { SizableText, XStack, YStack } from '@hanzo/gui';
|
|
24
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
25
|
+
import { slot } from '../backends/gui/slot.js';
|
|
26
|
+
const NAMES = {
|
|
27
|
+
sandbox: 'sandbox',
|
|
28
|
+
browser: 'browser',
|
|
29
|
+
};
|
|
30
|
+
/** Re-mint at 80% of a ticket's life — early enough that the frame never blinks. */
|
|
31
|
+
const EARLY = 0.8;
|
|
32
|
+
export function Screen({ sessionId, kind, mint, empty, title, ...rest }) {
|
|
33
|
+
const [state, setState] = useState({ phase: 'idle' });
|
|
34
|
+
const timer = useRef(null);
|
|
35
|
+
const live = useRef(true);
|
|
36
|
+
const open = useCallback(async () => {
|
|
37
|
+
if (!mint || !kind)
|
|
38
|
+
return;
|
|
39
|
+
setState({ phase: 'minting' });
|
|
40
|
+
try {
|
|
41
|
+
const ticket = await mint(sessionId);
|
|
42
|
+
if (!live.current)
|
|
43
|
+
return;
|
|
44
|
+
setState({ phase: 'watching', url: ticket.url });
|
|
45
|
+
if (ticket.expiresIn && ticket.expiresIn > 0) {
|
|
46
|
+
if (timer.current)
|
|
47
|
+
clearTimeout(timer.current);
|
|
48
|
+
timer.current = setTimeout(() => void open(), ticket.expiresIn * 1000 * EARLY);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
if (!live.current)
|
|
53
|
+
return;
|
|
54
|
+
setState({
|
|
55
|
+
phase: 'failed',
|
|
56
|
+
message: err instanceof Error ? err.message : 'Could not open this screen.',
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}, [mint, kind, sessionId]);
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
live.current = true;
|
|
62
|
+
void open();
|
|
63
|
+
return () => {
|
|
64
|
+
live.current = false;
|
|
65
|
+
if (timer.current)
|
|
66
|
+
clearTimeout(timer.current);
|
|
67
|
+
};
|
|
68
|
+
}, [open]);
|
|
69
|
+
if (!kind) {
|
|
70
|
+
return (_jsx(Frame, { title: title, ...rest, children: empty ?? (_jsx(SizableText, { size: "$2", color: "$soft", children: "This run has no screen." })) }));
|
|
71
|
+
}
|
|
72
|
+
if (!mint) {
|
|
73
|
+
return (_jsx(Frame, { title: title, ...rest, children: _jsx(SizableText, { size: "$2", color: "$soft", children: "Watching is not wired up on this surface." }) }));
|
|
74
|
+
}
|
|
75
|
+
if (state.phase === 'failed') {
|
|
76
|
+
return (_jsx(Frame, { title: title, ...rest, children: _jsxs(YStack, { gap: "$2", items: "center", children: [_jsx(SizableText, { size: "$2", color: "$soft", children: state.message }), _jsx(SizableText, { size: "$2", color: "$color", cursor: "pointer", role: "button", tabIndex: 0, onPress: () => void open(), children: "Try again" })] }) }));
|
|
77
|
+
}
|
|
78
|
+
if (state.phase !== 'watching') {
|
|
79
|
+
return (_jsx(Frame, { title: title, ...rest, children: _jsxs(SizableText, { size: "$2", color: "$soft", children: ["Opening the ", NAMES[kind], "\u2026"] }) }));
|
|
80
|
+
}
|
|
81
|
+
return (_jsxs(YStack, { ...slot('screen'), flex: 1, minH: 0, bg: "$panel", rounded: "$4", overflow: "hidden", ...rest, children: [title ? _jsx(Caption, { children: title }) : null, _jsx("iframe", { src: state.url, title: title ?? `Live ${NAMES[kind]}`, sandbox: "allow-scripts allow-same-origin allow-forms", referrerPolicy: "no-referrer", allow: "clipboard-read; clipboard-write", style: { border: 0, width: '100%', height: '100%', flex: 1, display: 'block' } })] }));
|
|
82
|
+
}
|
|
83
|
+
function Caption({ children }) {
|
|
84
|
+
return (_jsx(XStack, { px: "$3", py: "$2", borderBottomWidth: 1, borderColor: "$borderColor", children: _jsx(SizableText, { size: "$1", color: "$soft", numberOfLines: 1, children: children }) }));
|
|
85
|
+
}
|
|
86
|
+
function Frame({ title, children, ...rest }) {
|
|
87
|
+
return (_jsxs(YStack, { ...slot('screen'), flex: 1, minH: 0, bg: "$panel", rounded: "$4", overflow: "hidden", ...rest, children: [title ? _jsx(Caption, { children: title }) : null, _jsx(YStack, { flex: 1, items: "center", justify: "center", p: "$4", children: children })] }));
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=Screen.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Screen.js","sourceRoot":"","sources":["../../src/agents/Screen.tsx"],"names":[],"mappings":"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"}
|