dsh-tool-stats 0.3.0 → 0.4.0

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/lib/stats.web.js CHANGED
@@ -81,224 +81,616 @@ var en = {
81
81
  };
82
82
 
83
83
  // src/client/view.tsx
84
+ var import_react2 = require("react");
85
+
86
+ // src/client/ui.tsx
84
87
  var import_react = require("react");
85
88
  var import_jsx_runtime = require("react/jsx-runtime");
86
- var PANEL_PATH = "/api/stats.panel";
87
- var DETAIL_PATH = "/api/stats.detail";
88
- var EXPORT_PATH = "/api/stats.export";
89
- var CLEAR_PATH = "/api/stats.clear";
90
- var wrap = { display: "flex", flexDirection: "column", gap: 12, maxWidth: 820, fontFamily: "inherit" };
91
- var head = { display: "flex", alignItems: "baseline", gap: 12, flexWrap: "wrap" };
92
- var muted = { fontSize: 12, opacity: 0.75 };
93
- var table = { borderCollapse: "collapse", width: "100%" };
94
- var th = { textAlign: "left", padding: "4px 10px 4px 0", fontWeight: 600, fontSize: 12, opacity: 0.8, borderBottom: "0.5px solid rgba(128,128,128,0.4)" };
95
- var td = { padding: "6px 10px 6px 0", fontSize: 13, borderBottom: "0.5px solid rgba(128,128,128,0.18)" };
96
- var list = { margin: 0, paddingLeft: 18, fontSize: 13 };
97
- var card = { padding: "8px 12px", borderRadius: 6, border: "1px solid rgba(128,128,128,0.2)", fontSize: 13 };
98
- var statBox = { ...card, flex: 1, minWidth: 100, textAlign: "center" };
99
- var statRow = { display: "flex", gap: 12, flexWrap: "wrap" };
100
- var btn = { fontSize: 12, cursor: "pointer", padding: "3px 10px", borderRadius: 4, border: "0.5px solid rgba(128,128,128,0.4)" };
101
- var dangerBtn = { ...btn, color: "#e55", borderColor: "#e55" };
102
- var alertCard = { ...card, display: "flex", alignItems: "center", gap: 8, marginTop: 4 };
103
- var preBlock = { whiteSpace: "pre-wrap", wordBreak: "break-word", fontSize: 11, maxHeight: 150, overflow: "auto", padding: 6, borderRadius: 4, background: "rgba(128,128,128,0.06)" };
104
- var clickRow = { cursor: "pointer" };
105
- function usePanel() {
106
- const [state, setState] = (0, import_react.useState)({ payload: null, error: null });
89
+ var T = {
90
+ accent: "var(--accent, #4B8BBE)",
91
+ accentHover: "var(--accent-hover, #3a6f9e)",
92
+ error: "var(--error, #e53935)",
93
+ success: "var(--success, #2e7d32)",
94
+ warning: "var(--warning, #ed6c02)",
95
+ bg: "var(--bg-secondary, rgba(128,128,128,0.04))",
96
+ bgHover: "var(--bg-tertiary, rgba(128,128,128,0.08))",
97
+ border: "var(--border, rgba(128,128,128,0.2))",
98
+ text: "var(--text-primary, inherit)",
99
+ muted: "var(--text-secondary, rgba(128,128,128,0.65))",
100
+ surface: "var(--bg-primary, #fff)"
101
+ };
102
+ var injected = false;
103
+ function ensureStyles() {
104
+ if (injected || typeof document === "undefined") return;
105
+ injected = true;
106
+ const el = document.createElement("style");
107
+ el.textContent = `
108
+ @keyframes dsh-fade-in{from{opacity:0}to{opacity:1}}
109
+ @keyframes dsh-slide-up{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}
110
+ @keyframes dsh-spin{to{transform:rotate(360deg)}}
111
+ .dsh-modal-overlay{animation:dsh-fade-in .15s ease}
112
+ .dsh-modal-body{animation:dsh-slide-up .2s ease}
113
+ .dsh-toast{animation:dsh-slide-up .2s ease}
114
+ .dsh-spin{animation:dsh-spin .6s linear infinite}
115
+ `;
116
+ document.head.appendChild(el);
117
+ }
118
+ function Modal({
119
+ title,
120
+ onClose,
121
+ children,
122
+ footer,
123
+ width = 520
124
+ }) {
125
+ (0, import_react.useEffect)(() => {
126
+ ensureStyles();
127
+ const handler = (e) => {
128
+ if (e.key === "Escape") onClose();
129
+ };
130
+ document.addEventListener("keydown", handler);
131
+ return () => document.removeEventListener("keydown", handler);
132
+ }, [onClose]);
133
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
134
+ "div",
135
+ {
136
+ className: "dsh-modal-overlay",
137
+ style: {
138
+ position: "fixed",
139
+ inset: 0,
140
+ zIndex: 1e3,
141
+ display: "flex",
142
+ alignItems: "center",
143
+ justifyContent: "center",
144
+ background: "rgba(0,0,0,0.45)",
145
+ backdropFilter: "blur(3px)",
146
+ WebkitBackdropFilter: "blur(3px)"
147
+ },
148
+ onClick: onClose,
149
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
150
+ "div",
151
+ {
152
+ className: "dsh-modal-body",
153
+ style: {
154
+ background: T.surface,
155
+ borderRadius: 14,
156
+ boxShadow: "0 12px 40px rgba(0,0,0,0.25)",
157
+ width: `min(92vw, ${width}px)`,
158
+ maxHeight: "85vh",
159
+ display: "flex",
160
+ flexDirection: "column",
161
+ overflow: "hidden"
162
+ },
163
+ onClick: (e) => e.stopPropagation(),
164
+ children: [
165
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
166
+ "div",
167
+ {
168
+ style: {
169
+ display: "flex",
170
+ alignItems: "center",
171
+ justifyContent: "space-between",
172
+ padding: "16px 22px",
173
+ borderBottom: `1px solid ${T.border}`
174
+ },
175
+ children: [
176
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { style: { margin: 0, fontSize: 15, fontWeight: 600 }, children: title }),
177
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
178
+ "button",
179
+ {
180
+ type: "button",
181
+ onClick: onClose,
182
+ style: {
183
+ background: "none",
184
+ border: "none",
185
+ cursor: "pointer",
186
+ fontSize: 20,
187
+ color: T.muted,
188
+ padding: "0 4px",
189
+ lineHeight: 1,
190
+ borderRadius: 4
191
+ },
192
+ "aria-label": "Close",
193
+ children: "\xD7"
194
+ }
195
+ )
196
+ ]
197
+ }
198
+ ),
199
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { padding: 22, overflow: "auto", flex: 1 }, children }),
200
+ footer !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
201
+ "div",
202
+ {
203
+ style: {
204
+ padding: "14px 22px",
205
+ borderTop: `1px solid ${T.border}`,
206
+ display: "flex",
207
+ justifyContent: "flex-end",
208
+ gap: 10,
209
+ background: T.bg
210
+ },
211
+ children: footer
212
+ }
213
+ )
214
+ ]
215
+ }
216
+ )
217
+ }
218
+ );
219
+ }
220
+ function ConfirmDialog({
221
+ title,
222
+ message,
223
+ confirmLabel,
224
+ cancelLabel = "Cancel",
225
+ onConfirm,
226
+ onClose,
227
+ danger
228
+ }) {
229
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
230
+ Modal,
231
+ {
232
+ title,
233
+ onClose,
234
+ width: 420,
235
+ footer: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
236
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, { variant: "secondary", onClick: onClose, children: cancelLabel }),
237
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
238
+ Button,
239
+ {
240
+ variant: danger === true ? "danger" : "primary",
241
+ onClick: () => {
242
+ onConfirm();
243
+ onClose();
244
+ },
245
+ children: confirmLabel
246
+ }
247
+ )
248
+ ] }),
249
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { margin: 0, fontSize: 14, lineHeight: 1.6 }, children: message })
250
+ }
251
+ );
252
+ }
253
+ function Button({
254
+ variant = "secondary",
255
+ size = "md",
256
+ children,
257
+ style,
258
+ ...rest
259
+ }) {
260
+ const base = {
261
+ cursor: "pointer",
262
+ borderRadius: 7,
263
+ fontWeight: 500,
264
+ transition: "all 0.15s ease",
265
+ border: "none",
266
+ display: "inline-flex",
267
+ alignItems: "center",
268
+ gap: 4,
269
+ fontSize: size === "sm" ? 12 : 13,
270
+ padding: size === "sm" ? "5px 12px" : "8px 18px",
271
+ fontFamily: "inherit"
272
+ };
273
+ const variants = {
274
+ primary: { background: T.accent, color: "#fff" },
275
+ secondary: {
276
+ background: T.bg,
277
+ color: T.text,
278
+ border: `1px solid ${T.border}`
279
+ },
280
+ danger: {
281
+ background: "transparent",
282
+ color: T.error,
283
+ border: `1px solid ${T.error}`
284
+ },
285
+ ghost: { background: "transparent", color: T.muted, border: "none" }
286
+ };
287
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", style: { ...base, ...variants[variant], ...style }, ...rest, children });
288
+ }
289
+ function Card({
290
+ title,
291
+ icon,
292
+ children,
293
+ actions,
294
+ style,
295
+ padding = 14
296
+ }) {
297
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
298
+ "div",
299
+ {
300
+ style: {
301
+ borderRadius: 10,
302
+ border: `1px solid ${T.border}`,
303
+ background: T.bg,
304
+ overflow: "hidden",
305
+ ...style
306
+ },
307
+ children: [
308
+ (title !== void 0 || actions !== void 0) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
309
+ "div",
310
+ {
311
+ style: {
312
+ display: "flex",
313
+ alignItems: "center",
314
+ justifyContent: "space-between",
315
+ padding: "12px 16px",
316
+ borderBottom: title !== void 0 ? `1px solid ${T.border}` : "none"
317
+ },
318
+ children: [
319
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: { fontSize: 13, fontWeight: 600, display: "flex", alignItems: "center", gap: 6 }, children: [
320
+ icon !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: icon }),
321
+ title
322
+ ] }),
323
+ actions
324
+ ]
325
+ }
326
+ ),
327
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { padding }, children })
328
+ ]
329
+ }
330
+ );
331
+ }
332
+ function StatCard({
333
+ value,
334
+ label,
335
+ color,
336
+ unit
337
+ }) {
338
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
339
+ "div",
340
+ {
341
+ style: {
342
+ flex: 1,
343
+ minWidth: 90,
344
+ textAlign: "center",
345
+ padding: "14px 10px",
346
+ borderRadius: 10,
347
+ border: `1px solid ${T.border}`,
348
+ background: T.bg
349
+ },
350
+ children: [
351
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { fontSize: 24, fontWeight: 700, color: color ?? T.text, lineHeight: 1.2 }, children: [
352
+ value,
353
+ unit !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontSize: 13, fontWeight: 500, opacity: 0.7, marginLeft: 2 }, children: unit })
354
+ ] }),
355
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontSize: 11, color: T.muted, marginTop: 4 }, children: label })
356
+ ]
357
+ }
358
+ );
359
+ }
360
+ function Badge({
361
+ children,
362
+ color = "default"
363
+ }) {
364
+ const colors = {
365
+ default: { bg: "rgba(128,128,128,0.12)", fg: T.muted },
366
+ success: { bg: "rgba(46,125,50,0.12)", fg: T.success },
367
+ error: { bg: "rgba(229,57,53,0.12)", fg: T.error },
368
+ warning: { bg: "rgba(245,124,0,0.12)", fg: T.warning },
369
+ info: { bg: "rgba(75,139,190,0.12)", fg: T.accent }
370
+ };
371
+ const c = colors[color];
372
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
373
+ "span",
374
+ {
375
+ style: {
376
+ display: "inline-block",
377
+ padding: "2px 9px",
378
+ borderRadius: 10,
379
+ fontSize: 11,
380
+ fontWeight: 600,
381
+ background: c.bg,
382
+ color: c.fg
383
+ },
384
+ children
385
+ }
386
+ );
387
+ }
388
+ var ToastContext = (0, import_react.createContext)(
389
+ () => {
390
+ }
391
+ );
392
+ function ToastProvider({ children }) {
393
+ const [toasts, setToasts] = (0, import_react.useState)([]);
394
+ const show = (0, import_react.useCallback)((type, message) => {
395
+ const id = Date.now() + Math.random();
396
+ setToasts((t) => [...t, { id, type, message }]);
397
+ setTimeout(() => setToasts((t) => t.filter((x) => x.id !== id)), 3500);
398
+ }, []);
399
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ToastContext.Provider, { value: show, children: [
400
+ children,
401
+ toasts.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
402
+ "div",
403
+ {
404
+ style: {
405
+ position: "fixed",
406
+ bottom: 24,
407
+ right: 24,
408
+ zIndex: 2e3,
409
+ display: "flex",
410
+ flexDirection: "column",
411
+ gap: 8
412
+ },
413
+ children: toasts.map((t) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
414
+ "div",
415
+ {
416
+ className: "dsh-toast",
417
+ style: {
418
+ padding: "12px 18px",
419
+ borderRadius: 8,
420
+ fontSize: 13,
421
+ fontWeight: 500,
422
+ boxShadow: "0 6px 20px rgba(0,0,0,0.2)",
423
+ color: "#fff",
424
+ background: t.type === "success" ? T.success : t.type === "error" ? T.error : T.accent,
425
+ maxWidth: 360
426
+ },
427
+ children: t.message
428
+ },
429
+ t.id
430
+ ))
431
+ }
432
+ )
433
+ ] });
434
+ }
435
+ function useToast() {
436
+ return (0, import_react.useContext)(ToastContext);
437
+ }
438
+ function Spinner({ size = 16 }) {
439
+ ensureStyles();
440
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
441
+ "span",
442
+ {
443
+ className: "dsh-spin",
444
+ style: {
445
+ display: "inline-block",
446
+ width: size,
447
+ height: size,
448
+ border: `2px solid ${T.border}`,
449
+ borderTopColor: T.accent,
450
+ borderRadius: "50%"
451
+ }
452
+ }
453
+ );
454
+ }
455
+ function EmptyState({
456
+ icon,
457
+ message
458
+ }) {
459
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
460
+ "div",
461
+ {
462
+ style: {
463
+ textAlign: "center",
464
+ padding: "36px 16px",
465
+ color: T.muted
466
+ },
467
+ children: [
468
+ icon !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontSize: 36, marginBottom: 10, opacity: 0.4 }, children: icon }),
469
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontSize: 13 }, children: message })
470
+ ]
471
+ }
472
+ );
473
+ }
474
+ function SectionTitle({
475
+ icon,
476
+ children,
477
+ actions
478
+ }) {
479
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
480
+ "div",
481
+ {
482
+ style: {
483
+ display: "flex",
484
+ alignItems: "center",
485
+ justifyContent: "space-between",
486
+ marginTop: 6,
487
+ marginBottom: 8
488
+ },
489
+ children: [
490
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
491
+ "span",
492
+ {
493
+ style: {
494
+ fontSize: 14,
495
+ fontWeight: 600,
496
+ display: "flex",
497
+ alignItems: "center",
498
+ gap: 6
499
+ },
500
+ children: [
501
+ icon !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: icon }),
502
+ children
503
+ ]
504
+ }
505
+ ),
506
+ actions
507
+ ]
508
+ }
509
+ );
510
+ }
511
+ var tableStyles = {
512
+ table: {
513
+ borderCollapse: "collapse",
514
+ width: "100%"
515
+ },
516
+ th: {
517
+ textAlign: "left",
518
+ padding: "8px 12px 8px 0",
519
+ fontWeight: 600,
520
+ fontSize: 12,
521
+ color: T.muted,
522
+ borderBottom: `1px solid ${T.border}`
523
+ },
524
+ td: {
525
+ padding: "8px 12px 8px 0",
526
+ fontSize: 13,
527
+ borderBottom: "1px solid rgba(128,128,128,0.08)"
528
+ },
529
+ clickRow: { cursor: "pointer" }
530
+ };
531
+ function usePanel(path) {
532
+ const [payload, setPayload] = (0, import_react.useState)(null);
533
+ const [error, setError] = (0, import_react.useState)(null);
107
534
  const [tick, setTick] = (0, import_react.useState)(0);
108
535
  const reload = (0, import_react.useCallback)(() => setTick((v) => v + 1), []);
109
536
  (0, import_react.useEffect)(() => {
110
537
  const c = new AbortController();
111
- setState((p) => ({ ...p, error: null }));
112
- fetch(PANEL_PATH, { signal: c.signal }).then(async (r) => {
538
+ setError(null);
539
+ fetch(path, { signal: c.signal }).then(async (r) => {
113
540
  if (!r.ok) throw new Error(String(r.status));
114
541
  return r.json();
115
- }).then((payload) => {
116
- if (!c.signal.aborted) setState({ payload, error: null });
542
+ }).then((p) => {
543
+ if (!c.signal.aborted) setPayload(p);
117
544
  }).catch((e) => {
118
- if (!c.signal.aborted) setState({ payload: null, error: e instanceof Error ? e.message : String(e) });
545
+ if (!c.signal.aborted) setPayload(null), setError(e instanceof Error ? e.message : String(e));
119
546
  });
120
547
  return () => c.abort();
121
- }, [tick]);
122
- return { ...state, reload };
123
- }
124
- function AlertBanner({ alerts, t }) {
125
- if (alerts.length === 0) return null;
126
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: alerts.map((a, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { ...alertCard, borderColor: a.level === "error" ? "#e55" : "#ed0", background: a.level === "error" ? "rgba(255,80,80,0.06)" : "rgba(255,200,0,0.06)" }, children: [
127
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontSize: 16 }, children: a.level === "error" ? "\u{1F534}" : "\u{1F7E1}" }),
128
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
129
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: a.tool }),
130
- ": ",
131
- a.message
132
- ] })
133
- ] }, i)) });
548
+ }, [tick, path]);
549
+ return (0, import_react.useMemo)(() => ({ payload, error, reload }), [payload, error, reload]);
134
550
  }
135
- function Overview({ payload, t }) {
136
- const o = payload.overview;
137
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: statRow, children: [
138
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: statBox, children: [
139
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontSize: 18, fontWeight: 700 }, children: o.totalCalls }),
140
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: muted, children: t("totalCalls") })
141
- ] }),
142
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: statBox, children: [
143
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontSize: 18, fontWeight: 700, color: o.totalFailures > 0 ? "#e55" : void 0 }, children: o.totalFailures }),
144
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: muted, children: t("totalFailures") })
145
- ] }),
146
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: statBox, children: [
147
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { fontSize: 18, fontWeight: 700 }, children: [
148
- Math.round(o.overallFailureRate * 100),
149
- "%"
551
+
552
+ // src/client/view.tsx
553
+ var import_jsx_runtime2 = require("react/jsx-runtime");
554
+ var PANEL_PATH = "/api/stats.panel";
555
+ var DETAIL_PATH = "/api/stats.detail";
556
+ var EXPORT_PATH = "/api/stats.export";
557
+ var CLEAR_PATH = "/api/stats.clear";
558
+ function ToolModal({ tool, t, onClose }) {
559
+ const [detail, setDetail] = (0, import_react2.useState)(null);
560
+ const [loading, setLoading] = (0, import_react2.useState)(true);
561
+ (0, import_react2.useCallback)(() => {
562
+ setLoading(true);
563
+ fetch(`${DETAIL_PATH}?tool=${encodeURIComponent(tool)}`).then(async (r) => {
564
+ if (r.ok) setDetail(await r.json());
565
+ }).finally(() => setLoading(false));
566
+ }, [tool])();
567
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Modal, { title: `${t("tool")}: ${tool}`, onClose, width: 600, children: loading ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { display: "flex", justifyContent: "center", padding: 20 }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Spinner, { size: 24 }) }) : detail === null ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(EmptyState, { message: t("notFound") }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 12 }, children: [
568
+ detail.errorBreakdown.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Card, { title: t("errorBreakdown"), children: detail.errorBreakdown.map((e, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", gap: 8, alignItems: "center", marginBottom: 4 }, children: [
569
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Badge, { color: "error", children: [
570
+ "\xD7",
571
+ e.count
150
572
  ] }),
151
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: muted, children: t("failRate") })
152
- ] }),
153
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: statBox, children: [
154
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontSize: 18, fontWeight: 700 }, children: o.activeSessions }),
155
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: muted, children: t("sessions") })
156
- ] }),
157
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: statBox, children: [
158
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { fontSize: 18, fontWeight: 700 }, children: [
159
- o.avgLatencyMs,
573
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { fontSize: 12 }, children: e.message })
574
+ ] }, i)) }),
575
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Card, { title: t("recentCalls"), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("table", { style: tableStyles.table, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("tbody", { children: detail.recentCalls.slice(0, 15).map((c, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("tr", { children: [
576
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { style: tableStyles.td, children: c.success ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Badge, { color: "success", children: "\u2713" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Badge, { color: "error", children: "\u2717" }) }),
577
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("td", { style: tableStyles.td, children: [
578
+ c.latencyMs,
160
579
  "ms"
161
580
  ] }),
162
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: muted, children: t("avgLatency") })
163
- ] })
164
- ] });
581
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { style: tableStyles.td, children: new Date(c.timestamp).toLocaleTimeString() }),
582
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { style: { ...tableStyles.td, fontSize: 10, opacity: 0.6 }, children: c.errorMessage?.slice(0, 60) })
583
+ ] }, i)) }) }) })
584
+ ] }) });
165
585
  }
166
- function ToolDetailRow({ tool, t }) {
167
- const [detail, setDetail] = (0, import_react.useState)(null);
168
- const [loading, setLoading] = (0, import_react.useState)(false);
169
- const toggle = (0, import_react.useCallback)(async () => {
170
- if (detail) {
171
- setDetail(null);
172
- return;
586
+ function ToolStatsPanelInner({ t }) {
587
+ const { payload, error, reload } = usePanel(PANEL_PATH);
588
+ const toast = useToast();
589
+ const [toolModal, setToolModal] = (0, import_react2.useState)(null);
590
+ const [confirmClear, setConfirmClear] = (0, import_react2.useState)(false);
591
+ const handleClear = (0, import_react2.useCallback)(async () => {
592
+ const r = await fetch(CLEAR_PATH, { method: "POST" });
593
+ if (r.ok) {
594
+ toast("success", t("cleared"));
595
+ reload();
173
596
  }
174
- setLoading(true);
175
- try {
176
- const r = await fetch(`${DETAIL_PATH}?tool=${encodeURIComponent(tool)}`);
177
- if (r.ok) setDetail(await r.json());
178
- } finally {
179
- setLoading(false);
180
- }
181
- }, [detail, tool]);
182
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
183
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tr", { style: clickRow, onClick: toggle, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { style: td, children: loading ? "\u2026" : detail ? "\u25BC" : "\u25B6" }) }),
184
- detail && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { colSpan: 6, style: { padding: "4px 0" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: card, children: [
185
- detail.errorBreakdown.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginBottom: 8 }, children: [
186
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { style: { fontSize: 12 }, children: t("errorBreakdown") }),
187
- detail.errorBreakdown.map((e, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { ...preBlock, marginTop: 4 }, children: [
188
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: { color: "#e55", fontWeight: 600 }, children: [
189
- "\xD7",
190
- e.count
191
- ] }),
192
- " ",
193
- e.message
194
- ] }, i))
195
- ] }),
196
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { style: { fontSize: 12 }, children: t("recentCalls") }),
197
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("table", { style: { ...table, marginTop: 4 }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: detail.recentCalls.slice(0, 10).map((c, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
198
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { style: td, children: c.success ? "\u2713" : "\u2717" }),
199
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("td", { style: td, children: [
200
- c.latencyMs,
201
- "ms"
202
- ] }),
203
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { style: td, children: new Date(c.timestamp).toLocaleTimeString() }),
204
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { style: { ...td, fontSize: 10, opacity: 0.7 }, children: c.errorMessage?.slice(0, 60) })
205
- ] }, i)) }) })
206
- ] }) }) })
597
+ }, [t, toast, reload]);
598
+ const header = /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("header", { style: { display: "flex", alignItems: "center", gap: 12, marginBottom: 4 }, children: [
599
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("strong", { style: { fontSize: 15 }, children: [
600
+ "\u{1F527} ",
601
+ t("title")
602
+ ] }),
603
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { flex: 1 } }),
604
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("a", { href: EXPORT_PATH, download: true, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Button, { variant: "secondary", size: "sm", children: t("export") }) }),
605
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Button, { variant: "danger", size: "sm", onClick: () => setConfirmClear(true), children: t("clear") }),
606
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Button, { variant: "secondary", size: "sm", onClick: reload, children: t("refresh") })
207
607
  ] });
208
- }
209
- function ToolStatsPanel({ t }) {
210
- const { payload, error, reload } = usePanel();
211
- const handleClear = (0, import_react.useCallback)(() => {
212
- if (!confirm(t("confirmClear"))) return;
213
- fetch(CLEAR_PATH, { method: "POST" }).then(() => reload()).catch(() => {
214
- });
215
- }, [t, reload]);
216
- const header = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { style: head, children: [
217
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { style: { fontSize: 13 }, children: t("title") }),
218
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { flex: 1 } }),
219
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { href: EXPORT_PATH, download: true, style: { ...btn, textDecoration: "none", color: "inherit" }, children: t("export") }),
220
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", style: dangerBtn, onClick: handleClear, children: t("clear") }),
221
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", style: btn, onClick: reload, children: t("refresh") })
608
+ if (error !== null) return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { maxWidth: 820 }, children: [
609
+ header,
610
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Card, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { role: "alert", style: { margin: 0, fontSize: 13, color: "var(--error, #e53935)" }, children: [
611
+ t("failed"),
612
+ ": ",
613
+ error
614
+ ] }) })
222
615
  ] });
223
- if (error !== null) {
224
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: wrap, children: [
225
- header,
226
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { role: "alert", style: { margin: 0, fontSize: 13 }, children: [
227
- t("failed"),
228
- ": ",
229
- error
230
- ] }),
231
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: reload, style: { ...btn, alignSelf: "flex-start" }, children: t("retry") })
232
- ] });
233
- }
234
- if (payload === null) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: muted, "aria-live": "polite", children: t("loading") });
235
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: wrap, children: [
616
+ if (payload === null) return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { maxWidth: 820 }, children: [
236
617
  header,
237
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AlertBanner, { alerts: payload.alerts, t }),
238
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Overview, { payload, t }),
239
- payload.tools.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { margin: 0, fontSize: 13, opacity: 0.8 }, children: t("empty") }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("table", { style: table, children: [
240
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
241
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { style: th }),
242
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { style: th, children: t("tool") }),
243
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { style: th, children: t("calls") }),
244
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { style: th, children: t("failRate") }),
245
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { style: th, children: "p50" }),
246
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { style: th, children: "p95" })
247
- ] }) }),
248
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: payload.tools.map((row) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { style: clickRow, onClick: () => {
249
- }, children: [
250
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { style: td, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ToolDetailRow, { tool: row.tool, t }) }),
251
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { style: td, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { style: { fontSize: 11 }, children: row.tool }) }),
252
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { style: td, children: row.invocations }),
253
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("td", { style: { ...td, color: row.failureRate > 0.3 ? "#e55" : void 0 }, children: [
254
- Math.round(row.failureRate * 100),
255
- "%"
256
- ] }),
257
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("td", { style: td, children: [
258
- row.p50Latency,
259
- "ms"
260
- ] }),
261
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("td", { style: td, children: [
262
- row.p95Latency,
263
- "ms"
264
- ] })
265
- ] }, row.tool) })) })
618
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { display: "flex", justifyContent: "center", padding: 40 }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Spinner, { size: 28 }) })
619
+ ] });
620
+ const o = payload.overview;
621
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 14, maxWidth: 820 }, children: [
622
+ header,
623
+ payload.alerts.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: payload.alerts.map((a, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Card, { style: { borderColor: a.level === "error" ? "var(--error, #e53935)" : "var(--warning, #ed6c02)" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
624
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Badge, { color: a.level === "error" ? "error" : "warning", children: a.level === "error" ? "\u{1F534}" : "\u{1F7E1}" }),
625
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("strong", { children: a.tool }),
626
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { fontSize: 12 }, children: a.message })
627
+ ] }) }, i)) }),
628
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", gap: 10, flexWrap: "wrap" }, children: [
629
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(StatCard, { value: o.totalCalls, label: t("totalCalls") }),
630
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(StatCard, { value: o.totalFailures, label: t("totalFailures"), color: o.totalFailures > 0 ? "var(--error, #e53935)" : void 0 }),
631
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(StatCard, { value: `${Math.round(o.overallFailureRate * 100)}%`, label: t("failRate") }),
632
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(StatCard, { value: o.activeSessions, label: t("sessions") }),
633
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(StatCard, { value: o.avgLatencyMs, unit: "ms", label: t("avgLatency") })
266
634
  ] }),
267
- payload.deadTools.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
268
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("strong", { style: { fontSize: 13 }, children: [
269
- "\u{1F480} ",
270
- t("deadTools")
271
- ] }),
272
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { style: list, children: payload.deadTools.map((row) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { children: [
273
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { style: { fontSize: 11 }, children: row.tool }),
635
+ payload.tools.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(EmptyState, { icon: "\u{1F4ED}", message: t("empty") }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
636
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SectionTitle, { icon: "\u{1F4CA}", children: t("tools") }),
637
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Card, { padding: 0, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("table", { style: tableStyles.table, children: [
638
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("tr", { children: [
639
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("th", { style: tableStyles.th, children: t("tool") }),
640
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("th", { style: tableStyles.th, children: t("calls") }),
641
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("th", { style: tableStyles.th, children: t("failRate") }),
642
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("th", { style: tableStyles.th, children: "p50" }),
643
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("th", { style: tableStyles.th, children: "p95" })
644
+ ] }) }),
645
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("tbody", { children: payload.tools.map((row) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("tr", { style: tableStyles.clickRow, onClick: () => setToolModal(row.tool), children: [
646
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { style: tableStyles.td, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { style: { fontSize: 11 }, children: row.tool }) }),
647
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { style: tableStyles.td, children: row.invocations }),
648
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("td", { style: { ...tableStyles.td, color: row.failureRate > 0.3 ? "var(--error, #e53935)" : void 0 }, children: [
649
+ Math.round(row.failureRate * 100),
650
+ "%"
651
+ ] }),
652
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("td", { style: tableStyles.td, children: [
653
+ row.p50Latency,
654
+ "ms"
655
+ ] }),
656
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("td", { style: tableStyles.td, children: [
657
+ row.p95Latency,
658
+ "ms"
659
+ ] })
660
+ ] }, row.tool)) })
661
+ ] }) })
662
+ ] }),
663
+ payload.deadTools.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
664
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SectionTitle, { icon: "\u{1F480}", children: t("deadTools") }),
665
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Card, { children: payload.deadTools.map((row) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { marginBottom: 4 }, children: [
666
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { style: { fontSize: 11 }, children: row.tool }),
274
667
  " \u2014 ",
275
668
  row.sessionsSinceLastUse,
276
669
  " ",
277
670
  t("sessionsUnused")
278
671
  ] }, row.tool)) })
279
672
  ] }),
280
- payload.failingTools.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
281
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("strong", { style: { fontSize: 13 }, children: [
282
- "\u26A0\uFE0F ",
283
- t("failingTools")
284
- ] }),
285
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { style: list, children: payload.failingTools.map((row) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { children: [
286
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { style: { fontSize: 11 }, children: row.tool }),
673
+ payload.failingTools.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
674
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SectionTitle, { icon: "\u26A0\uFE0F", children: t("failingTools") }),
675
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Card, { children: payload.failingTools.map((row) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { marginBottom: 4 }, children: [
676
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { style: { fontSize: 11 }, children: row.tool }),
287
677
  " \xB7 ",
288
678
  Math.round(row.failureRate * 100),
289
679
  "%",
290
680
  row.lastError ? ` \u2014 ${row.lastError.slice(0, 60)}` : ""
291
681
  ] }, row.tool)) })
292
682
  ] }),
293
- payload.recommendations.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
294
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("strong", { style: { fontSize: 13 }, children: [
295
- "\u{1F4A1} ",
296
- t("recommendations")
297
- ] }),
298
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { style: list, children: payload.recommendations.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: row.message }, i)) })
299
- ] })
683
+ payload.recommendations.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
684
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SectionTitle, { icon: "\u{1F4A1}", children: t("recommendations") }),
685
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Card, { children: payload.recommendations.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { marginBottom: 4 }, children: row.message }, i)) })
686
+ ] }),
687
+ toolModal !== null && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ToolModal, { tool: toolModal, t, onClose: () => setToolModal(null) }),
688
+ confirmClear && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ConfirmDialog, { title: t("clear"), message: t("confirmClear"), confirmLabel: t("clear"), danger: true, onConfirm: handleClear, onClose: () => setConfirmClear(false) })
300
689
  ] });
301
690
  }
691
+ function ToolStatsPanel({ t }) {
692
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ToastProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ToolStatsPanelInner, { t }) });
693
+ }
302
694
 
303
695
  // src/client/index.tsx
304
696
  var inject = ["slots", "locale"];