@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/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 xpForEvent(e) {
89
- switch (e.type) {
90
- case "phase.started":
91
- return 18;
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 useBeep() {
112
- const ctxRef = (0, import_react.useRef)(null);
113
- function ensure() {
114
- if (!ctxRef.current) {
115
- const AC = window.AudioContext || window.webkitAudioContext;
116
- if (AC) ctxRef.current = new AC();
117
- }
118
- return ctxRef.current;
119
- }
120
- function tone(freq, ms) {
121
- const ctx = ensure();
122
- if (!ctx) return;
123
- const o = ctx.createOscillator();
124
- const g = ctx.createGain();
125
- o.type = "sine";
126
- o.frequency.value = freq;
127
- g.gain.value = 0.03;
128
- o.connect(g);
129
- g.connect(ctx.destination);
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 { tick, success, error };
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 apiBaseUrl = props.apiBaseUrl ?? "";
151
- const locale = props.locale ?? "ja";
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 [muted, setMuted] = (0, import_react.useState)(props.mute ?? true);
158
- const esRef = (0, import_react.useRef)(null);
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
- let cancelled = false;
161
- async function load() {
162
- const url = `${apiBaseUrl}/v1/runs/${encodeURIComponent(props.runId)}?token=${encodeURIComponent(props.token)}&limit=2000`;
163
- const res = await fetch(url);
164
- if (!res.ok) throw new Error(await res.text());
165
- const data = await res.json();
166
- if (cancelled) return;
167
- setRun(data.run);
168
- setEvents(data.events || []);
169
- const mergedBrand = { ...data.workspace_brand, ...props.brandOverride || {} };
170
- setBrand(mergedBrand);
171
- setMuted(props.mute ?? !!mergedBrand.mute_default);
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
- load().catch(() => {
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
- return () => {
176
- cancelled = true;
177
- };
178
- }, [apiBaseUrl, props.runId, props.token]);
179
- (0, import_react.useEffect)(() => {
180
- const url = `${apiBaseUrl}/v1/runs/${encodeURIComponent(props.runId)}/stream?token=${encodeURIComponent(props.token)}`;
181
- const es = new EventSource(url);
182
- esRef.current = es;
183
- es.onmessage = (msg) => {
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 e = JSON.parse(msg.data);
186
- setEvents((prev) => [...prev, e]);
187
- if (e.type === "run.completed") {
188
- setRun((r) => r ? { ...r, status: "completed", ended_at: e.ts } : r);
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
- if (e.type === "run.failed") {
191
- setRun((r) => r ? { ...r, status: "failed", ended_at: e.ts } : r);
297
+ const r = await res.json();
298
+ if (!r?.run) {
299
+ lastErr = new Error("Invalid response from AgentJoy API.");
300
+ continue;
192
301
  }
193
- if (props.sound && !muted) {
194
- if (e.type === "phase.started") tick();
195
- if (e.type === "run.completed") success();
196
- if (e.type === "run.failed" || e.type === "error") error();
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
- } catch {
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
- es.onerror = () => {
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
- es.close();
205
- esRef.current = null;
536
+ closed = true;
537
+ if (timer) window.clearTimeout(timer);
538
+ try {
539
+ es?.close();
540
+ } catch {
541
+ }
206
542
  };
207
- }, [apiBaseUrl, props.runId, props.token, props.sound, muted]);
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 (run.status === "completed") props.onComplete?.(run);
211
- if (run.status === "failed") props.onError?.(run);
212
- }, [run?.status]);
213
- const resolvedBrand = (0, import_react.useMemo)(() => {
214
- if (!brand) return null;
215
- return { ...brand, ...props.brandOverride || {} };
216
- }, [brand, props.brandOverride]);
217
- const showXp = props.showXp ?? resolvedBrand?.show_xp ?? true;
218
- const showPersona = props.showPersona ?? resolvedBrand?.show_persona ?? true;
219
- const filtered = (0, import_react.useMemo)(() => {
220
- const arr = showPersona ? events : events.filter((e) => e.type !== "system.comment");
221
- return arr.slice(-limit);
222
- }, [events, limit, showPersona]);
223
- const currentPhase = (0, import_react.useMemo)(() => {
224
- for (let i = events.length - 1; i >= 0; i--) {
225
- const p = events[i].phase;
226
- if (p) return p;
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
- return "prepare";
229
- }, [events]);
230
- const lastProgress = (0, import_react.useMemo)(() => {
231
- for (let i = events.length - 1; i >= 0; i--) {
232
- const p = clamp01(events[i].progress ?? null);
233
- if (typeof p === "number") return p;
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
- return null;
236
- }, [events]);
237
- const progressPct = (0, import_react.useMemo)(() => {
238
- if (typeof lastProgress === "number") return Math.round(lastProgress * 100);
239
- const order = ["prepare", "research", "execute", "format", "verify", "finalize"];
240
- const idx = Math.max(0, order.indexOf(currentPhase));
241
- return Math.round((idx + 0.4) / order.length * 100);
242
- }, [currentPhase, lastProgress]);
243
- const xp = (0, import_react.useMemo)(() => events.reduce((acc, e) => acc + xpForEvent(e), 0), [events]);
244
- const level = Math.floor(xp / 250) + 1;
245
- const title = resolvedBrand?.product_name || "AgentJoy";
246
- const tone = resolvedBrand?.tone || "polite";
247
- const status = run?.status || "running";
248
- const rootStyle = {
249
- // @ts-ignore CSS variable typing
250
- ["--aj-primary"]: resolvedBrand?.primary_color || "#4F46E5"
251
- };
252
- if (!run) {
253
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ajw-root", style: rootStyle, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ajw-card", children: [
254
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ajw-header", children: [
255
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ajw-logo", children: "AJ" }),
256
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
257
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ajw-title", children: title }),
258
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ajw-sub", children: "loading\u2026" })
259
- ] })
260
- ] }),
261
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ajw-body", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ajw-event ajw-eventSystem", children: [
262
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ajw-eventMsg", children: "Loading run\u2026" }),
263
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ajw-eventMeta", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "ajw-mono", children: [
264
- "runId=",
265
- props.runId
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.jsx)("div", { className: "ajw-root", style: rootStyle, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ajw-card", children: [
271
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ajw-header", children: [
272
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ajw-logo", children: resolvedBrand?.logo_url ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { src: resolvedBrand.logo_url, style: { width: 28, height: 28, objectFit: "cover" } }) : "AJ" }),
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.jsxs)("div", { className: "ajw-sub", children: [
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.jsx)("div", { className: "ajw-actions", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "ajw-btn", onClick: () => setMuted((m) => !m), title: "mute/unmute", children: muted ? "\u{1F507}" : "\u{1F50A}" }) })
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-body", children: [
287
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ajw-progressRow", children: [
288
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ajw-progressBar", "aria-label": "progress", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { width: `${progressPct}%` } }) }),
289
- showXp ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ajw-xp", children: [
290
- "Lv.",
291
- level,
292
- " \xB7 XP ",
293
- xp
294
- ] }) : null
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)("div", { className: "ajw-events", children: filtered.map((e) => {
297
- if (e.type === "system.comment") {
298
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ajw-persona", children: [
299
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ajw-avatar", "aria-hidden": true, children: avatarForPack(run.pack) }),
300
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ajw-bubble", children: e.message })
301
- ] }, e.id);
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
- const cls = e.type === "warning" ? "ajw-event ajw-eventWarning" : e.type === "error" || e.type === "run.failed" ? "ajw-event ajw-eventError" : e.type === "run.created" ? "ajw-event ajw-eventSystem" : "ajw-event";
304
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: cls, children: [
305
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ajw-eventMsg", children: e.message }),
306
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ajw-eventMeta", children: [
307
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "ajw-mono", children: [
308
- e.type,
309
- e.phase ? ` \xB7 ${e.phase}` : "",
310
- e.tool?.name ? ` \xB7 tool=${e.tool.name}` : ""
311
- ] }),
312
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: formatTs(e.ts, locale) }),
313
- typeof e.progress === "number" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
314
- "progress=",
315
- Math.round(e.progress * 100),
316
- "%"
317
- ] }) : null
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.detail ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ajw-eventMeta", style: { whiteSpace: "pre-wrap" }, children: e.detail }) : null
320
- ] }, e.id);
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 = {