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