@vnl-works/agentjoy-react 0.1.2 → 0.1.6
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/README.md +59 -18
- package/dist/index.d.mts +34 -49
- package/dist/index.d.ts +34 -49
- package/dist/index.js +699 -244
- package/dist/index.mjs +700 -245
- package/dist/styles.css +354 -82
- package/package.json +1 -1
- package/dist/index.css +0 -171
package/dist/index.mjs
CHANGED
|
@@ -2,45 +2,6 @@
|
|
|
2
2
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
3
3
|
|
|
4
4
|
// src/utils.ts
|
|
5
|
-
function phaseLabel(phase, tone, locale = "ja") {
|
|
6
|
-
const ja = {
|
|
7
|
-
prepare: "\u6E96\u5099",
|
|
8
|
-
research: "\u8ABF\u67FB",
|
|
9
|
-
execute: "\u5B9F\u884C",
|
|
10
|
-
format: "\u6574\u5F62",
|
|
11
|
-
verify: "\u691C\u8A3C",
|
|
12
|
-
finalize: "\u4ED5\u4E0A\u3052"
|
|
13
|
-
};
|
|
14
|
-
const en = {
|
|
15
|
-
prepare: "Prepare",
|
|
16
|
-
research: "Research",
|
|
17
|
-
execute: "Execute",
|
|
18
|
-
format: "Format",
|
|
19
|
-
verify: "Verify",
|
|
20
|
-
finalize: "Finalize"
|
|
21
|
-
};
|
|
22
|
-
const base = locale === "ja" ? ja[phase] : en[phase];
|
|
23
|
-
return base;
|
|
24
|
-
}
|
|
25
|
-
function statusLabel(status, tone, locale = "ja") {
|
|
26
|
-
if (locale === "en") {
|
|
27
|
-
if (status === "running") return "Running";
|
|
28
|
-
if (status === "completed") return "Completed";
|
|
29
|
-
return "Failed";
|
|
30
|
-
}
|
|
31
|
-
const polite = { running: "\u5B9F\u884C\u4E2D", completed: "\u5B8C\u4E86", failed: "\u5931\u6557" };
|
|
32
|
-
const formal = { running: "\u5B9F\u884C\u4E2D", completed: "\u5B8C\u4E86", failed: "\u5931\u6557" };
|
|
33
|
-
const casual = { running: "\u5B9F\u884C\u4E2D", completed: "\u5B8C\u4E86", failed: "\u5931\u6557" };
|
|
34
|
-
const map = tone === "formal" ? formal : tone === "casual" ? casual : polite;
|
|
35
|
-
return map[status];
|
|
36
|
-
}
|
|
37
|
-
function formatTs(ts, locale = "ja") {
|
|
38
|
-
try {
|
|
39
|
-
return new Date(ts).toLocaleString(locale === "ja" ? "ja-JP" : "en-US");
|
|
40
|
-
} catch {
|
|
41
|
-
return ts;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
5
|
function avatarForPack(packId) {
|
|
45
6
|
const m = {
|
|
46
7
|
default: "\u{1F9ED}",
|
|
@@ -56,245 +17,739 @@ function clamp01(x) {
|
|
|
56
17
|
if (x > 1) return 1;
|
|
57
18
|
return x;
|
|
58
19
|
}
|
|
20
|
+
function formatRelative(date, now = /* @__PURE__ */ new Date(), locale = "en") {
|
|
21
|
+
const d = date instanceof Date ? date : new Date(date);
|
|
22
|
+
const diffMs = now.getTime() - d.getTime();
|
|
23
|
+
const diffSec = Math.floor(diffMs / 1e3);
|
|
24
|
+
if (!Number.isFinite(diffSec)) return "";
|
|
25
|
+
if (diffSec < 0) return locale === "ja" ? "\u672A\u6765" : "in the future";
|
|
26
|
+
if (diffSec < 10) return locale === "ja" ? "\u305F\u3063\u305F\u4ECA" : "just now";
|
|
27
|
+
if (diffSec < 60) return locale === "ja" ? `${diffSec}\u79D2\u524D` : `${diffSec}s ago`;
|
|
28
|
+
const diffMin = Math.floor(diffSec / 60);
|
|
29
|
+
if (diffMin < 60) return locale === "ja" ? `${diffMin}\u5206\u524D` : `${diffMin}m ago`;
|
|
30
|
+
const diffHr = Math.floor(diffMin / 60);
|
|
31
|
+
if (diffHr < 24) return locale === "ja" ? `${diffHr}\u6642\u9593\u524D` : `${diffHr}h ago`;
|
|
32
|
+
const diffDay = Math.floor(diffHr / 24);
|
|
33
|
+
return locale === "ja" ? `${diffDay}\u65E5\u524D` : `${diffDay}d ago`;
|
|
34
|
+
}
|
|
35
|
+
var EVENT_LABELS = {
|
|
36
|
+
"run.created": "Run created",
|
|
37
|
+
"phase.started": "Phase started",
|
|
38
|
+
"phase.progress": "Progress",
|
|
39
|
+
"tool.called": "Tool called",
|
|
40
|
+
"tool.completed": "Tool completed",
|
|
41
|
+
"artifact.ready": "Artifact ready",
|
|
42
|
+
warning: "Warning",
|
|
43
|
+
error: "Error",
|
|
44
|
+
"run.completed": "Completed",
|
|
45
|
+
"run.failed": "Failed",
|
|
46
|
+
"system.comment": "Comment"
|
|
47
|
+
};
|
|
48
|
+
function humanizeEventType(t) {
|
|
49
|
+
const key = String(t);
|
|
50
|
+
if (EVENT_LABELS[key]) return EVENT_LABELS[key];
|
|
51
|
+
return key.split(".").map((s) => s ? s[0].toUpperCase() + s.slice(1) : s).join(" ");
|
|
52
|
+
}
|
|
53
|
+
function xpFloorForLevel(level, base = 40, inc = 20) {
|
|
54
|
+
const l = Math.max(1, Math.floor(level));
|
|
55
|
+
const n = l - 1;
|
|
56
|
+
return n * base + inc * n * (n - 1) / 2;
|
|
57
|
+
}
|
|
58
|
+
function levelFromXp(xp) {
|
|
59
|
+
const safeXp = Math.max(0, Math.floor(Number.isFinite(xp) ? xp : 0));
|
|
60
|
+
let level = 1;
|
|
61
|
+
while (level < 999 && safeXp >= xpFloorForLevel(level + 1)) level++;
|
|
62
|
+
return { level, xpFloor: xpFloorForLevel(level), xpCeil: xpFloorForLevel(level + 1) };
|
|
63
|
+
}
|
|
64
|
+
function hashString(s) {
|
|
65
|
+
let h = 0;
|
|
66
|
+
for (let i = 0; i < s.length; i++) h = h * 31 + s.charCodeAt(i) | 0;
|
|
67
|
+
return Math.abs(h);
|
|
68
|
+
}
|
|
69
|
+
function pickFrom(list, key) {
|
|
70
|
+
if (list.length === 0) return "";
|
|
71
|
+
return list[hashString(key) % list.length];
|
|
72
|
+
}
|
|
73
|
+
function pickComment(mode, pack, eventType) {
|
|
74
|
+
const p = (pack || "default").toLowerCase();
|
|
75
|
+
const suffix = p.includes("cat") || p.includes("nyan") ? " \u306B\u3083" : "";
|
|
76
|
+
const professional = {
|
|
77
|
+
"tool.called": ["\u51E6\u7406\u3092\u958B\u59CB\u3057\u307E\u3057\u305F\u3002", "\u30C4\u30FC\u30EB\u3092\u547C\u3073\u51FA\u3057\u3066\u3044\u307E\u3059\u3002", "\u4F5C\u696D\u3092\u9032\u3081\u307E\u3059\u3002"],
|
|
78
|
+
"tool.completed": ["\u51E6\u7406\u304C\u5B8C\u4E86\u3057\u307E\u3057\u305F\u3002", "\u30C4\u30FC\u30EB\u306E\u5B9F\u884C\u304C\u5B8C\u4E86\u3057\u307E\u3057\u305F\u3002", "\u6B21\u306E\u5DE5\u7A0B\u306B\u9032\u307F\u307E\u3059\u3002"],
|
|
79
|
+
"artifact.ready": ["\u6210\u679C\u7269\u3092\u751F\u6210\u3057\u307E\u3057\u305F\u3002", "\u51FA\u529B\u3092\u6E96\u5099\u3057\u307E\u3057\u305F\u3002", "\u78BA\u8A8D\u3067\u304D\u308B\u72B6\u614B\u306B\u306A\u308A\u307E\u3057\u305F\u3002"],
|
|
80
|
+
"phase.progress": ["\u9032\u6357\u3092\u66F4\u65B0\u3057\u307E\u3057\u305F\u3002", "\u9806\u8ABF\u306B\u9032\u3093\u3067\u3044\u307E\u3059\u3002", "\u5F15\u304D\u7D9A\u304D\u51E6\u7406\u4E2D\u3067\u3059\u3002"],
|
|
81
|
+
warning: ["\u6CE8\u610F\u70B9\u304C\u3042\u308A\u307E\u3059\u3002", "\u8EFD\u5FAE\u306A\u554F\u984C\u304C\u691C\u51FA\u3055\u308C\u307E\u3057\u305F\u3002", "\u5FF5\u306E\u305F\u3081\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002"],
|
|
82
|
+
error: ["\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F\u3002", "\u51E6\u7406\u306B\u5931\u6557\u3057\u307E\u3057\u305F\u3002", "\u539F\u56E0\u3092\u78BA\u8A8D\u3057\u3066\u3044\u307E\u3059\u3002"],
|
|
83
|
+
"run.completed": ["\u5B8C\u4E86\u3057\u307E\u3057\u305F\u3002\u304A\u75B2\u308C\u3055\u307E\u3067\u3057\u305F\u3002", "\u6B63\u5E38\u306B\u5B8C\u4E86\u3057\u307E\u3057\u305F\u3002", "\u5B9F\u884C\u304C\u5B8C\u4E86\u3057\u307E\u3057\u305F\u3002"],
|
|
84
|
+
"run.failed": ["\u5931\u6557\u3057\u307E\u3057\u305F\u3002\u539F\u56E0\u3092\u78BA\u8A8D\u3057\u307E\u3059\u3002", "\u51E6\u7406\u304C\u4E2D\u65AD\u3055\u308C\u307E\u3057\u305F\u3002", "\u518D\u5B9F\u884C\u307E\u305F\u306F\u8A2D\u5B9A\u78BA\u8A8D\u3092\u63A8\u5968\u3057\u307E\u3059\u3002"]
|
|
85
|
+
};
|
|
86
|
+
const playful = {
|
|
87
|
+
"tool.called": ["\u3084\u3063\u3066\u307F\u308B\u306D\uFF01", "\u30C4\u30FC\u30EB\u547C\u3076\u3088\u301C", "\u3044\u304F\u3088\u3063\uFF01"],
|
|
88
|
+
"tool.completed": ["\u3067\u304D\u305F\uFF01", "\u5B8C\u4E86\u301C\uFF01", "\u3046\u307E\u304F\u3044\u3063\u305F\uFF01"],
|
|
89
|
+
"artifact.ready": ["\u3067\u304D\u3042\u304C\u308A\uFF01", "\u6210\u679C\u7269\u3067\u304D\u305F\u3088\uFF01", "\u307B\u3044\u3063\uFF01"],
|
|
90
|
+
"phase.progress": ["\u9032\u3093\u3067\u308B\u3088\u301C", "\u3044\u3044\u611F\u3058\uFF01", "\u3082\u3046\u5C11\u3057\uFF01"],
|
|
91
|
+
warning: ["\u3061\u3087\u3063\u3068\u602A\u3057\u3044\u304B\u3082", "\u6CE8\u610F\u3060\u3088\uFF01", "\u78BA\u8A8D\u3057\u3066\u306D"],
|
|
92
|
+
error: ["\u3046\u308F\u3063\u3001\u30A8\u30E9\u30FC\uFF01", "\u3084\u3089\u304B\u3057\u305F\u2026", "\u3054\u3081\u3093\u3001\u5931\u6557\uFF01"],
|
|
93
|
+
"run.completed": ["\u3084\u308A\u5207\u3063\u305F\uFF01\u{1F389}", "\u5B8C\u4E86\u301C\uFF01", "\u304A\u3064\u304B\u308C\u3055\u307E\uFF01"],
|
|
94
|
+
"run.failed": ["\u3080\u3080\u2026\u3082\u3046\u4E00\u56DE\u3044\u304F\uFF1F", "\u5931\u6557\u3057\u3061\u3083\u3063\u305F\u2026", "\u6B21\u306F\u3046\u307E\u304F\u3084\u308B\uFF01"]
|
|
95
|
+
};
|
|
96
|
+
const dict = mode === "playful" ? playful : professional;
|
|
97
|
+
const candidates = dict[eventType] || dict["phase.progress"] || ["..."];
|
|
98
|
+
return pickFrom(candidates, `${mode}:${pack}:${eventType}`) + suffix;
|
|
99
|
+
}
|
|
59
100
|
|
|
60
101
|
// src/AgentJoyRun.tsx
|
|
61
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
62
|
-
function
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
case "phase.progress":
|
|
67
|
-
return 6;
|
|
68
|
-
case "tool.called":
|
|
69
|
-
return 10;
|
|
70
|
-
case "tool.completed":
|
|
71
|
-
return 12;
|
|
72
|
-
case "warning":
|
|
73
|
-
return 2;
|
|
74
|
-
case "error":
|
|
75
|
-
case "run.failed":
|
|
76
|
-
return 1;
|
|
77
|
-
case "run.completed":
|
|
78
|
-
return 25;
|
|
79
|
-
case "system.comment":
|
|
80
|
-
return 0;
|
|
81
|
-
default:
|
|
82
|
-
return 4;
|
|
102
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
103
|
+
function safeOrigin() {
|
|
104
|
+
try {
|
|
105
|
+
if (typeof window !== "undefined" && window.location?.origin) return window.location.origin;
|
|
106
|
+
} catch {
|
|
83
107
|
}
|
|
108
|
+
return "";
|
|
84
109
|
}
|
|
85
|
-
function
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
o.start();
|
|
105
|
-
setTimeout(() => {
|
|
106
|
-
o.stop();
|
|
107
|
-
o.disconnect();
|
|
108
|
-
g.disconnect();
|
|
109
|
-
}, ms);
|
|
110
|
-
}
|
|
111
|
-
function success() {
|
|
112
|
-
tone(740, 90);
|
|
113
|
-
setTimeout(() => tone(880, 120), 110);
|
|
114
|
-
}
|
|
115
|
-
function error() {
|
|
116
|
-
tone(220, 200);
|
|
117
|
-
}
|
|
118
|
-
function tick() {
|
|
119
|
-
tone(520, 55);
|
|
110
|
+
function isImportantEvent(e) {
|
|
111
|
+
return e.type === "warning" || e.type === "error" || e.type === "run.failed" || e.type === "run.completed" || e.type === "artifact.ready";
|
|
112
|
+
}
|
|
113
|
+
function isToolEvent(e) {
|
|
114
|
+
return e.type.startsWith("tool.") || !!e.tool;
|
|
115
|
+
}
|
|
116
|
+
function isArtifactEvent(e) {
|
|
117
|
+
return e.type === "artifact.ready" || !!e.artifacts && e.artifacts.length > 0;
|
|
118
|
+
}
|
|
119
|
+
function eventSearchText(e) {
|
|
120
|
+
const parts = [];
|
|
121
|
+
parts.push(e.type || "");
|
|
122
|
+
parts.push(e.message || "");
|
|
123
|
+
parts.push(e.detail || "");
|
|
124
|
+
if (e.tool?.name) parts.push(e.tool.name);
|
|
125
|
+
if (e.tool?.input_summary) parts.push(e.tool.input_summary);
|
|
126
|
+
if (e.tool?.output_summary) parts.push(e.tool.output_summary);
|
|
127
|
+
if (e.artifacts?.length) {
|
|
128
|
+
for (const a of e.artifacts) parts.push(`${a.label ?? ""} ${a.value ?? ""}`.trim());
|
|
120
129
|
}
|
|
121
|
-
return
|
|
130
|
+
return parts.join(" ").toLowerCase();
|
|
122
131
|
}
|
|
132
|
+
var DEMO_SCRIPT = [
|
|
133
|
+
{ type: "run.created", message: "AgentJoy demo: watching an AI agent work\u2026", progress: 0.02 },
|
|
134
|
+
{
|
|
135
|
+
type: "phase.started",
|
|
136
|
+
message: "Phase: research",
|
|
137
|
+
progress: 0.08
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
type: "tool.called",
|
|
141
|
+
message: "Searching docs for pricing benchmarks\u2026",
|
|
142
|
+
progress: 0.15,
|
|
143
|
+
tool: { name: "web.search", input_summary: "agent monitoring widget pricing", latency_ms: 320 }
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
type: "tool.completed",
|
|
147
|
+
message: "Summarized 6 sources. Drafting plan\u2026",
|
|
148
|
+
progress: 0.32,
|
|
149
|
+
tool: { name: "web.search", output_summary: "key patterns + positioning", latency_ms: 1420 }
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
type: "phase.progress",
|
|
153
|
+
message: "Writing LP copy (hero + CTA + FAQ)\u2026",
|
|
154
|
+
progress: 0.52
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
type: "warning",
|
|
158
|
+
message: "Minor: rate limited once, retrying gracefully.",
|
|
159
|
+
progress: 0.62
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
type: "artifact.ready",
|
|
163
|
+
message: "LP copy draft is ready.",
|
|
164
|
+
progress: 0.82,
|
|
165
|
+
artifacts: [
|
|
166
|
+
{ label: "Landing page copy", kind: "url", value: "https://example.com/agentjoy-demo-lp" },
|
|
167
|
+
{ label: "Pricing bullets", kind: "text", value: "Starter / Pro / Team tiers" }
|
|
168
|
+
]
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
type: "phase.progress",
|
|
172
|
+
message: "Final checks\u2026",
|
|
173
|
+
progress: 0.94
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
type: "run.completed",
|
|
177
|
+
message: "Done. Ready to ship \u{1F680}",
|
|
178
|
+
progress: 1
|
|
179
|
+
}
|
|
180
|
+
];
|
|
123
181
|
function AgentJoyRun(props) {
|
|
124
|
-
const
|
|
125
|
-
const
|
|
126
|
-
const limit = props.limit ?? 200;
|
|
127
|
-
const { tick, success, error } = useBeep();
|
|
182
|
+
const apiBase = (props.apiBaseUrl ?? "").replace(/\/$/, "");
|
|
183
|
+
const isDemo = !!props.demo;
|
|
128
184
|
const [run, setRun] = useState(null);
|
|
129
|
-
const [events, setEvents] = useState([]);
|
|
130
185
|
const [brand, setBrand] = useState(null);
|
|
131
|
-
const [
|
|
132
|
-
const
|
|
186
|
+
const [events, setEvents] = useState([]);
|
|
187
|
+
const [loading, setLoading] = useState(true);
|
|
188
|
+
const [loadError, setLoadError] = useState(null);
|
|
189
|
+
const [conn, setConn] = useState("idle");
|
|
190
|
+
const [muted, setMuted] = useState(props.mute ?? false);
|
|
191
|
+
const [soundEnabled, setSoundEnabled] = useState(props.sound ?? true);
|
|
192
|
+
const [xp, setXp] = useState(0);
|
|
193
|
+
const listRef = useRef(null);
|
|
194
|
+
const [stickToBottom, setStickToBottom] = useState(true);
|
|
195
|
+
const audioCtxRef = useRef(null);
|
|
196
|
+
const lastBeepAtRef = useRef(0);
|
|
197
|
+
const seenIdsRef = useRef(/* @__PURE__ */ new Set());
|
|
198
|
+
const lastSeenEventIdRef = useRef(null);
|
|
199
|
+
const maxEvents = props.maxEvents ?? 5e3;
|
|
200
|
+
const autoScroll = props.autoScroll ?? true;
|
|
201
|
+
const compact = props.compact ?? false;
|
|
202
|
+
const showHeader = props.showHeader ?? true;
|
|
203
|
+
const showControls = props.showControls ?? true;
|
|
204
|
+
const showToolbar = props.showToolbar ?? true;
|
|
205
|
+
const pollIntervalMs = props.pollIntervalMs ?? (isDemo ? 0 : 8e3);
|
|
206
|
+
const showXp = props.showXp ?? (brand?.show_xp ?? true);
|
|
207
|
+
const showPersona = props.showPersona ?? (brand?.show_persona ?? true);
|
|
208
|
+
const locale = props.locale ?? (brand?.locale ?? "en");
|
|
209
|
+
const [filter, setFilter] = useState(props.defaultFilter ?? "all");
|
|
210
|
+
const [query, setQuery] = useState("");
|
|
133
211
|
useEffect(() => {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
212
|
+
if (props.mute !== void 0) setMuted(props.mute);
|
|
213
|
+
}, [props.mute]);
|
|
214
|
+
function resetEvents(next) {
|
|
215
|
+
const trimmed = next.length > maxEvents ? next.slice(next.length - maxEvents) : next;
|
|
216
|
+
seenIdsRef.current = new Set(trimmed.map((e) => String(e.id)));
|
|
217
|
+
let lastId = null;
|
|
218
|
+
for (let i = trimmed.length - 1; i >= 0; i--) {
|
|
219
|
+
const idAny = trimmed[i]?.id;
|
|
220
|
+
if (typeof idAny === "number" && Number.isFinite(idAny)) {
|
|
221
|
+
lastId = idAny;
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
146
224
|
}
|
|
147
|
-
|
|
225
|
+
lastSeenEventIdRef.current = lastId;
|
|
226
|
+
setEvents(trimmed);
|
|
227
|
+
}
|
|
228
|
+
function appendEvent(ev) {
|
|
229
|
+
if (ev == null || ev.id == null) return;
|
|
230
|
+
const evId = String(ev.id);
|
|
231
|
+
if (seenIdsRef.current.has(evId)) return;
|
|
232
|
+
seenIdsRef.current.add(evId);
|
|
233
|
+
if (typeof ev.id === "number" && Number.isFinite(ev.id)) {
|
|
234
|
+
lastSeenEventIdRef.current = ev.id;
|
|
235
|
+
}
|
|
236
|
+
if (ev.type === "run.completed") {
|
|
237
|
+
setRun((r) => r ? { ...r, status: "completed", ended_at: r.ended_at ?? ev.ts } : r);
|
|
238
|
+
} else if (ev.type === "run.failed") {
|
|
239
|
+
setRun((r) => r ? { ...r, status: "failed", ended_at: r.ended_at ?? ev.ts } : r);
|
|
240
|
+
}
|
|
241
|
+
setEvents((prev) => {
|
|
242
|
+
const next = [...prev, ev];
|
|
243
|
+
if (next.length <= maxEvents) return next;
|
|
244
|
+
return next.slice(next.length - maxEvents);
|
|
148
245
|
});
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
246
|
+
}
|
|
247
|
+
async function fetchPublic(opts = {}) {
|
|
248
|
+
if (!props.runId || !props.token) {
|
|
249
|
+
setLoadError("Missing runId or token (or use `<AgentJoyRun demo />` to try instantly).");
|
|
250
|
+
setLoading(false);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
const limit = Math.max(1, Math.min(5e3, maxEvents));
|
|
254
|
+
const qs = new URLSearchParams();
|
|
255
|
+
qs.set("token", props.token);
|
|
256
|
+
qs.set("limit", String(limit));
|
|
257
|
+
const id = encodeURIComponent(props.runId);
|
|
258
|
+
const urls = [
|
|
259
|
+
`${apiBase}/v1/runs/${id}/public?${qs.toString()}`,
|
|
260
|
+
`${apiBase}/v1/runs/${id}?${qs.toString()}`
|
|
261
|
+
];
|
|
262
|
+
let lastErr = null;
|
|
263
|
+
for (const url of urls) {
|
|
158
264
|
try {
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
265
|
+
const res = await fetch(url);
|
|
266
|
+
if (!res.ok) {
|
|
267
|
+
const t = await res.text().catch(() => "");
|
|
268
|
+
lastErr = new Error(`${res.status} ${res.statusText}${t ? `: ${t}` : ""}`);
|
|
269
|
+
continue;
|
|
163
270
|
}
|
|
164
|
-
|
|
165
|
-
|
|
271
|
+
const r = await res.json();
|
|
272
|
+
if (!r?.run) {
|
|
273
|
+
lastErr = new Error("Invalid response from AgentJoy API.");
|
|
274
|
+
continue;
|
|
166
275
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
276
|
+
setRun(r.run);
|
|
277
|
+
setBrand(r.workspace_brand);
|
|
278
|
+
if (opts.reset) {
|
|
279
|
+
resetEvents(r.events ?? []);
|
|
280
|
+
} else if (r.events?.length) {
|
|
281
|
+
for (const e of r.events) appendEvent(e);
|
|
171
282
|
}
|
|
172
|
-
|
|
283
|
+
if (props.mute === void 0) setMuted(!!r.workspace_brand?.mute_default);
|
|
284
|
+
return;
|
|
285
|
+
} catch (e) {
|
|
286
|
+
lastErr = e;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
throw lastErr ?? new Error("Failed to load run detail.");
|
|
290
|
+
}
|
|
291
|
+
async function fetchEventsAfter(afterId) {
|
|
292
|
+
if (!props.runId || !props.token) return;
|
|
293
|
+
const safeAfter = typeof afterId === "number" && Number.isFinite(afterId) ? afterId : null;
|
|
294
|
+
if (safeAfter === null) {
|
|
295
|
+
await fetchPublic({ reset: false });
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
const qs = new URLSearchParams();
|
|
299
|
+
qs.set("token", props.token);
|
|
300
|
+
qs.set("after_id", String(safeAfter));
|
|
301
|
+
qs.set("limit", "1000");
|
|
302
|
+
const url = `${apiBase}/v1/runs/${encodeURIComponent(props.runId)}/events?${qs.toString()}`;
|
|
303
|
+
const res = await fetch(url);
|
|
304
|
+
if (!res.ok) {
|
|
305
|
+
if (res.status === 404 || res.status === 405) {
|
|
306
|
+
await fetchPublic({ reset: false });
|
|
307
|
+
return;
|
|
173
308
|
}
|
|
309
|
+
const t = await res.text().catch(() => "");
|
|
310
|
+
throw new Error(`${res.status} ${res.statusText}${t ? `: ${t}` : ""}`);
|
|
311
|
+
}
|
|
312
|
+
const data = await res.json();
|
|
313
|
+
const evs = Array.isArray(data?.events) ? data.events : [];
|
|
314
|
+
if (evs.length) {
|
|
315
|
+
for (const e of evs) appendEvent(e);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
async function load() {
|
|
319
|
+
if (isDemo) return;
|
|
320
|
+
setLoading(true);
|
|
321
|
+
setLoadError(null);
|
|
322
|
+
try {
|
|
323
|
+
await fetchPublic({ reset: true });
|
|
324
|
+
} catch (e) {
|
|
325
|
+
setLoadError(e?.message ?? String(e));
|
|
326
|
+
} finally {
|
|
327
|
+
setLoading(false);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
useEffect(() => {
|
|
331
|
+
if (!isDemo) return;
|
|
332
|
+
setLoading(false);
|
|
333
|
+
setLoadError(null);
|
|
334
|
+
setConn("open");
|
|
335
|
+
const demoBrand = {
|
|
336
|
+
product_name: "AgentJoy",
|
|
337
|
+
show_xp: true,
|
|
338
|
+
show_persona: true,
|
|
339
|
+
mute_default: false,
|
|
340
|
+
tone: "playful"
|
|
174
341
|
};
|
|
175
|
-
|
|
342
|
+
const demoRun = {
|
|
343
|
+
id: "demo",
|
|
344
|
+
title: "Demo: AgentJoy Widget",
|
|
345
|
+
status: "running",
|
|
346
|
+
mode: "playful",
|
|
347
|
+
pack: "nyan",
|
|
348
|
+
share_token: ""
|
|
176
349
|
};
|
|
350
|
+
setBrand(demoBrand);
|
|
351
|
+
setRun(demoRun);
|
|
352
|
+
resetEvents([]);
|
|
353
|
+
let i = 0;
|
|
354
|
+
const speed = Math.max(200, props.demoSpeedMs ?? 700);
|
|
355
|
+
const timer = window.setInterval(() => {
|
|
356
|
+
const src = DEMO_SCRIPT[i];
|
|
357
|
+
if (!src) return;
|
|
358
|
+
const ev = {
|
|
359
|
+
id: `demo-${i + 1}`,
|
|
360
|
+
ts: new Date(Date.now() + i * speed).toISOString(),
|
|
361
|
+
...src
|
|
362
|
+
};
|
|
363
|
+
appendEvent(ev);
|
|
364
|
+
if (src.type === "run.completed") {
|
|
365
|
+
setRun((r) => r ? { ...r, status: "completed" } : r);
|
|
366
|
+
}
|
|
367
|
+
i += 1;
|
|
368
|
+
if (i >= DEMO_SCRIPT.length) window.clearInterval(timer);
|
|
369
|
+
}, speed);
|
|
370
|
+
return () => window.clearInterval(timer);
|
|
371
|
+
}, [props.demo]);
|
|
372
|
+
useEffect(() => {
|
|
373
|
+
if (isDemo) return;
|
|
374
|
+
load();
|
|
375
|
+
}, [apiBase, props.runId, props.token, isDemo]);
|
|
376
|
+
useEffect(() => {
|
|
377
|
+
const total = events.reduce((acc, e) => {
|
|
378
|
+
if (e.type === "tool.completed") return acc + 8;
|
|
379
|
+
if (e.type === "artifact.ready") return acc + 12;
|
|
380
|
+
if (e.type === "warning") return acc + 2;
|
|
381
|
+
if (e.type === "error" || e.type === "run.failed") return acc + 1;
|
|
382
|
+
if (e.type === "phase.progress") return acc + 1;
|
|
383
|
+
return acc;
|
|
384
|
+
}, 0);
|
|
385
|
+
setXp(total);
|
|
386
|
+
}, [events]);
|
|
387
|
+
const level = useMemo(() => levelFromXp(xp), [xp]);
|
|
388
|
+
const shareUrl = useMemo(() => {
|
|
389
|
+
if (props.shareUrl) return props.shareUrl;
|
|
390
|
+
if (run?.share_token) {
|
|
391
|
+
const origin = apiBase ? apiBase.replace(/\/$/, "") : safeOrigin();
|
|
392
|
+
return `${origin}/share/${run.share_token}`;
|
|
393
|
+
}
|
|
394
|
+
return null;
|
|
395
|
+
}, [props.shareUrl, run?.share_token, apiBase]);
|
|
396
|
+
function onScroll() {
|
|
397
|
+
const el = listRef.current;
|
|
398
|
+
if (!el) return;
|
|
399
|
+
const nearBottom = el.scrollTop + el.clientHeight >= el.scrollHeight - 40;
|
|
400
|
+
setStickToBottom(nearBottom);
|
|
401
|
+
}
|
|
402
|
+
function scrollToBottom() {
|
|
403
|
+
const el = listRef.current;
|
|
404
|
+
if (!el) return;
|
|
405
|
+
el.scrollTop = el.scrollHeight;
|
|
406
|
+
setStickToBottom(true);
|
|
407
|
+
}
|
|
408
|
+
useEffect(() => {
|
|
409
|
+
if (!autoScroll) return;
|
|
410
|
+
if (!stickToBottom) return;
|
|
411
|
+
const el = listRef.current;
|
|
412
|
+
if (!el) return;
|
|
413
|
+
el.scrollTop = el.scrollHeight;
|
|
414
|
+
}, [events.length, stickToBottom, autoScroll]);
|
|
415
|
+
function ensureAudio() {
|
|
416
|
+
if (audioCtxRef.current) return audioCtxRef.current;
|
|
417
|
+
const AC = window?.AudioContext || window?.webkitAudioContext;
|
|
418
|
+
if (!AC) return null;
|
|
419
|
+
try {
|
|
420
|
+
audioCtxRef.current = new AC();
|
|
421
|
+
} catch {
|
|
422
|
+
return null;
|
|
423
|
+
}
|
|
424
|
+
return audioCtxRef.current;
|
|
425
|
+
}
|
|
426
|
+
function beep(freq) {
|
|
427
|
+
if (!soundEnabled || muted) return;
|
|
428
|
+
if (typeof window === "undefined") return;
|
|
429
|
+
const now = Date.now();
|
|
430
|
+
if (now - lastBeepAtRef.current < 120) return;
|
|
431
|
+
lastBeepAtRef.current = now;
|
|
432
|
+
const ac = ensureAudio();
|
|
433
|
+
if (!ac) return;
|
|
434
|
+
try {
|
|
435
|
+
const osc = ac.createOscillator();
|
|
436
|
+
const gain = ac.createGain();
|
|
437
|
+
osc.type = "sine";
|
|
438
|
+
osc.frequency.value = freq;
|
|
439
|
+
gain.gain.value = 0.02;
|
|
440
|
+
osc.connect(gain);
|
|
441
|
+
gain.connect(ac.destination);
|
|
442
|
+
osc.start();
|
|
443
|
+
osc.stop(ac.currentTime + 0.08);
|
|
444
|
+
} catch {
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
useEffect(() => {
|
|
448
|
+
if (isDemo) return;
|
|
449
|
+
if (!run) return;
|
|
450
|
+
if (run.status !== "running") return;
|
|
451
|
+
if (!props.runId || !props.token) return;
|
|
452
|
+
const runId = props.runId;
|
|
453
|
+
const token = props.token;
|
|
454
|
+
const buildUrl = () => {
|
|
455
|
+
const qs = new URLSearchParams();
|
|
456
|
+
qs.set("token", token);
|
|
457
|
+
const afterId = lastSeenEventIdRef.current;
|
|
458
|
+
if (typeof afterId === "number" && Number.isFinite(afterId)) {
|
|
459
|
+
qs.set("after_id", String(afterId));
|
|
460
|
+
}
|
|
461
|
+
return `${apiBase}/v1/runs/${encodeURIComponent(runId)}/stream?${qs.toString()}`;
|
|
462
|
+
};
|
|
463
|
+
let closed = false;
|
|
464
|
+
let es = null;
|
|
465
|
+
let retry = 0;
|
|
466
|
+
let timer = null;
|
|
467
|
+
const connect = () => {
|
|
468
|
+
if (closed) return;
|
|
469
|
+
setConn(retry === 0 ? "connecting" : "reconnecting");
|
|
470
|
+
try {
|
|
471
|
+
es = new EventSource(buildUrl());
|
|
472
|
+
} catch {
|
|
473
|
+
setConn("error");
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
es.onopen = () => {
|
|
477
|
+
retry = 0;
|
|
478
|
+
setConn("open");
|
|
479
|
+
};
|
|
480
|
+
es.onerror = () => {
|
|
481
|
+
if (closed) return;
|
|
482
|
+
setConn("reconnecting");
|
|
483
|
+
try {
|
|
484
|
+
es?.close();
|
|
485
|
+
} catch {
|
|
486
|
+
}
|
|
487
|
+
es = null;
|
|
488
|
+
retry = Math.min(retry + 1, 8);
|
|
489
|
+
const base = Math.min(3e4, 500 * Math.pow(2, retry));
|
|
490
|
+
const jitter = Math.floor(Math.random() * 250);
|
|
491
|
+
timer = window.setTimeout(connect, base + jitter);
|
|
492
|
+
};
|
|
493
|
+
es.onmessage = (msg) => {
|
|
494
|
+
try {
|
|
495
|
+
const payload = JSON.parse(msg.data);
|
|
496
|
+
const ev = payload && payload.event ? payload.event : payload;
|
|
497
|
+
if (!ev || ev.id == null) return;
|
|
498
|
+
props.onEvent?.(ev);
|
|
499
|
+
appendEvent(ev);
|
|
500
|
+
if (ev.type === "warning") beep(420);
|
|
501
|
+
else if (ev.type === "error" || ev.type === "run.failed") beep(180);
|
|
502
|
+
else if (ev.type === "artifact.ready") beep(660);
|
|
503
|
+
else if (ev.type === "tool.completed") beep(520);
|
|
504
|
+
} catch {
|
|
505
|
+
}
|
|
506
|
+
};
|
|
507
|
+
};
|
|
508
|
+
connect();
|
|
177
509
|
return () => {
|
|
178
|
-
|
|
179
|
-
|
|
510
|
+
closed = true;
|
|
511
|
+
if (timer) window.clearTimeout(timer);
|
|
512
|
+
try {
|
|
513
|
+
es?.close();
|
|
514
|
+
} catch {
|
|
515
|
+
}
|
|
180
516
|
};
|
|
181
|
-
}, [
|
|
517
|
+
}, [apiBase, props.runId, props.token, isDemo, run?.status]);
|
|
182
518
|
useEffect(() => {
|
|
519
|
+
if (isDemo) return;
|
|
520
|
+
if (!props.runId || !props.token) return;
|
|
183
521
|
if (!run) return;
|
|
184
|
-
if (
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
const
|
|
195
|
-
return
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
522
|
+
if (pollIntervalMs <= 0) return;
|
|
523
|
+
let stopped = false;
|
|
524
|
+
const tick = async () => {
|
|
525
|
+
if (stopped) return;
|
|
526
|
+
try {
|
|
527
|
+
await fetchEventsAfter(lastSeenEventIdRef.current);
|
|
528
|
+
} catch {
|
|
529
|
+
}
|
|
530
|
+
};
|
|
531
|
+
tick();
|
|
532
|
+
const id = window.setInterval(tick, pollIntervalMs);
|
|
533
|
+
return () => {
|
|
534
|
+
stopped = true;
|
|
535
|
+
window.clearInterval(id);
|
|
536
|
+
};
|
|
537
|
+
}, [apiBase, props.runId, props.token, isDemo, run?.status, pollIntervalMs]);
|
|
538
|
+
async function copyToClipboard(text) {
|
|
539
|
+
try {
|
|
540
|
+
await navigator.clipboard.writeText(text);
|
|
541
|
+
beep(740);
|
|
542
|
+
} catch {
|
|
201
543
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
const
|
|
207
|
-
|
|
544
|
+
}
|
|
545
|
+
function downloadJsonl() {
|
|
546
|
+
try {
|
|
547
|
+
const lines = events.map((e) => JSON.stringify(e));
|
|
548
|
+
const blob = new Blob([lines.join("\n") + "\n"], { type: "application/x-ndjson" });
|
|
549
|
+
const url = URL.createObjectURL(blob);
|
|
550
|
+
const a = document.createElement("a");
|
|
551
|
+
a.href = url;
|
|
552
|
+
a.download = `agentjoy-${run?.id ?? "run"}-events.jsonl`;
|
|
553
|
+
document.body.appendChild(a);
|
|
554
|
+
a.click();
|
|
555
|
+
a.remove();
|
|
556
|
+
URL.revokeObjectURL(url);
|
|
557
|
+
} catch {
|
|
208
558
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
return
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
559
|
+
}
|
|
560
|
+
const title = run?.title || brand?.product_name || "AgentJoy";
|
|
561
|
+
const progressRaw = clamp01(Math.max(...events.map((e) => e.progress ?? 0), 0));
|
|
562
|
+
const progressValue = progressRaw ?? 0;
|
|
563
|
+
const progressPct = Math.round(progressValue * 100);
|
|
564
|
+
const progressLabel = progressRaw === null ? "\u2014" : `${progressPct}%`;
|
|
565
|
+
const lastEventAt = (() => {
|
|
566
|
+
if (!events.length) return null;
|
|
567
|
+
const ts = events[events.length - 1]?.ts;
|
|
568
|
+
return ts ? new Date(ts) : null;
|
|
569
|
+
})();
|
|
570
|
+
const statusLabel = (() => {
|
|
571
|
+
if (!run) return "loading";
|
|
572
|
+
if (run.status === "running") return conn === "open" ? "running" : conn;
|
|
573
|
+
return run.status;
|
|
574
|
+
})();
|
|
575
|
+
const persona = useMemo(() => {
|
|
576
|
+
const pack = run?.pack || "default";
|
|
577
|
+
return avatarForPack(pack);
|
|
578
|
+
}, [run?.pack]);
|
|
579
|
+
const comment = useMemo(() => {
|
|
580
|
+
if (!showPersona) return null;
|
|
581
|
+
const last = events[events.length - 1];
|
|
582
|
+
if (!last) return null;
|
|
583
|
+
if (last.type === "system.comment") return last.message;
|
|
584
|
+
return pickComment(run?.mode || "professional", run?.pack || "default", last.type);
|
|
585
|
+
}, [events, run?.mode, run?.pack, showPersona]);
|
|
586
|
+
const listMaxHeight = props.height ?? 360;
|
|
587
|
+
const normalizedQuery = query.trim().toLowerCase();
|
|
588
|
+
const visibleEvents = useMemo(() => {
|
|
589
|
+
let base = events;
|
|
590
|
+
if (filter === "important") base = base.filter(isImportantEvent);
|
|
591
|
+
if (filter === "tools") base = base.filter(isToolEvent);
|
|
592
|
+
if (filter === "artifacts") base = base.filter(isArtifactEvent);
|
|
593
|
+
if (normalizedQuery) {
|
|
594
|
+
base = base.filter((e) => eventSearchText(e).includes(normalizedQuery));
|
|
595
|
+
}
|
|
596
|
+
return base;
|
|
597
|
+
}, [events, filter, normalizedQuery]);
|
|
598
|
+
if (!isDemo && (!props.runId || !props.token)) {
|
|
599
|
+
return /* @__PURE__ */ jsx("div", { className: "ajw-wrap", children: /* @__PURE__ */ jsxs("div", { className: "ajw-error", children: [
|
|
600
|
+
/* @__PURE__ */ jsx("div", { className: "ajw-error-title", children: "Missing configuration" }),
|
|
601
|
+
/* @__PURE__ */ jsxs("div", { className: "ajw-error-detail", children: [
|
|
602
|
+
"Provide ",
|
|
603
|
+
/* @__PURE__ */ jsx("code", { children: "runId" }),
|
|
604
|
+
" and ",
|
|
605
|
+
/* @__PURE__ */ jsx("code", { children: "token" }),
|
|
606
|
+
" (live mode), or use ",
|
|
607
|
+
/* @__PURE__ */ jsx("code", { children: "<AgentJoyRun demo />" }),
|
|
608
|
+
" ",
|
|
609
|
+
"to try instantly."
|
|
610
|
+
] })
|
|
242
611
|
] }) });
|
|
243
612
|
}
|
|
244
|
-
return /* @__PURE__ */
|
|
245
|
-
/* @__PURE__ */ jsxs("div", { className: "ajw-header", children: [
|
|
246
|
-
/* @__PURE__ */
|
|
247
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
613
|
+
return /* @__PURE__ */ jsxs("div", { className: `ajw-wrap ${compact ? "ajw-compact" : ""}`, "data-locale": locale, children: [
|
|
614
|
+
showHeader && /* @__PURE__ */ jsxs("div", { className: "ajw-header", children: [
|
|
615
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
|
|
248
616
|
/* @__PURE__ */ jsx("div", { className: "ajw-title", children: title }),
|
|
249
|
-
/* @__PURE__ */
|
|
250
|
-
statusLabel(status, tone, locale),
|
|
251
|
-
" \xB7 ",
|
|
252
|
-
phaseLabel(currentPhase, tone, locale),
|
|
253
|
-
" \xB7 ",
|
|
254
|
-
progressPct,
|
|
255
|
-
"%"
|
|
256
|
-
] })
|
|
617
|
+
/* @__PURE__ */ jsx("span", { className: `ajw-pill ajw-pill-${statusLabel}`, children: statusLabel })
|
|
257
618
|
] }),
|
|
258
|
-
/* @__PURE__ */
|
|
619
|
+
/* @__PURE__ */ jsxs("div", { className: "ajw-sub", children: [
|
|
620
|
+
lastEventAt ? `last: ${formatRelative(lastEventAt, /* @__PURE__ */ new Date(), locale)}` : "waiting for events",
|
|
621
|
+
autoScroll && !stickToBottom ? " \u2022 scroll paused" : ""
|
|
622
|
+
] })
|
|
259
623
|
] }),
|
|
260
|
-
/* @__PURE__ */ jsxs("div", { className: "ajw-
|
|
261
|
-
/* @__PURE__ */ jsxs(
|
|
262
|
-
/* @__PURE__ */ jsx(
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
624
|
+
showControls && /* @__PURE__ */ jsxs("div", { className: "ajw-actions", children: [
|
|
625
|
+
shareUrl && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
626
|
+
/* @__PURE__ */ jsx(
|
|
627
|
+
"button",
|
|
628
|
+
{
|
|
629
|
+
type: "button",
|
|
630
|
+
className: "ajw-btn",
|
|
631
|
+
onClick: () => copyToClipboard(shareUrl),
|
|
632
|
+
title: "Copy share URL",
|
|
633
|
+
children: "Copy link"
|
|
634
|
+
}
|
|
635
|
+
),
|
|
636
|
+
/* @__PURE__ */ jsx("a", { className: "ajw-btn ajw-btn-link", href: shareUrl, target: "_blank", rel: "noreferrer", title: "Open share URL", children: "Open" })
|
|
269
637
|
] }),
|
|
270
|
-
/* @__PURE__ */ jsx("
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
638
|
+
autoScroll && !stickToBottom && /* @__PURE__ */ jsx("button", { type: "button", className: "ajw-btn", onClick: scrollToBottom, title: "Jump to latest", children: "Jump to latest" }),
|
|
639
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "ajw-btn", onClick: downloadJsonl, title: "Download events as JSONL", children: "Download log" }),
|
|
640
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "ajw-btn", onClick: () => setMuted((m) => !m), title: "Toggle mute", children: muted ? "Unmute" : "Mute" }),
|
|
641
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "ajw-btn", onClick: () => setSoundEnabled((s) => !s), title: "Toggle sound", children: soundEnabled ? "Sound on" : "Sound off" })
|
|
642
|
+
] }),
|
|
643
|
+
showToolbar && /* @__PURE__ */ jsxs("div", { className: "ajw-toolbar", children: [
|
|
644
|
+
/* @__PURE__ */ jsx(
|
|
645
|
+
"input",
|
|
646
|
+
{
|
|
647
|
+
className: "ajw-search",
|
|
648
|
+
value: query,
|
|
649
|
+
onChange: (e) => setQuery(e.target.value),
|
|
650
|
+
placeholder: locale === "ja" ? "\u691C\u7D22\uFF08\u30E1\u30C3\u30BB\u30FC\u30B8 / \u30C4\u30FC\u30EB / \u7A2E\u5225\uFF09" : "Search (message / tool / type)"
|
|
276
651
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
652
|
+
),
|
|
653
|
+
/* @__PURE__ */ jsx("div", { className: "ajw-filters", role: "tablist", "aria-label": "Event filters", children: [
|
|
654
|
+
["all", locale === "ja" ? "\u3059\u3079\u3066" : "All"],
|
|
655
|
+
["important", locale === "ja" ? "\u91CD\u8981" : "Important"],
|
|
656
|
+
["tools", locale === "ja" ? "\u30C4\u30FC\u30EB" : "Tools"],
|
|
657
|
+
["artifacts", locale === "ja" ? "\u6210\u679C\u7269" : "Artifacts"]
|
|
658
|
+
].map(([key, label]) => /* @__PURE__ */ jsx(
|
|
659
|
+
"button",
|
|
660
|
+
{
|
|
661
|
+
type: "button",
|
|
662
|
+
className: `ajw-chip ${filter === key ? "ajw-chip-active" : ""}`,
|
|
663
|
+
onClick: () => setFilter(key),
|
|
664
|
+
role: "tab",
|
|
665
|
+
"aria-selected": filter === key,
|
|
666
|
+
children: label
|
|
667
|
+
},
|
|
668
|
+
key
|
|
669
|
+
)) }),
|
|
670
|
+
/* @__PURE__ */ jsxs("div", { className: "ajw-count", title: "Visible / Total", children: [
|
|
671
|
+
visibleEvents.length,
|
|
672
|
+
"/",
|
|
673
|
+
events.length
|
|
674
|
+
] })
|
|
675
|
+
] }),
|
|
676
|
+
showXp && /* @__PURE__ */ jsxs("div", { className: "ajw-progress", children: [
|
|
677
|
+
/* @__PURE__ */ jsx("div", { className: "ajw-bar", "aria-label": "Progress", children: /* @__PURE__ */ jsx("div", { className: "ajw-bar-fill", style: { width: `${progressPct}%` } }) }),
|
|
678
|
+
/* @__PURE__ */ jsxs("div", { className: "ajw-xp", children: [
|
|
679
|
+
"Lv ",
|
|
680
|
+
level.level,
|
|
681
|
+
" \u2022 XP ",
|
|
682
|
+
xp,
|
|
683
|
+
" \u2022 ",
|
|
684
|
+
progressLabel
|
|
685
|
+
] })
|
|
686
|
+
] }),
|
|
687
|
+
showPersona && comment && /* @__PURE__ */ jsxs("div", { className: "ajw-persona", children: [
|
|
688
|
+
/* @__PURE__ */ jsx("div", { className: "ajw-avatar", "aria-hidden": "true", children: persona }),
|
|
689
|
+
/* @__PURE__ */ jsx("div", { className: "ajw-bubble", children: comment })
|
|
690
|
+
] }),
|
|
691
|
+
/* @__PURE__ */ jsxs(
|
|
692
|
+
"div",
|
|
693
|
+
{
|
|
694
|
+
className: "ajw-events",
|
|
695
|
+
ref: listRef,
|
|
696
|
+
onScroll,
|
|
697
|
+
style: { maxHeight: typeof listMaxHeight === "number" ? `${listMaxHeight}px` : listMaxHeight },
|
|
698
|
+
children: [
|
|
699
|
+
loading && /* @__PURE__ */ jsxs("div", { className: "ajw-skeleton", children: [
|
|
700
|
+
/* @__PURE__ */ jsx("div", { className: "ajw-skeleton-line" }),
|
|
701
|
+
/* @__PURE__ */ jsx("div", { className: "ajw-skeleton-line" }),
|
|
702
|
+
/* @__PURE__ */ jsx("div", { className: "ajw-skeleton-line" })
|
|
703
|
+
] }),
|
|
704
|
+
!loading && loadError && /* @__PURE__ */ jsxs("div", { className: "ajw-error", children: [
|
|
705
|
+
/* @__PURE__ */ jsx("div", { className: "ajw-error-title", children: "Failed to load" }),
|
|
706
|
+
/* @__PURE__ */ jsx("div", { className: "ajw-error-detail", children: loadError }),
|
|
707
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "ajw-btn", onClick: () => load(), children: "Retry" })
|
|
292
708
|
] }),
|
|
293
|
-
e
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
709
|
+
!loading && !loadError && visibleEvents.map((e) => /* @__PURE__ */ jsxs("div", { className: `ajw-ev ajw-ev-${e.type.replace(".", "-")}`, children: [
|
|
710
|
+
/* @__PURE__ */ jsxs("div", { className: "ajw-ev-top", children: [
|
|
711
|
+
/* @__PURE__ */ jsx("div", { className: "ajw-ev-type", children: humanizeEventType(e.type) }),
|
|
712
|
+
/* @__PURE__ */ jsxs("div", { className: "ajw-ev-right", children: [
|
|
713
|
+
/* @__PURE__ */ jsx("div", { className: "ajw-ev-ts", children: e.ts?.slice(11, 19) ?? "" }),
|
|
714
|
+
/* @__PURE__ */ jsx(
|
|
715
|
+
"button",
|
|
716
|
+
{
|
|
717
|
+
type: "button",
|
|
718
|
+
className: "ajw-iconbtn",
|
|
719
|
+
title: locale === "ja" ? "\u30A4\u30D9\u30F3\u30C8\u3092\u30B3\u30D4\u30FC" : "Copy event",
|
|
720
|
+
onClick: () => copyToClipboard(JSON.stringify(e)),
|
|
721
|
+
children: "\u29C9"
|
|
722
|
+
}
|
|
723
|
+
)
|
|
724
|
+
] })
|
|
725
|
+
] }),
|
|
726
|
+
/* @__PURE__ */ jsx("div", { className: "ajw-ev-msg", children: e.message }),
|
|
727
|
+
e.tool && /* @__PURE__ */ jsxs("div", { className: "ajw-ev-tool", children: [
|
|
728
|
+
/* @__PURE__ */ jsx("div", { className: "ajw-ev-tool-name", children: e.tool.name }),
|
|
729
|
+
e.tool.input_summary && /* @__PURE__ */ jsxs("div", { className: "ajw-ev-tool-io", children: [
|
|
730
|
+
"in: ",
|
|
731
|
+
e.tool.input_summary
|
|
732
|
+
] }),
|
|
733
|
+
e.tool.output_summary && /* @__PURE__ */ jsxs("div", { className: "ajw-ev-tool-io", children: [
|
|
734
|
+
"out: ",
|
|
735
|
+
e.tool.output_summary
|
|
736
|
+
] }),
|
|
737
|
+
typeof e.tool.latency_ms === "number" && /* @__PURE__ */ jsxs("div", { className: "ajw-ev-tool-io", children: [
|
|
738
|
+
e.tool.latency_ms,
|
|
739
|
+
"ms"
|
|
740
|
+
] })
|
|
741
|
+
] }),
|
|
742
|
+
e.artifacts && e.artifacts.length > 0 && /* @__PURE__ */ jsx("div", { className: "ajw-ev-artifacts", children: e.artifacts.map((a, i) => /* @__PURE__ */ jsxs("div", { className: "ajw-ev-artifact", children: [
|
|
743
|
+
/* @__PURE__ */ jsx("span", { className: "ajw-ev-artifact-label", children: a.label }),
|
|
744
|
+
a.kind === "url" ? /* @__PURE__ */ jsx("a", { href: a.value, target: "_blank", rel: "noreferrer", children: a.value }) : /* @__PURE__ */ jsx("code", { children: a.value })
|
|
745
|
+
] }, i)) }),
|
|
746
|
+
e.detail && /* @__PURE__ */ jsx("div", { className: "ajw-ev-detail", children: e.detail })
|
|
747
|
+
] }, e.id))
|
|
748
|
+
]
|
|
749
|
+
}
|
|
750
|
+
),
|
|
751
|
+
autoScroll && !stickToBottom && /* @__PURE__ */ jsx("button", { type: "button", className: "ajw-float", onClick: scrollToBottom, title: "Jump to latest", children: "\u2193 Latest" })
|
|
752
|
+
] });
|
|
298
753
|
}
|
|
299
754
|
export {
|
|
300
755
|
AgentJoyRun
|