@hanzo/ui 8.14.0 → 8.14.2
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/Pane.cjs +74 -0
- package/dist/agents/Pane.d.ts +84 -0
- package/dist/agents/Pane.d.ts.map +1 -0
- package/dist/agents/Pane.js +68 -0
- package/dist/agents/Pane.js.map +1 -0
- package/dist/agents/Progress.cjs +130 -0
- package/dist/agents/Progress.d.ts +67 -0
- package/dist/agents/Progress.d.ts.map +1 -0
- package/dist/agents/Progress.js +127 -0
- package/dist/agents/Progress.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 +45 -0
- package/dist/agents/index.d.ts +28 -0
- package/dist/agents/index.d.ts.map +1 -0
- package/dist/agents/index.js +28 -0
- package/dist/agents/index.js.map +1 -0
- 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/css.cjs +17 -0
- package/dist/css.d.ts +30 -0
- package/dist/css.d.ts.map +1 -0
- package/dist/css.js +14 -0
- package/dist/css.js.map +1 -0
- package/dist/gallery.html +1 -1
- package/dist/index.cjs +3 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/index.node.js +1 -0
- package/dist/next.cjs +6 -0
- package/package.json +8 -2
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
'use client';"use strict";
|
|
2
|
+
'use client';
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.ProgressBlock = ProgressBlock;
|
|
5
|
+
exports.steps = steps;
|
|
6
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
7
|
+
/**
|
|
8
|
+
* ProgressBlock — an agent's turn, as the steps it took.
|
|
9
|
+
*
|
|
10
|
+
* A run emits one event per token and per tool call, and printed flat that is a
|
|
11
|
+
* wall. What a reader wants is the SHAPE of the work: it thought, it searched,
|
|
12
|
+
* it read two files, it finished — each one line, each openable for the detail,
|
|
13
|
+
* and the answer underneath at full size.
|
|
14
|
+
*
|
|
15
|
+
* So a step is a LABEL plus DETAIL, and the detail is closed by default. The
|
|
16
|
+
* label is the claim ("Read 2 files"); the detail is the evidence
|
|
17
|
+
* ("BuyNowButton.tsx", "styles.css"). A reader skims labels and opens the one
|
|
18
|
+
* they doubt — which is the whole reason this beats a transcript.
|
|
19
|
+
*
|
|
20
|
+
* `done` is a state of the BLOCK, not a step: a run that is still working shows
|
|
21
|
+
* no tick, and a tick that appears before the work stops is a lie a reader will
|
|
22
|
+
* only catch once.
|
|
23
|
+
*
|
|
24
|
+
* Presentational. `steps()` maps session events to this shape and is exported
|
|
25
|
+
* beside it, because every surface reading the same stream should group it the
|
|
26
|
+
* same way.
|
|
27
|
+
*/
|
|
28
|
+
const gui_1 = require("@hanzo/gui");
|
|
29
|
+
const lucide_icons_2_1 = require("@hanzogui/lucide-icons-2");
|
|
30
|
+
const react_1 = require("react");
|
|
31
|
+
const slot_1 = require("../backends/gui/slot.cjs");
|
|
32
|
+
const ICON = {
|
|
33
|
+
think: lucide_icons_2_1.Lightbulb,
|
|
34
|
+
search: lucide_icons_2_1.Sparkles,
|
|
35
|
+
read: lucide_icons_2_1.FileText,
|
|
36
|
+
run: lucide_icons_2_1.Terminal,
|
|
37
|
+
write: lucide_icons_2_1.FileText,
|
|
38
|
+
error: lucide_icons_2_1.TriangleAlert,
|
|
39
|
+
step: lucide_icons_2_1.CircleDot,
|
|
40
|
+
};
|
|
41
|
+
function Row({ step }) {
|
|
42
|
+
const [open, setOpen] = (0, react_1.useState)(step.open ?? false);
|
|
43
|
+
const Icon = ICON[step.kind];
|
|
44
|
+
const has = !!step.detail?.length;
|
|
45
|
+
return ((0, jsx_runtime_1.jsxs)(gui_1.YStack, { gap: "$0.5", children: [(0, jsx_runtime_1.jsxs)(gui_1.XStack, { items: "center", gap: "$1.5", cursor: has ? 'pointer' : 'default', role: has ? 'button' : undefined, tabIndex: has ? 0 : undefined, "aria-expanded": has ? open : undefined, onPress: () => has && setOpen((v) => !v), children: [(0, jsx_runtime_1.jsx)(Icon, { size: 13, opacity: 0.55 }), (0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$2", color: "$quiet", children: step.label }), has ? (open ? ((0, jsx_runtime_1.jsx)(lucide_icons_2_1.ChevronDown, { size: 12, opacity: 0.5 })) : ((0, jsx_runtime_1.jsx)(lucide_icons_2_1.ChevronRight, { size: 12, opacity: 0.5 }))) : null] }), has && open ? ((0, jsx_runtime_1.jsx)(gui_1.YStack, { pl: "$4", ml: "$1", gap: "$0.5", borderLeftWidth: 1, borderColor: "$borderColor", children: step.detail.map((line, i) => ((0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$2", color: "$soft", pl: "$2", children: line }, `${step.key}-${i}`))) })) : null] }));
|
|
46
|
+
}
|
|
47
|
+
function ProgressBlock({ steps, done = false, doneLabel = 'Done', children, ...rest }) {
|
|
48
|
+
return ((0, jsx_runtime_1.jsxs)(gui_1.YStack, { ...(0, slot_1.slot)('progress-block'), gap: "$1.5", ...rest, children: [steps.map((step) => ((0, jsx_runtime_1.jsx)(Row, { step: step }, step.key))), done ? ((0, jsx_runtime_1.jsxs)(gui_1.XStack, { items: "center", gap: "$1.5", children: [(0, jsx_runtime_1.jsx)(lucide_icons_2_1.Check, { size: 13, opacity: 0.75 }), (0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$2", color: "$quiet", children: doneLabel })] })) : null, children ? (0, jsx_runtime_1.jsx)(gui_1.YStack, { pt: "$1", children: children }) : null] }));
|
|
49
|
+
}
|
|
50
|
+
/** Map an event's kind onto a step kind. Unknown kinds stay generic rather than
|
|
51
|
+
* being forced into the nearest icon, which would misreport what happened. */
|
|
52
|
+
function kindOf(kind) {
|
|
53
|
+
const k = kind.toLowerCase();
|
|
54
|
+
if (k.includes('think') || k.includes('reason'))
|
|
55
|
+
return 'think';
|
|
56
|
+
if (k.includes('search') || k.includes('grep'))
|
|
57
|
+
return 'search';
|
|
58
|
+
if (k.includes('read') || k.includes('file'))
|
|
59
|
+
return 'read';
|
|
60
|
+
if (k.includes('tool') || k.includes('exec') || k.includes('command'))
|
|
61
|
+
return 'run';
|
|
62
|
+
if (k.includes('write') || k.includes('edit') || k.includes('patch'))
|
|
63
|
+
return 'write';
|
|
64
|
+
if (k.includes('error') || k.includes('fail'))
|
|
65
|
+
return 'error';
|
|
66
|
+
return 'step';
|
|
67
|
+
}
|
|
68
|
+
/** One readable line out of whatever a payload turned out to be. */
|
|
69
|
+
function say(payload) {
|
|
70
|
+
if (payload == null)
|
|
71
|
+
return '';
|
|
72
|
+
if (typeof payload === 'string')
|
|
73
|
+
return payload;
|
|
74
|
+
if (typeof payload !== 'object')
|
|
75
|
+
return String(payload);
|
|
76
|
+
const bag = payload;
|
|
77
|
+
for (const key of ['text', 'message', 'preview', 'content', 'name', 'command', 'path']) {
|
|
78
|
+
const found = bag[key];
|
|
79
|
+
if (typeof found === 'string' && found.trim())
|
|
80
|
+
return found;
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
return JSON.stringify(payload);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return '';
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Group a run's events into steps.
|
|
91
|
+
*
|
|
92
|
+
* Consecutive events of the same kind become ONE step whose detail is their
|
|
93
|
+
* lines — that is what turns twelve `tool-call` events into "Read 2 files" with
|
|
94
|
+
* two names under it. `message` events are NOT steps: they are the answer, and
|
|
95
|
+
* the caller renders them as `children`.
|
|
96
|
+
*/
|
|
97
|
+
function steps(events) {
|
|
98
|
+
const out = [];
|
|
99
|
+
const said = [];
|
|
100
|
+
events.forEach((event, i) => {
|
|
101
|
+
const kind = event.kind ?? 'step';
|
|
102
|
+
const line = say(event.payload).trim();
|
|
103
|
+
if (kind === 'message') {
|
|
104
|
+
if (line)
|
|
105
|
+
said.push(line);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const step = kindOf(kind);
|
|
109
|
+
const last = out[out.length - 1];
|
|
110
|
+
if (last && last.kind === step) {
|
|
111
|
+
if (line)
|
|
112
|
+
(last.detail ??= []).push(line);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
out.push({
|
|
116
|
+
key: `${event.seq ?? event.id ?? i}`,
|
|
117
|
+
kind: step,
|
|
118
|
+
label: kind,
|
|
119
|
+
detail: line ? [line] : undefined,
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
// A step's label counts what it did, once the run is grouped — "Read 2 files"
|
|
123
|
+
// rather than "read". The verb is the event's own; the number is ours.
|
|
124
|
+
for (const step of out) {
|
|
125
|
+
const n = step.detail?.length ?? 0;
|
|
126
|
+
if (n > 1)
|
|
127
|
+
step.label = `${step.label} ${n}`;
|
|
128
|
+
}
|
|
129
|
+
return { steps: out, message: said.join('') };
|
|
130
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ProgressBlock — an agent's turn, as the steps it took.
|
|
3
|
+
*
|
|
4
|
+
* A run emits one event per token and per tool call, and printed flat that is a
|
|
5
|
+
* wall. What a reader wants is the SHAPE of the work: it thought, it searched,
|
|
6
|
+
* it read two files, it finished — each one line, each openable for the detail,
|
|
7
|
+
* and the answer underneath at full size.
|
|
8
|
+
*
|
|
9
|
+
* So a step is a LABEL plus DETAIL, and the detail is closed by default. The
|
|
10
|
+
* label is the claim ("Read 2 files"); the detail is the evidence
|
|
11
|
+
* ("BuyNowButton.tsx", "styles.css"). A reader skims labels and opens the one
|
|
12
|
+
* they doubt — which is the whole reason this beats a transcript.
|
|
13
|
+
*
|
|
14
|
+
* `done` is a state of the BLOCK, not a step: a run that is still working shows
|
|
15
|
+
* no tick, and a tick that appears before the work stops is a lie a reader will
|
|
16
|
+
* only catch once.
|
|
17
|
+
*
|
|
18
|
+
* Presentational. `steps()` maps session events to this shape and is exported
|
|
19
|
+
* beside it, because every surface reading the same stream should group it the
|
|
20
|
+
* same way.
|
|
21
|
+
*/
|
|
22
|
+
import { YStack } from '@hanzo/gui';
|
|
23
|
+
import { type ComponentProps, type ReactNode } from 'react';
|
|
24
|
+
type Col = Omit<ComponentProps<typeof YStack>, 'children'>;
|
|
25
|
+
/** What a step was. Chooses the icon, and nothing else. */
|
|
26
|
+
export type StepKind = 'think' | 'search' | 'read' | 'run' | 'write' | 'error' | 'step';
|
|
27
|
+
export interface Step {
|
|
28
|
+
key: string;
|
|
29
|
+
kind: StepKind;
|
|
30
|
+
/** The claim — one line, always visible. */
|
|
31
|
+
label: string;
|
|
32
|
+
/** The evidence, one line each. Absent means nothing to open. */
|
|
33
|
+
detail?: string[];
|
|
34
|
+
/** Open on first render — for the step being worked on right now. */
|
|
35
|
+
open?: boolean;
|
|
36
|
+
}
|
|
37
|
+
export interface ProgressBlockProps extends Col {
|
|
38
|
+
steps: Step[];
|
|
39
|
+
/** The work has stopped. Draws the tick. */
|
|
40
|
+
done?: boolean;
|
|
41
|
+
doneLabel?: string;
|
|
42
|
+
/** The agent's message, at full size under the steps. */
|
|
43
|
+
children?: ReactNode;
|
|
44
|
+
}
|
|
45
|
+
export declare function ProgressBlock({ steps, done, doneLabel, children, ...rest }: ProgressBlockProps): import("react/jsx-runtime").JSX.Element;
|
|
46
|
+
/** The shape `steps()` reads. A session event, or anything that looks like one. */
|
|
47
|
+
export interface Event {
|
|
48
|
+
seq?: number;
|
|
49
|
+
id?: string;
|
|
50
|
+
kind?: string;
|
|
51
|
+
actor?: string;
|
|
52
|
+
payload?: unknown;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Group a run's events into steps.
|
|
56
|
+
*
|
|
57
|
+
* Consecutive events of the same kind become ONE step whose detail is their
|
|
58
|
+
* lines — that is what turns twelve `tool-call` events into "Read 2 files" with
|
|
59
|
+
* two names under it. `message` events are NOT steps: they are the answer, and
|
|
60
|
+
* the caller renders them as `children`.
|
|
61
|
+
*/
|
|
62
|
+
export declare function steps(events: Event[]): {
|
|
63
|
+
steps: Step[];
|
|
64
|
+
message: string;
|
|
65
|
+
};
|
|
66
|
+
export {};
|
|
67
|
+
//# sourceMappingURL=Progress.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Progress.d.ts","sourceRoot":"","sources":["../../src/agents/Progress.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EAAuB,MAAM,EAAE,MAAM,YAAY,CAAA;AAaxD,OAAO,EAAY,KAAK,cAAc,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAIrE,KAAK,GAAG,GAAG,IAAI,CAAC,cAAc,CAAC,OAAO,MAAM,CAAC,EAAE,UAAU,CAAC,CAAA;AAE1D,2DAA2D;AAC3D,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,CAAA;AAYvF,MAAM,WAAW,IAAI;IACnB,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,QAAQ,CAAA;IACd,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAA;IACb,iEAAiE;IACjE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB,qEAAqE;IACrE,IAAI,CAAC,EAAE,OAAO,CAAA;CACf;AA8CD,MAAM,WAAW,kBAAmB,SAAQ,GAAG;IAC7C,KAAK,EAAE,IAAI,EAAE,CAAA;IACb,4CAA4C;IAC5C,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,yDAAyD;IACzD,QAAQ,CAAC,EAAE,SAAS,CAAA;CACrB;AAED,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,IAAY,EACZ,SAAkB,EAClB,QAAQ,EACR,GAAG,IAAI,EACR,EAAE,kBAAkB,2CAmBpB;AAED,mFAAmF;AACnF,MAAM,WAAW,KAAK;IACpB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAgCD;;;;;;;GAOG;AACH,wBAAgB,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG;IAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAiCzE"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* ProgressBlock — an agent's turn, as the steps it took.
|
|
5
|
+
*
|
|
6
|
+
* A run emits one event per token and per tool call, and printed flat that is a
|
|
7
|
+
* wall. What a reader wants is the SHAPE of the work: it thought, it searched,
|
|
8
|
+
* it read two files, it finished — each one line, each openable for the detail,
|
|
9
|
+
* and the answer underneath at full size.
|
|
10
|
+
*
|
|
11
|
+
* So a step is a LABEL plus DETAIL, and the detail is closed by default. The
|
|
12
|
+
* label is the claim ("Read 2 files"); the detail is the evidence
|
|
13
|
+
* ("BuyNowButton.tsx", "styles.css"). A reader skims labels and opens the one
|
|
14
|
+
* they doubt — which is the whole reason this beats a transcript.
|
|
15
|
+
*
|
|
16
|
+
* `done` is a state of the BLOCK, not a step: a run that is still working shows
|
|
17
|
+
* no tick, and a tick that appears before the work stops is a lie a reader will
|
|
18
|
+
* only catch once.
|
|
19
|
+
*
|
|
20
|
+
* Presentational. `steps()` maps session events to this shape and is exported
|
|
21
|
+
* beside it, because every surface reading the same stream should group it the
|
|
22
|
+
* same way.
|
|
23
|
+
*/
|
|
24
|
+
import { SizableText, XStack, YStack } from '@hanzo/gui';
|
|
25
|
+
import { Check, ChevronDown, ChevronRight, CircleDot, FileText, Lightbulb, Sparkles, Terminal, TriangleAlert, } from '@hanzogui/lucide-icons-2';
|
|
26
|
+
import { useState } from 'react';
|
|
27
|
+
import { slot } from '../backends/gui/slot.js';
|
|
28
|
+
const ICON = {
|
|
29
|
+
think: Lightbulb,
|
|
30
|
+
search: Sparkles,
|
|
31
|
+
read: FileText,
|
|
32
|
+
run: Terminal,
|
|
33
|
+
write: FileText,
|
|
34
|
+
error: TriangleAlert,
|
|
35
|
+
step: CircleDot,
|
|
36
|
+
};
|
|
37
|
+
function Row({ step }) {
|
|
38
|
+
const [open, setOpen] = useState(step.open ?? false);
|
|
39
|
+
const Icon = ICON[step.kind];
|
|
40
|
+
const has = !!step.detail?.length;
|
|
41
|
+
return (_jsxs(YStack, { gap: "$0.5", children: [_jsxs(XStack, { items: "center", gap: "$1.5", cursor: has ? 'pointer' : 'default', role: has ? 'button' : undefined, tabIndex: has ? 0 : undefined, "aria-expanded": has ? open : undefined, onPress: () => has && setOpen((v) => !v), children: [_jsx(Icon, { size: 13, opacity: 0.55 }), _jsx(SizableText, { size: "$2", color: "$quiet", children: step.label }), has ? (open ? (_jsx(ChevronDown, { size: 12, opacity: 0.5 })) : (_jsx(ChevronRight, { size: 12, opacity: 0.5 }))) : null] }), has && open ? (_jsx(YStack, { pl: "$4", ml: "$1", gap: "$0.5", borderLeftWidth: 1, borderColor: "$borderColor", children: step.detail.map((line, i) => (_jsx(SizableText, { size: "$2", color: "$soft", pl: "$2", children: line }, `${step.key}-${i}`))) })) : null] }));
|
|
42
|
+
}
|
|
43
|
+
export function ProgressBlock({ steps, done = false, doneLabel = 'Done', children, ...rest }) {
|
|
44
|
+
return (_jsxs(YStack, { ...slot('progress-block'), gap: "$1.5", ...rest, children: [steps.map((step) => (_jsx(Row, { step: step }, step.key))), done ? (_jsxs(XStack, { items: "center", gap: "$1.5", children: [_jsx(Check, { size: 13, opacity: 0.75 }), _jsx(SizableText, { size: "$2", color: "$quiet", children: doneLabel })] })) : null, children ? _jsx(YStack, { pt: "$1", children: children }) : null] }));
|
|
45
|
+
}
|
|
46
|
+
/** Map an event's kind onto a step kind. Unknown kinds stay generic rather than
|
|
47
|
+
* being forced into the nearest icon, which would misreport what happened. */
|
|
48
|
+
function kindOf(kind) {
|
|
49
|
+
const k = kind.toLowerCase();
|
|
50
|
+
if (k.includes('think') || k.includes('reason'))
|
|
51
|
+
return 'think';
|
|
52
|
+
if (k.includes('search') || k.includes('grep'))
|
|
53
|
+
return 'search';
|
|
54
|
+
if (k.includes('read') || k.includes('file'))
|
|
55
|
+
return 'read';
|
|
56
|
+
if (k.includes('tool') || k.includes('exec') || k.includes('command'))
|
|
57
|
+
return 'run';
|
|
58
|
+
if (k.includes('write') || k.includes('edit') || k.includes('patch'))
|
|
59
|
+
return 'write';
|
|
60
|
+
if (k.includes('error') || k.includes('fail'))
|
|
61
|
+
return 'error';
|
|
62
|
+
return 'step';
|
|
63
|
+
}
|
|
64
|
+
/** One readable line out of whatever a payload turned out to be. */
|
|
65
|
+
function say(payload) {
|
|
66
|
+
if (payload == null)
|
|
67
|
+
return '';
|
|
68
|
+
if (typeof payload === 'string')
|
|
69
|
+
return payload;
|
|
70
|
+
if (typeof payload !== 'object')
|
|
71
|
+
return String(payload);
|
|
72
|
+
const bag = payload;
|
|
73
|
+
for (const key of ['text', 'message', 'preview', 'content', 'name', 'command', 'path']) {
|
|
74
|
+
const found = bag[key];
|
|
75
|
+
if (typeof found === 'string' && found.trim())
|
|
76
|
+
return found;
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
return JSON.stringify(payload);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return '';
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Group a run's events into steps.
|
|
87
|
+
*
|
|
88
|
+
* Consecutive events of the same kind become ONE step whose detail is their
|
|
89
|
+
* lines — that is what turns twelve `tool-call` events into "Read 2 files" with
|
|
90
|
+
* two names under it. `message` events are NOT steps: they are the answer, and
|
|
91
|
+
* the caller renders them as `children`.
|
|
92
|
+
*/
|
|
93
|
+
export function steps(events) {
|
|
94
|
+
const out = [];
|
|
95
|
+
const said = [];
|
|
96
|
+
events.forEach((event, i) => {
|
|
97
|
+
const kind = event.kind ?? 'step';
|
|
98
|
+
const line = say(event.payload).trim();
|
|
99
|
+
if (kind === 'message') {
|
|
100
|
+
if (line)
|
|
101
|
+
said.push(line);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const step = kindOf(kind);
|
|
105
|
+
const last = out[out.length - 1];
|
|
106
|
+
if (last && last.kind === step) {
|
|
107
|
+
if (line)
|
|
108
|
+
(last.detail ??= []).push(line);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
out.push({
|
|
112
|
+
key: `${event.seq ?? event.id ?? i}`,
|
|
113
|
+
kind: step,
|
|
114
|
+
label: kind,
|
|
115
|
+
detail: line ? [line] : undefined,
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
// A step's label counts what it did, once the run is grouped — "Read 2 files"
|
|
119
|
+
// rather than "read". The verb is the event's own; the number is ours.
|
|
120
|
+
for (const step of out) {
|
|
121
|
+
const n = step.detail?.length ?? 0;
|
|
122
|
+
if (n > 1)
|
|
123
|
+
step.label = `${step.label} ${n}`;
|
|
124
|
+
}
|
|
125
|
+
return { steps: out, message: said.join('') };
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=Progress.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Progress.js","sourceRoot":"","sources":["../../src/agents/Progress.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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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
'use client';"use strict";
|
|
2
|
+
'use client';
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.Steer = Steer;
|
|
5
|
+
exports.MemberRow = MemberRow;
|
|
6
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
7
|
+
/**
|
|
8
|
+
* Steer — the controls that reach a run mid-flight, and the roster row.
|
|
9
|
+
*
|
|
10
|
+
* A command is QUEUED, not applied: an agent reads it between turns, so the
|
|
11
|
+
* label a person sees after pressing has to say that. A control that reports
|
|
12
|
+
* "stopped" the instant it is pressed is lying for however long the current
|
|
13
|
+
* turn takes.
|
|
14
|
+
*
|
|
15
|
+
* Presentational: the verbs go out through `onCommand` and the surface decides
|
|
16
|
+
* what they mean. `@hanzo/ai`'s `sessions.steer(id, command)` is the one it
|
|
17
|
+
* wants, but this row does not import it — a channel workspace steering through
|
|
18
|
+
* a BFF composes the same control.
|
|
19
|
+
*/
|
|
20
|
+
const gui_1 = require("@hanzo/gui");
|
|
21
|
+
const lucide_icons_2_1 = require("@hanzogui/lucide-icons-2");
|
|
22
|
+
const slot_1 = require("../backends/gui/slot.cjs");
|
|
23
|
+
const CONTROLS = [
|
|
24
|
+
{ command: 'pause', label: 'Pause', icon: lucide_icons_2_1.Pause },
|
|
25
|
+
{ command: 'resume', label: 'Resume', icon: lucide_icons_2_1.Play },
|
|
26
|
+
{ command: 'stop', label: 'Stop', icon: lucide_icons_2_1.Square },
|
|
27
|
+
];
|
|
28
|
+
function Steer({ onCommand, withhold = [], note, ...rest }) {
|
|
29
|
+
return ((0, jsx_runtime_1.jsxs)(gui_1.YStack, { ...(0, slot_1.slot)('steer'), gap: "$1", children: [(0, jsx_runtime_1.jsx)(gui_1.XStack, { gap: "$1", items: "center", ...rest, children: CONTROLS.map(({ command, label, icon: Icon }) => {
|
|
30
|
+
const off = withhold.includes(command);
|
|
31
|
+
return ((0, jsx_runtime_1.jsxs)(gui_1.XStack, { items: "center", gap: "$1.5", px: "$2", py: "$1", rounded: "$3", opacity: off ? 0.4 : 0.8, cursor: off ? 'default' : 'pointer', role: "button", tabIndex: off ? -1 : 0, "aria-disabled": off, "aria-label": label, ...(0, slot_1.tip)(label), hoverStyle: off ? undefined : { bg: '$hover', opacity: 1 }, pressStyle: off ? undefined : { bg: '$raised' }, onPress: () => !off && onCommand?.(command), children: [(0, jsx_runtime_1.jsx)(Icon, { size: 13 }), (0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$1", children: label })] }, command));
|
|
32
|
+
}) }), note ? ((0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$1", color: "$soft", children: note })) : null] }));
|
|
33
|
+
}
|
|
34
|
+
const KIND = {
|
|
35
|
+
agent: { icon: lucide_icons_2_1.Zap, says: 'agent' },
|
|
36
|
+
bot: { icon: lucide_icons_2_1.Bot, says: 'always on' },
|
|
37
|
+
human: { icon: lucide_icons_2_1.User, says: 'person' },
|
|
38
|
+
};
|
|
39
|
+
function MemberRow({ name, kind, detail, onPress, ...rest }) {
|
|
40
|
+
const { icon: Icon, says } = KIND[kind];
|
|
41
|
+
return ((0, jsx_runtime_1.jsxs)(gui_1.XStack, { ...(0, slot_1.slot)('member-row'), items: "center", gap: "$2", px: "$2", py: "$1.5", rounded: "$3", cursor: onPress ? 'pointer' : 'default', role: onPress ? 'button' : undefined, tabIndex: onPress ? 0 : undefined, hoverStyle: onPress ? { bg: '$hover' } : undefined, onPress: onPress, ...rest, children: [(0, jsx_runtime_1.jsx)(gui_1.XStack, { width: 22, height: 22, rounded: 9999, items: "center", justify: "center", bg: "$raised", shrink: 0, children: (0, jsx_runtime_1.jsx)(Icon, { size: 12 }) }), (0, jsx_runtime_1.jsxs)(gui_1.YStack, { flex: 1, shrink: 1, children: [(0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$2", numberOfLines: 1, children: name }), detail ? ((0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$1", color: "$soft", numberOfLines: 1, children: detail })) : null] }), (0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$1", color: "$color8", children: says })] }));
|
|
42
|
+
}
|