@mhosaic/feedback 0.32.0 → 0.34.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.
@@ -145,6 +145,32 @@ function createApiClient(options) {
145
145
  if (response.status === 404) return [];
146
146
  return readJsonArray(response, "listMine");
147
147
  }
148
+ async function diagnose(opts) {
149
+ const onLine = opts?.onLine ?? (typeof navigator !== "undefined" ? navigator.onLine : true);
150
+ if (onLine === false) return "offline";
151
+ const probeUrl = `${endpoint}/api/feedback/v1/widget-manifest/?pk=${encodeURIComponent(options.apiKey)}`;
152
+ let simple;
153
+ try {
154
+ simple = await fetcher(probeUrl, { method: "GET", redirect: "manual" });
155
+ } catch {
156
+ return "blocked";
157
+ }
158
+ if (simple.type === "opaqueredirect" || simple.status >= 300 && simple.status < 400) {
159
+ return "intercepted";
160
+ }
161
+ let authed;
162
+ try {
163
+ authed = await fetcher(probeUrl, {
164
+ method: "GET",
165
+ headers: { Authorization: `Bearer ${options.apiKey}` }
166
+ });
167
+ } catch {
168
+ return "preflight";
169
+ }
170
+ if (authed.status === 401 || authed.status === 403) return "key";
171
+ if (authed.status === 429) return "throttle";
172
+ return "healthy";
173
+ }
148
174
  async function listChangelog(externalId) {
149
175
  const response = await fetcher(
150
176
  `${endpoint}/api/feedback/v1/reports/widget/changelog/`,
@@ -330,6 +356,14 @@ function createApiClient(options) {
330
356
  checkVisibility,
331
357
  listMine,
332
358
  listChangelog,
359
+ diagnose,
360
+ endpointHost: (() => {
361
+ try {
362
+ return new URL(endpoint).host;
363
+ } catch {
364
+ return endpoint;
365
+ }
366
+ })(),
333
367
  getReport,
334
368
  getReportBySeq,
335
369
  addComment,
@@ -739,6 +773,19 @@ var DEFAULT_STRINGS = {
739
773
  "mine.empty.title": "No reports yet",
740
774
  "mine.empty.body": "Once you send feedback you can follow the thread here.",
741
775
  "mine.refresh": "Refresh",
776
+ "mine.validate.banner": "{count} fix(es) awaiting your confirmation",
777
+ "mine.validate.confirm": "Fix works",
778
+ "mine.validate.reopen": "Still broken",
779
+ "mine.validate.view": "Review",
780
+ "diag.run": "Diagnose",
781
+ "diag.running": "Checking the connection\u2026",
782
+ "diag.offline": "You appear to be offline. Reconnect and retry.",
783
+ "diag.blocked": "The feedback service is unreachable from this network. A firewall or proxy is blocking it \u2014 ask IT to allow {host}.",
784
+ "diag.intercepted": "Your corporate network (SSL-inspection proxy) is intercepting widget traffic. Ask IT to add {host} to the inspection bypass list.",
785
+ "diag.preflight": "A network proxy is blocking the widget\u2019s secure requests (CORS preflight). Ask IT to allow {host}.",
786
+ "diag.key": "The widget\u2019s access key was rejected. Contact the site team.",
787
+ "diag.throttle": "Too many requests right now \u2014 wait a moment and retry.",
788
+ "diag.healthy": "The service is reachable \u2014 this looks like a temporary hiccup. Retry.",
742
789
  "mine.loading": "Loading\u2026",
743
790
  "mine.error": "Could not load your reports.",
744
791
  "mine.jump.placeholder": "#",
@@ -926,6 +973,19 @@ var FRENCH_STRINGS = {
926
973
  "mine.empty.title": "Aucun rapport",
927
974
  "mine.empty.body": "Apr\xE8s votre premier envoi vous pourrez suivre la conversation ici.",
928
975
  "mine.refresh": "Actualiser",
976
+ "mine.validate.banner": "{count} correctif(s) \xE0 confirmer",
977
+ "mine.validate.confirm": "Corrig\xE9 \u2713",
978
+ "mine.validate.reopen": "Toujours cass\xE9",
979
+ "mine.validate.view": "Voir",
980
+ "diag.run": "Diagnostiquer",
981
+ "diag.running": "V\xE9rification de la connexion\u2026",
982
+ "diag.offline": "Vous semblez hors ligne. Reconnectez-vous puis r\xE9essayez.",
983
+ "diag.blocked": "Le service de feedback est injoignable depuis ce r\xE9seau. Un pare-feu ou proxy le bloque \u2014 demandez \xE0 votre TI d\u2019autoriser {host}.",
984
+ "diag.intercepted": "Votre r\xE9seau d\u2019entreprise (inspection SSL) intercepte le trafic du widget. Demandez \xE0 votre TI d\u2019ajouter {host} \xE0 la liste d\u2019exclusion.",
985
+ "diag.preflight": "Un proxy r\xE9seau bloque les requ\xEAtes s\xE9curis\xE9es du widget (pr\xE9flight CORS). Demandez \xE0 votre TI d\u2019autoriser {host}.",
986
+ "diag.key": "La cl\xE9 d\u2019acc\xE8s du widget a \xE9t\xE9 refus\xE9e. Contactez l\u2019\xE9quipe du site.",
987
+ "diag.throttle": "Trop de requ\xEAtes pour le moment \u2014 patientez puis r\xE9essayez.",
988
+ "diag.healthy": "Le service est joignable \u2014 le probl\xE8me semble passager. R\xE9essayez.",
929
989
  "mine.loading": "Chargement\u2026",
930
990
  "mine.error": "Impossible de charger vos rapports.",
931
991
  "mine.jump.placeholder": "#",
@@ -1017,7 +1077,7 @@ function resolveStrings(overrides, options = {}) {
1017
1077
 
1018
1078
  // src/widget/mount.tsx
1019
1079
  import { h, render } from "preact";
1020
- import { useCallback as useCallback2, useEffect as useEffect10, useRef as useRef7, useState as useState9 } from "preact/hooks";
1080
+ import { useCallback as useCallback2, useEffect as useEffect10, useRef as useRef7, useState as useState10 } from "preact/hooks";
1021
1081
 
1022
1082
  // src/widget/currentPage.ts
1023
1083
  function currentPagePath(opts) {
@@ -1050,7 +1110,30 @@ function onLocationChange(cb) {
1050
1110
  }
1051
1111
 
1052
1112
  // src/widget/BoardView.tsx
1053
- import { useEffect as useEffect2, useMemo, useState as useState2 } from "preact/hooks";
1113
+ import { useEffect as useEffect2, useMemo, useState as useState3 } from "preact/hooks";
1114
+
1115
+ // src/widget/DiagnosticHint.tsx
1116
+ import { useState } from "preact/hooks";
1117
+ import { jsx } from "preact/jsx-runtime";
1118
+ function DiagnosticHint({ api, strings, host }) {
1119
+ const [state, setState] = useState("idle");
1120
+ const run = async () => {
1121
+ setState("running");
1122
+ try {
1123
+ setState(await api.diagnose());
1124
+ } catch {
1125
+ setState("blocked");
1126
+ }
1127
+ };
1128
+ if (state === "idle") {
1129
+ return /* @__PURE__ */ jsx("button", { type: "button", class: "btn diag-btn", onClick: () => void run(), children: strings["diag.run"] });
1130
+ }
1131
+ if (state === "running") {
1132
+ return /* @__PURE__ */ jsx("div", { class: "diag-result", children: strings["diag.running"] });
1133
+ }
1134
+ const key = `diag.${state}`;
1135
+ return /* @__PURE__ */ jsx("div", { class: "diag-result", children: (strings[key] ?? state).replace("{host}", host) });
1136
+ }
1054
1137
 
1055
1138
  // src/widget/boardCache.ts
1056
1139
  var TTL_MS = 6e4;
@@ -1142,7 +1225,7 @@ function rateLimitMessage(err, strings) {
1142
1225
  }
1143
1226
 
1144
1227
  // src/widget/ReportDetailView.tsx
1145
- import { useEffect, useRef, useState } from "preact/hooks";
1228
+ import { useEffect, useRef, useState as useState2 } from "preact/hooks";
1146
1229
 
1147
1230
  // src/widget/linkifySeq.tsx
1148
1231
  import { jsxs } from "preact/jsx-runtime";
@@ -1179,7 +1262,7 @@ function linkifySeq(text, onOpenSeq) {
1179
1262
  }
1180
1263
 
1181
1264
  // src/widget/CommentBubble.tsx
1182
- import { jsx, jsxs as jsxs2 } from "preact/jsx-runtime";
1265
+ import { jsx as jsx2, jsxs as jsxs2 } from "preact/jsx-runtime";
1183
1266
  function CommentBubble({ comment, strings, onOpenSeq, stableUrl }) {
1184
1267
  const isMine = comment.is_mine;
1185
1268
  const isAgent = !isMine && comment.author_source === "mcp";
@@ -1189,23 +1272,23 @@ function CommentBubble({ comment, strings, onOpenSeq, stableUrl }) {
1189
1272
  const label = comment.author_label || strings[labelKey];
1190
1273
  const images = (comment.attachments ?? []).filter((a) => a.url);
1191
1274
  return /* @__PURE__ */ jsxs2("div", { class: `comment-bubble ${isMine ? "is-mine" : "is-other"}`, children: [
1192
- !isMine && label && /* @__PURE__ */ jsx("div", { class: `comment-author comment-author--${variant}`, children: label }),
1193
- comment.body && /* @__PURE__ */ jsx("div", { class: "comment-body", children: linkifySeq(comment.body, onOpenSeq) }),
1194
- images.length > 0 && /* @__PURE__ */ jsx("div", { class: "comment-attachments", children: images.map((a) => {
1275
+ !isMine && label && /* @__PURE__ */ jsx2("div", { class: `comment-author comment-author--${variant}`, children: label }),
1276
+ comment.body && /* @__PURE__ */ jsx2("div", { class: "comment-body", children: linkifySeq(comment.body, onOpenSeq) }),
1277
+ images.length > 0 && /* @__PURE__ */ jsx2("div", { class: "comment-attachments", children: images.map((a) => {
1195
1278
  const src = stableUrl ? stableUrl(`att:${a.id}`, a.url) : a.url;
1196
- return /* @__PURE__ */ jsx(
1279
+ return /* @__PURE__ */ jsx2(
1197
1280
  "a",
1198
1281
  {
1199
1282
  class: "comment-attachment",
1200
1283
  href: src,
1201
1284
  target: "_blank",
1202
1285
  rel: "noopener noreferrer",
1203
- children: /* @__PURE__ */ jsx("img", { src, alt: strings["detail.attachment_alt"], loading: "lazy" })
1286
+ children: /* @__PURE__ */ jsx2("img", { src, alt: strings["detail.attachment_alt"], loading: "lazy" })
1204
1287
  },
1205
1288
  a.id
1206
1289
  );
1207
1290
  }) }),
1208
- /* @__PURE__ */ jsx("div", { class: "comment-time", children: formatTime(comment.created_at) })
1291
+ /* @__PURE__ */ jsx2("div", { class: "comment-time", children: formatTime(comment.created_at) })
1209
1292
  ] });
1210
1293
  }
1211
1294
  function formatTime(iso) {
@@ -1260,7 +1343,7 @@ function safeExternalHref(url) {
1260
1343
  }
1261
1344
 
1262
1345
  // src/widget/ReportDetailView.tsx
1263
- import { Fragment, jsx as jsx2, jsxs as jsxs3 } from "preact/jsx-runtime";
1346
+ import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "preact/jsx-runtime";
1264
1347
  var POLL_MS = 3e4;
1265
1348
  function ReportDetailView({
1266
1349
  api,
@@ -1274,23 +1357,23 @@ function ReportDetailView({
1274
1357
  buildPermalink,
1275
1358
  seed
1276
1359
  }) {
1277
- const [detail, setDetail] = useState(null);
1278
- const [error, setError] = useState(null);
1279
- const [copied, setCopied] = useState(false);
1280
- const [editing, setEditing] = useState(false);
1281
- const [editBody, setEditBody] = useState("");
1282
- const [savingEdit, setSavingEdit] = useState(false);
1283
- const [editError, setEditError] = useState(false);
1284
- const [composeBody, setComposeBody] = useState("");
1285
- const [sending, setSending] = useState(false);
1286
- const [closing, setClosing] = useState(false);
1287
- const [reopening, setReopening] = useState(false);
1288
- const [composeShots, setComposeShots] = useState([]);
1289
- const [attachError, setAttachError] = useState("");
1360
+ const [detail, setDetail] = useState2(null);
1361
+ const [error, setError] = useState2(null);
1362
+ const [copied, setCopied] = useState2(false);
1363
+ const [editing, setEditing] = useState2(false);
1364
+ const [editBody, setEditBody] = useState2("");
1365
+ const [savingEdit, setSavingEdit] = useState2(false);
1366
+ const [editError, setEditError] = useState2(false);
1367
+ const [composeBody, setComposeBody] = useState2("");
1368
+ const [sending, setSending] = useState2(false);
1369
+ const [closing, setClosing] = useState2(false);
1370
+ const [reopening, setReopening] = useState2(false);
1371
+ const [composeShots, setComposeShots] = useState2([]);
1372
+ const [attachError, setAttachError] = useState2("");
1290
1373
  const fileInputRef = useRef(null);
1291
1374
  const mountedRef = useRef(true);
1292
1375
  const urlPins = useRef(/* @__PURE__ */ new Map());
1293
- const [, bumpPinTick] = useState(0);
1376
+ const [, bumpPinTick] = useState2(0);
1294
1377
  const PIN_MAX_AGE_MS = 45 * 6e4;
1295
1378
  const pinUrl = (key, url) => {
1296
1379
  if (!url) return url;
@@ -1474,7 +1557,7 @@ function ReportDetailView({
1474
1557
  };
1475
1558
  if (!detail && !error) {
1476
1559
  if (seed) {
1477
- const seedPill = /* @__PURE__ */ jsx2("span", { class: `pill pill-status pill-status--${seed.status}`, children: strings[`status.${seed.status}`] ?? seed.status });
1560
+ const seedPill = /* @__PURE__ */ jsx3("span", { class: `pill pill-status pill-status--${seed.status}`, children: strings[`status.${seed.status}`] ?? seed.status });
1478
1561
  return /* @__PURE__ */ jsxs3("div", { class: `report-detail variant-${variant} is-seeded`, children: [
1479
1562
  variant === "modal" && /* @__PURE__ */ jsxs3("div", { class: "report-detail-header", children: [
1480
1563
  /* @__PURE__ */ jsxs3("button", { type: "button", class: "btn", onClick: onBack, children: [
@@ -1500,19 +1583,19 @@ function ReportDetailView({
1500
1583
  seedPill
1501
1584
  ] }),
1502
1585
  /* @__PURE__ */ jsxs3("div", { class: "report-detail-body", children: [
1503
- /* @__PURE__ */ jsx2("div", { class: "report-detail-description-row", children: /* @__PURE__ */ jsx2("p", { class: "report-detail-description", children: linkifySeq(seed.description, onOpenSeq) }) }),
1504
- /* @__PURE__ */ jsx2("h3", { class: "report-detail-section", children: strings["detail.thread"] }),
1505
- /* @__PURE__ */ jsx2("div", { class: "mine-loading", children: strings["mine.loading"] })
1586
+ /* @__PURE__ */ jsx3("div", { class: "report-detail-description-row", children: /* @__PURE__ */ jsx3("p", { class: "report-detail-description", children: linkifySeq(seed.description, onOpenSeq) }) }),
1587
+ /* @__PURE__ */ jsx3("h3", { class: "report-detail-section", children: strings["detail.thread"] }),
1588
+ /* @__PURE__ */ jsx3("div", { class: "mine-loading", children: strings["mine.loading"] })
1506
1589
  ] })
1507
1590
  ] });
1508
1591
  }
1509
- return /* @__PURE__ */ jsx2("div", { class: "mine-loading", children: strings["mine.loading"] });
1592
+ return /* @__PURE__ */ jsx3("div", { class: "mine-loading", children: strings["mine.loading"] });
1510
1593
  }
1511
1594
  if (!detail) {
1512
1595
  if (error && error !== "load_failed") {
1513
1596
  return /* @__PURE__ */ jsxs3("div", { class: "report-detail-empty-state", role: "alert", children: [
1514
- /* @__PURE__ */ jsx2("p", { class: "report-detail-empty-state-body", children: error }),
1515
- /* @__PURE__ */ jsx2("button", { type: "button", class: "btn", onClick: () => void fetchDetail(), children: strings["board.retry"] }),
1597
+ /* @__PURE__ */ jsx3("p", { class: "report-detail-empty-state-body", children: error }),
1598
+ /* @__PURE__ */ jsx3("button", { type: "button", class: "btn", onClick: () => void fetchDetail(), children: strings["board.retry"] }),
1516
1599
  /* @__PURE__ */ jsxs3("button", { type: "button", class: "btn", onClick: onBack, children: [
1517
1600
  "\u2190 ",
1518
1601
  strings["detail.load_failed.cta"]
@@ -1520,8 +1603,8 @@ function ReportDetailView({
1520
1603
  ] });
1521
1604
  }
1522
1605
  return /* @__PURE__ */ jsxs3("div", { class: "report-detail-empty-state", role: "alert", children: [
1523
- /* @__PURE__ */ jsx2("p", { class: "report-detail-empty-state-title", children: strings["detail.load_failed.title"] }),
1524
- /* @__PURE__ */ jsx2("p", { class: "report-detail-empty-state-body", children: strings["detail.load_failed.body"] }),
1606
+ /* @__PURE__ */ jsx3("p", { class: "report-detail-empty-state-title", children: strings["detail.load_failed.title"] }),
1607
+ /* @__PURE__ */ jsx3("p", { class: "report-detail-empty-state-body", children: strings["detail.load_failed.body"] }),
1525
1608
  /* @__PURE__ */ jsxs3("button", { type: "button", class: "btn", onClick: onBack, children: [
1526
1609
  "\u2190 ",
1527
1610
  strings["detail.load_failed.cta"]
@@ -1538,7 +1621,7 @@ function ReportDetailView({
1538
1621
  "\u2190 ",
1539
1622
  strings["detail.back"]
1540
1623
  ] }),
1541
- /* @__PURE__ */ jsx2("span", { class: `pill pill-status pill-status--${detail.status}`, children: strings[`status.${detail.status}`] ?? detail.status })
1624
+ /* @__PURE__ */ jsx3("span", { class: `pill pill-status pill-status--${detail.status}`, children: strings[`status.${detail.status}`] ?? detail.status })
1542
1625
  ] }),
1543
1626
  variant === "board" && /* @__PURE__ */ jsxs3("div", { class: "report-detail-header report-detail-header--board", children: [
1544
1627
  /* @__PURE__ */ jsxs3(
@@ -1554,12 +1637,12 @@ function ReportDetailView({
1554
1637
  ]
1555
1638
  }
1556
1639
  ),
1557
- /* @__PURE__ */ jsx2("span", { class: `pill pill-status pill-status--${detail.status}`, children: strings[`status.${detail.status}`] ?? detail.status })
1640
+ /* @__PURE__ */ jsx3("span", { class: `pill pill-status pill-status--${detail.status}`, children: strings[`status.${detail.status}`] ?? detail.status })
1558
1641
  ] }),
1559
1642
  /* @__PURE__ */ jsxs3("div", { class: "report-detail-body", children: [
1560
- /* @__PURE__ */ jsx2(ContextBlock, { detail, strings }),
1643
+ /* @__PURE__ */ jsx3(ContextBlock, { detail, strings }),
1561
1644
  editing ? /* @__PURE__ */ jsxs3("div", { class: "report-detail-edit", children: [
1562
- /* @__PURE__ */ jsx2(
1645
+ /* @__PURE__ */ jsx3(
1563
1646
  "textarea",
1564
1647
  {
1565
1648
  class: "report-detail-edit-input",
@@ -1571,9 +1654,9 @@ function ReportDetailView({
1571
1654
  disabled: savingEdit
1572
1655
  }
1573
1656
  ),
1574
- editError && /* @__PURE__ */ jsx2("p", { class: "error", role: "alert", children: strings["detail.edit_failed"] }),
1657
+ editError && /* @__PURE__ */ jsx3("p", { class: "error", role: "alert", children: strings["detail.edit_failed"] }),
1575
1658
  /* @__PURE__ */ jsxs3("div", { class: "report-detail-edit-actions", children: [
1576
- /* @__PURE__ */ jsx2(
1659
+ /* @__PURE__ */ jsx3(
1577
1660
  "button",
1578
1661
  {
1579
1662
  type: "button",
@@ -1583,7 +1666,7 @@ function ReportDetailView({
1583
1666
  children: strings["detail.edit_cancel"]
1584
1667
  }
1585
1668
  ),
1586
- /* @__PURE__ */ jsx2(
1669
+ /* @__PURE__ */ jsx3(
1587
1670
  "button",
1588
1671
  {
1589
1672
  type: "button",
@@ -1595,8 +1678,8 @@ function ReportDetailView({
1595
1678
  )
1596
1679
  ] })
1597
1680
  ] }) : /* @__PURE__ */ jsxs3("div", { class: "report-detail-description-row", children: [
1598
- /* @__PURE__ */ jsx2("p", { class: "report-detail-description", children: linkifySeq(detail.description, onOpenSeq) }),
1599
- isMine && /* @__PURE__ */ jsx2(
1681
+ /* @__PURE__ */ jsx3("p", { class: "report-detail-description", children: linkifySeq(detail.description, onOpenSeq) }),
1682
+ isMine && /* @__PURE__ */ jsx3(
1600
1683
  "button",
1601
1684
  {
1602
1685
  type: "button",
@@ -1606,7 +1689,7 @@ function ReportDetailView({
1606
1689
  }
1607
1690
  )
1608
1691
  ] }),
1609
- buildPermalink && /* @__PURE__ */ jsx2("div", { class: "report-detail-permalink", children: /* @__PURE__ */ jsx2(
1692
+ buildPermalink && /* @__PURE__ */ jsx3("div", { class: "report-detail-permalink", children: /* @__PURE__ */ jsx3(
1610
1693
  "button",
1611
1694
  {
1612
1695
  type: "button",
@@ -1617,19 +1700,19 @@ function ReportDetailView({
1617
1700
  ) }),
1618
1701
  (detail.screenshots?.length ? detail.screenshots.map((s) => s.url) : [detail.screenshot_url]).map((url, i) => ({ url: pinUrl(`shot:${i}`, url ?? null), pinKey: `shot:${i}` })).filter((s) => Boolean(s.url)).map(({ url, pinKey }) => {
1619
1702
  const safeHref = safeExternalHref(url);
1620
- return safeHref ? /* @__PURE__ */ jsx2(
1703
+ return safeHref ? /* @__PURE__ */ jsx3(
1621
1704
  "a",
1622
1705
  {
1623
1706
  class: "report-detail-screenshot",
1624
1707
  href: safeHref,
1625
1708
  target: "_blank",
1626
1709
  rel: "noopener noreferrer",
1627
- children: /* @__PURE__ */ jsx2("img", { src: url, alt: "", loading: "lazy", onError: () => unpinUrl(pinKey) })
1710
+ children: /* @__PURE__ */ jsx3("img", { src: url, alt: "", loading: "lazy", onError: () => unpinUrl(pinKey) })
1628
1711
  }
1629
- ) : /* @__PURE__ */ jsx2("div", { class: "report-detail-screenshot", children: /* @__PURE__ */ jsx2("img", { src: url, alt: "", loading: "lazy", onError: () => unpinUrl(pinKey) }) });
1712
+ ) : /* @__PURE__ */ jsx3("div", { class: "report-detail-screenshot", children: /* @__PURE__ */ jsx3("img", { src: url, alt: "", loading: "lazy", onError: () => unpinUrl(pinKey) }) });
1630
1713
  }),
1631
- /* @__PURE__ */ jsx2("h3", { class: "report-detail-section", children: strings["detail.thread"] }),
1632
- detail.comments.length === 0 ? /* @__PURE__ */ jsx2("p", { class: "report-detail-empty", children: strings["detail.no_replies"] }) : /* @__PURE__ */ jsx2("ul", { class: "report-comments", children: detail.comments.map((c) => /* @__PURE__ */ jsx2("li", { children: /* @__PURE__ */ jsx2(
1714
+ /* @__PURE__ */ jsx3("h3", { class: "report-detail-section", children: strings["detail.thread"] }),
1715
+ detail.comments.length === 0 ? /* @__PURE__ */ jsx3("p", { class: "report-detail-empty", children: strings["detail.no_replies"] }) : /* @__PURE__ */ jsx3("ul", { class: "report-comments", children: detail.comments.map((c) => /* @__PURE__ */ jsx3("li", { children: /* @__PURE__ */ jsx3(
1633
1716
  CommentBubble,
1634
1717
  {
1635
1718
  comment: c,
@@ -1638,10 +1721,10 @@ function ReportDetailView({
1638
1721
  stableUrl: (key, url) => pinUrl(key, url) ?? url
1639
1722
  }
1640
1723
  ) })) }),
1641
- detail.status_history && detail.status_history.length > 0 && /* @__PURE__ */ jsx2(StatusHistorySection, { rows: detail.status_history, strings }),
1642
- detail.technical_context && /* @__PURE__ */ jsx2(TechnicalContextDrawer, { ctx: detail.technical_context, strings }),
1724
+ detail.status_history && detail.status_history.length > 0 && /* @__PURE__ */ jsx3(StatusHistorySection, { rows: detail.status_history, strings }),
1725
+ detail.technical_context && /* @__PURE__ */ jsx3(TechnicalContextDrawer, { ctx: detail.technical_context, strings }),
1643
1726
  showComposeBox ? /* @__PURE__ */ jsxs3("div", { class: "report-compose", children: [
1644
- /* @__PURE__ */ jsx2(
1727
+ /* @__PURE__ */ jsx3(
1645
1728
  "textarea",
1646
1729
  {
1647
1730
  value: composeBody,
@@ -1650,9 +1733,9 @@ function ReportDetailView({
1650
1733
  disabled: sending
1651
1734
  }
1652
1735
  ),
1653
- composeShots.length > 0 && /* @__PURE__ */ jsx2("div", { class: "compose-attachments", children: composeShots.map((s, i) => /* @__PURE__ */ jsxs3("div", { class: "compose-attachment", children: [
1654
- /* @__PURE__ */ jsx2("img", { src: s.preview, alt: strings["detail.attachment_alt"] }),
1655
- /* @__PURE__ */ jsx2(
1736
+ composeShots.length > 0 && /* @__PURE__ */ jsx3("div", { class: "compose-attachments", children: composeShots.map((s, i) => /* @__PURE__ */ jsxs3("div", { class: "compose-attachment", children: [
1737
+ /* @__PURE__ */ jsx3("img", { src: s.preview, alt: strings["detail.attachment_alt"] }),
1738
+ /* @__PURE__ */ jsx3(
1656
1739
  "button",
1657
1740
  {
1658
1741
  type: "button",
@@ -1664,8 +1747,8 @@ function ReportDetailView({
1664
1747
  }
1665
1748
  )
1666
1749
  ] }, s.preview)) }),
1667
- attachError && /* @__PURE__ */ jsx2("p", { class: "compose-attach-error", role: "alert", children: attachError }),
1668
- /* @__PURE__ */ jsx2(
1750
+ attachError && /* @__PURE__ */ jsx3("p", { class: "compose-attach-error", role: "alert", children: attachError }),
1751
+ /* @__PURE__ */ jsx3(
1669
1752
  "input",
1670
1753
  {
1671
1754
  ref: fileInputRef,
@@ -1678,7 +1761,7 @@ function ReportDetailView({
1678
1761
  }
1679
1762
  ),
1680
1763
  /* @__PURE__ */ jsxs3("div", { class: "report-compose-actions", children: [
1681
- /* @__PURE__ */ jsx2(
1764
+ /* @__PURE__ */ jsx3(
1682
1765
  "button",
1683
1766
  {
1684
1767
  type: "button",
@@ -1688,7 +1771,7 @@ function ReportDetailView({
1688
1771
  children: strings["detail.attach_add"]
1689
1772
  }
1690
1773
  ),
1691
- showCloseCta && /* @__PURE__ */ jsx2(
1774
+ showCloseCta && /* @__PURE__ */ jsx3(
1692
1775
  "button",
1693
1776
  {
1694
1777
  type: "button",
@@ -1698,7 +1781,7 @@ function ReportDetailView({
1698
1781
  children: closing ? strings["detail.close_busy"] : strings["detail.close_cta"]
1699
1782
  }
1700
1783
  ),
1701
- showCloseCta && /* @__PURE__ */ jsx2(
1784
+ showCloseCta && /* @__PURE__ */ jsx3(
1702
1785
  "button",
1703
1786
  {
1704
1787
  type: "button",
@@ -1708,7 +1791,7 @@ function ReportDetailView({
1708
1791
  children: reopening ? strings["detail.reopen_busy"] : strings["detail.reopen_cta"]
1709
1792
  }
1710
1793
  ),
1711
- /* @__PURE__ */ jsx2(
1794
+ /* @__PURE__ */ jsx3(
1712
1795
  "button",
1713
1796
  {
1714
1797
  type: "button",
@@ -1724,9 +1807,9 @@ function ReportDetailView({
1724
1807
  // to the submitter, so we hide the compose box entirely (action
1725
1808
  // is genuinely unavailable) and surface a short notice so the
1726
1809
  // viewer understands why.
1727
- /* @__PURE__ */ jsx2("p", { class: "report-detail-teammate-notice", role: "note", children: strings["detail.teammate_notice"] })
1810
+ /* @__PURE__ */ jsx3("p", { class: "report-detail-teammate-notice", role: "note", children: strings["detail.teammate_notice"] })
1728
1811
  ),
1729
- error && /* @__PURE__ */ jsx2("div", { class: "error", children: error })
1812
+ error && /* @__PURE__ */ jsx3("div", { class: "error", children: error })
1730
1813
  ] })
1731
1814
  ] });
1732
1815
  }
@@ -1739,19 +1822,19 @@ function ContextBlock({ detail, strings }) {
1739
1822
  const captureLabel = strings[captureKey] ?? detail.capture_method;
1740
1823
  return /* @__PURE__ */ jsxs3("div", { class: "report-detail-context", children: [
1741
1824
  /* @__PURE__ */ jsxs3("div", { class: "report-detail-context-pills", children: [
1742
- /* @__PURE__ */ jsx2("span", { class: "pill pill-type", children: strings[`type.${detail.feedback_type}`] }),
1743
- /* @__PURE__ */ jsx2("span", { class: `pill pill-severity pill-severity--${detail.severity}`, children: strings[`severity.${detail.severity}`] }),
1744
- /* @__PURE__ */ jsx2("span", { class: "pill pill-capture", children: captureLabel })
1825
+ /* @__PURE__ */ jsx3("span", { class: "pill pill-type", children: strings[`type.${detail.feedback_type}`] }),
1826
+ /* @__PURE__ */ jsx3("span", { class: `pill pill-severity pill-severity--${detail.severity}`, children: strings[`severity.${detail.severity}`] }),
1827
+ /* @__PURE__ */ jsx3("span", { class: "pill pill-capture", children: captureLabel })
1745
1828
  ] }),
1746
1829
  /* @__PURE__ */ jsxs3("div", { class: "report-detail-context-line", children: [
1747
- /* @__PURE__ */ jsx2("span", { class: "report-detail-context-label", children: strings["detail.context.submitted_at"] }),
1748
- /* @__PURE__ */ jsx2("span", { children: formatSubmittedAt(detail.created_at) })
1830
+ /* @__PURE__ */ jsx3("span", { class: "report-detail-context-label", children: strings["detail.context.submitted_at"] }),
1831
+ /* @__PURE__ */ jsx3("span", { children: formatSubmittedAt(detail.created_at) })
1749
1832
  ] }),
1750
1833
  detail.page_url && (() => {
1751
1834
  const safeHref = safeExternalHref(detail.page_url);
1752
1835
  return /* @__PURE__ */ jsxs3("div", { class: "report-detail-context-line", title: detail.page_url, children: [
1753
- /* @__PURE__ */ jsx2("span", { class: "report-detail-context-label", children: strings["detail.context.page"] }),
1754
- safeHref ? /* @__PURE__ */ jsx2(
1836
+ /* @__PURE__ */ jsx3("span", { class: "report-detail-context-label", children: strings["detail.context.page"] }),
1837
+ safeHref ? /* @__PURE__ */ jsx3(
1755
1838
  "a",
1756
1839
  {
1757
1840
  class: "report-detail-context-url",
@@ -1760,7 +1843,7 @@ function ContextBlock({ detail, strings }) {
1760
1843
  rel: "noopener noreferrer",
1761
1844
  children: truncateUrl(detail.page_url, 64)
1762
1845
  }
1763
- ) : /* @__PURE__ */ jsx2("span", { class: "report-detail-context-url", children: truncateUrl(detail.page_url, 64) })
1846
+ ) : /* @__PURE__ */ jsx3("span", { class: "report-detail-context-url", children: truncateUrl(detail.page_url, 64) })
1764
1847
  ] });
1765
1848
  })()
1766
1849
  ] });
@@ -1787,12 +1870,12 @@ function TechnicalContextDrawer({ ctx, strings }) {
1787
1870
  const errorCount = errors.length;
1788
1871
  const summary = errorCount > 0 ? `${strings["detail.tech.title"]} \xB7 ${errorCount} ${errorCount === 1 ? strings["detail.tech.errors_one"] : strings["detail.tech.errors_many"]}` : strings["detail.tech.title"];
1789
1872
  return /* @__PURE__ */ jsxs3("details", { class: "report-detail-tech", children: [
1790
- /* @__PURE__ */ jsx2("summary", { children: summary }),
1873
+ /* @__PURE__ */ jsx3("summary", { children: summary }),
1791
1874
  /* @__PURE__ */ jsxs3("div", { class: "tech-body", children: [
1792
- device && /* @__PURE__ */ jsx2(DeviceSection, { device, strings }),
1793
- errors.length > 0 && /* @__PURE__ */ jsx2(ErrorsSection, { errors, strings }),
1794
- consoleLogs.length > 0 && /* @__PURE__ */ jsx2(ConsoleSection, { logs: consoleLogs, strings }),
1795
- network.length > 0 && /* @__PURE__ */ jsx2(NetworkSection, { rows: network, strings })
1875
+ device && /* @__PURE__ */ jsx3(DeviceSection, { device, strings }),
1876
+ errors.length > 0 && /* @__PURE__ */ jsx3(ErrorsSection, { errors, strings }),
1877
+ consoleLogs.length > 0 && /* @__PURE__ */ jsx3(ConsoleSection, { logs: consoleLogs, strings }),
1878
+ network.length > 0 && /* @__PURE__ */ jsx3(NetworkSection, { rows: network, strings })
1796
1879
  ] })
1797
1880
  ] });
1798
1881
  }
@@ -1814,10 +1897,10 @@ function DeviceSection({
1814
1897
  if (device?.connection) parts.push({ label: strings["detail.tech.device.connection"], value: device.connection });
1815
1898
  if (parts.length === 0) return null;
1816
1899
  return /* @__PURE__ */ jsxs3("div", { class: "tech-section", children: [
1817
- /* @__PURE__ */ jsx2("h4", { children: strings["detail.tech.device"] }),
1818
- /* @__PURE__ */ jsx2("dl", { class: "tech-device", children: parts.map((p) => /* @__PURE__ */ jsxs3(Fragment, { children: [
1819
- /* @__PURE__ */ jsx2("dt", { children: p.label }),
1820
- /* @__PURE__ */ jsx2("dd", { children: p.value })
1900
+ /* @__PURE__ */ jsx3("h4", { children: strings["detail.tech.device"] }),
1901
+ /* @__PURE__ */ jsx3("dl", { class: "tech-device", children: parts.map((p) => /* @__PURE__ */ jsxs3(Fragment, { children: [
1902
+ /* @__PURE__ */ jsx3("dt", { children: p.label }),
1903
+ /* @__PURE__ */ jsx3("dd", { children: p.value })
1821
1904
  ] })) })
1822
1905
  ] });
1823
1906
  }
@@ -1826,10 +1909,10 @@ function ErrorsSection({
1826
1909
  strings
1827
1910
  }) {
1828
1911
  return /* @__PURE__ */ jsxs3("div", { class: "tech-section", children: [
1829
- /* @__PURE__ */ jsx2("h4", { children: strings["detail.tech.errors"] }),
1830
- /* @__PURE__ */ jsx2("ul", { class: "tech-errors", children: errors.map((e) => /* @__PURE__ */ jsxs3("li", { children: [
1831
- /* @__PURE__ */ jsx2("div", { class: "tech-errors-msg", children: e.message }),
1832
- e.stack && /* @__PURE__ */ jsx2("pre", { class: "tech-errors-stack", children: truncateStack(e.stack) })
1912
+ /* @__PURE__ */ jsx3("h4", { children: strings["detail.tech.errors"] }),
1913
+ /* @__PURE__ */ jsx3("ul", { class: "tech-errors", children: errors.map((e) => /* @__PURE__ */ jsxs3("li", { children: [
1914
+ /* @__PURE__ */ jsx3("div", { class: "tech-errors-msg", children: e.message }),
1915
+ e.stack && /* @__PURE__ */ jsx3("pre", { class: "tech-errors-stack", children: truncateStack(e.stack) })
1833
1916
  ] })) })
1834
1917
  ] });
1835
1918
  }
@@ -1838,10 +1921,10 @@ function ConsoleSection({
1838
1921
  strings
1839
1922
  }) {
1840
1923
  return /* @__PURE__ */ jsxs3("div", { class: "tech-section", children: [
1841
- /* @__PURE__ */ jsx2("h4", { children: strings["detail.tech.console"] }),
1842
- /* @__PURE__ */ jsx2("ul", { class: "tech-console", children: logs.map((l) => /* @__PURE__ */ jsxs3("li", { class: `tech-console-row tech-console-row--${l.level}`, children: [
1843
- /* @__PURE__ */ jsx2("span", { class: "tech-console-level", children: l.level }),
1844
- /* @__PURE__ */ jsx2("span", { class: "tech-console-msg", children: l.message })
1924
+ /* @__PURE__ */ jsx3("h4", { children: strings["detail.tech.console"] }),
1925
+ /* @__PURE__ */ jsx3("ul", { class: "tech-console", children: logs.map((l) => /* @__PURE__ */ jsxs3("li", { class: `tech-console-row tech-console-row--${l.level}`, children: [
1926
+ /* @__PURE__ */ jsx3("span", { class: "tech-console-level", children: l.level }),
1927
+ /* @__PURE__ */ jsx3("span", { class: "tech-console-msg", children: l.message })
1845
1928
  ] })) })
1846
1929
  ] });
1847
1930
  }
@@ -1850,13 +1933,13 @@ function NetworkSection({
1850
1933
  strings
1851
1934
  }) {
1852
1935
  return /* @__PURE__ */ jsxs3("div", { class: "tech-section", children: [
1853
- /* @__PURE__ */ jsx2("h4", { children: strings["detail.tech.network"] }),
1854
- /* @__PURE__ */ jsx2("ul", { class: "tech-network", children: rows.map((n) => {
1936
+ /* @__PURE__ */ jsx3("h4", { children: strings["detail.tech.network"] }),
1937
+ /* @__PURE__ */ jsx3("ul", { class: "tech-network", children: rows.map((n) => {
1855
1938
  const failed = n.status >= 400 || n.status === 0;
1856
1939
  return /* @__PURE__ */ jsxs3("li", { class: `tech-network-row${failed ? " tech-network-row--fail" : ""}`, children: [
1857
- /* @__PURE__ */ jsx2("span", { class: "tech-network-status", children: n.status || "\u2014" }),
1858
- /* @__PURE__ */ jsx2("span", { class: "tech-network-method", children: n.method }),
1859
- /* @__PURE__ */ jsx2("span", { class: "tech-network-url", title: n.url, children: truncateUrl(n.url, 56) }),
1940
+ /* @__PURE__ */ jsx3("span", { class: "tech-network-status", children: n.status || "\u2014" }),
1941
+ /* @__PURE__ */ jsx3("span", { class: "tech-network-method", children: n.method }),
1942
+ /* @__PURE__ */ jsx3("span", { class: "tech-network-url", title: n.url, children: truncateUrl(n.url, 56) }),
1860
1943
  /* @__PURE__ */ jsxs3("span", { class: "tech-network-time", children: [
1861
1944
  Math.round(n.durationMs),
1862
1945
  "ms"
@@ -1872,26 +1955,26 @@ function truncateStack(stack) {
1872
1955
  }
1873
1956
  function StatusHistorySection({ rows, strings }) {
1874
1957
  return /* @__PURE__ */ jsxs3("div", { class: "report-detail-history", children: [
1875
- /* @__PURE__ */ jsx2("h3", { class: "report-detail-section", children: strings["detail.history"] }),
1876
- /* @__PURE__ */ jsx2("ol", { class: "status-history", children: rows.map((r) => {
1958
+ /* @__PURE__ */ jsx3("h3", { class: "report-detail-section", children: strings["detail.history"] }),
1959
+ /* @__PURE__ */ jsx3("ol", { class: "status-history", children: rows.map((r) => {
1877
1960
  const from = strings[`status.${r.from_status}`] ?? r.from_status;
1878
1961
  const to = strings[`status.${r.to_status}`] ?? r.to_status;
1879
1962
  const sourceKey = r.changed_by_source === "mcp" ? "detail.author.mcp" : r.changed_by_source === "system" ? "detail.author.system" : "detail.author.staff";
1880
1963
  return /* @__PURE__ */ jsxs3("li", { class: "status-history-row", children: [
1881
- /* @__PURE__ */ jsx2("span", { class: "status-history-time", children: formatSubmittedAt(r.created_at) }),
1964
+ /* @__PURE__ */ jsx3("span", { class: "status-history-time", children: formatSubmittedAt(r.created_at) }),
1882
1965
  /* @__PURE__ */ jsxs3("span", { class: "status-history-transition", children: [
1883
- /* @__PURE__ */ jsx2("span", { class: `pill pill-status pill-status--${r.from_status}`, children: from }),
1884
- /* @__PURE__ */ jsx2("span", { class: "status-history-arrow", "aria-hidden": "true", children: "\u2192" }),
1885
- /* @__PURE__ */ jsx2("span", { class: `pill pill-status pill-status--${r.to_status}`, children: to })
1966
+ /* @__PURE__ */ jsx3("span", { class: `pill pill-status pill-status--${r.from_status}`, children: from }),
1967
+ /* @__PURE__ */ jsx3("span", { class: "status-history-arrow", "aria-hidden": "true", children: "\u2192" }),
1968
+ /* @__PURE__ */ jsx3("span", { class: `pill pill-status pill-status--${r.to_status}`, children: to })
1886
1969
  ] }),
1887
- /* @__PURE__ */ jsx2("span", { class: `status-history-source status-history-source--${r.changed_by_source}`, children: strings[sourceKey] })
1970
+ /* @__PURE__ */ jsx3("span", { class: `status-history-source status-history-source--${r.changed_by_source}`, children: strings[sourceKey] })
1888
1971
  ] });
1889
1972
  }) })
1890
1973
  ] });
1891
1974
  }
1892
1975
 
1893
1976
  // src/widget/BoardView.tsx
1894
- import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs4 } from "preact/jsx-runtime";
1977
+ import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "preact/jsx-runtime";
1895
1978
  var POLL_MS2 = 3e4;
1896
1979
  var SEARCH_DEBOUNCE_MS = 300;
1897
1980
  var SORT_OPTIONS = ["recent", "oldest", "updated", "severity", "status"];
@@ -1911,22 +1994,22 @@ function BoardView({
1911
1994
  getCurrentPage,
1912
1995
  initialSelectedId
1913
1996
  }) {
1914
- const [filters, setFilters] = useState2(() => loadBoardView(externalId));
1915
- const [thisPage, setThisPage] = useState2(true);
1997
+ const [filters, setFilters] = useState3(() => loadBoardView(externalId));
1998
+ const [thisPage, setThisPage] = useState3(true);
1916
1999
  useEffect2(() => {
1917
2000
  saveBoardView(externalId, filters);
1918
2001
  }, [externalId, filtersHash(filters)]);
1919
- const [page, setPage] = useState2(null);
1920
- const [kpis, setKpis] = useState2(null);
1921
- const [loading, setLoading] = useState2(true);
1922
- const [stale, setStale] = useState2(false);
1923
- const [error, setError] = useState2(null);
1924
- const [selectedId, setSelectedId] = useState2(initialSelectedId ?? null);
1925
- const [detailKey, setDetailKey] = useState2(0);
1926
- const [reloadTick, setReloadTick] = useState2(0);
1927
- const [extra, setExtra] = useState2(null);
1928
- const [loadingMore, setLoadingMore] = useState2(false);
1929
- const [qDraft, setQDraft] = useState2(filters.q ?? "");
2002
+ const [page, setPage] = useState3(null);
2003
+ const [kpis, setKpis] = useState3(null);
2004
+ const [loading, setLoading] = useState3(true);
2005
+ const [stale, setStale] = useState3(false);
2006
+ const [error, setError] = useState3(null);
2007
+ const [selectedId, setSelectedId] = useState3(initialSelectedId ?? null);
2008
+ const [detailKey, setDetailKey] = useState3(0);
2009
+ const [reloadTick, setReloadTick] = useState3(0);
2010
+ const [extra, setExtra] = useState3(null);
2011
+ const [loadingMore, setLoadingMore] = useState3(false);
2012
+ const [qDraft, setQDraft] = useState3(filters.q ?? "");
1930
2013
  const debouncedQ = useDebouncedValue(qDraft, SEARCH_DEBOUNCE_MS);
1931
2014
  useEffect2(() => {
1932
2015
  setFilters((f) => {
@@ -2033,8 +2116,8 @@ function BoardView({
2033
2116
  setDetailKey((k) => k + 1);
2034
2117
  };
2035
2118
  return /* @__PURE__ */ jsxs4("div", { class: `board-view ${stale ? "is-stale" : ""}`, children: [
2036
- /* @__PURE__ */ jsx3(BoardHeader, { strings, kpis, thisPage }),
2037
- /* @__PURE__ */ jsx3(
2119
+ /* @__PURE__ */ jsx4(BoardHeader, { strings, kpis, thisPage }),
2120
+ /* @__PURE__ */ jsx4(
2038
2121
  BoardFilters,
2039
2122
  {
2040
2123
  filters,
@@ -2050,8 +2133,8 @@ function BoardView({
2050
2133
  /* @__PURE__ */ jsxs4("div", { class: "board-body", children: [
2051
2134
  /* @__PURE__ */ jsxs4("div", { class: "board-list-wrap", "aria-busy": loading, children: [
2052
2135
  error && /* @__PURE__ */ jsxs4("div", { class: "board-error", role: "alert", children: [
2053
- /* @__PURE__ */ jsx3("span", { children: error }),
2054
- /* @__PURE__ */ jsx3(
2136
+ /* @__PURE__ */ jsx4("span", { children: error }),
2137
+ /* @__PURE__ */ jsx4(
2055
2138
  "button",
2056
2139
  {
2057
2140
  type: "button",
@@ -2063,10 +2146,11 @@ function BoardView({
2063
2146
  },
2064
2147
  children: strings["board.retry"]
2065
2148
  }
2066
- )
2149
+ ),
2150
+ /* @__PURE__ */ jsx4(DiagnosticHint, { api, strings, host: api.endpointHost })
2067
2151
  ] }),
2068
- !error && loading && !page && /* @__PURE__ */ jsx3(BoardListSkeleton, {}),
2069
- !error && page && visibleRows.length === 0 && (!loading || stale) && /* @__PURE__ */ jsx3(
2152
+ !error && loading && !page && /* @__PURE__ */ jsx4(BoardListSkeleton, {}),
2153
+ !error && page && visibleRows.length === 0 && (!loading || stale) && /* @__PURE__ */ jsx4(
2070
2154
  BoardEmpty,
2071
2155
  {
2072
2156
  strings,
@@ -2075,7 +2159,7 @@ function BoardView({
2075
2159
  }
2076
2160
  ),
2077
2161
  !error && page && visibleRows.length > 0 && /* @__PURE__ */ jsxs4(Fragment2, { children: [
2078
- /* @__PURE__ */ jsx3(
2162
+ /* @__PURE__ */ jsx4(
2079
2163
  BoardList,
2080
2164
  {
2081
2165
  rows: visibleRows,
@@ -2086,7 +2170,7 @@ function BoardView({
2086
2170
  thisPage
2087
2171
  }
2088
2172
  ),
2089
- hasMore && !stale && /* @__PURE__ */ jsx3(
2173
+ hasMore && !stale && /* @__PURE__ */ jsx4(
2090
2174
  "button",
2091
2175
  {
2092
2176
  type: "button",
@@ -2098,7 +2182,7 @@ function BoardView({
2098
2182
  )
2099
2183
  ] })
2100
2184
  ] }),
2101
- /* @__PURE__ */ jsx3("div", { class: `board-detail-wrap ${selectedId ? "has-selection" : ""}`, children: selectedId ? /* @__PURE__ */ jsx3(
2185
+ /* @__PURE__ */ jsx4("div", { class: `board-detail-wrap ${selectedId ? "has-selection" : ""}`, children: selectedId ? /* @__PURE__ */ jsx4(
2102
2186
  ReportDetailView,
2103
2187
  {
2104
2188
  api,
@@ -2119,8 +2203,8 @@ function BoardView({
2119
2203
  },
2120
2204
  detailKey
2121
2205
  ) : /* @__PURE__ */ jsxs4("div", { class: "board-detail-empty", children: [
2122
- /* @__PURE__ */ jsx3(DetailEmptyIllustration, {}),
2123
- /* @__PURE__ */ jsx3("p", { children: strings["board.detail.empty"] })
2206
+ /* @__PURE__ */ jsx4(DetailEmptyIllustration, {}),
2207
+ /* @__PURE__ */ jsx4("p", { children: strings["board.detail.empty"] })
2124
2208
  ] }) })
2125
2209
  ] })
2126
2210
  ] });
@@ -2133,13 +2217,13 @@ function BoardHeader({
2133
2217
  const scopeLabel = thisPage ? strings["board.scope.onThisPage"] : kpis?.scope === "project" ? strings["board.scope.project"] : strings["board.scope.mine"];
2134
2218
  return /* @__PURE__ */ jsxs4("header", { class: "board-header", children: [
2135
2219
  /* @__PURE__ */ jsxs4("div", { class: "board-header-title", children: [
2136
- /* @__PURE__ */ jsx3("span", { class: "board-header-emoji", "aria-hidden": "true", children: "\u{1F4CB}" }),
2220
+ /* @__PURE__ */ jsx4("span", { class: "board-header-emoji", "aria-hidden": "true", children: "\u{1F4CB}" }),
2137
2221
  /* @__PURE__ */ jsxs4("div", { children: [
2138
- /* @__PURE__ */ jsx3("h2", { class: "board-header-h", children: strings["tab.board"] }),
2139
- /* @__PURE__ */ jsx3("p", { class: "board-header-sub", children: kpis ? `${kpis.total} \xB7 ${scopeLabel}` : scopeLabel })
2222
+ /* @__PURE__ */ jsx4("h2", { class: "board-header-h", children: strings["tab.board"] }),
2223
+ /* @__PURE__ */ jsx4("p", { class: "board-header-sub", children: kpis ? `${kpis.total} \xB7 ${scopeLabel}` : scopeLabel })
2140
2224
  ] })
2141
2225
  ] }),
2142
- /* @__PURE__ */ jsx3(BoardKpiStrip, { kpis, strings })
2226
+ /* @__PURE__ */ jsx4(BoardKpiStrip, { kpis, strings })
2143
2227
  ] });
2144
2228
  }
2145
2229
  function BoardKpiStrip({
@@ -2172,9 +2256,9 @@ function BoardKpiStrip({
2172
2256
  tone: "tone-rate"
2173
2257
  }
2174
2258
  ];
2175
- return /* @__PURE__ */ jsx3("div", { class: `board-kpi-strip ${kpis ? "is-ready" : "is-loading"}`, "aria-live": "polite", children: cells.map((c) => /* @__PURE__ */ jsxs4("div", { class: `board-kpi-card ${c.tone}`, children: [
2176
- /* @__PURE__ */ jsx3("div", { class: "board-kpi-value", children: c.value }),
2177
- /* @__PURE__ */ jsx3("div", { class: "board-kpi-label", children: c.label })
2259
+ return /* @__PURE__ */ jsx4("div", { class: `board-kpi-strip ${kpis ? "is-ready" : "is-loading"}`, "aria-live": "polite", children: cells.map((c) => /* @__PURE__ */ jsxs4("div", { class: `board-kpi-card ${c.tone}`, children: [
2260
+ /* @__PURE__ */ jsx4("div", { class: "board-kpi-value", children: c.value }),
2261
+ /* @__PURE__ */ jsx4("div", { class: "board-kpi-label", children: c.label })
2178
2262
  ] }, c.key)) });
2179
2263
  }
2180
2264
  function BoardFilters({
@@ -2227,7 +2311,7 @@ function BoardFilters({
2227
2311
  const clear = () => onChange({});
2228
2312
  return /* @__PURE__ */ jsxs4("div", { class: "board-filters", children: [
2229
2313
  /* @__PURE__ */ jsxs4("div", { class: "board-scope", role: "group", "aria-label": strings["board.scope.thisPage"], children: [
2230
- /* @__PURE__ */ jsx3(
2314
+ /* @__PURE__ */ jsx4(
2231
2315
  "button",
2232
2316
  {
2233
2317
  type: "button",
@@ -2237,7 +2321,7 @@ function BoardFilters({
2237
2321
  children: strings["board.scope.thisPage"]
2238
2322
  }
2239
2323
  ),
2240
- /* @__PURE__ */ jsx3(
2324
+ /* @__PURE__ */ jsx4(
2241
2325
  "button",
2242
2326
  {
2243
2327
  type: "button",
@@ -2248,14 +2332,14 @@ function BoardFilters({
2248
2332
  }
2249
2333
  )
2250
2334
  ] }),
2251
- /* @__PURE__ */ jsx3(
2335
+ /* @__PURE__ */ jsx4(
2252
2336
  "select",
2253
2337
  {
2254
2338
  class: "board-filter-select",
2255
2339
  "aria-label": strings["board.sort"],
2256
2340
  value: filters.ordering ?? "recent",
2257
2341
  onChange: (e) => setOrdering(e.target.value),
2258
- children: SORT_OPTIONS.map((o) => /* @__PURE__ */ jsx3("option", { value: o, children: strings[`board.sort.${o}`] }, o))
2342
+ children: SORT_OPTIONS.map((o) => /* @__PURE__ */ jsx4("option", { value: o, children: strings[`board.sort.${o}`] }, o))
2259
2343
  }
2260
2344
  ),
2261
2345
  /* @__PURE__ */ jsxs4(
@@ -2266,8 +2350,8 @@ function BoardFilters({
2266
2350
  value: filters.status?.[0] ?? "",
2267
2351
  onChange: (e) => setStatus(e.target.value),
2268
2352
  children: [
2269
- /* @__PURE__ */ jsx3("option", { value: "", children: strings["board.filter.status"] }),
2270
- STATUSES.map((s) => /* @__PURE__ */ jsx3("option", { value: s, children: strings[`board.kpi.${s}`] ?? s }, s))
2353
+ /* @__PURE__ */ jsx4("option", { value: "", children: strings["board.filter.status"] }),
2354
+ STATUSES.map((s) => /* @__PURE__ */ jsx4("option", { value: s, children: strings[`board.kpi.${s}`] ?? s }, s))
2271
2355
  ]
2272
2356
  }
2273
2357
  ),
@@ -2279,8 +2363,8 @@ function BoardFilters({
2279
2363
  value: filters.type?.[0] ?? "",
2280
2364
  onChange: (e) => setType(e.target.value),
2281
2365
  children: [
2282
- /* @__PURE__ */ jsx3("option", { value: "", children: strings["board.filter.type"] }),
2283
- TYPES.map((t) => /* @__PURE__ */ jsx3("option", { value: t, children: strings[`type.${t}`] ?? t }, t))
2366
+ /* @__PURE__ */ jsx4("option", { value: "", children: strings["board.filter.type"] }),
2367
+ TYPES.map((t) => /* @__PURE__ */ jsx4("option", { value: t, children: strings[`type.${t}`] ?? t }, t))
2284
2368
  ]
2285
2369
  }
2286
2370
  ),
@@ -2292,12 +2376,12 @@ function BoardFilters({
2292
2376
  value: filters.severity?.[0] ?? "",
2293
2377
  onChange: (e) => setSeverity(e.target.value),
2294
2378
  children: [
2295
- /* @__PURE__ */ jsx3("option", { value: "", children: strings["board.filter.severity"] }),
2296
- SEVERITIES.map((s) => /* @__PURE__ */ jsx3("option", { value: s, children: strings[`severity.${s}`] ?? s }, s))
2379
+ /* @__PURE__ */ jsx4("option", { value: "", children: strings["board.filter.severity"] }),
2380
+ SEVERITIES.map((s) => /* @__PURE__ */ jsx4("option", { value: s, children: strings[`severity.${s}`] ?? s }, s))
2297
2381
  ]
2298
2382
  }
2299
2383
  ),
2300
- /* @__PURE__ */ jsx3(
2384
+ /* @__PURE__ */ jsx4(
2301
2385
  "input",
2302
2386
  {
2303
2387
  type: "search",
@@ -2308,7 +2392,7 @@ function BoardFilters({
2308
2392
  }
2309
2393
  ),
2310
2394
  /* @__PURE__ */ jsxs4("label", { class: "board-filter-toggle", children: [
2311
- /* @__PURE__ */ jsx3(
2395
+ /* @__PURE__ */ jsx4(
2312
2396
  "input",
2313
2397
  {
2314
2398
  type: "checkbox",
@@ -2316,10 +2400,10 @@ function BoardFilters({
2316
2400
  onChange: toggleMine
2317
2401
  }
2318
2402
  ),
2319
- /* @__PURE__ */ jsx3("span", { children: strings["board.filter.mine"] })
2403
+ /* @__PURE__ */ jsx4("span", { children: strings["board.filter.mine"] })
2320
2404
  ] }),
2321
2405
  activeCount > 0 && /* @__PURE__ */ jsxs4("button", { type: "button", class: "board-filter-clear", onClick: clear, children: [
2322
- /* @__PURE__ */ jsx3(CloseIcon, {}),
2406
+ /* @__PURE__ */ jsx4(CloseIcon, {}),
2323
2407
  strings["board.filter.clear"]
2324
2408
  ] })
2325
2409
  ] });
@@ -2333,8 +2417,8 @@ function BoardList({
2333
2417
  thisPage
2334
2418
  }) {
2335
2419
  return /* @__PURE__ */ jsxs4(Fragment2, { children: [
2336
- /* @__PURE__ */ jsx3("div", { class: "board-list-count", children: strings[thisPage ? "board.list.count.page" : "board.list.count"].replace("{n}", String(rows.length)).replace("{total}", String(total)) }),
2337
- /* @__PURE__ */ jsx3("ul", { class: "board-list", role: "list", children: rows.map((row) => /* @__PURE__ */ jsx3(
2420
+ /* @__PURE__ */ jsx4("div", { class: "board-list-count", children: strings[thisPage ? "board.list.count.page" : "board.list.count"].replace("{n}", String(rows.length)).replace("{total}", String(total)) }),
2421
+ /* @__PURE__ */ jsx4("ul", { class: "board-list", role: "list", children: rows.map((row) => /* @__PURE__ */ jsx4(
2338
2422
  BoardRowCard,
2339
2423
  {
2340
2424
  row,
@@ -2353,7 +2437,7 @@ function BoardRowCard({
2353
2437
  strings
2354
2438
  }) {
2355
2439
  const author = row.is_mine ? strings["board.list.you"] : row.author_label || "\u2014";
2356
- return /* @__PURE__ */ jsx3("li", { children: /* @__PURE__ */ jsxs4(
2440
+ return /* @__PURE__ */ jsx4("li", { children: /* @__PURE__ */ jsxs4(
2357
2441
  "button",
2358
2442
  {
2359
2443
  type: "button",
@@ -2362,18 +2446,18 @@ function BoardRowCard({
2362
2446
  "aria-pressed": selected,
2363
2447
  children: [
2364
2448
  /* @__PURE__ */ jsxs4("div", { class: "board-row-badges", children: [
2365
- /* @__PURE__ */ jsx3("span", { class: `badge status-${row.status}`, children: strings[`status.${row.status}`] ?? row.status }),
2366
- /* @__PURE__ */ jsx3("span", { class: `badge severity-${row.severity}`, children: strings[`severity.${row.severity}`] ?? row.severity })
2449
+ /* @__PURE__ */ jsx4("span", { class: `badge status-${row.status}`, children: strings[`status.${row.status}`] ?? row.status }),
2450
+ /* @__PURE__ */ jsx4("span", { class: `badge severity-${row.severity}`, children: strings[`severity.${row.severity}`] ?? row.severity })
2367
2451
  ] }),
2368
- /* @__PURE__ */ jsx3("div", { class: "board-row-description", children: row.description }),
2452
+ /* @__PURE__ */ jsx4("div", { class: "board-row-description", children: row.description }),
2369
2453
  /* @__PURE__ */ jsxs4("div", { class: "board-row-meta", children: [
2370
- /* @__PURE__ */ jsx3("span", { class: "board-row-author", children: author }),
2454
+ /* @__PURE__ */ jsx4("span", { class: "board-row-author", children: author }),
2371
2455
  row.comment_count > 0 && /* @__PURE__ */ jsxs4("span", { class: "board-row-comments", children: [
2372
- /* @__PURE__ */ jsx3(CommentIcon, {}),
2456
+ /* @__PURE__ */ jsx4(CommentIcon, {}),
2373
2457
  " ",
2374
2458
  row.comment_count
2375
2459
  ] }),
2376
- /* @__PURE__ */ jsx3("span", { class: "board-row-time", children: formatRelative(row.created_at) })
2460
+ /* @__PURE__ */ jsx4("span", { class: "board-row-time", children: formatRelative(row.created_at) })
2377
2461
  ] })
2378
2462
  ]
2379
2463
  }
@@ -2385,44 +2469,44 @@ function BoardEmpty({
2385
2469
  onAllPages
2386
2470
  }) {
2387
2471
  return /* @__PURE__ */ jsxs4("div", { class: "board-empty", children: [
2388
- /* @__PURE__ */ jsx3(EmptyIllustration, {}),
2389
- /* @__PURE__ */ jsx3("h3", { children: strings[thisPage ? "board.list.empty.page.title" : "board.list.empty.title"] }),
2390
- /* @__PURE__ */ jsx3("p", { children: strings[thisPage ? "board.list.empty.page.description" : "board.list.empty.description"] }),
2391
- thisPage && /* @__PURE__ */ jsx3("button", { type: "button", class: "btn btn--ghost", onClick: onAllPages, children: strings["board.scope.allPages"] })
2472
+ /* @__PURE__ */ jsx4(EmptyIllustration, {}),
2473
+ /* @__PURE__ */ jsx4("h3", { children: strings[thisPage ? "board.list.empty.page.title" : "board.list.empty.title"] }),
2474
+ /* @__PURE__ */ jsx4("p", { children: strings[thisPage ? "board.list.empty.page.description" : "board.list.empty.description"] }),
2475
+ thisPage && /* @__PURE__ */ jsx4("button", { type: "button", class: "btn btn--ghost", onClick: onAllPages, children: strings["board.scope.allPages"] })
2392
2476
  ] });
2393
2477
  }
2394
2478
  function BoardListSkeleton() {
2395
- return /* @__PURE__ */ jsx3("ul", { class: "board-list board-list-skeleton", "aria-hidden": "true", children: Array.from({ length: 5 }).map((_, i) => /* @__PURE__ */ jsx3("li", { children: /* @__PURE__ */ jsxs4("div", { class: "board-row skeleton-row", children: [
2396
- /* @__PURE__ */ jsx3("div", { class: "skeleton-line skeleton-badges" }),
2397
- /* @__PURE__ */ jsx3("div", { class: "skeleton-line skeleton-text" }),
2398
- /* @__PURE__ */ jsx3("div", { class: "skeleton-line skeleton-text short" })
2479
+ return /* @__PURE__ */ jsx4("ul", { class: "board-list board-list-skeleton", "aria-hidden": "true", children: Array.from({ length: 5 }).map((_, i) => /* @__PURE__ */ jsx4("li", { children: /* @__PURE__ */ jsxs4("div", { class: "board-row skeleton-row", children: [
2480
+ /* @__PURE__ */ jsx4("div", { class: "skeleton-line skeleton-badges" }),
2481
+ /* @__PURE__ */ jsx4("div", { class: "skeleton-line skeleton-text" }),
2482
+ /* @__PURE__ */ jsx4("div", { class: "skeleton-line skeleton-text short" })
2399
2483
  ] }) }, i)) });
2400
2484
  }
2401
2485
  function CommentIcon() {
2402
- return /* @__PURE__ */ jsx3("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx3("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }) });
2486
+ return /* @__PURE__ */ jsx4("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx4("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }) });
2403
2487
  }
2404
2488
  function CloseIcon() {
2405
2489
  return /* @__PURE__ */ jsxs4("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true", children: [
2406
- /* @__PURE__ */ jsx3("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
2407
- /* @__PURE__ */ jsx3("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
2490
+ /* @__PURE__ */ jsx4("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
2491
+ /* @__PURE__ */ jsx4("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
2408
2492
  ] });
2409
2493
  }
2410
2494
  function EmptyIllustration() {
2411
2495
  return /* @__PURE__ */ jsxs4("svg", { width: "64", height: "64", viewBox: "0 0 64 64", fill: "none", "aria-hidden": "true", children: [
2412
- /* @__PURE__ */ jsx3("circle", { cx: "32", cy: "32", r: "28", stroke: "currentColor", "stroke-opacity": "0.18", "stroke-width": "2" }),
2413
- /* @__PURE__ */ jsx3("path", { d: "M22 32h20M32 22v20", stroke: "currentColor", "stroke-opacity": "0.35", "stroke-width": "2", "stroke-linecap": "round" })
2496
+ /* @__PURE__ */ jsx4("circle", { cx: "32", cy: "32", r: "28", stroke: "currentColor", "stroke-opacity": "0.18", "stroke-width": "2" }),
2497
+ /* @__PURE__ */ jsx4("path", { d: "M22 32h20M32 22v20", stroke: "currentColor", "stroke-opacity": "0.35", "stroke-width": "2", "stroke-linecap": "round" })
2414
2498
  ] });
2415
2499
  }
2416
2500
  function DetailEmptyIllustration() {
2417
2501
  return /* @__PURE__ */ jsxs4("svg", { width: "80", height: "80", viewBox: "0 0 64 64", fill: "none", "aria-hidden": "true", children: [
2418
- /* @__PURE__ */ jsx3("rect", { x: "10", y: "12", width: "44", height: "36", rx: "4", stroke: "currentColor", "stroke-opacity": "0.22", "stroke-width": "2" }),
2419
- /* @__PURE__ */ jsx3("line", { x1: "18", y1: "22", x2: "46", y2: "22", stroke: "currentColor", "stroke-opacity": "0.22", "stroke-width": "2", "stroke-linecap": "round" }),
2420
- /* @__PURE__ */ jsx3("line", { x1: "18", y1: "30", x2: "38", y2: "30", stroke: "currentColor", "stroke-opacity": "0.18", "stroke-width": "2", "stroke-linecap": "round" }),
2421
- /* @__PURE__ */ jsx3("line", { x1: "18", y1: "38", x2: "32", y2: "38", stroke: "currentColor", "stroke-opacity": "0.14", "stroke-width": "2", "stroke-linecap": "round" })
2502
+ /* @__PURE__ */ jsx4("rect", { x: "10", y: "12", width: "44", height: "36", rx: "4", stroke: "currentColor", "stroke-opacity": "0.22", "stroke-width": "2" }),
2503
+ /* @__PURE__ */ jsx4("line", { x1: "18", y1: "22", x2: "46", y2: "22", stroke: "currentColor", "stroke-opacity": "0.22", "stroke-width": "2", "stroke-linecap": "round" }),
2504
+ /* @__PURE__ */ jsx4("line", { x1: "18", y1: "30", x2: "38", y2: "30", stroke: "currentColor", "stroke-opacity": "0.18", "stroke-width": "2", "stroke-linecap": "round" }),
2505
+ /* @__PURE__ */ jsx4("line", { x1: "18", y1: "38", x2: "32", y2: "38", stroke: "currentColor", "stroke-opacity": "0.14", "stroke-width": "2", "stroke-linecap": "round" })
2422
2506
  ] });
2423
2507
  }
2424
2508
  function useDebouncedValue(value, ms) {
2425
- const [debounced, setDebounced] = useState2(value);
2509
+ const [debounced, setDebounced] = useState3(value);
2426
2510
  useEffect2(() => {
2427
2511
  const t = setTimeout(() => setDebounced(value), ms);
2428
2512
  return () => clearTimeout(t);
@@ -2482,10 +2566,10 @@ function formatRelative(iso) {
2482
2566
  }
2483
2567
 
2484
2568
  // src/widget/ChangelogList.tsx
2485
- import { useEffect as useEffect3, useMemo as useMemo2, useRef as useRef2, useState as useState3 } from "preact/hooks";
2569
+ import { useEffect as useEffect3, useMemo as useMemo2, useRef as useRef2, useState as useState4 } from "preact/hooks";
2486
2570
 
2487
2571
  // src/widget/ReportRow.tsx
2488
- import { jsx as jsx4, jsxs as jsxs5 } from "preact/jsx-runtime";
2572
+ import { jsx as jsx5, jsxs as jsxs5 } from "preact/jsx-runtime";
2489
2573
  function statusClassName(status) {
2490
2574
  return `pill pill-status pill-status--${status}`;
2491
2575
  }
@@ -2515,13 +2599,13 @@ function ReportRow({ row, strings, onClick, extraMeta }) {
2515
2599
  const preview = row.description.length > 120 ? row.description.slice(0, 117) + "\u2026" : row.description;
2516
2600
  return /* @__PURE__ */ jsxs5("button", { type: "button", class: "mine-row", onClick, children: [
2517
2601
  /* @__PURE__ */ jsxs5("div", { class: "mine-row-pills", children: [
2518
- /* @__PURE__ */ jsx4("span", { class: statusClassName(row.status), children: strings[`status.${row.status}`] ?? row.status }),
2519
- /* @__PURE__ */ jsx4("span", { class: typeClassName(), children: strings[`type.${row.feedback_type}`] }),
2520
- /* @__PURE__ */ jsx4("span", { class: severityClassName(row.severity), children: strings[`severity.${row.severity}`] })
2602
+ /* @__PURE__ */ jsx5("span", { class: statusClassName(row.status), children: strings[`status.${row.status}`] ?? row.status }),
2603
+ /* @__PURE__ */ jsx5("span", { class: typeClassName(), children: strings[`type.${row.feedback_type}`] }),
2604
+ /* @__PURE__ */ jsx5("span", { class: severityClassName(row.severity), children: strings[`severity.${row.severity}`] })
2521
2605
  ] }),
2522
- /* @__PURE__ */ jsx4("div", { class: "mine-row-preview", children: preview }),
2606
+ /* @__PURE__ */ jsx5("div", { class: "mine-row-preview", children: preview }),
2523
2607
  /* @__PURE__ */ jsxs5("div", { class: "mine-row-meta", children: [
2524
- /* @__PURE__ */ jsx4("span", { children: formatRelative2(row.updated_at || row.created_at) }),
2608
+ /* @__PURE__ */ jsx5("span", { children: formatRelative2(row.updated_at || row.created_at) }),
2525
2609
  row.comment_count > 0 && /* @__PURE__ */ jsxs5("span", { children: [
2526
2610
  "\xB7 ",
2527
2611
  repliesLabel(row.comment_count, strings)
@@ -2535,7 +2619,7 @@ function ReportRow({ row, strings, onClick, extraMeta }) {
2535
2619
  }
2536
2620
 
2537
2621
  // src/widget/ChangelogList.tsx
2538
- import { jsx as jsx5, jsxs as jsxs6 } from "preact/jsx-runtime";
2622
+ import { jsx as jsx6, jsxs as jsxs6 } from "preact/jsx-runtime";
2539
2623
  var POLL_MS3 = 3e4;
2540
2624
  function isoWeekKey(iso) {
2541
2625
  const d = new Date(iso);
@@ -2571,9 +2655,9 @@ function formatWeekLabel(weekKey) {
2571
2655
  }
2572
2656
  }
2573
2657
  function ChangelogList({ api, externalId, strings, onSelect }) {
2574
- const [rows, setRows] = useState3(null);
2575
- const [error, setError] = useState3(null);
2576
- const [refreshing, setRefreshing] = useState3(false);
2658
+ const [rows, setRows] = useState4(null);
2659
+ const [error, setError] = useState4(null);
2660
+ const [refreshing, setRefreshing] = useState4(false);
2577
2661
  const mountedRef = useRef2(true);
2578
2662
  const fetchRows = async () => {
2579
2663
  setRefreshing(true);
@@ -2606,8 +2690,8 @@ function ChangelogList({ api, externalId, strings, onSelect }) {
2606
2690
  const isEmpty = rows !== null && rows.length === 0;
2607
2691
  return /* @__PURE__ */ jsxs6("div", { class: "mine-list", children: [
2608
2692
  /* @__PURE__ */ jsxs6("div", { class: "mine-list-header", children: [
2609
- /* @__PURE__ */ jsx5("h2", { children: strings["tab.changelog"] }),
2610
- /* @__PURE__ */ jsx5(
2693
+ /* @__PURE__ */ jsx6("h2", { children: strings["tab.changelog"] }),
2694
+ /* @__PURE__ */ jsx6(
2611
2695
  "button",
2612
2696
  {
2613
2697
  type: "button",
@@ -2620,20 +2704,20 @@ function ChangelogList({ api, externalId, strings, onSelect }) {
2620
2704
  }
2621
2705
  )
2622
2706
  ] }),
2623
- isLoading && /* @__PURE__ */ jsx5("div", { class: "mine-loading", children: strings["mine.loading"] }),
2624
- error && /* @__PURE__ */ jsx5("div", { class: "error", children: error }),
2707
+ isLoading && /* @__PURE__ */ jsx6("div", { class: "mine-loading", children: strings["mine.loading"] }),
2708
+ error && /* @__PURE__ */ jsx6("div", { class: "error", children: error }),
2625
2709
  isEmpty && /* @__PURE__ */ jsxs6("div", { class: "mine-empty", children: [
2626
- /* @__PURE__ */ jsx5("strong", { children: strings["changelog.empty.title"] }),
2627
- /* @__PURE__ */ jsx5("p", { children: strings["changelog.empty.body"] })
2710
+ /* @__PURE__ */ jsx6("strong", { children: strings["changelog.empty.title"] }),
2711
+ /* @__PURE__ */ jsx6("p", { children: strings["changelog.empty.body"] })
2628
2712
  ] }),
2629
- groups.length > 0 && /* @__PURE__ */ jsx5("div", { class: "changelog-groups", children: groups.map((g) => /* @__PURE__ */ jsxs6("section", { class: "changelog-group", children: [
2713
+ groups.length > 0 && /* @__PURE__ */ jsx6("div", { class: "changelog-groups", children: groups.map((g) => /* @__PURE__ */ jsxs6("section", { class: "changelog-group", children: [
2630
2714
  /* @__PURE__ */ jsxs6("header", { class: "changelog-group-header", children: [
2631
- /* @__PURE__ */ jsx5("span", { class: "changelog-group-marker", "aria-hidden": "true", children: "\u25CF" }),
2632
- /* @__PURE__ */ jsx5("span", { class: "changelog-group-label", children: strings["changelog.week_of"].replace("{date}", g.label) }),
2633
- /* @__PURE__ */ jsx5("span", { class: "changelog-group-rule", "aria-hidden": "true" }),
2634
- /* @__PURE__ */ jsx5("span", { class: "changelog-group-count", children: strings[g.rows.length === 1 ? "changelog.resolved_one" : "changelog.resolved_many"].replace("{count}", String(g.rows.length)) })
2715
+ /* @__PURE__ */ jsx6("span", { class: "changelog-group-marker", "aria-hidden": "true", children: "\u25CF" }),
2716
+ /* @__PURE__ */ jsx6("span", { class: "changelog-group-label", children: strings["changelog.week_of"].replace("{date}", g.label) }),
2717
+ /* @__PURE__ */ jsx6("span", { class: "changelog-group-rule", "aria-hidden": "true" }),
2718
+ /* @__PURE__ */ jsx6("span", { class: "changelog-group-count", children: strings[g.rows.length === 1 ? "changelog.resolved_one" : "changelog.resolved_many"].replace("{count}", String(g.rows.length)) })
2635
2719
  ] }),
2636
- /* @__PURE__ */ jsx5("ul", { class: "mine-rows", children: g.rows.map((row) => /* @__PURE__ */ jsx5("li", { children: /* @__PURE__ */ jsx5(
2720
+ /* @__PURE__ */ jsx6("ul", { class: "mine-rows", children: g.rows.map((row) => /* @__PURE__ */ jsx6("li", { children: /* @__PURE__ */ jsx6(
2637
2721
  ReportRow,
2638
2722
  {
2639
2723
  row,
@@ -2647,7 +2731,7 @@ function ChangelogList({ api, externalId, strings, onSelect }) {
2647
2731
  }
2648
2732
 
2649
2733
  // src/widget/Fab.tsx
2650
- import { jsx as jsx6, jsxs as jsxs7 } from "preact/jsx-runtime";
2734
+ import { jsx as jsx7, jsxs as jsxs7 } from "preact/jsx-runtime";
2651
2735
  function BugIcon() {
2652
2736
  return /* @__PURE__ */ jsxs7(
2653
2737
  "svg",
@@ -2663,34 +2747,34 @@ function BugIcon() {
2663
2747
  "aria-hidden": "true",
2664
2748
  focusable: "false",
2665
2749
  children: [
2666
- /* @__PURE__ */ jsx6("path", { d: "m8 2 1.88 1.88" }),
2667
- /* @__PURE__ */ jsx6("path", { d: "M14.12 3.88 16 2" }),
2668
- /* @__PURE__ */ jsx6("path", { d: "M9 7.13v-1a3.003 3.003 0 1 1 6 0v1" }),
2669
- /* @__PURE__ */ jsx6("path", { d: "M12 20c-3.3 0-6-2.7-6-6v-3a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v3c0 3.3-2.7 6-6 6" }),
2670
- /* @__PURE__ */ jsx6("path", { d: "M12 20v-9" }),
2671
- /* @__PURE__ */ jsx6("path", { d: "M6.53 9C4.6 8.8 3 7.1 3 5" }),
2672
- /* @__PURE__ */ jsx6("path", { d: "M6 13H2" }),
2673
- /* @__PURE__ */ jsx6("path", { d: "M3 21c0-2.1 1.7-3.9 3.8-4" }),
2674
- /* @__PURE__ */ jsx6("path", { d: "M20.97 5c0 2.1-1.6 3.8-3.5 4" }),
2675
- /* @__PURE__ */ jsx6("path", { d: "M22 13h-4" }),
2676
- /* @__PURE__ */ jsx6("path", { d: "M17.2 17c2.1.1 3.8 1.9 3.8 4" })
2750
+ /* @__PURE__ */ jsx7("path", { d: "m8 2 1.88 1.88" }),
2751
+ /* @__PURE__ */ jsx7("path", { d: "M14.12 3.88 16 2" }),
2752
+ /* @__PURE__ */ jsx7("path", { d: "M9 7.13v-1a3.003 3.003 0 1 1 6 0v1" }),
2753
+ /* @__PURE__ */ jsx7("path", { d: "M12 20c-3.3 0-6-2.7-6-6v-3a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v3c0 3.3-2.7 6-6 6" }),
2754
+ /* @__PURE__ */ jsx7("path", { d: "M12 20v-9" }),
2755
+ /* @__PURE__ */ jsx7("path", { d: "M6.53 9C4.6 8.8 3 7.1 3 5" }),
2756
+ /* @__PURE__ */ jsx7("path", { d: "M6 13H2" }),
2757
+ /* @__PURE__ */ jsx7("path", { d: "M3 21c0-2.1 1.7-3.9 3.8-4" }),
2758
+ /* @__PURE__ */ jsx7("path", { d: "M20.97 5c0 2.1-1.6 3.8-3.5 4" }),
2759
+ /* @__PURE__ */ jsx7("path", { d: "M22 13h-4" }),
2760
+ /* @__PURE__ */ jsx7("path", { d: "M17.2 17c2.1.1 3.8 1.9 3.8 4" })
2677
2761
  ]
2678
2762
  }
2679
2763
  );
2680
2764
  }
2681
2765
  function Fab({ label, onClick, count }) {
2682
2766
  return /* @__PURE__ */ jsxs7("button", { type: "button", class: "fab", "aria-label": label, title: label, onClick, children: [
2683
- /* @__PURE__ */ jsx6(BugIcon, {}),
2684
- count !== void 0 && count > 0 && /* @__PURE__ */ jsx6("span", { class: "fab-badge", "aria-hidden": "true", children: count > 9 ? "9+" : String(count) })
2767
+ /* @__PURE__ */ jsx7(BugIcon, {}),
2768
+ count !== void 0 && count > 0 && /* @__PURE__ */ jsx7("span", { class: "fab-badge", "aria-hidden": "true", children: count > 9 ? "9+" : String(count) })
2685
2769
  ] });
2686
2770
  }
2687
2771
 
2688
2772
  // src/widget/Form.tsx
2689
- import { useEffect as useEffect5, useRef as useRef4, useState as useState5 } from "preact/hooks";
2773
+ import { useEffect as useEffect5, useRef as useRef4, useState as useState6 } from "preact/hooks";
2690
2774
 
2691
2775
  // src/widget/Annotator.tsx
2692
- import { useEffect as useEffect4, useLayoutEffect, useRef as useRef3, useState as useState4 } from "preact/hooks";
2693
- import { jsx as jsx7, jsxs as jsxs8 } from "preact/jsx-runtime";
2776
+ import { useEffect as useEffect4, useLayoutEffect, useRef as useRef3, useState as useState5 } from "preact/hooks";
2777
+ import { jsx as jsx8, jsxs as jsxs8 } from "preact/jsx-runtime";
2694
2778
  var COLORS = ["#ef4444", "#f59e0b", "#10b981", "#3b82f6", "#ffffff"];
2695
2779
  var HIGHLIGHT_ALPHA = 0.35;
2696
2780
  function drawShape(ctx, shape, sourceImage) {
@@ -2812,43 +2896,43 @@ function drawBlur(ctx, shape, sourceImage) {
2812
2896
  ctx.imageSmoothingEnabled = true;
2813
2897
  }
2814
2898
  var Icon = {
2815
- rect: /* @__PURE__ */ jsx7("svg", { width: "16", height: "16", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx7("rect", { x: "2", y: "3", width: "12", height: "10", fill: "none", stroke: "currentColor", "stroke-width": "1.5" }) }),
2816
- arrow: /* @__PURE__ */ jsx7("svg", { width: "16", height: "16", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx7("path", { d: "M2 8h11M9 4l4 4-4 4", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round", "stroke-linejoin": "round" }) }),
2817
- pencil: /* @__PURE__ */ jsx7("svg", { width: "16", height: "16", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx7("path", { d: "M11.5 2.5l2 2L5 13H3v-2l8.5-8.5z", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linejoin": "round" }) }),
2818
- text: /* @__PURE__ */ jsx7("svg", { width: "16", height: "16", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx7("path", { d: "M3 3h10M8 3v10M5 13h6", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round" }) }),
2899
+ rect: /* @__PURE__ */ jsx8("svg", { width: "16", height: "16", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx8("rect", { x: "2", y: "3", width: "12", height: "10", fill: "none", stroke: "currentColor", "stroke-width": "1.5" }) }),
2900
+ arrow: /* @__PURE__ */ jsx8("svg", { width: "16", height: "16", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx8("path", { d: "M2 8h11M9 4l4 4-4 4", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round", "stroke-linejoin": "round" }) }),
2901
+ pencil: /* @__PURE__ */ jsx8("svg", { width: "16", height: "16", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx8("path", { d: "M11.5 2.5l2 2L5 13H3v-2l8.5-8.5z", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linejoin": "round" }) }),
2902
+ text: /* @__PURE__ */ jsx8("svg", { width: "16", height: "16", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx8("path", { d: "M3 3h10M8 3v10M5 13h6", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round" }) }),
2819
2903
  highlight: /* @__PURE__ */ jsxs8("svg", { width: "16", height: "16", viewBox: "0 0 16 16", "aria-hidden": "true", children: [
2820
- /* @__PURE__ */ jsx7("path", { d: "M3 13l3-3L11 5l-2-2-5 5-3 3v2h2zM10 4l2 2", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round", "stroke-linejoin": "round" }),
2821
- /* @__PURE__ */ jsx7("path", { d: "M2 14h12", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round" })
2904
+ /* @__PURE__ */ jsx8("path", { d: "M3 13l3-3L11 5l-2-2-5 5-3 3v2h2zM10 4l2 2", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round", "stroke-linejoin": "round" }),
2905
+ /* @__PURE__ */ jsx8("path", { d: "M2 14h12", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round" })
2822
2906
  ] }),
2823
2907
  blur: /* @__PURE__ */ jsxs8("svg", { width: "16", height: "16", viewBox: "0 0 16 16", "aria-hidden": "true", children: [
2824
- /* @__PURE__ */ jsx7("rect", { x: "2", y: "2", width: "4", height: "4", fill: "currentColor", opacity: "0.85" }),
2825
- /* @__PURE__ */ jsx7("rect", { x: "7", y: "2", width: "3", height: "3", fill: "currentColor", opacity: "0.55" }),
2826
- /* @__PURE__ */ jsx7("rect", { x: "11", y: "2", width: "3", height: "4", fill: "currentColor", opacity: "0.4" }),
2827
- /* @__PURE__ */ jsx7("rect", { x: "2", y: "7", width: "3", height: "3", fill: "currentColor", opacity: "0.6" }),
2828
- /* @__PURE__ */ jsx7("rect", { x: "6", y: "7", width: "3", height: "3", fill: "currentColor", opacity: "0.85" }),
2829
- /* @__PURE__ */ jsx7("rect", { x: "10", y: "7", width: "4", height: "3", fill: "currentColor", opacity: "0.5" }),
2830
- /* @__PURE__ */ jsx7("rect", { x: "2", y: "11", width: "4", height: "3", fill: "currentColor", opacity: "0.4" }),
2831
- /* @__PURE__ */ jsx7("rect", { x: "7", y: "11", width: "3", height: "3", fill: "currentColor", opacity: "0.65" }),
2832
- /* @__PURE__ */ jsx7("rect", { x: "11", y: "11", width: "3", height: "3", fill: "currentColor", opacity: "0.85" })
2908
+ /* @__PURE__ */ jsx8("rect", { x: "2", y: "2", width: "4", height: "4", fill: "currentColor", opacity: "0.85" }),
2909
+ /* @__PURE__ */ jsx8("rect", { x: "7", y: "2", width: "3", height: "3", fill: "currentColor", opacity: "0.55" }),
2910
+ /* @__PURE__ */ jsx8("rect", { x: "11", y: "2", width: "3", height: "4", fill: "currentColor", opacity: "0.4" }),
2911
+ /* @__PURE__ */ jsx8("rect", { x: "2", y: "7", width: "3", height: "3", fill: "currentColor", opacity: "0.6" }),
2912
+ /* @__PURE__ */ jsx8("rect", { x: "6", y: "7", width: "3", height: "3", fill: "currentColor", opacity: "0.85" }),
2913
+ /* @__PURE__ */ jsx8("rect", { x: "10", y: "7", width: "4", height: "3", fill: "currentColor", opacity: "0.5" }),
2914
+ /* @__PURE__ */ jsx8("rect", { x: "2", y: "11", width: "4", height: "3", fill: "currentColor", opacity: "0.4" }),
2915
+ /* @__PURE__ */ jsx8("rect", { x: "7", y: "11", width: "3", height: "3", fill: "currentColor", opacity: "0.65" }),
2916
+ /* @__PURE__ */ jsx8("rect", { x: "11", y: "11", width: "3", height: "3", fill: "currentColor", opacity: "0.85" })
2833
2917
  ] }),
2834
- undo: /* @__PURE__ */ jsx7("svg", { width: "14", height: "14", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx7("path", { d: "M4 7l3-3M4 7l3 3M4 7h6a3 3 0 0 1 0 6H7", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round", "stroke-linejoin": "round" }) }),
2835
- redo: /* @__PURE__ */ jsx7("svg", { width: "14", height: "14", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx7("path", { d: "M12 7l-3-3M12 7l-3 3M12 7H6a3 3 0 0 0 0 6h2", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round", "stroke-linejoin": "round" }) }),
2836
- trash: /* @__PURE__ */ jsx7("svg", { width: "14", height: "14", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx7("path", { d: "M3 4h10M6 4V2.5h4V4M5 4l.5 9h5L11 4", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round", "stroke-linejoin": "round" }) }),
2837
- close: /* @__PURE__ */ jsx7("svg", { width: "16", height: "16", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx7("path", { d: "M4 4l8 8M12 4l-8 8", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round" }) })
2918
+ undo: /* @__PURE__ */ jsx8("svg", { width: "14", height: "14", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx8("path", { d: "M4 7l3-3M4 7l3 3M4 7h6a3 3 0 0 1 0 6H7", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round", "stroke-linejoin": "round" }) }),
2919
+ redo: /* @__PURE__ */ jsx8("svg", { width: "14", height: "14", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx8("path", { d: "M12 7l-3-3M12 7l-3 3M12 7H6a3 3 0 0 0 0 6h2", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round", "stroke-linejoin": "round" }) }),
2920
+ trash: /* @__PURE__ */ jsx8("svg", { width: "14", height: "14", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx8("path", { d: "M3 4h10M6 4V2.5h4V4M5 4l.5 9h5L11 4", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round", "stroke-linejoin": "round" }) }),
2921
+ close: /* @__PURE__ */ jsx8("svg", { width: "16", height: "16", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx8("path", { d: "M4 4l8 8M12 4l-8 8", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round" }) })
2838
2922
  };
2839
2923
  function Annotator({ imageBlob, strings, onSave, onCancel }) {
2840
2924
  const canvasRef = useRef3(null);
2841
2925
  const containerRef = useRef3(null);
2842
2926
  const imageRef = useRef3(null);
2843
- const [tool, setTool] = useState4("rectangle");
2844
- const [color, setColor] = useState4(COLORS[0]);
2845
- const [shapes, setShapes] = useState4([]);
2846
- const [past, setPast] = useState4([]);
2847
- const [future, setFuture] = useState4([]);
2927
+ const [tool, setTool] = useState5("rectangle");
2928
+ const [color, setColor] = useState5(COLORS[0]);
2929
+ const [shapes, setShapes] = useState5([]);
2930
+ const [past, setPast] = useState5([]);
2931
+ const [future, setFuture] = useState5([]);
2848
2932
  const isDrawingRef = useRef3(false);
2849
- const [draftShape, setDraftShape] = useState4(null);
2850
- const [imageLoaded, setImageLoaded] = useState4(false);
2851
- const [saving, setSaving] = useState4(false);
2933
+ const [draftShape, setDraftShape] = useState5(null);
2934
+ const [imageLoaded, setImageLoaded] = useState5(false);
2935
+ const [saving, setSaving] = useState5(false);
2852
2936
  const scaleRef = useRef3(1);
2853
2937
  function addShape(shape) {
2854
2938
  setShapes((s) => {
@@ -3068,7 +3152,7 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
3068
3152
  { id: "highlight", icon: Icon.highlight, label: strings["annotator.tool.highlight"] },
3069
3153
  { id: "blur", icon: Icon.blur, label: strings["annotator.tool.blur"] }
3070
3154
  ];
3071
- return /* @__PURE__ */ jsx7(
3155
+ return /* @__PURE__ */ jsx8(
3072
3156
  "div",
3073
3157
  {
3074
3158
  class: "annotator-backdrop",
@@ -3078,8 +3162,8 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
3078
3162
  },
3079
3163
  children: /* @__PURE__ */ jsxs8("div", { class: "annotator", role: "dialog", "aria-modal": "true", "aria-label": strings["annotator.title"], children: [
3080
3164
  /* @__PURE__ */ jsxs8("div", { class: "annotator-header", children: [
3081
- /* @__PURE__ */ jsx7("span", { children: strings["annotator.title"] }),
3082
- /* @__PURE__ */ jsx7(
3165
+ /* @__PURE__ */ jsx8("span", { children: strings["annotator.title"] }),
3166
+ /* @__PURE__ */ jsx8(
3083
3167
  "button",
3084
3168
  {
3085
3169
  type: "button",
@@ -3091,7 +3175,7 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
3091
3175
  )
3092
3176
  ] }),
3093
3177
  /* @__PURE__ */ jsxs8("div", { class: "annotator-toolbar", children: [
3094
- /* @__PURE__ */ jsx7("div", { class: "annotator-tools", children: tools.map((t) => /* @__PURE__ */ jsx7(
3178
+ /* @__PURE__ */ jsx8("div", { class: "annotator-tools", children: tools.map((t) => /* @__PURE__ */ jsx8(
3095
3179
  "button",
3096
3180
  {
3097
3181
  type: "button",
@@ -3104,9 +3188,9 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
3104
3188
  },
3105
3189
  t.id
3106
3190
  )) }),
3107
- /* @__PURE__ */ jsx7("span", { class: "annotator-sep" }),
3191
+ /* @__PURE__ */ jsx8("span", { class: "annotator-sep" }),
3108
3192
  /* @__PURE__ */ jsxs8("div", { class: "annotator-colors", children: [
3109
- COLORS.map((c) => /* @__PURE__ */ jsx7(
3193
+ COLORS.map((c) => /* @__PURE__ */ jsx8(
3110
3194
  "button",
3111
3195
  {
3112
3196
  type: "button",
@@ -3118,7 +3202,7 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
3118
3202
  },
3119
3203
  c
3120
3204
  )),
3121
- /* @__PURE__ */ jsx7("label", { class: "annotator-color annotator-color-picker", title: strings["annotator.color_picker"], children: /* @__PURE__ */ jsx7(
3205
+ /* @__PURE__ */ jsx8("label", { class: "annotator-color annotator-color-picker", title: strings["annotator.color_picker"], children: /* @__PURE__ */ jsx8(
3122
3206
  "input",
3123
3207
  {
3124
3208
  type: "color",
@@ -3128,7 +3212,7 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
3128
3212
  }
3129
3213
  ) })
3130
3214
  ] }),
3131
- /* @__PURE__ */ jsx7("span", { class: "annotator-sep" }),
3215
+ /* @__PURE__ */ jsx8("span", { class: "annotator-sep" }),
3132
3216
  /* @__PURE__ */ jsxs8(
3133
3217
  "button",
3134
3218
  {
@@ -3139,7 +3223,7 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
3139
3223
  title: strings["annotator.undo"],
3140
3224
  children: [
3141
3225
  Icon.undo,
3142
- /* @__PURE__ */ jsx7("span", { children: strings["annotator.undo"] })
3226
+ /* @__PURE__ */ jsx8("span", { children: strings["annotator.undo"] })
3143
3227
  ]
3144
3228
  }
3145
3229
  ),
@@ -3153,7 +3237,7 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
3153
3237
  title: strings["annotator.redo"],
3154
3238
  children: [
3155
3239
  Icon.redo,
3156
- /* @__PURE__ */ jsx7("span", { children: strings["annotator.redo"] })
3240
+ /* @__PURE__ */ jsx8("span", { children: strings["annotator.redo"] })
3157
3241
  ]
3158
3242
  }
3159
3243
  ),
@@ -3166,18 +3250,18 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
3166
3250
  disabled: shapes.length === 0,
3167
3251
  children: [
3168
3252
  Icon.trash,
3169
- /* @__PURE__ */ jsx7("span", { children: strings["annotator.clear"] })
3253
+ /* @__PURE__ */ jsx8("span", { children: strings["annotator.clear"] })
3170
3254
  ]
3171
3255
  }
3172
3256
  ),
3173
- /* @__PURE__ */ jsx7("span", { class: "annotator-spacer" }),
3257
+ /* @__PURE__ */ jsx8("span", { class: "annotator-spacer" }),
3174
3258
  /* @__PURE__ */ jsxs8("span", { class: "annotator-count", children: [
3175
3259
  shapes.length,
3176
3260
  " ",
3177
3261
  strings["annotator.count_suffix"]
3178
3262
  ] })
3179
3263
  ] }),
3180
- /* @__PURE__ */ jsx7("div", { ref: containerRef, class: "annotator-canvas-wrap", children: !imageLoaded ? /* @__PURE__ */ jsx7("span", { class: "annotator-loading", children: strings["annotator.loading"] }) : /* @__PURE__ */ jsx7(
3264
+ /* @__PURE__ */ jsx8("div", { ref: containerRef, class: "annotator-canvas-wrap", children: !imageLoaded ? /* @__PURE__ */ jsx8("span", { class: "annotator-loading", children: strings["annotator.loading"] }) : /* @__PURE__ */ jsx8(
3181
3265
  "canvas",
3182
3266
  {
3183
3267
  ref: canvasRef,
@@ -3189,8 +3273,8 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
3189
3273
  }
3190
3274
  ) }),
3191
3275
  /* @__PURE__ */ jsxs8("div", { class: "annotator-footer", children: [
3192
- /* @__PURE__ */ jsx7("button", { type: "button", class: "btn", onClick: onCancel, children: strings["form.cancel"] }),
3193
- /* @__PURE__ */ jsx7(
3276
+ /* @__PURE__ */ jsx8("button", { type: "button", class: "btn", onClick: onCancel, children: strings["form.cancel"] }),
3277
+ /* @__PURE__ */ jsx8(
3194
3278
  "button",
3195
3279
  {
3196
3280
  type: "button",
@@ -3207,7 +3291,7 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
3207
3291
  }
3208
3292
 
3209
3293
  // src/widget/Form.tsx
3210
- import { jsx as jsx8, jsxs as jsxs9 } from "preact/jsx-runtime";
3294
+ import { jsx as jsx9, jsxs as jsxs9 } from "preact/jsx-runtime";
3211
3295
  var TYPES2 = ["bug", "feature", "question", "praise", "typo"];
3212
3296
  var SEVERITIES2 = ["blocker", "high", "medium", "low"];
3213
3297
  function Form({
@@ -3219,13 +3303,13 @@ function Form({
3219
3303
  submittedSeq,
3220
3304
  onDirtyChange
3221
3305
  }) {
3222
- const [description, setDescription] = useState5("");
3223
- const [feedbackType, setFeedbackType] = useState5("bug");
3224
- const [severity, setSeverity] = useState5("medium");
3225
- const [localError, setLocalError] = useState5("");
3226
- const [shots, setShots] = useState5([]);
3227
- const [isDragOver, setIsDragOver] = useState5(false);
3228
- const [annotatingIndex, setAnnotatingIndex] = useState5(null);
3306
+ const [description, setDescription] = useState6("");
3307
+ const [feedbackType, setFeedbackType] = useState6("bug");
3308
+ const [severity, setSeverity] = useState6("medium");
3309
+ const [localError, setLocalError] = useState6("");
3310
+ const [shots, setShots] = useState6([]);
3311
+ const [isDragOver, setIsDragOver] = useState6(false);
3312
+ const [annotatingIndex, setAnnotatingIndex] = useState6(null);
3229
3313
  const fileInputRef = useRef4(null);
3230
3314
  const dropZoneRef = useRef4(null);
3231
3315
  const submitting = status === "submitting";
@@ -3347,10 +3431,10 @@ function Form({
3347
3431
  onSubmit(values);
3348
3432
  };
3349
3433
  return /* @__PURE__ */ jsxs9("form", { onSubmit: handleSubmit, children: [
3350
- /* @__PURE__ */ jsx8("h2", { children: strings["form.title"] }),
3434
+ /* @__PURE__ */ jsx9("h2", { children: strings["form.title"] }),
3351
3435
  /* @__PURE__ */ jsxs9("div", { class: "field", children: [
3352
- /* @__PURE__ */ jsx8("label", { for: "mfb-desc", children: strings["form.description.label"] }),
3353
- /* @__PURE__ */ jsx8(
3436
+ /* @__PURE__ */ jsx9("label", { for: "mfb-desc", children: strings["form.description.label"] }),
3437
+ /* @__PURE__ */ jsx9(
3354
3438
  "textarea",
3355
3439
  {
3356
3440
  id: "mfb-desc",
@@ -3362,33 +3446,33 @@ function Form({
3362
3446
  ] }),
3363
3447
  /* @__PURE__ */ jsxs9("div", { class: "row", children: [
3364
3448
  /* @__PURE__ */ jsxs9("div", { class: "field", children: [
3365
- /* @__PURE__ */ jsx8("label", { for: "mfb-type", children: strings["form.type.label"] }),
3366
- /* @__PURE__ */ jsx8(
3449
+ /* @__PURE__ */ jsx9("label", { for: "mfb-type", children: strings["form.type.label"] }),
3450
+ /* @__PURE__ */ jsx9(
3367
3451
  "select",
3368
3452
  {
3369
3453
  id: "mfb-type",
3370
3454
  value: feedbackType,
3371
3455
  onChange: (e) => setFeedbackType(e.target.value),
3372
- children: TYPES2.map((t) => /* @__PURE__ */ jsx8("option", { value: t, children: strings[`type.${t}`] }))
3456
+ children: TYPES2.map((t) => /* @__PURE__ */ jsx9("option", { value: t, children: strings[`type.${t}`] }))
3373
3457
  }
3374
3458
  )
3375
3459
  ] }),
3376
3460
  /* @__PURE__ */ jsxs9("div", { class: "field", children: [
3377
- /* @__PURE__ */ jsx8("label", { for: "mfb-sev", children: strings["form.severity.label"] }),
3378
- /* @__PURE__ */ jsx8(
3461
+ /* @__PURE__ */ jsx9("label", { for: "mfb-sev", children: strings["form.severity.label"] }),
3462
+ /* @__PURE__ */ jsx9(
3379
3463
  "select",
3380
3464
  {
3381
3465
  id: "mfb-sev",
3382
3466
  value: severity,
3383
3467
  onChange: (e) => setSeverity(e.target.value),
3384
- children: SEVERITIES2.map((s) => /* @__PURE__ */ jsx8("option", { value: s, children: strings[`severity.${s}`] }))
3468
+ children: SEVERITIES2.map((s) => /* @__PURE__ */ jsx9("option", { value: s, children: strings[`severity.${s}`] }))
3385
3469
  }
3386
3470
  )
3387
3471
  ] })
3388
3472
  ] }),
3389
3473
  /* @__PURE__ */ jsxs9("div", { class: "field", children: [
3390
- /* @__PURE__ */ jsx8("label", { children: strings["form.screenshot.label"] }),
3391
- /* @__PURE__ */ jsx8(
3474
+ /* @__PURE__ */ jsx9("label", { children: strings["form.screenshot.label"] }),
3475
+ /* @__PURE__ */ jsx9(
3392
3476
  "input",
3393
3477
  {
3394
3478
  ref: fileInputRef,
@@ -3401,8 +3485,8 @@ function Form({
3401
3485
  tabIndex: -1
3402
3486
  }
3403
3487
  ),
3404
- shots.length > 0 && /* @__PURE__ */ jsx8("div", { class: "screenshot-strip", children: shots.map((shot, index) => /* @__PURE__ */ jsxs9("div", { class: "screenshot-preview", children: [
3405
- /* @__PURE__ */ jsx8("img", { src: shot.preview, alt: "" }),
3488
+ shots.length > 0 && /* @__PURE__ */ jsx9("div", { class: "screenshot-strip", children: shots.map((shot, index) => /* @__PURE__ */ jsxs9("div", { class: "screenshot-preview", children: [
3489
+ /* @__PURE__ */ jsx9("img", { src: shot.preview, alt: "" }),
3406
3490
  /* @__PURE__ */ jsxs9("div", { class: "screenshot-preview-actions", children: [
3407
3491
  /* @__PURE__ */ jsxs9(
3408
3492
  "button",
@@ -3412,15 +3496,15 @@ function Form({
3412
3496
  onClick: () => setAnnotatingIndex(index),
3413
3497
  children: [
3414
3498
  /* @__PURE__ */ jsxs9("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true", children: [
3415
- /* @__PURE__ */ jsx8("path", { d: "M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" }),
3416
- /* @__PURE__ */ jsx8("path", { d: "M18.5 2.5a2.12 2.12 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" })
3499
+ /* @__PURE__ */ jsx9("path", { d: "M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" }),
3500
+ /* @__PURE__ */ jsx9("path", { d: "M18.5 2.5a2.12 2.12 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" })
3417
3501
  ] }),
3418
3502
  strings["form.screenshot.annotate"]
3419
3503
  ]
3420
3504
  }
3421
3505
  ),
3422
3506
  /* @__PURE__ */ jsxs9("button", { type: "button", class: "btn", onClick: () => removeShot(index), children: [
3423
- /* @__PURE__ */ jsx8("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx8("path", { d: "M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2m3 0v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6h14z" }) }),
3507
+ /* @__PURE__ */ jsx9("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx9("path", { d: "M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2m3 0v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6h14z" }) }),
3424
3508
  strings["form.screenshot.remove"]
3425
3509
  ] })
3426
3510
  ] })
@@ -3445,33 +3529,33 @@ function Form({
3445
3529
  onDrop: handleDrop,
3446
3530
  children: [
3447
3531
  /* @__PURE__ */ jsxs9("svg", { class: "screenshot-icon", width: "28", height: "28", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "1.6", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true", children: [
3448
- /* @__PURE__ */ jsx8("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
3449
- /* @__PURE__ */ jsx8("polyline", { points: "17 8 12 3 7 8" }),
3450
- /* @__PURE__ */ jsx8("line", { x1: "12", y1: "3", x2: "12", y2: "15" })
3532
+ /* @__PURE__ */ jsx9("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
3533
+ /* @__PURE__ */ jsx9("polyline", { points: "17 8 12 3 7 8" }),
3534
+ /* @__PURE__ */ jsx9("line", { x1: "12", y1: "3", x2: "12", y2: "15" })
3451
3535
  ] }),
3452
3536
  /* @__PURE__ */ jsxs9("div", { class: "screenshot-cta", children: [
3453
- /* @__PURE__ */ jsx8("strong", { children: strings["form.screenshot.cta_click"] }),
3537
+ /* @__PURE__ */ jsx9("strong", { children: strings["form.screenshot.cta_click"] }),
3454
3538
  ", ",
3455
3539
  strings["form.screenshot.cta_rest"]
3456
3540
  ] }),
3457
- /* @__PURE__ */ jsx8("div", { class: "screenshot-formats", children: strings["form.screenshot.formats"] })
3541
+ /* @__PURE__ */ jsx9("div", { class: "screenshot-formats", children: strings["form.screenshot.formats"] })
3458
3542
  ]
3459
3543
  }
3460
3544
  )
3461
3545
  ] }),
3462
3546
  pageUrl && /* @__PURE__ */ jsxs9("div", { class: "page-context", title: pageUrl, children: [
3463
- /* @__PURE__ */ jsx8("span", { class: "page-context-label", children: strings["form.context.label"] }),
3464
- /* @__PURE__ */ jsx8("span", { class: "page-context-url", children: truncateUrl(pageUrl, 90) })
3547
+ /* @__PURE__ */ jsx9("span", { class: "page-context-label", children: strings["form.context.label"] }),
3548
+ /* @__PURE__ */ jsx9("span", { class: "page-context-url", children: truncateUrl(pageUrl, 90) })
3465
3549
  ] }),
3466
- /* @__PURE__ */ jsx8("div", { class: "capture-notice", children: strings["form.capture.notice"] }),
3467
- localError && /* @__PURE__ */ jsx8("div", { class: "error", children: localError }),
3468
- status === "error" && errorMessage && /* @__PURE__ */ jsx8("div", { class: "error", children: errorMessage }),
3469
- status === "success" && /* @__PURE__ */ jsx8("div", { class: "success", children: submittedSeq !== void 0 ? strings["form.success.seq"].replace("{seq}", String(submittedSeq)) : strings["form.success"] }),
3550
+ /* @__PURE__ */ jsx9("div", { class: "capture-notice", children: strings["form.capture.notice"] }),
3551
+ localError && /* @__PURE__ */ jsx9("div", { class: "error", children: localError }),
3552
+ status === "error" && errorMessage && /* @__PURE__ */ jsx9("div", { class: "error", children: errorMessage }),
3553
+ status === "success" && /* @__PURE__ */ jsx9("div", { class: "success", children: submittedSeq !== void 0 ? strings["form.success.seq"].replace("{seq}", String(submittedSeq)) : strings["form.success"] }),
3470
3554
  /* @__PURE__ */ jsxs9("div", { class: "actions", children: [
3471
- /* @__PURE__ */ jsx8("button", { type: "button", class: "btn", onClick: onCancel, disabled: submitting, children: strings["form.cancel"] }),
3472
- /* @__PURE__ */ jsx8("button", { type: "submit", class: "btn btn--primary", disabled: submitting, children: submitLabel })
3555
+ /* @__PURE__ */ jsx9("button", { type: "button", class: "btn", onClick: onCancel, disabled: submitting, children: strings["form.cancel"] }),
3556
+ /* @__PURE__ */ jsx9("button", { type: "submit", class: "btn btn--primary", disabled: submitting, children: submitLabel })
3473
3557
  ] }),
3474
- annotatingIndex !== null && shots[annotatingIndex] && /* @__PURE__ */ jsx8(
3558
+ annotatingIndex !== null && shots[annotatingIndex] && /* @__PURE__ */ jsx9(
3475
3559
  Annotator,
3476
3560
  {
3477
3561
  imageBlob: shots[annotatingIndex].blob,
@@ -3484,10 +3568,10 @@ function Form({
3484
3568
  }
3485
3569
 
3486
3570
  // src/widget/MineList.tsx
3487
- import { useEffect as useEffect6, useRef as useRef5, useState as useState6 } from "preact/hooks";
3571
+ import { useEffect as useEffect6, useRef as useRef5, useState as useState7 } from "preact/hooks";
3488
3572
 
3489
3573
  // src/widget/KpiStrip.tsx
3490
- import { jsx as jsx9, jsxs as jsxs10 } from "preact/jsx-runtime";
3574
+ import { jsx as jsx10, jsxs as jsxs10 } from "preact/jsx-runtime";
3491
3575
  function computeKpiCounts(rows) {
3492
3576
  const counts = {
3493
3577
  new: 0,
@@ -3554,7 +3638,7 @@ function KpiStrip({ rows, filter, onFilter, strings }) {
3554
3638
  value: resolutionRate === null ? "\u2014" : `${resolutionRate}%`
3555
3639
  }
3556
3640
  ];
3557
- return /* @__PURE__ */ jsx9("div", { class: "kpi-strip", role: "toolbar", children: cells.map((c) => {
3641
+ return /* @__PURE__ */ jsx10("div", { class: "kpi-strip", role: "toolbar", children: cells.map((c) => {
3558
3642
  const active = filter === c.key;
3559
3643
  const toggleTo = active ? "all" : c.key;
3560
3644
  return /* @__PURE__ */ jsxs10(
@@ -3565,8 +3649,8 @@ function KpiStrip({ rows, filter, onFilter, strings }) {
3565
3649
  onClick: () => onFilter(toggleTo),
3566
3650
  "aria-pressed": active,
3567
3651
  children: [
3568
- /* @__PURE__ */ jsx9("span", { class: "kpi-value", children: c.value }),
3569
- /* @__PURE__ */ jsx9("span", { class: "kpi-label", children: c.label })
3652
+ /* @__PURE__ */ jsx10("span", { class: "kpi-value", children: c.value }),
3653
+ /* @__PURE__ */ jsx10("span", { class: "kpi-label", children: c.label })
3570
3654
  ]
3571
3655
  }
3572
3656
  );
@@ -3574,16 +3658,30 @@ function KpiStrip({ rows, filter, onFilter, strings }) {
3574
3658
  }
3575
3659
 
3576
3660
  // src/widget/MineList.tsx
3577
- import { jsx as jsx10, jsxs as jsxs11 } from "preact/jsx-runtime";
3661
+ import { jsx as jsx11, jsxs as jsxs11 } from "preact/jsx-runtime";
3578
3662
  var POLL_MS4 = 3e4;
3579
3663
  function MineList({ api, externalId, strings, onSelect, onOpenSeq }) {
3580
- const [rows, setRows] = useState6(null);
3581
- const [error, setError] = useState6(null);
3582
- const [refreshing, setRefreshing] = useState6(false);
3583
- const [filter, setFilter] = useState6("all");
3584
- const [jumpValue, setJumpValue] = useState6("");
3585
- const [jumpError, setJumpError] = useState6(false);
3586
- const [jumping, setJumping] = useState6(false);
3664
+ const [rows, setRows] = useState7(null);
3665
+ const [error, setError] = useState7(null);
3666
+ const [refreshing, setRefreshing] = useState7(false);
3667
+ const [filter, setFilter] = useState7("all");
3668
+ const [validating, setValidating] = useState7(null);
3669
+ const validate = async (reportId, kind) => {
3670
+ setValidating(reportId);
3671
+ try {
3672
+ if (kind === "confirm") await api.closeAsResolved(reportId, externalId);
3673
+ else await api.reopenUnresolved(reportId, externalId);
3674
+ await fetchRows();
3675
+ } catch (err) {
3676
+ if (typeof console !== "undefined") console.warn("[mhosaic] validate:", err);
3677
+ if (mountedRef.current) setError(strings["mine.error"]);
3678
+ } finally {
3679
+ if (mountedRef.current) setValidating(null);
3680
+ }
3681
+ };
3682
+ const [jumpValue, setJumpValue] = useState7("");
3683
+ const [jumpError, setJumpError] = useState7(false);
3684
+ const [jumping, setJumping] = useState7(false);
3587
3685
  const mountedRef = useRef5(true);
3588
3686
  const handleJump = async (e) => {
3589
3687
  e.preventDefault();
@@ -3635,10 +3733,10 @@ function MineList({ api, externalId, strings, onSelect, onOpenSeq }) {
3635
3733
  const visibleEmpty = !!rows && rows.length > 0 && (visibleRows?.length ?? 0) === 0;
3636
3734
  return /* @__PURE__ */ jsxs11("div", { class: "mine-list", children: [
3637
3735
  /* @__PURE__ */ jsxs11("div", { class: "mine-list-header", children: [
3638
- /* @__PURE__ */ jsx10("h2", { children: strings["tab.mine"] }),
3736
+ /* @__PURE__ */ jsx11("h2", { children: strings["tab.mine"] }),
3639
3737
  /* @__PURE__ */ jsxs11("div", { class: "mine-list-header-actions", children: [
3640
3738
  onOpenSeq && /* @__PURE__ */ jsxs11("form", { class: "mine-jump", onSubmit: handleJump, children: [
3641
- /* @__PURE__ */ jsx10(
3739
+ /* @__PURE__ */ jsx11(
3642
3740
  "input",
3643
3741
  {
3644
3742
  type: "text",
@@ -3653,9 +3751,9 @@ function MineList({ api, externalId, strings, onSelect, onOpenSeq }) {
3653
3751
  disabled: jumping
3654
3752
  }
3655
3753
  ),
3656
- /* @__PURE__ */ jsx10("button", { type: "submit", class: "btn", disabled: jumping || !jumpValue.trim(), children: strings["mine.jump.go"] })
3754
+ /* @__PURE__ */ jsx11("button", { type: "submit", class: "btn", disabled: jumping || !jumpValue.trim(), children: strings["mine.jump.go"] })
3657
3755
  ] }),
3658
- /* @__PURE__ */ jsx10(
3756
+ /* @__PURE__ */ jsx11(
3659
3757
  "button",
3660
3758
  {
3661
3759
  type: "button",
@@ -3669,22 +3767,68 @@ function MineList({ api, externalId, strings, onSelect, onOpenSeq }) {
3669
3767
  )
3670
3768
  ] })
3671
3769
  ] }),
3672
- jumpError && /* @__PURE__ */ jsx10("div", { class: "mine-jump-error error", role: "alert", children: strings["mine.jump.not_found"] }),
3673
- rows && rows.length > 0 && /* @__PURE__ */ jsx10(KpiStrip, { rows, filter, onFilter: setFilter, strings }),
3674
- isLoading && /* @__PURE__ */ jsx10("div", { class: "mine-loading", children: strings["mine.loading"] }),
3675
- error && /* @__PURE__ */ jsx10("div", { class: "error", children: error }),
3770
+ jumpError && /* @__PURE__ */ jsx11("div", { class: "mine-jump-error error", role: "alert", children: strings["mine.jump.not_found"] }),
3771
+ rows && rows.some((r) => r.status === "awaiting_validation") && /* @__PURE__ */ jsxs11("div", { class: "validate-banner", role: "status", children: [
3772
+ /* @__PURE__ */ jsx11("span", { children: strings["mine.validate.banner"].replace(
3773
+ "{count}",
3774
+ String(rows.filter((r) => r.status === "awaiting_validation").length)
3775
+ ) }),
3776
+ /* @__PURE__ */ jsx11(
3777
+ "button",
3778
+ {
3779
+ type: "button",
3780
+ class: "btn",
3781
+ onClick: () => setFilter("awaiting_validation"),
3782
+ children: strings["mine.validate.view"]
3783
+ }
3784
+ )
3785
+ ] }),
3786
+ rows && rows.length > 0 && /* @__PURE__ */ jsx11(KpiStrip, { rows, filter, onFilter: setFilter, strings }),
3787
+ isLoading && /* @__PURE__ */ jsx11("div", { class: "mine-loading", children: strings["mine.loading"] }),
3788
+ error && /* @__PURE__ */ jsxs11("div", { class: "error", children: [
3789
+ error,
3790
+ /* @__PURE__ */ jsx11(DiagnosticHint, { api, strings, host: api.endpointHost })
3791
+ ] }),
3676
3792
  isEmpty && /* @__PURE__ */ jsxs11("div", { class: "mine-empty", children: [
3677
- /* @__PURE__ */ jsx10("strong", { children: strings["mine.empty.title"] }),
3678
- /* @__PURE__ */ jsx10("p", { children: strings["mine.empty.body"] })
3793
+ /* @__PURE__ */ jsx11("strong", { children: strings["mine.empty.title"] }),
3794
+ /* @__PURE__ */ jsx11("p", { children: strings["mine.empty.body"] })
3679
3795
  ] }),
3680
- visibleEmpty && /* @__PURE__ */ jsx10("div", { class: "mine-empty", children: /* @__PURE__ */ jsx10("p", { children: strings["mine.filter.empty"] }) }),
3681
- visibleRows && visibleRows.length > 0 && /* @__PURE__ */ jsx10("ul", { class: "mine-rows", children: visibleRows.map((row) => /* @__PURE__ */ jsx10("li", { children: /* @__PURE__ */ jsx10(ReportRow, { row, strings, onClick: () => onSelect(row) }) })) })
3796
+ visibleEmpty && /* @__PURE__ */ jsx11("div", { class: "mine-empty", children: /* @__PURE__ */ jsx11("p", { children: strings["mine.filter.empty"] }) }),
3797
+ visibleRows && visibleRows.length > 0 && /* @__PURE__ */ jsx11("ul", { class: "mine-rows", children: visibleRows.map((row) => /* @__PURE__ */ jsxs11("li", { children: [
3798
+ /* @__PURE__ */ jsx11(ReportRow, { row, strings, onClick: () => onSelect(row) }),
3799
+ row.status === "awaiting_validation" && /* @__PURE__ */ jsxs11("div", { class: "validate-actions", children: [
3800
+ /* @__PURE__ */ jsx11(
3801
+ "button",
3802
+ {
3803
+ type: "button",
3804
+ class: "btn",
3805
+ disabled: validating === row.id,
3806
+ onClick: () => {
3807
+ void validate(row.id, "confirm");
3808
+ },
3809
+ children: strings["mine.validate.confirm"]
3810
+ }
3811
+ ),
3812
+ /* @__PURE__ */ jsx11(
3813
+ "button",
3814
+ {
3815
+ type: "button",
3816
+ class: "btn btn--ghost",
3817
+ disabled: validating === row.id,
3818
+ onClick: () => {
3819
+ void validate(row.id, "reopen");
3820
+ },
3821
+ children: strings["mine.validate.reopen"]
3822
+ }
3823
+ )
3824
+ ] })
3825
+ ] })) })
3682
3826
  ] });
3683
3827
  }
3684
3828
 
3685
3829
  // src/widget/Modal.tsx
3686
3830
  import { useEffect as useEffect7, useRef as useRef6 } from "preact/hooks";
3687
- import { jsx as jsx11, jsxs as jsxs12 } from "preact/jsx-runtime";
3831
+ import { jsx as jsx12, jsxs as jsxs12 } from "preact/jsx-runtime";
3688
3832
  function Modal({ onDismiss, children, closeLabel = "Close", expanded = false }) {
3689
3833
  const modalRef = useRef6(null);
3690
3834
  const previouslyFocused = useRef6(null);
@@ -3710,7 +3854,7 @@ function Modal({ onDismiss, children, closeLabel = "Close", expanded = false })
3710
3854
  if (prev && typeof prev.focus === "function") prev.focus();
3711
3855
  };
3712
3856
  }, [onDismiss]);
3713
- return /* @__PURE__ */ jsx11(
3857
+ return /* @__PURE__ */ jsx12(
3714
3858
  "div",
3715
3859
  {
3716
3860
  class: `backdrop ${expanded ? "is-expanded" : ""}`,
@@ -3726,7 +3870,7 @@ function Modal({ onDismiss, children, closeLabel = "Close", expanded = false })
3726
3870
  role: "dialog",
3727
3871
  "aria-modal": "true",
3728
3872
  children: [
3729
- /* @__PURE__ */ jsx11(
3873
+ /* @__PURE__ */ jsx12(
3730
3874
  "button",
3731
3875
  {
3732
3876
  type: "button",
@@ -3745,7 +3889,7 @@ function Modal({ onDismiss, children, closeLabel = "Close", expanded = false })
3745
3889
  }
3746
3890
 
3747
3891
  // src/widget/PageActivityStrip.tsx
3748
- import { jsx as jsx12, jsxs as jsxs13 } from "preact/jsx-runtime";
3892
+ import { jsx as jsx13, jsxs as jsxs13 } from "preact/jsx-runtime";
3749
3893
  function PageActivityStrip({
3750
3894
  open,
3751
3895
  strings,
@@ -3754,14 +3898,14 @@ function PageActivityStrip({
3754
3898
  if (open === 0) return null;
3755
3899
  const text = open === 1 ? strings["page.strip.text.one"] : strings["page.strip.text"].replace("{n}", String(open));
3756
3900
  return /* @__PURE__ */ jsxs13("div", { class: "page-strip", children: [
3757
- /* @__PURE__ */ jsx12("span", { children: text }),
3758
- /* @__PURE__ */ jsx12("button", { type: "button", class: "page-strip-view", onClick: onView, children: strings["page.strip.view"] })
3901
+ /* @__PURE__ */ jsx13("span", { children: text }),
3902
+ /* @__PURE__ */ jsx13("button", { type: "button", class: "page-strip-view", onClick: onView, children: strings["page.strip.view"] })
3759
3903
  ] });
3760
3904
  }
3761
3905
 
3762
3906
  // src/widget/PagePeekPanel.tsx
3763
- import { useEffect as useEffect8, useState as useState7 } from "preact/hooks";
3764
- import { jsx as jsx13, jsxs as jsxs14 } from "preact/jsx-runtime";
3907
+ import { useEffect as useEffect8, useState as useState8 } from "preact/hooks";
3908
+ import { jsx as jsx14, jsxs as jsxs14 } from "preact/jsx-runtime";
3765
3909
  function PagePeekPanel({
3766
3910
  api,
3767
3911
  externalId,
@@ -3772,8 +3916,8 @@ function PagePeekPanel({
3772
3916
  onPickRow,
3773
3917
  onSend
3774
3918
  }) {
3775
- const [rows, setRows] = useState7(null);
3776
- const [failed, setFailed] = useState7(false);
3919
+ const [rows, setRows] = useState8(null);
3920
+ const [failed, setFailed] = useState8(false);
3777
3921
  useEffect8(() => {
3778
3922
  let cancelled = false;
3779
3923
  const pagePath = currentPagePath(getCurrentPage !== void 0 ? { getCurrentPage } : {});
@@ -3790,11 +3934,11 @@ function PagePeekPanel({
3790
3934
  };
3791
3935
  }, [api, externalId]);
3792
3936
  return /* @__PURE__ */ jsxs14("div", { class: "page-peek", role: "region", "aria-label": strings["page.peek.title"], children: [
3793
- /* @__PURE__ */ jsx13("div", { class: "page-peek-title", children: strings["page.peek.title"] }),
3937
+ /* @__PURE__ */ jsx14("div", { class: "page-peek-title", children: strings["page.peek.title"] }),
3794
3938
  rows === null && !failed && /* @__PURE__ */ jsxs14("div", { class: "page-peek-skeleton", "aria-hidden": "true", children: [
3795
- /* @__PURE__ */ jsx13("div", {}),
3939
+ /* @__PURE__ */ jsx14("div", {}),
3796
3940
  " ",
3797
- /* @__PURE__ */ jsx13("div", {})
3941
+ /* @__PURE__ */ jsx14("div", {})
3798
3942
  ] }),
3799
3943
  rows !== null && rows.map((row) => /* @__PURE__ */ jsxs14(
3800
3944
  "button",
@@ -3803,21 +3947,21 @@ function PagePeekPanel({
3803
3947
  class: "page-peek-row",
3804
3948
  onClick: () => onPickRow?.(row.id),
3805
3949
  children: [
3806
- /* @__PURE__ */ jsx13("span", { class: `page-peek-dot status-${row.status}`, "aria-hidden": "true" }),
3807
- /* @__PURE__ */ jsx13("span", { class: "page-peek-desc", children: row.description })
3950
+ /* @__PURE__ */ jsx14("span", { class: `page-peek-dot status-${row.status}`, "aria-hidden": "true" }),
3951
+ /* @__PURE__ */ jsx14("span", { class: "page-peek-desc", children: row.description })
3808
3952
  ]
3809
3953
  },
3810
3954
  row.id
3811
3955
  )),
3812
3956
  /* @__PURE__ */ jsxs14("div", { class: "page-peek-footer", children: [
3813
- /* @__PURE__ */ jsx13("button", { type: "button", class: "page-peek-viewall", onClick: onViewAll, children: open === 1 ? strings["page.peek.viewAll.one"] : strings["page.peek.viewAll"].replace("{n}", String(open)) }),
3814
- /* @__PURE__ */ jsx13("button", { type: "button", class: "page-peek-send", onClick: onSend, children: strings["fab.label"] })
3957
+ /* @__PURE__ */ jsx14("button", { type: "button", class: "page-peek-viewall", onClick: onViewAll, children: open === 1 ? strings["page.peek.viewAll.one"] : strings["page.peek.viewAll"].replace("{n}", String(open)) }),
3958
+ /* @__PURE__ */ jsx14("button", { type: "button", class: "page-peek-send", onClick: onSend, children: strings["fab.label"] })
3815
3959
  ] })
3816
3960
  ] });
3817
3961
  }
3818
3962
 
3819
3963
  // src/widget/pageSignal.ts
3820
- import { useCallback, useEffect as useEffect9, useState as useState8 } from "preact/hooks";
3964
+ import { useCallback, useEffect as useEffect9, useState as useState9 } from "preact/hooks";
3821
3965
  var TTL_MS2 = 6e4;
3822
3966
  function computeOpenCount(kpis) {
3823
3967
  const s = kpis.by_status;
@@ -3846,8 +3990,8 @@ function fetchOpen(api, externalId, path) {
3846
3990
  }
3847
3991
  function usePageOpenCount(opts) {
3848
3992
  const { api, externalId, getCurrentPage, enabled } = opts;
3849
- const [open, setOpen] = useState8(0);
3850
- const [tick, setTick] = useState8(0);
3993
+ const [open, setOpen] = useState9(0);
3994
+ const [tick, setTick] = useState9(0);
3851
3995
  const refresh = useCallback(() => {
3852
3996
  invalidatePageSignal();
3853
3997
  setTick((t) => t + 1);
@@ -4696,6 +4840,22 @@ var WIDGET_STYLES = `
4696
4840
  }
4697
4841
  .mine-empty strong { display: block; color: var(--mfb-text); margin-bottom: 4px; }
4698
4842
 
4843
+ .validate-banner {
4844
+ display: flex;
4845
+ align-items: center;
4846
+ justify-content: space-between;
4847
+ gap: 8px;
4848
+ padding: 8px 12px;
4849
+ margin-bottom: 8px;
4850
+ border: 1px solid var(--mfb-accent, #2563eb);
4851
+ border-radius: 8px;
4852
+ font-size: 13px;
4853
+ }
4854
+ .validate-actions {
4855
+ display: flex;
4856
+ gap: 8px;
4857
+ margin: 4px 0 8px;
4858
+ }
4699
4859
  .mine-rows {
4700
4860
  list-style: none;
4701
4861
  margin: 0;
@@ -5942,7 +6102,7 @@ button.report-detail-edit {
5942
6102
  `;
5943
6103
 
5944
6104
  // src/widget/mount.tsx
5945
- import { Fragment as Fragment3, jsx as jsx14, jsxs as jsxs15 } from "preact/jsx-runtime";
6105
+ import { Fragment as Fragment3, jsx as jsx15, jsxs as jsxs15 } from "preact/jsx-runtime";
5946
6106
  function computeFabVisible(opts, externalId, visibilityAllowed) {
5947
6107
  if (!opts.showFAB) return false;
5948
6108
  if (opts.getExternalId !== void 0 && !externalId) return false;
@@ -6040,7 +6200,7 @@ function mountWidget(options) {
6040
6200
  enabled: pageActivityEnabled
6041
6201
  });
6042
6202
  const fabLabel = pageSignal.open === 1 ? options.strings["page.badge.aria.one"] : pageSignal.open > 0 ? options.strings["page.badge.aria"].replace("{n}", String(pageSignal.open)) : options.strings["fab.label"];
6043
- const [visibilityAllowed, setVisibilityAllowed] = useState9(
6203
+ const [visibilityAllowed, setVisibilityAllowed] = useState10(
6044
6204
  !options.requiresVisibilityCheck
6045
6205
  );
6046
6206
  useEffect10(() => {
@@ -6064,7 +6224,7 @@ function mountWidget(options) {
6064
6224
  }, [externalId]);
6065
6225
  const fabVisible = computeFabVisible(options, externalId, visibilityAllowed);
6066
6226
  const showMineTab = Boolean(options.api && externalId);
6067
- const [peekOpen, setPeekOpen] = useState9(false);
6227
+ const [peekOpen, setPeekOpen] = useState10(false);
6068
6228
  const peekTimers = useRef7({});
6069
6229
  const peekEnter = () => {
6070
6230
  if (peekTimers.current.close) clearTimeout(peekTimers.current.close);
@@ -6088,7 +6248,7 @@ function mountWidget(options) {
6088
6248
  onFocusOut: () => setPeekOpen(false),
6089
6249
  onKeyDown: (e) => e.key === "Escape" && setPeekOpen(false),
6090
6250
  children: [
6091
- peekOpen && !state.open && pageActivityEnabled && pageSignal.open > 0 && options.api && externalId && /* @__PURE__ */ jsx14(
6251
+ peekOpen && !state.open && pageActivityEnabled && pageSignal.open > 0 && options.api && externalId && /* @__PURE__ */ jsx15(
6092
6252
  PagePeekPanel,
6093
6253
  {
6094
6254
  api: options.api,
@@ -6112,7 +6272,7 @@ function mountWidget(options) {
6112
6272
  }
6113
6273
  }
6114
6274
  ),
6115
- /* @__PURE__ */ jsx14(
6275
+ /* @__PURE__ */ jsx15(
6116
6276
  Fab,
6117
6277
  {
6118
6278
  label: fabLabel,
@@ -6144,11 +6304,11 @@ function mountWidget(options) {
6144
6304
  closeLabel: options.strings["form.close"],
6145
6305
  expanded: state.tab === "board",
6146
6306
  children: [
6147
- state.confirmingDiscard && /* @__PURE__ */ jsx14("div", { class: "discard-confirm", role: "alertdialog", "aria-modal": "true", children: /* @__PURE__ */ jsxs15("div", { class: "discard-confirm-card", children: [
6148
- /* @__PURE__ */ jsx14("p", { class: "discard-confirm-title", children: options.strings["form.discard.title"] }),
6149
- /* @__PURE__ */ jsx14("p", { class: "discard-confirm-body", children: options.strings["form.discard.body"] }),
6307
+ state.confirmingDiscard && /* @__PURE__ */ jsx15("div", { class: "discard-confirm", role: "alertdialog", "aria-modal": "true", children: /* @__PURE__ */ jsxs15("div", { class: "discard-confirm-card", children: [
6308
+ /* @__PURE__ */ jsx15("p", { class: "discard-confirm-title", children: options.strings["form.discard.title"] }),
6309
+ /* @__PURE__ */ jsx15("p", { class: "discard-confirm-body", children: options.strings["form.discard.body"] }),
6150
6310
  /* @__PURE__ */ jsxs15("div", { class: "discard-confirm-actions", children: [
6151
- /* @__PURE__ */ jsx14(
6311
+ /* @__PURE__ */ jsx15(
6152
6312
  "button",
6153
6313
  {
6154
6314
  type: "button",
@@ -6157,7 +6317,7 @@ function mountWidget(options) {
6157
6317
  children: options.strings["form.discard.keep"]
6158
6318
  }
6159
6319
  ),
6160
- /* @__PURE__ */ jsx14(
6320
+ /* @__PURE__ */ jsx15(
6161
6321
  "button",
6162
6322
  {
6163
6323
  type: "button",
@@ -6179,7 +6339,7 @@ function mountWidget(options) {
6179
6339
  ] })
6180
6340
  ] }) }),
6181
6341
  showMineTab && /* @__PURE__ */ jsxs15("div", { class: "tab-strip", role: "tablist", children: [
6182
- /* @__PURE__ */ jsx14(
6342
+ /* @__PURE__ */ jsx15(
6183
6343
  "button",
6184
6344
  {
6185
6345
  type: "button",
@@ -6190,7 +6350,7 @@ function mountWidget(options) {
6190
6350
  children: options.strings["tab.send"]
6191
6351
  }
6192
6352
  ),
6193
- /* @__PURE__ */ jsx14(
6353
+ /* @__PURE__ */ jsx15(
6194
6354
  "button",
6195
6355
  {
6196
6356
  type: "button",
@@ -6201,7 +6361,7 @@ function mountWidget(options) {
6201
6361
  children: options.strings["tab.mine"]
6202
6362
  }
6203
6363
  ),
6204
- /* @__PURE__ */ jsx14(
6364
+ /* @__PURE__ */ jsx15(
6205
6365
  "button",
6206
6366
  {
6207
6367
  type: "button",
@@ -6212,7 +6372,7 @@ function mountWidget(options) {
6212
6372
  children: options.strings["tab.changelog"]
6213
6373
  }
6214
6374
  ),
6215
- /* @__PURE__ */ jsx14(
6375
+ /* @__PURE__ */ jsx15(
6216
6376
  "button",
6217
6377
  {
6218
6378
  type: "button",
@@ -6225,7 +6385,7 @@ function mountWidget(options) {
6225
6385
  )
6226
6386
  ] }),
6227
6387
  state.tab === "send" && /* @__PURE__ */ jsxs15(Fragment3, { children: [
6228
- showMineTab && pageActivityEnabled && /* @__PURE__ */ jsx14(
6388
+ showMineTab && pageActivityEnabled && /* @__PURE__ */ jsx15(
6229
6389
  PageActivityStrip,
6230
6390
  {
6231
6391
  open: pageSignal.open,
@@ -6233,7 +6393,7 @@ function mountWidget(options) {
6233
6393
  onView: () => rerender(clearSelected({ ...currentState, tab: "board" }))
6234
6394
  }
6235
6395
  ),
6236
- /* @__PURE__ */ jsx14(
6396
+ /* @__PURE__ */ jsx15(
6237
6397
  Form,
6238
6398
  {
6239
6399
  strings: options.strings,
@@ -6250,7 +6410,7 @@ function mountWidget(options) {
6250
6410
  }
6251
6411
  )
6252
6412
  ] }),
6253
- state.tab === "mine" && options.api && externalId && !state.selectedReportId && /* @__PURE__ */ jsx14(
6413
+ state.tab === "mine" && options.api && externalId && !state.selectedReportId && /* @__PURE__ */ jsx15(
6254
6414
  MineList,
6255
6415
  {
6256
6416
  api: options.api,
@@ -6260,7 +6420,7 @@ function mountWidget(options) {
6260
6420
  onOpenSeq: openSeq
6261
6421
  }
6262
6422
  ),
6263
- (state.tab === "mine" || state.tab === "changelog") && options.api && externalId && state.selectedReportId && /* @__PURE__ */ jsx14(
6423
+ (state.tab === "mine" || state.tab === "changelog") && options.api && externalId && state.selectedReportId && /* @__PURE__ */ jsx15(
6264
6424
  ReportDetailView,
6265
6425
  {
6266
6426
  api: options.api,
@@ -6272,7 +6432,7 @@ function mountWidget(options) {
6272
6432
  ...buildPermalink && { buildPermalink }
6273
6433
  }
6274
6434
  ),
6275
- state.tab === "changelog" && options.api && externalId && !state.selectedReportId && /* @__PURE__ */ jsx14(
6435
+ state.tab === "changelog" && options.api && externalId && !state.selectedReportId && /* @__PURE__ */ jsx15(
6276
6436
  ChangelogList,
6277
6437
  {
6278
6438
  api: options.api,
@@ -6283,7 +6443,7 @@ function mountWidget(options) {
6283
6443
  ),
6284
6444
  state.tab === "board" && options.api && externalId && // BoardView owns its own master/detail navigation — no
6285
6445
  // selectedReportId routing through the modal-level state.
6286
- /* @__PURE__ */ jsx14(
6446
+ /* @__PURE__ */ jsx15(
6287
6447
  BoardView,
6288
6448
  {
6289
6449
  api: options.api,
@@ -6404,8 +6564,8 @@ function createFeedback(config) {
6404
6564
  capture_method: captureMethod,
6405
6565
  technical_context
6406
6566
  };
6407
- if ("0.32.0") {
6408
- payload.widget_version = "0.32.0";
6567
+ if ("0.34.0") {
6568
+ payload.widget_version = "0.34.0";
6409
6569
  }
6410
6570
  if (manualScreenshots?.length) {
6411
6571
  payload.screenshots = manualScreenshots;
@@ -6531,4 +6691,4 @@ function createFeedback(config) {
6531
6691
  export {
6532
6692
  createFeedback
6533
6693
  };
6534
- //# sourceMappingURL=chunk-YNDBZR37.mjs.map
6694
+ //# sourceMappingURL=chunk-J45ACCSP.mjs.map