@mushi-mushi/core 1.33.5 → 1.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.
- package/LICENSE +1 -1
- package/README.md +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js.map +1 -1
- package/dist/reporter-channels.cjs.map +1 -1
- package/dist/reporter-channels.d.cts +1 -1
- package/dist/reporter-channels.d.ts +1 -1
- package/dist/reporter-channels.js.map +1 -1
- package/dist/reporter-ui.cjs.map +1 -1
- package/dist/reporter-ui.js.map +1 -1
- package/dist/{types-DkC08B8U.d.cts → types-Nv_WYhjE.d.cts} +18 -5
- package/dist/{types-DkC08B8U.d.ts → types-Nv_WYhjE.d.ts} +18 -5
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/reporter-channels.ts"],"names":[],"mappings":"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","file":"reporter-channels.cjs","sourcesContent":["// SPDX-License-Identifier: MIT\n// Copyright (c) 2024–2026
|
|
1
|
+
{"version":3,"sources":["../src/reporter-channels.ts"],"names":[],"mappings":"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","file":"reporter-channels.cjs","sourcesContent":["// SPDX-License-Identifier: MIT\n// Copyright (c) 2024–2026 kensaurus LLC — Mushi Mushi\n/**\n * FILE: reporter-channels.ts\n * PURPOSE: Typed calls for the reporter loop v2 routes (Plan 018 §4, §6.2):\n * the badge / toast feed, mark-read, the report timeline, and the\n * reporter's own email / push opt-in.\n *\n * USAGE:\n * import { reporterChannels } from '@mushi-mushi/core/reporter-channels'\n * const rc = reporterChannels(apiClient)\n * const res = await rc.getUpdates(reporterToken)\n *\n * Shipped as a subpath so the main `@mushi-mushi/core` entry stays inside\n * its size budget. Every call goes through `apiClient.reporterRequest`, which\n * signs the reporter digest headers, times out, retries once and never\n * rejects: failures resolve as `{ ok: false }`.\n *\n * Email and push are opt-in. The server refuses a channel the project has\n * not turned on, or cannot send (`EMAIL_NOT_AVAILABLE` / `PUSH_NOT_AVAILABLE`\n * with `reason: 'not_configured' | 'project_disabled'`), so never show an\n * opt-in control unless `GET /v1/sdk/config` → `reporter.emailEnabled` /\n * `reporter.pushEnabled` is true.\n */\n\nimport type { MushiApiClient, MushiApiResponse } from './types';\n\n/** `GET /v1/reporter/updates` — the unread badge and the next-visit toast. */\nexport interface MushiReporterUpdates {\n unread_total: number;\n /** Up to 3 newest unread events, `report_id` is always the reporter's own report. */\n latest: Array<{ report_id: string; kind: string | null; preview: string; at: string }>;\n server_time: string;\n}\n\nexport type MushiReporterChannelUnavailable = 'not_configured' | 'project_disabled';\n\n/** `GET|PUT /v1/reporter/notification-prefs`. The address is always masked. */\nexport interface MushiReporterNotificationPrefs {\n email: string | null;\n email_verified: boolean;\n /** An address was given; the confirmation link has not been clicked yet. */\n email_pending: boolean;\n unsubscribed: boolean;\n channels: { in_app: boolean; email: boolean; push: boolean };\n push_subscriptions: number;\n available: { email: boolean; push: boolean };\n unavailable_reason: { email: MushiReporterChannelUnavailable | null; push: MushiReporterChannelUnavailable | null };\n /** PUT only: a confirmation email went out. */\n verification_sent?: boolean;\n}\n\n/** `email`: a new address starts double opt-in; `null` forgets the address. */\nexport interface MushiReporterPrefsUpdate {\n email?: string | null;\n channels?: { email?: boolean; push?: boolean };\n}\n\n/** `PushSubscription.toJSON()` shape. */\nexport interface MushiPushSubscriptionJSON {\n endpoint: string;\n keys: { p256dh: string; auth: string };\n}\n\n/** One entry of `GET /v1/reporter/reports/:id` → `timeline`. */\nexport interface MushiReporterTimelineItem {\n kind: string;\n at: string;\n /** English text; pipeline kinds can be re-rendered per locale unless `custom`. */\n text: string;\n /** Developer- or project-written text: show `text` as is. */\n custom?: boolean;\n body?: string;\n version?: string;\n closed_reason?: string;\n comment_id?: number | string;\n}\n\nexport interface MushiReporterReportDetail {\n report: Record<string, unknown> & { id: string; status: string };\n timeline: MushiReporterTimelineItem[];\n can_verify: boolean;\n}\n\nexport interface MushiReporterChannels {\n getUpdates(reporterToken: string, since?: string): Promise<MushiApiResponse<MushiReporterUpdates>>;\n getReport(reportId: string, reporterToken: string): Promise<MushiApiResponse<MushiReporterReportDetail>>;\n /** Mark one report's updates read; resolves the new unread total. */\n markReportRead(reportId: string, reporterToken: string): Promise<MushiApiResponse<{ marked_read: number; unread_total: number | null }>>;\n markAllRead(reporterToken: string): Promise<MushiApiResponse<{ marked_read: number; unread_total: number }>>;\n getPrefs(reporterToken: string): Promise<MushiApiResponse<MushiReporterNotificationPrefs>>;\n setPrefs(reporterToken: string, update: MushiReporterPrefsUpdate): Promise<MushiApiResponse<MushiReporterNotificationPrefs>>;\n subscribePush(reporterToken: string, subscription: MushiPushSubscriptionJSON): Promise<MushiApiResponse<{ subscribed: boolean }>>;\n unsubscribePush(reporterToken: string, endpoint: string): Promise<MushiApiResponse<{ removed: number }>>;\n}\n\nexport function reporterChannels(client: Pick<MushiApiClient, 'reporterRequest'>): MushiReporterChannels {\n const r = client.reporterRequest;\n const id = (reportId: string) => encodeURIComponent(reportId);\n return {\n getUpdates: (token, since) =>\n r('GET', `/v1/reporter/updates${since ? `?since=${encodeURIComponent(since)}` : ''}`, token),\n getReport: (reportId, token) => r('GET', `/v1/reporter/reports/${id(reportId)}`, token),\n markReportRead: (reportId, token) => r('POST', `/v1/reporter/reports/${id(reportId)}/read`, token, {}),\n markAllRead: (token) => r('POST', '/v1/reporter/notifications/read-all', token, {}),\n getPrefs: (token) => r('GET', '/v1/reporter/notification-prefs', token),\n setPrefs: (token, update) => r('PUT', '/v1/reporter/notification-prefs', token, update),\n subscribePush: (token, subscription) => r('POST', '/v1/reporter/push-subscriptions', token, subscription),\n unsubscribePush: (token, endpoint) => r('DELETE', '/v1/reporter/push-subscriptions', token, { endpoint }),\n };\n}\n\n/**\n * Browser push for a reporter: permission prompt, service worker, push\n * subscription, then `subscribePush`. Call it from a click (\"Notify me\"):\n * browsers only show the permission prompt after a user action. Never throws.\n *\n * `serviceWorkerPath` is a worker on the host app's own origin that shows\n * the push payload (`{ title, body, tag, url? }`); `vapidPublicKey` comes\n * from `GET /v1/sdk/config` → `reporter.vapidPublicKey` (null = not offered).\n */\nexport async function subscribeBrowserPush(\n channels: Pick<MushiReporterChannels, 'subscribePush'>,\n reporterToken: string,\n serviceWorkerPath: string | null | undefined,\n vapidPublicKey: string | null | undefined,\n): Promise<{ ok: true } | { ok: false; reason: string }> {\n try {\n if (!serviceWorkerPath || typeof Notification === 'undefined' || !navigator.serviceWorker) {\n return { ok: false, reason: 'unsupported' };\n }\n if (!vapidPublicKey) return { ok: false, reason: 'not_available' };\n if ((await Notification.requestPermission()) !== 'granted') return { ok: false, reason: 'denied' };\n const reg = await navigator.serviceWorker.register(serviceWorkerPath);\n const b64 = vapidPublicKey.replace(/-/g, '+').replace(/_/g, '/');\n const raw = atob(b64 + '='.repeat((4 - (b64.length % 4)) % 4));\n const sub = await reg.pushManager.subscribe({\n userVisibleOnly: true,\n applicationServerKey: Uint8Array.from(raw, (ch) => ch.charCodeAt(0)),\n });\n const res = await channels.subscribePush(reporterToken, sub.toJSON() as MushiPushSubscriptionJSON);\n return res.ok ? { ok: true } : { ok: false, reason: res.error?.code ?? 'error' };\n } catch (err) {\n return { ok: false, reason: err instanceof Error ? err.name || 'error' : 'error' };\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { U as MushiPushSubscriptionJSON, Y as MushiReporterChannelUnavailable, am as MushiReporterChannels, _ as MushiReporterNotificationPrefs, $ as MushiReporterPrefsUpdate, a1 as MushiReporterReportDetail, a2 as MushiReporterTimelineItem, a3 as MushiReporterUpdates, an as reporterChannels, ao as subscribeBrowserPush } from './types-
|
|
1
|
+
export { U as MushiPushSubscriptionJSON, Y as MushiReporterChannelUnavailable, am as MushiReporterChannels, _ as MushiReporterNotificationPrefs, $ as MushiReporterPrefsUpdate, a1 as MushiReporterReportDetail, a2 as MushiReporterTimelineItem, a3 as MushiReporterUpdates, an as reporterChannels, ao as subscribeBrowserPush } from './types-Nv_WYhjE.cjs';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { U as MushiPushSubscriptionJSON, Y as MushiReporterChannelUnavailable, am as MushiReporterChannels, _ as MushiReporterNotificationPrefs, $ as MushiReporterPrefsUpdate, a1 as MushiReporterReportDetail, a2 as MushiReporterTimelineItem, a3 as MushiReporterUpdates, an as reporterChannels, ao as subscribeBrowserPush } from './types-
|
|
1
|
+
export { U as MushiPushSubscriptionJSON, Y as MushiReporterChannelUnavailable, am as MushiReporterChannels, _ as MushiReporterNotificationPrefs, $ as MushiReporterPrefsUpdate, a1 as MushiReporterReportDetail, a2 as MushiReporterTimelineItem, a3 as MushiReporterUpdates, an as reporterChannels, ao as subscribeBrowserPush } from './types-Nv_WYhjE.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/reporter-channels.ts"],"names":[],"mappings":"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","file":"reporter-channels.js","sourcesContent":["// SPDX-License-Identifier: MIT\n// Copyright (c) 2024–2026
|
|
1
|
+
{"version":3,"sources":["../src/reporter-channels.ts"],"names":[],"mappings":"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","file":"reporter-channels.js","sourcesContent":["// SPDX-License-Identifier: MIT\n// Copyright (c) 2024–2026 kensaurus LLC — Mushi Mushi\n/**\n * FILE: reporter-channels.ts\n * PURPOSE: Typed calls for the reporter loop v2 routes (Plan 018 §4, §6.2):\n * the badge / toast feed, mark-read, the report timeline, and the\n * reporter's own email / push opt-in.\n *\n * USAGE:\n * import { reporterChannels } from '@mushi-mushi/core/reporter-channels'\n * const rc = reporterChannels(apiClient)\n * const res = await rc.getUpdates(reporterToken)\n *\n * Shipped as a subpath so the main `@mushi-mushi/core` entry stays inside\n * its size budget. Every call goes through `apiClient.reporterRequest`, which\n * signs the reporter digest headers, times out, retries once and never\n * rejects: failures resolve as `{ ok: false }`.\n *\n * Email and push are opt-in. The server refuses a channel the project has\n * not turned on, or cannot send (`EMAIL_NOT_AVAILABLE` / `PUSH_NOT_AVAILABLE`\n * with `reason: 'not_configured' | 'project_disabled'`), so never show an\n * opt-in control unless `GET /v1/sdk/config` → `reporter.emailEnabled` /\n * `reporter.pushEnabled` is true.\n */\n\nimport type { MushiApiClient, MushiApiResponse } from './types';\n\n/** `GET /v1/reporter/updates` — the unread badge and the next-visit toast. */\nexport interface MushiReporterUpdates {\n unread_total: number;\n /** Up to 3 newest unread events, `report_id` is always the reporter's own report. */\n latest: Array<{ report_id: string; kind: string | null; preview: string; at: string }>;\n server_time: string;\n}\n\nexport type MushiReporterChannelUnavailable = 'not_configured' | 'project_disabled';\n\n/** `GET|PUT /v1/reporter/notification-prefs`. The address is always masked. */\nexport interface MushiReporterNotificationPrefs {\n email: string | null;\n email_verified: boolean;\n /** An address was given; the confirmation link has not been clicked yet. */\n email_pending: boolean;\n unsubscribed: boolean;\n channels: { in_app: boolean; email: boolean; push: boolean };\n push_subscriptions: number;\n available: { email: boolean; push: boolean };\n unavailable_reason: { email: MushiReporterChannelUnavailable | null; push: MushiReporterChannelUnavailable | null };\n /** PUT only: a confirmation email went out. */\n verification_sent?: boolean;\n}\n\n/** `email`: a new address starts double opt-in; `null` forgets the address. */\nexport interface MushiReporterPrefsUpdate {\n email?: string | null;\n channels?: { email?: boolean; push?: boolean };\n}\n\n/** `PushSubscription.toJSON()` shape. */\nexport interface MushiPushSubscriptionJSON {\n endpoint: string;\n keys: { p256dh: string; auth: string };\n}\n\n/** One entry of `GET /v1/reporter/reports/:id` → `timeline`. */\nexport interface MushiReporterTimelineItem {\n kind: string;\n at: string;\n /** English text; pipeline kinds can be re-rendered per locale unless `custom`. */\n text: string;\n /** Developer- or project-written text: show `text` as is. */\n custom?: boolean;\n body?: string;\n version?: string;\n closed_reason?: string;\n comment_id?: number | string;\n}\n\nexport interface MushiReporterReportDetail {\n report: Record<string, unknown> & { id: string; status: string };\n timeline: MushiReporterTimelineItem[];\n can_verify: boolean;\n}\n\nexport interface MushiReporterChannels {\n getUpdates(reporterToken: string, since?: string): Promise<MushiApiResponse<MushiReporterUpdates>>;\n getReport(reportId: string, reporterToken: string): Promise<MushiApiResponse<MushiReporterReportDetail>>;\n /** Mark one report's updates read; resolves the new unread total. */\n markReportRead(reportId: string, reporterToken: string): Promise<MushiApiResponse<{ marked_read: number; unread_total: number | null }>>;\n markAllRead(reporterToken: string): Promise<MushiApiResponse<{ marked_read: number; unread_total: number }>>;\n getPrefs(reporterToken: string): Promise<MushiApiResponse<MushiReporterNotificationPrefs>>;\n setPrefs(reporterToken: string, update: MushiReporterPrefsUpdate): Promise<MushiApiResponse<MushiReporterNotificationPrefs>>;\n subscribePush(reporterToken: string, subscription: MushiPushSubscriptionJSON): Promise<MushiApiResponse<{ subscribed: boolean }>>;\n unsubscribePush(reporterToken: string, endpoint: string): Promise<MushiApiResponse<{ removed: number }>>;\n}\n\nexport function reporterChannels(client: Pick<MushiApiClient, 'reporterRequest'>): MushiReporterChannels {\n const r = client.reporterRequest;\n const id = (reportId: string) => encodeURIComponent(reportId);\n return {\n getUpdates: (token, since) =>\n r('GET', `/v1/reporter/updates${since ? `?since=${encodeURIComponent(since)}` : ''}`, token),\n getReport: (reportId, token) => r('GET', `/v1/reporter/reports/${id(reportId)}`, token),\n markReportRead: (reportId, token) => r('POST', `/v1/reporter/reports/${id(reportId)}/read`, token, {}),\n markAllRead: (token) => r('POST', '/v1/reporter/notifications/read-all', token, {}),\n getPrefs: (token) => r('GET', '/v1/reporter/notification-prefs', token),\n setPrefs: (token, update) => r('PUT', '/v1/reporter/notification-prefs', token, update),\n subscribePush: (token, subscription) => r('POST', '/v1/reporter/push-subscriptions', token, subscription),\n unsubscribePush: (token, endpoint) => r('DELETE', '/v1/reporter/push-subscriptions', token, { endpoint }),\n };\n}\n\n/**\n * Browser push for a reporter: permission prompt, service worker, push\n * subscription, then `subscribePush`. Call it from a click (\"Notify me\"):\n * browsers only show the permission prompt after a user action. Never throws.\n *\n * `serviceWorkerPath` is a worker on the host app's own origin that shows\n * the push payload (`{ title, body, tag, url? }`); `vapidPublicKey` comes\n * from `GET /v1/sdk/config` → `reporter.vapidPublicKey` (null = not offered).\n */\nexport async function subscribeBrowserPush(\n channels: Pick<MushiReporterChannels, 'subscribePush'>,\n reporterToken: string,\n serviceWorkerPath: string | null | undefined,\n vapidPublicKey: string | null | undefined,\n): Promise<{ ok: true } | { ok: false; reason: string }> {\n try {\n if (!serviceWorkerPath || typeof Notification === 'undefined' || !navigator.serviceWorker) {\n return { ok: false, reason: 'unsupported' };\n }\n if (!vapidPublicKey) return { ok: false, reason: 'not_available' };\n if ((await Notification.requestPermission()) !== 'granted') return { ok: false, reason: 'denied' };\n const reg = await navigator.serviceWorker.register(serviceWorkerPath);\n const b64 = vapidPublicKey.replace(/-/g, '+').replace(/_/g, '/');\n const raw = atob(b64 + '='.repeat((4 - (b64.length % 4)) % 4));\n const sub = await reg.pushManager.subscribe({\n userVisibleOnly: true,\n applicationServerKey: Uint8Array.from(raw, (ch) => ch.charCodeAt(0)),\n });\n const res = await channels.subscribePush(reporterToken, sub.toJSON() as MushiPushSubscriptionJSON);\n return res.ok ? { ok: true } : { ok: false, reason: res.error?.code ?? 'error' };\n } catch (err) {\n return { ok: false, reason: err instanceof Error ? err.name || 'error' : 'error' };\n }\n}\n"]}
|
package/dist/reporter-ui.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/reporter-ui.ts"],"names":[],"mappings":"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","file":"reporter-ui.cjs","sourcesContent":["// SPDX-License-Identifier: MIT\n// Copyright (c) 2024–2026 Kenji Sakuramoto / Kensaurus 合同会社 — Mushi Mushi\n/**\n * FILE: reporter-ui.ts\n * PURPOSE: The one reporter-facing vocabulary shared by every SDK widget\n * (web, React Native, Capacitor): report categories, the end-user\n * status table, the timeline kinds and their copy in en/ja/es/th.\n *\n * OVERVIEW:\n * - `reporterStatus(report, locale)` maps all 14 internal `reports.status`\n * values (plus the `awaiting_reporter` and duplicate-group overlays and the\n * feature-request variants) onto eight end-user states. There is no branch\n * that echoes the raw status: an unknown value reads as \"Received\".\n * - `reporterTimelineText(kind, params, locale)` renders pipeline events from\n * a fixed template per kind. Stored notification payload text is never\n * shown, so internal category / severity can not leak to a reporter.\n *\n * USAGE:\n * - `import { reporterStatus } from '@mushi-mushi/core/reporter-ui'`\n * - Shipped as a subpath so the main `@mushi-mushi/core` entry stays within\n * its size budget; nothing here is re-exported from `index.ts`.\n *\n * Plan 018 (docs/execplans/reporter-loop-v2.md) §2.1 and §2.3.\n */\n\n/** Every value `reports_status_check` allows. Pinned to the server list by a test. */\nexport const REPORTER_CANONICAL_STATUSES = [\n 'new',\n 'pending',\n 'submitted',\n 'queued',\n 'classified',\n 'grouped',\n 'fixing',\n 'fixed',\n 'dismissed',\n 'triaged',\n 'in_progress',\n 'resolved',\n 'verified',\n 'reopened',\n] as const;\n\nexport type ReporterCanonicalStatus = (typeof REPORTER_CANONICAL_STATUSES)[number];\n\nexport const REPORTER_LOCALES = ['en', 'ja', 'es', 'th', 'zh'] as const;\nexport type ReporterLocale = (typeof REPORTER_LOCALES)[number];\n\n/** Optional type chips on the one-screen report form, in display order. */\nexport const REPORTER_CATEGORIES = ['bug', 'slow', 'visual', 'confusing', 'idea'] as const;\nexport type ReporterCategory = (typeof REPORTER_CATEGORIES)[number];\n\n/** `closed_reason` values the `reports_closed_reason_check` constraint allows. */\nexport const REPORTER_CLOSED_REASONS = [\n 'duplicate',\n 'not_reproducible',\n 'wont_fix',\n 'working_as_intended',\n 'spam',\n] as const;\nexport type ReporterClosedReason = (typeof REPORTER_CLOSED_REASONS)[number];\n\nexport type ReporterTone =\n | 'neutral'\n | 'info'\n | 'attention'\n | 'progress'\n | 'success'\n | 'success-muted'\n | 'muted';\n\n/** The end-user state a report is shown in. */\nexport type ReporterStatusKey =\n | 'received'\n | 'reviewing'\n | 'reviewing_again'\n | 'fixing'\n | 'verified'\n | 'fixed_next'\n | 'fixed_version'\n | 'fixed'\n | 'closed'\n | 'waiting';\n\n/** Base state per internal status, before overlays. Exhaustive by type. */\nconst BASE_STATE: Record<ReporterCanonicalStatus, ReporterStatusKey> = {\n new: 'received',\n pending: 'received',\n submitted: 'received',\n queued: 'received',\n classified: 'reviewing',\n triaged: 'reviewing',\n grouped: 'reviewing',\n reopened: 'reviewing_again',\n fixing: 'fixing',\n in_progress: 'fixing',\n verified: 'verified',\n fixed: 'fixed_next',\n resolved: 'fixed',\n dismissed: 'closed',\n};\n\nconst TONE: Record<ReporterStatusKey, ReporterTone> = {\n received: 'neutral',\n reviewing: 'info',\n reviewing_again: 'info',\n fixing: 'progress',\n verified: 'success-muted',\n fixed_next: 'success',\n fixed_version: 'success',\n fixed: 'success',\n closed: 'muted',\n waiting: 'attention',\n};\n\n/** Timeline entry kinds the reporter detail view renders (§2.3). */\nexport const REPORTER_TIMELINE_KINDS = [\n 'received',\n 'reviewing',\n 'duplicate_linked',\n 'info_requested',\n 'comment',\n 'reporter_comment',\n 'fix_started',\n 'fixed',\n 'released',\n 'verified',\n 'reopened',\n 'closed',\n] as const;\nexport type ReporterTimelineKind = (typeof REPORTER_TIMELINE_KINDS)[number];\n\nexport interface ReporterCopy {\n status: Record<ReporterStatusKey, string>;\n detail: Record<ReporterStatusKey, string>;\n idea: { received: string; reviewing: string; fixing: string; shipped: string; shippedVersion: string };\n closedReason: Record<Exclude<ReporterClosedReason, 'spam'>, string> & { none: string };\n others: { few: string; many: string };\n timeline: Record<Exclude<ReporterTimelineKind, 'comment' | 'closed'>, string>;\n categories: Record<ReporterCategory, string>;\n ui: {\n yes: string;\n notYet: string;\n empty: string;\n loadError: string;\n retry: string;\n sending: string;\n sent: string;\n sendFailed: string;\n noReplies: string;\n you: string;\n developer: string;\n addWords: string;\n developerReplied: string;\n updates: string;\n /** Receipt after sending (replaces the form in place). */\n receipt: string;\n trackIt: string;\n done: string;\n /** Email opt-in on the receipt / report detail (never pre-ticked). */\n emailOptIn: string;\n emailPlaceholder: string;\n emailSubmit: string;\n emailCheckInbox: string;\n emailInvalid: string;\n emailFailed: string;\n /** Next-visit toast. */\n toastReplied: string;\n toastFixed: string;\n view: string;\n };\n}\n\nconst EN: ReporterCopy = {\n status: {\n received: 'Received',\n reviewing: 'Looking into it',\n reviewing_again: 'Looking into it again',\n fixing: 'Fix in progress',\n verified: 'Confirmed fixed',\n fixed_next: 'Fixed — coming in the next update',\n fixed_version: 'Fixed in v{version}',\n fixed: 'Fixed',\n closed: 'Closed',\n waiting: 'Waiting on you',\n },\n detail: {\n received: 'We got it.',\n reviewing: 'The developer has it on their list.',\n reviewing_again: 'Thanks — we reopened it.',\n fixing: 'A fix is being worked on.',\n verified: 'You confirmed the fix. Thank you!',\n fixed_next: 'It ships in the next release.',\n fixed_version: 'Update to v{version}. Does it work for you now?',\n fixed: 'Does it work for you now?',\n closed: 'Closed.',\n waiting: 'The developer asked a question — reply below.',\n },\n idea: {\n received: 'Thanks for the idea',\n reviewing: 'Under consideration',\n fixing: 'Being built',\n shipped: 'Shipped',\n shippedVersion: 'Shipped in v{version}',\n },\n closedReason: {\n duplicate: \"Same as an earlier report — we'll update you there.\",\n not_reproducible: \"We couldn't reproduce it. Reply if it happens again.\",\n wont_fix: 'We decided not to change this.',\n working_as_intended: 'This is expected behaviour.',\n none: 'Closed.',\n },\n others: {\n few: 'You and a few others reported this',\n many: 'Many people reported this',\n },\n timeline: {\n received: 'You reported this',\n reviewing: 'The developer is looking into it',\n duplicate_linked: \"Same as an existing report — we'll update you there\",\n info_requested: 'The developer asked: {text}',\n reporter_comment: 'You',\n fix_started: 'A fix is in progress',\n fixed: 'Fixed — coming in the next update',\n released: 'Shipped in v{version} — update to get it',\n verified: \"You confirmed it's fixed\",\n reopened: 'Reopened',\n },\n categories: {\n bug: 'Bug',\n slow: 'Slow',\n visual: 'Looks wrong',\n confusing: 'Confusing',\n idea: 'Idea',\n },\n ui: {\n yes: 'Yes',\n notYet: 'Not yet',\n empty: 'Nothing yet — your reports will show up here with updates.',\n loadError: \"Couldn't load updates\",\n retry: 'Retry',\n sending: 'Sending…',\n sent: 'Sent',\n sendFailed: 'Failed',\n noReplies: 'No developer replies yet.',\n you: 'You',\n developer: 'Developer',\n addWords: 'Add a few words',\n developerReplied: 'Developer replied: “{text}”',\n updates: '{n} updates on your reports',\n receipt: \"Sent. We'll let you know here when there's news.\",\n trackIt: 'Track it',\n done: 'Done',\n emailOptIn: 'Get updates by email',\n emailPlaceholder: 'you@example.com',\n emailSubmit: 'Email me updates',\n emailCheckInbox: 'Check your inbox and tap the link to confirm.',\n emailInvalid: 'That does not look like an email address.',\n emailFailed: \"Couldn't set up email updates. Try again later.\",\n toastReplied: 'The developer replied to your report',\n toastFixed: 'Your bug is fixed',\n view: 'View',\n },\n};\n\nconst JA: ReporterCopy = {\n status: {\n received: '受け付けました',\n reviewing: '確認中',\n reviewing_again: '再確認中',\n fixing: '修正中',\n verified: '修正を確認済み',\n fixed_next: '修正済み — 次のアップデートで反映',\n fixed_version: 'v{version} で修正済み',\n fixed: '修正済み',\n closed: '終了',\n waiting: 'あなたの返信待ち',\n },\n detail: {\n received: '報告を受け取りました。',\n reviewing: '開発者が対応リストに入れました。',\n reviewing_again: 'ありがとうございます。再度確認しています。',\n fixing: '修正に取り組んでいます。',\n verified: '修正を確認いただきありがとうございます!',\n fixed_next: '次のリリースで反映されます。',\n fixed_version: 'v{version} にアップデートしてください。解決しましたか?',\n fixed: '解決しましたか?',\n closed: '終了しました。',\n waiting: '開発者から質問があります。下から返信してください。',\n },\n idea: {\n received: 'アイデアをありがとうございます',\n reviewing: '検討中',\n fixing: '開発中',\n shipped: 'リリース済み',\n shippedVersion: 'v{version} でリリース済み',\n },\n closedReason: {\n duplicate: '以前の報告と同じ内容です。そちらで進捗をお知らせします。',\n not_reproducible: '再現できませんでした。再発したら返信してください。',\n wont_fix: 'この点は変更しないことにしました。',\n working_as_intended: 'これは想定どおりの動作です。',\n none: '終了しました。',\n },\n others: {\n few: 'あなたを含め数人が報告しています',\n many: '多くの人が報告しています',\n },\n timeline: {\n received: 'あなたが報告しました',\n reviewing: '開発者が確認しています',\n duplicate_linked: '既存の報告と同じ内容です。そちらで進捗をお知らせします',\n info_requested: '開発者からの質問: {text}',\n reporter_comment: 'あなた',\n fix_started: '修正中です',\n fixed: '修正済み — 次のアップデートで反映',\n released: 'v{version} でリリース — アップデートしてください',\n verified: '修正を確認しました',\n reopened: '再オープンしました',\n },\n categories: {\n bug: 'バグ',\n slow: '遅い',\n visual: '表示がおかしい',\n confusing: 'わかりにくい',\n idea: 'アイデア',\n },\n ui: {\n yes: 'はい',\n notYet: 'まだ',\n empty: 'まだありません。報告するとここに進捗が表示されます。',\n loadError: '更新を読み込めませんでした',\n retry: '再試行',\n sending: '送信中…',\n sent: '送信しました',\n sendFailed: '送信失敗',\n noReplies: '開発者からの返信はまだありません。',\n you: 'あなた',\n developer: '開発者',\n addWords: 'もう少し詳しく書いてください',\n developerReplied: '開発者からの返信:「{text}」',\n updates: 'あなたの報告に {n} 件の更新があります',\n receipt: '送信しました。進展があればここでお知らせします。',\n trackIt: '経過を見る',\n done: '閉じる',\n emailOptIn: 'メールで更新を受け取る',\n emailPlaceholder: 'you@example.com',\n emailSubmit: 'メールで受け取る',\n emailCheckInbox: '受信トレイのメールのリンクをタップして確認してください。',\n emailInvalid: 'メールアドレスの形式が正しくありません。',\n emailFailed: 'メール通知を設定できませんでした。後でもう一度お試しください。',\n toastReplied: '開発者があなたの報告に返信しました',\n toastFixed: 'あなたが報告した不具合が修正されました',\n view: '見る',\n },\n};\n\nconst ES: ReporterCopy = {\n status: {\n received: 'Recibido',\n reviewing: 'Lo estamos revisando',\n reviewing_again: 'Lo revisamos de nuevo',\n fixing: 'Corrección en curso',\n verified: 'Corrección confirmada',\n fixed_next: 'Corregido — llega en la próxima actualización',\n fixed_version: 'Corregido en v{version}',\n fixed: 'Corregido',\n closed: 'Cerrado',\n waiting: 'Esperando tu respuesta',\n },\n detail: {\n received: 'Lo recibimos.',\n reviewing: 'El desarrollador lo tiene en su lista.',\n reviewing_again: 'Gracias, lo reabrimos.',\n fixing: 'Se está trabajando en una corrección.',\n verified: 'Confirmaste la corrección. ¡Gracias!',\n fixed_next: 'Llega en la próxima versión.',\n fixed_version: 'Actualiza a v{version}. ¿Ya funciona?',\n fixed: '¿Ya funciona?',\n closed: 'Cerrado.',\n waiting: 'El desarrollador hizo una pregunta: responde abajo.',\n },\n idea: {\n received: 'Gracias por la idea',\n reviewing: 'En consideración',\n fixing: 'En desarrollo',\n shipped: 'Publicado',\n shippedVersion: 'Publicado en v{version}',\n },\n closedReason: {\n duplicate: 'Es igual a un reporte anterior; te avisaremos allí.',\n not_reproducible: 'No pudimos reproducirlo. Responde si vuelve a pasar.',\n wont_fix: 'Decidimos no cambiar esto.',\n working_as_intended: 'Este es el comportamiento esperado.',\n none: 'Cerrado.',\n },\n others: {\n few: 'Tú y algunas personas más reportaron esto',\n many: 'Muchas personas reportaron esto',\n },\n timeline: {\n received: 'Reportaste esto',\n reviewing: 'El desarrollador lo está revisando',\n duplicate_linked: 'Es igual a un reporte existente; te avisaremos allí',\n info_requested: 'El desarrollador preguntó: {text}',\n reporter_comment: 'Tú',\n fix_started: 'Hay una corrección en curso',\n fixed: 'Corregido — llega en la próxima actualización',\n released: 'Publicado en v{version}: actualiza para tenerlo',\n verified: 'Confirmaste que está corregido',\n reopened: 'Reabierto',\n },\n categories: {\n bug: 'Error',\n slow: 'Lento',\n visual: 'Se ve mal',\n confusing: 'Confuso',\n idea: 'Idea',\n },\n ui: {\n yes: 'Sí',\n notYet: 'Todavía no',\n empty: 'Nada todavía: tus reportes aparecerán aquí con novedades.',\n loadError: 'No se pudieron cargar las novedades',\n retry: 'Reintentar',\n sending: 'Enviando…',\n sent: 'Enviado',\n sendFailed: 'Error al enviar',\n noReplies: 'El desarrollador aún no ha respondido.',\n you: 'Tú',\n developer: 'Desarrollador',\n addWords: 'Agrega algunas palabras',\n developerReplied: 'El desarrollador respondió: “{text}”',\n updates: '{n} novedades en tus reportes',\n receipt: 'Enviado. Te avisaremos aquí cuando haya novedades.',\n trackIt: 'Seguirlo',\n done: 'Listo',\n emailOptIn: 'Recibir novedades por correo',\n emailPlaceholder: 'tu@ejemplo.com',\n emailSubmit: 'Enviarme novedades',\n emailCheckInbox: 'Revisa tu correo y toca el enlace para confirmar.',\n emailInvalid: 'Eso no parece un correo electrónico.',\n emailFailed: 'No pudimos activar las novedades por correo. Inténtalo más tarde.',\n toastReplied: 'El desarrollador respondió a tu reporte',\n toastFixed: 'Tu error está corregido',\n view: 'Ver',\n },\n};\n\nconst TH: ReporterCopy = {\n status: {\n received: 'ได้รับแล้ว',\n reviewing: 'กำลังตรวจสอบ',\n reviewing_again: 'กำลังตรวจสอบอีกครั้ง',\n fixing: 'กำลังแก้ไข',\n verified: 'ยืนยันว่าแก้แล้ว',\n fixed_next: 'แก้แล้ว — จะมาในอัปเดตถัดไป',\n fixed_version: 'แก้แล้วใน v{version}',\n fixed: 'แก้แล้ว',\n closed: 'ปิดแล้ว',\n waiting: 'รอคำตอบจากคุณ',\n },\n detail: {\n received: 'เราได้รับรายงานแล้ว',\n reviewing: 'นักพัฒนาใส่ไว้ในรายการแล้ว',\n reviewing_again: 'ขอบคุณ — เราเปิดเรื่องนี้อีกครั้งแล้ว',\n fixing: 'กำลังดำเนินการแก้ไข',\n verified: 'คุณยืนยันการแก้ไขแล้ว ขอบคุณ!',\n fixed_next: 'จะมาในรุ่นถัดไป',\n fixed_version: 'อัปเดตเป็น v{version} ตอนนี้ใช้ได้หรือยัง?',\n fixed: 'ตอนนี้ใช้ได้หรือยัง?',\n closed: 'ปิดแล้ว',\n waiting: 'นักพัฒนามีคำถาม — ตอบด้านล่าง',\n },\n idea: {\n received: 'ขอบคุณสำหรับไอเดีย',\n reviewing: 'กำลังพิจารณา',\n fixing: 'กำลังสร้าง',\n shipped: 'เปิดใช้แล้ว',\n shippedVersion: 'เปิดใช้แล้วใน v{version}',\n },\n closedReason: {\n duplicate: 'เหมือนกับรายงานก่อนหน้า — เราจะแจ้งความคืบหน้าที่นั่น',\n not_reproducible: 'เราทำให้เกิดซ้ำไม่ได้ ตอบกลับหากเกิดขึ้นอีก',\n wont_fix: 'เราตัดสินใจไม่เปลี่ยนส่วนนี้',\n working_as_intended: 'นี่เป็นการทำงานตามที่ตั้งใจไว้',\n none: 'ปิดแล้ว',\n },\n others: {\n few: 'คุณและอีกไม่กี่คนรายงานเรื่องนี้',\n many: 'มีหลายคนรายงานเรื่องนี้',\n },\n timeline: {\n received: 'คุณรายงานเรื่องนี้',\n reviewing: 'นักพัฒนากำลังตรวจสอบ',\n duplicate_linked: 'เหมือนกับรายงานที่มีอยู่ — เราจะแจ้งความคืบหน้าที่นั่น',\n info_requested: 'นักพัฒนาถามว่า: {text}',\n reporter_comment: 'คุณ',\n fix_started: 'กำลังแก้ไข',\n fixed: 'แก้แล้ว — จะมาในอัปเดตถัดไป',\n released: 'เปิดใช้ใน v{version} — อัปเดตเพื่อรับการแก้ไข',\n verified: 'คุณยืนยันว่าแก้แล้ว',\n reopened: 'เปิดอีกครั้ง',\n },\n categories: {\n bug: 'บั๊ก',\n slow: 'ช้า',\n visual: 'แสดงผลผิด',\n confusing: 'สับสน',\n idea: 'ไอเดีย',\n },\n ui: {\n yes: 'ใช่',\n notYet: 'ยังไม่',\n empty: 'ยังไม่มี — รายงานของคุณจะแสดงที่นี่พร้อมความคืบหน้า',\n loadError: 'โหลดความคืบหน้าไม่ได้',\n retry: 'ลองอีกครั้ง',\n sending: 'กำลังส่ง…',\n sent: 'ส่งแล้ว',\n sendFailed: 'ส่งไม่สำเร็จ',\n noReplies: 'ยังไม่มีคำตอบจากนักพัฒนา',\n you: 'คุณ',\n developer: 'นักพัฒนา',\n addWords: 'เพิ่มรายละเอียดอีกเล็กน้อย',\n developerReplied: 'นักพัฒนาตอบว่า: “{text}”',\n updates: 'มีความคืบหน้า {n} รายการในรายงานของคุณ',\n receipt: 'ส่งแล้ว เราจะแจ้งคุณที่นี่เมื่อมีความคืบหน้า',\n trackIt: 'ติดตาม',\n done: 'เสร็จ',\n emailOptIn: 'รับความคืบหน้าทางอีเมล',\n emailPlaceholder: 'you@example.com',\n emailSubmit: 'ส่งความคืบหน้าทางอีเมล',\n emailCheckInbox: 'ตรวจสอบกล่องจดหมายแล้วแตะลิงก์เพื่อยืนยัน',\n emailInvalid: 'ดูเหมือนจะไม่ใช่อีเมลที่ถูกต้อง',\n emailFailed: 'ตั้งค่าการแจ้งทางอีเมลไม่สำเร็จ ลองใหม่ภายหลัง',\n toastReplied: 'นักพัฒนาตอบกลับรายงานของคุณแล้ว',\n toastFixed: 'บั๊กที่คุณรายงานได้รับการแก้ไขแล้ว',\n view: 'ดู',\n },\n};\n\nconst ZH: ReporterCopy = {\n status: {\n received: '已收到',\n reviewing: '处理中',\n reviewing_again: '重新查看中',\n fixing: '修复中',\n verified: '已确认修复',\n fixed_next: '已修复 — 下次更新上线',\n fixed_version: '已在 v{version} 修复',\n fixed: '已修复',\n closed: '已关闭',\n waiting: '等待你的回复',\n },\n detail: {\n received: '我们已收到你的反馈。',\n reviewing: '开发者已把它加入待处理列表。',\n reviewing_again: '谢谢,我们正在重新查看。',\n fixing: '正在修复。',\n verified: '感谢你确认修复!',\n fixed_next: '会在下一个版本中上线。',\n fixed_version: '请更新到 v{version}。问题解决了吗?',\n fixed: '问题解决了吗?',\n closed: '已关闭。',\n waiting: '开发者有问题想问你,请在下方回复。',\n },\n idea: {\n received: '谢谢你的想法',\n reviewing: '考虑中',\n fixing: '开发中',\n shipped: '已上线',\n shippedVersion: '已在 v{version} 上线',\n },\n closedReason: {\n duplicate: '和之前的一条反馈相同,我们会在那条里通知进展。',\n not_reproducible: '我们没能复现。如果再次发生,请回复。',\n wont_fix: '我们决定不修改这一点。',\n working_as_intended: '这是预期的行为。',\n none: '已关闭。',\n },\n others: {\n few: '包括你在内,有几个人反馈了这个问题',\n many: '很多人反馈了这个问题',\n },\n timeline: {\n received: '你提交了反馈',\n reviewing: '开发者正在查看',\n duplicate_linked: '和已有反馈相同,我们会在那条里通知进展',\n info_requested: '开发者的问题:{text}',\n reporter_comment: '你',\n fix_started: '正在修复',\n fixed: '已修复 — 下次更新上线',\n released: '已在 v{version} 发布 — 请更新',\n verified: '已确认修复',\n reopened: '已重新打开',\n },\n categories: {\n bug: '错误',\n slow: '慢',\n visual: '显示问题',\n confusing: '难以理解',\n idea: '想法',\n },\n ui: {\n yes: '是',\n notYet: '还没有',\n empty: '还没有反馈。提交后会在这里显示进展。',\n loadError: '无法加载更新',\n retry: '重试',\n sending: '发送中…',\n sent: '已发送',\n sendFailed: '发送失败',\n noReplies: '开发者还没有回复。',\n you: '你',\n developer: '开发者',\n addWords: '请再多写几个字',\n developerReplied: '开发者回复:“{text}”',\n updates: '你的反馈有 {n} 条更新',\n receipt: '已发送。有进展时会在这里通知你。',\n trackIt: '查看进展',\n done: '完成',\n emailOptIn: '通过邮件接收更新',\n emailPlaceholder: 'you@example.com',\n emailSubmit: '用邮件接收',\n emailCheckInbox: '请点击收件箱中邮件里的链接完成确认。',\n emailInvalid: '邮箱地址格式不正确。',\n emailFailed: '无法设置邮件通知,请稍后再试。',\n toastReplied: '开发者回复了你的反馈',\n toastFixed: '你反馈的问题已修复',\n view: '查看',\n },\n};\n\nconst COPY: Record<ReporterLocale, ReporterCopy> = { en: EN, ja: JA, es: ES, th: TH, zh: ZH };\n\n/** Resolve a BCP-47 tag (`ja-JP`, `es-419`) to a supported locale; English otherwise. */\nexport function resolveReporterLocale(tag: string | null | undefined): ReporterLocale {\n const base = (tag ?? '').toLowerCase().split(/[-_]/)[0];\n return (REPORTER_LOCALES as readonly string[]).includes(base) ? (base as ReporterLocale) : 'en';\n}\n\n/** The full copy table for a locale (unknown tags fall back to English). */\nexport function reporterCopy(locale: string = 'en'): ReporterCopy {\n return COPY[resolveReporterLocale(locale)];\n}\n\n/** Replace `{name}` placeholders; a missing value leaves an empty string. */\nexport function fillReporterTemplate(\n template: string,\n params: Record<string, string | number | null | undefined> = {},\n): string {\n return template.replace(/\\{(\\w+)\\}/g, (_, key: string) => {\n const value = params[key];\n return value === null || value === undefined ? '' : String(value);\n });\n}\n\n/** Fields of a reporter-list row `reporterStatus` reads. All but `status` are optional. */\nexport interface ReporterStatusInput {\n status: string;\n /** True (or a timestamp) while the developer waits for the reporter. */\n awaiting_reporter?: boolean | null;\n awaiting_reporter_at?: string | null;\n fixed_in_version?: string | null;\n closed_reason?: string | null;\n /** Bucketed duplicate count; never a raw number. */\n group_bucket?: 'none' | 'few' | 'many' | null;\n /** `user_category`; \"Feature request\" / \"idea\" selects the idea labels. */\n user_category?: string | null;\n}\n\nexport interface ReporterStatusView {\n key: ReporterStatusKey;\n label: string;\n tone: ReporterTone;\n detail: string;\n /** Show the Yes / Not yet verification buttons. */\n canVerify: boolean;\n /** \"You and a few others reported this\", or null. */\n othersNote: string | null;\n /** `closed_reason = 'spam'`: hide the report from the reporter entirely. */\n hidden: boolean;\n}\n\nconst OPEN_KEYS: ReadonlySet<ReporterStatusKey> = new Set([\n 'received',\n 'reviewing',\n 'reviewing_again',\n 'fixing',\n]);\n\nfunction isCanonical(status: string): status is ReporterCanonicalStatus {\n return (REPORTER_CANONICAL_STATUSES as readonly string[]).includes(status);\n}\n\n/** True when the row is a feature request rather than a bug. */\nexport function isReporterIdea(input: Pick<ReporterStatusInput, 'user_category'>): boolean {\n const cat = (input.user_category ?? '').trim().toLowerCase().replace(/[\\s-]+/g, '_');\n // The web widget and the ingest path send 'feature'; older clients sent\n // 'Feature request'. The server's isFeatureRequest() accepts the same set.\n return cat === 'feature' || cat === 'feature_request' || cat === 'idea';\n}\n\n/**\n * The end-user view of a report. Overlays are checked before the base\n * status: \"Waiting on you\" wins for any open report, and the duplicate-group\n * note is added on top of whatever the base state is.\n */\nexport function reporterStatus(input: ReporterStatusInput, locale: string = 'en'): ReporterStatusView {\n const copy = reporterCopy(locale);\n const status = (input.status ?? '').trim();\n // No raw passthrough: anything the table does not know reads as Received.\n let key: ReporterStatusKey = isCanonical(status) ? BASE_STATE[status] : 'received';\n\n const version = input.fixed_in_version?.trim() || null;\n if ((status === 'fixed' || status === 'resolved') && version) key = 'fixed_version';\n\n const awaiting = Boolean(input.awaiting_reporter) || Boolean(input.awaiting_reporter_at);\n if (awaiting && OPEN_KEYS.has(key)) key = 'waiting';\n\n const idea = isReporterIdea(input);\n let label = copy.status[key];\n let detail = copy.detail[key];\n if (idea) {\n if (key === 'received') label = copy.idea.received;\n else if (key === 'reviewing' || key === 'reviewing_again') label = copy.idea.reviewing;\n else if (key === 'fixing') label = copy.idea.fixing;\n else if (key === 'fixed_version') label = copy.idea.shippedVersion;\n else if (key === 'fixed_next' || key === 'fixed') label = copy.idea.shipped;\n }\n\n const reason = input.closed_reason ?? null;\n if (key === 'closed') {\n detail =\n reason && reason !== 'spam' && reason in copy.closedReason\n ? copy.closedReason[reason as keyof ReporterCopy['closedReason']]\n : copy.closedReason.none;\n }\n\n const bucket = input.group_bucket ?? 'none';\n const othersNote =\n OPEN_KEYS.has(key) || key === 'waiting'\n ? bucket === 'few'\n ? copy.others.few\n : bucket === 'many'\n ? copy.others.many\n : null\n : null;\n\n return {\n key,\n label: fillReporterTemplate(label, { version }),\n tone: TONE[key],\n detail: fillReporterTemplate(detail, { version }),\n canVerify: key === 'fixed_next' || key === 'fixed_version' || key === 'fixed',\n othersNote,\n hidden: key === 'closed' && reason === 'spam',\n };\n}\n\n/** Parameters a timeline template may interpolate. */\nexport interface ReporterTimelineParams {\n /** Verbatim developer text for `comment` and `info_requested`; the reporter's own for `reporter_comment`. */\n text?: string | null;\n version?: string | null;\n closed_reason?: string | null;\n}\n\n/**\n * Render one timeline entry. Pipeline kinds always come from the template for\n * their kind; only the developer's own words (`comment`, `info_requested`) are\n * passed through, verbatim.\n */\nexport function reporterTimelineText(\n kind: ReporterTimelineKind,\n params: ReporterTimelineParams = {},\n locale: string = 'en',\n): string {\n const copy = reporterCopy(locale);\n if (kind === 'comment') return params.text ?? '';\n if (kind === 'closed') {\n const reason = params.closed_reason ?? '';\n return reason && reason !== 'spam' && reason in copy.closedReason\n ? copy.closedReason[reason as keyof ReporterCopy['closedReason']]\n : copy.closedReason.none;\n }\n if (kind === 'reporter_comment') return params.text ?? copy.timeline.reporter_comment;\n return fillReporterTemplate(copy.timeline[kind], { text: params.text, version: params.version });\n}\n\n/** Reporter-facing label for a category chip. */\nexport function reporterCategoryLabel(category: ReporterCategory, locale: string = 'en'): string {\n return reporterCopy(locale).categories[category];\n}\n\n// ---------------------------------------------------------------------------\n// Widget rules shared by web and React Native (Plan 018 §1.1, §2.3, §4.2)\n// ---------------------------------------------------------------------------\n\n/** A few words are enough; an attachment speaks for itself (§1.1). */\nexport const REPORTER_MIN_DESCRIPTION = 8;\n\n/** Characters Send needs: `min` (default 8), or 0 with a screenshot / element attached. */\nexport function reporterRequiredLength(min: number | undefined, hasAttachment: boolean): number {\n if (hasAttachment) return 0;\n return typeof min === 'number' && Number.isFinite(min) && min >= 0 ? Math.floor(min) : REPORTER_MIN_DESCRIPTION;\n}\n\n/** Whether Send is enabled: some text or an attachment, and enough words. */\nexport function reporterCanSend(text: string, hasAttachment: boolean, min?: number): boolean {\n const trimmed = text.trim();\n if (!trimmed && !hasAttachment) return false;\n return trimmed.length >= reporterRequiredLength(min, hasAttachment);\n}\n\n/** Wire category for an optional chip: none → `other` (triage sets the type); Idea → a feature request. */\nexport function reporterChipToReport(\n chip: ReporterCategory | null,\n): { category: 'bug' | 'slow' | 'visual' | 'confusing' | 'other'; userCategory?: string } {\n if (!chip) return { category: 'other' };\n if (chip === 'idea') return { category: 'other', userCategory: 'feature' };\n return { category: chip };\n}\n\n/** One entry of `GET /v1/reporter/reports/:id` → `timeline` (subset the widgets read). */\nexport interface ReporterTimelineEntry {\n kind: string;\n text: string;\n /** Developer- or project-written text: show `text` as is. */\n custom?: boolean;\n body?: string;\n version?: string;\n closed_reason?: string;\n}\n\n/** True for the people talking (developer / reporter), false for pipeline events. */\nexport function isReporterConversation(entry: Pick<ReporterTimelineEntry, 'kind'>): boolean {\n return entry.kind === 'comment' || entry.kind === 'reporter_comment' || entry.kind === 'info_requested';\n}\n\n/** Text for one timeline entry in the reporter's locale; custom wording is shown as is. */\nexport function reporterTimelineEntryText(entry: ReporterTimelineEntry, locale: string = 'en'): string {\n if (entry.custom || !(REPORTER_TIMELINE_KINDS as readonly string[]).includes(entry.kind)) return entry.text;\n return reporterTimelineText(\n entry.kind as ReporterTimelineKind,\n { text: entry.body ?? null, version: entry.version ?? null, closed_reason: entry.closed_reason ?? null },\n locale,\n );\n}\n\nexport const REPORTER_TOAST_INTERVAL_MS = 24 * 60 * 60 * 1000;\n\n/** The next-visit toast: one per session and at most one a day, only with something unread (§4.2). */\nexport function reporterShouldShowToast(input: {\n enabled: boolean;\n unreadTotal: number;\n shownThisSession: boolean;\n lastShownAt: number | null;\n now: number;\n}): boolean {\n if (!input.enabled || input.unreadTotal <= 0 || input.shownThisSession) return false;\n return input.lastShownAt === null || input.now - input.lastShownAt >= REPORTER_TOAST_INTERVAL_MS;\n}\n\n/** \"The developer replied…\", \"Your bug is fixed\", or \"3 updates on your reports\". */\nexport function reporterToastMessage(\n updates: { unread_total: number; latest: Array<{ kind: string | null }> },\n locale: string = 'en',\n): string {\n const ui = reporterCopy(locale).ui;\n const kind = updates.unread_total === 1 ? updates.latest[0]?.kind : null;\n if (kind === 'comment' || kind === 'info_requested') return ui.toastReplied;\n if (kind === 'fixed' || kind === 'released') return ui.toastFixed;\n return ui.updates.replace('{n}', String(updates.unread_total));\n}\n\n/** A typo screen for the email opt-in box; the server validates the address. */\nexport function isPlausibleReporterEmail(raw: string): boolean {\n return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]{2,}$/.test(raw.trim());\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/reporter-ui.ts"],"names":[],"mappings":"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","file":"reporter-ui.cjs","sourcesContent":["// SPDX-License-Identifier: MIT\n// Copyright (c) 2024–2026 kensaurus LLC — Mushi Mushi\n/**\n * FILE: reporter-ui.ts\n * PURPOSE: The one reporter-facing vocabulary shared by every SDK widget\n * (web, React Native, Capacitor): report categories, the end-user\n * status table, the timeline kinds and their copy in en/ja/es/th.\n *\n * OVERVIEW:\n * - `reporterStatus(report, locale)` maps all 14 internal `reports.status`\n * values (plus the `awaiting_reporter` and duplicate-group overlays and the\n * feature-request variants) onto eight end-user states. There is no branch\n * that echoes the raw status: an unknown value reads as \"Received\".\n * - `reporterTimelineText(kind, params, locale)` renders pipeline events from\n * a fixed template per kind. Stored notification payload text is never\n * shown, so internal category / severity can not leak to a reporter.\n *\n * USAGE:\n * - `import { reporterStatus } from '@mushi-mushi/core/reporter-ui'`\n * - Shipped as a subpath so the main `@mushi-mushi/core` entry stays within\n * its size budget; nothing here is re-exported from `index.ts`.\n *\n * Plan 018 (docs/execplans/reporter-loop-v2.md) §2.1 and §2.3.\n */\n\n/** Every value `reports_status_check` allows. Pinned to the server list by a test. */\nexport const REPORTER_CANONICAL_STATUSES = [\n 'new',\n 'pending',\n 'submitted',\n 'queued',\n 'classified',\n 'grouped',\n 'fixing',\n 'fixed',\n 'dismissed',\n 'triaged',\n 'in_progress',\n 'resolved',\n 'verified',\n 'reopened',\n] as const;\n\nexport type ReporterCanonicalStatus = (typeof REPORTER_CANONICAL_STATUSES)[number];\n\nexport const REPORTER_LOCALES = ['en', 'ja', 'es', 'th', 'zh'] as const;\nexport type ReporterLocale = (typeof REPORTER_LOCALES)[number];\n\n/** Optional type chips on the one-screen report form, in display order. */\nexport const REPORTER_CATEGORIES = ['bug', 'slow', 'visual', 'confusing', 'idea'] as const;\nexport type ReporterCategory = (typeof REPORTER_CATEGORIES)[number];\n\n/** `closed_reason` values the `reports_closed_reason_check` constraint allows. */\nexport const REPORTER_CLOSED_REASONS = [\n 'duplicate',\n 'not_reproducible',\n 'wont_fix',\n 'working_as_intended',\n 'spam',\n] as const;\nexport type ReporterClosedReason = (typeof REPORTER_CLOSED_REASONS)[number];\n\nexport type ReporterTone =\n | 'neutral'\n | 'info'\n | 'attention'\n | 'progress'\n | 'success'\n | 'success-muted'\n | 'muted';\n\n/** The end-user state a report is shown in. */\nexport type ReporterStatusKey =\n | 'received'\n | 'reviewing'\n | 'reviewing_again'\n | 'fixing'\n | 'verified'\n | 'fixed_next'\n | 'fixed_version'\n | 'fixed'\n | 'closed'\n | 'waiting';\n\n/** Base state per internal status, before overlays. Exhaustive by type. */\nconst BASE_STATE: Record<ReporterCanonicalStatus, ReporterStatusKey> = {\n new: 'received',\n pending: 'received',\n submitted: 'received',\n queued: 'received',\n classified: 'reviewing',\n triaged: 'reviewing',\n grouped: 'reviewing',\n reopened: 'reviewing_again',\n fixing: 'fixing',\n in_progress: 'fixing',\n verified: 'verified',\n fixed: 'fixed_next',\n resolved: 'fixed',\n dismissed: 'closed',\n};\n\nconst TONE: Record<ReporterStatusKey, ReporterTone> = {\n received: 'neutral',\n reviewing: 'info',\n reviewing_again: 'info',\n fixing: 'progress',\n verified: 'success-muted',\n fixed_next: 'success',\n fixed_version: 'success',\n fixed: 'success',\n closed: 'muted',\n waiting: 'attention',\n};\n\n/** Timeline entry kinds the reporter detail view renders (§2.3). */\nexport const REPORTER_TIMELINE_KINDS = [\n 'received',\n 'reviewing',\n 'duplicate_linked',\n 'info_requested',\n 'comment',\n 'reporter_comment',\n 'fix_started',\n 'fixed',\n 'released',\n 'verified',\n 'reopened',\n 'closed',\n] as const;\nexport type ReporterTimelineKind = (typeof REPORTER_TIMELINE_KINDS)[number];\n\nexport interface ReporterCopy {\n status: Record<ReporterStatusKey, string>;\n detail: Record<ReporterStatusKey, string>;\n idea: { received: string; reviewing: string; fixing: string; shipped: string; shippedVersion: string };\n closedReason: Record<Exclude<ReporterClosedReason, 'spam'>, string> & { none: string };\n others: { few: string; many: string };\n timeline: Record<Exclude<ReporterTimelineKind, 'comment' | 'closed'>, string>;\n categories: Record<ReporterCategory, string>;\n ui: {\n yes: string;\n notYet: string;\n empty: string;\n loadError: string;\n retry: string;\n sending: string;\n sent: string;\n sendFailed: string;\n noReplies: string;\n you: string;\n developer: string;\n addWords: string;\n developerReplied: string;\n updates: string;\n /** Receipt after sending (replaces the form in place). */\n receipt: string;\n trackIt: string;\n done: string;\n /** Email opt-in on the receipt / report detail (never pre-ticked). */\n emailOptIn: string;\n emailPlaceholder: string;\n emailSubmit: string;\n emailCheckInbox: string;\n emailInvalid: string;\n emailFailed: string;\n /** Next-visit toast. */\n toastReplied: string;\n toastFixed: string;\n view: string;\n };\n}\n\nconst EN: ReporterCopy = {\n status: {\n received: 'Received',\n reviewing: 'Looking into it',\n reviewing_again: 'Looking into it again',\n fixing: 'Fix in progress',\n verified: 'Confirmed fixed',\n fixed_next: 'Fixed — coming in the next update',\n fixed_version: 'Fixed in v{version}',\n fixed: 'Fixed',\n closed: 'Closed',\n waiting: 'Waiting on you',\n },\n detail: {\n received: 'We got it.',\n reviewing: 'The developer has it on their list.',\n reviewing_again: 'Thanks — we reopened it.',\n fixing: 'A fix is being worked on.',\n verified: 'You confirmed the fix. Thank you!',\n fixed_next: 'It ships in the next release.',\n fixed_version: 'Update to v{version}. Does it work for you now?',\n fixed: 'Does it work for you now?',\n closed: 'Closed.',\n waiting: 'The developer asked a question — reply below.',\n },\n idea: {\n received: 'Thanks for the idea',\n reviewing: 'Under consideration',\n fixing: 'Being built',\n shipped: 'Shipped',\n shippedVersion: 'Shipped in v{version}',\n },\n closedReason: {\n duplicate: \"Same as an earlier report — we'll update you there.\",\n not_reproducible: \"We couldn't reproduce it. Reply if it happens again.\",\n wont_fix: 'We decided not to change this.',\n working_as_intended: 'This is expected behaviour.',\n none: 'Closed.',\n },\n others: {\n few: 'You and a few others reported this',\n many: 'Many people reported this',\n },\n timeline: {\n received: 'You reported this',\n reviewing: 'The developer is looking into it',\n duplicate_linked: \"Same as an existing report — we'll update you there\",\n info_requested: 'The developer asked: {text}',\n reporter_comment: 'You',\n fix_started: 'A fix is in progress',\n fixed: 'Fixed — coming in the next update',\n released: 'Shipped in v{version} — update to get it',\n verified: \"You confirmed it's fixed\",\n reopened: 'Reopened',\n },\n categories: {\n bug: 'Bug',\n slow: 'Slow',\n visual: 'Looks wrong',\n confusing: 'Confusing',\n idea: 'Idea',\n },\n ui: {\n yes: 'Yes',\n notYet: 'Not yet',\n empty: 'Nothing yet — your reports will show up here with updates.',\n loadError: \"Couldn't load updates\",\n retry: 'Retry',\n sending: 'Sending…',\n sent: 'Sent',\n sendFailed: 'Failed',\n noReplies: 'No developer replies yet.',\n you: 'You',\n developer: 'Developer',\n addWords: 'Add a few words',\n developerReplied: 'Developer replied: “{text}”',\n updates: '{n} updates on your reports',\n receipt: \"Sent. We'll let you know here when there's news.\",\n trackIt: 'Track it',\n done: 'Done',\n emailOptIn: 'Get updates by email',\n emailPlaceholder: 'you@example.com',\n emailSubmit: 'Email me updates',\n emailCheckInbox: 'Check your inbox and tap the link to confirm.',\n emailInvalid: 'That does not look like an email address.',\n emailFailed: \"Couldn't set up email updates. Try again later.\",\n toastReplied: 'The developer replied to your report',\n toastFixed: 'Your bug is fixed',\n view: 'View',\n },\n};\n\nconst JA: ReporterCopy = {\n status: {\n received: '受け付けました',\n reviewing: '確認中',\n reviewing_again: '再確認中',\n fixing: '修正中',\n verified: '修正を確認済み',\n fixed_next: '修正済み — 次のアップデートで反映',\n fixed_version: 'v{version} で修正済み',\n fixed: '修正済み',\n closed: '終了',\n waiting: 'あなたの返信待ち',\n },\n detail: {\n received: '報告を受け取りました。',\n reviewing: '開発者が対応リストに入れました。',\n reviewing_again: 'ありがとうございます。再度確認しています。',\n fixing: '修正に取り組んでいます。',\n verified: '修正を確認いただきありがとうございます!',\n fixed_next: '次のリリースで反映されます。',\n fixed_version: 'v{version} にアップデートしてください。解決しましたか?',\n fixed: '解決しましたか?',\n closed: '終了しました。',\n waiting: '開発者から質問があります。下から返信してください。',\n },\n idea: {\n received: 'アイデアをありがとうございます',\n reviewing: '検討中',\n fixing: '開発中',\n shipped: 'リリース済み',\n shippedVersion: 'v{version} でリリース済み',\n },\n closedReason: {\n duplicate: '以前の報告と同じ内容です。そちらで進捗をお知らせします。',\n not_reproducible: '再現できませんでした。再発したら返信してください。',\n wont_fix: 'この点は変更しないことにしました。',\n working_as_intended: 'これは想定どおりの動作です。',\n none: '終了しました。',\n },\n others: {\n few: 'あなたを含め数人が報告しています',\n many: '多くの人が報告しています',\n },\n timeline: {\n received: 'あなたが報告しました',\n reviewing: '開発者が確認しています',\n duplicate_linked: '既存の報告と同じ内容です。そちらで進捗をお知らせします',\n info_requested: '開発者からの質問: {text}',\n reporter_comment: 'あなた',\n fix_started: '修正中です',\n fixed: '修正済み — 次のアップデートで反映',\n released: 'v{version} でリリース — アップデートしてください',\n verified: '修正を確認しました',\n reopened: '再オープンしました',\n },\n categories: {\n bug: 'バグ',\n slow: '遅い',\n visual: '表示がおかしい',\n confusing: 'わかりにくい',\n idea: 'アイデア',\n },\n ui: {\n yes: 'はい',\n notYet: 'まだ',\n empty: 'まだありません。報告するとここに進捗が表示されます。',\n loadError: '更新を読み込めませんでした',\n retry: '再試行',\n sending: '送信中…',\n sent: '送信しました',\n sendFailed: '送信失敗',\n noReplies: '開発者からの返信はまだありません。',\n you: 'あなた',\n developer: '開発者',\n addWords: 'もう少し詳しく書いてください',\n developerReplied: '開発者からの返信:「{text}」',\n updates: 'あなたの報告に {n} 件の更新があります',\n receipt: '送信しました。進展があればここでお知らせします。',\n trackIt: '経過を見る',\n done: '閉じる',\n emailOptIn: 'メールで更新を受け取る',\n emailPlaceholder: 'you@example.com',\n emailSubmit: 'メールで受け取る',\n emailCheckInbox: '受信トレイのメールのリンクをタップして確認してください。',\n emailInvalid: 'メールアドレスの形式が正しくありません。',\n emailFailed: 'メール通知を設定できませんでした。後でもう一度お試しください。',\n toastReplied: '開発者があなたの報告に返信しました',\n toastFixed: 'あなたが報告した不具合が修正されました',\n view: '見る',\n },\n};\n\nconst ES: ReporterCopy = {\n status: {\n received: 'Recibido',\n reviewing: 'Lo estamos revisando',\n reviewing_again: 'Lo revisamos de nuevo',\n fixing: 'Corrección en curso',\n verified: 'Corrección confirmada',\n fixed_next: 'Corregido — llega en la próxima actualización',\n fixed_version: 'Corregido en v{version}',\n fixed: 'Corregido',\n closed: 'Cerrado',\n waiting: 'Esperando tu respuesta',\n },\n detail: {\n received: 'Lo recibimos.',\n reviewing: 'El desarrollador lo tiene en su lista.',\n reviewing_again: 'Gracias, lo reabrimos.',\n fixing: 'Se está trabajando en una corrección.',\n verified: 'Confirmaste la corrección. ¡Gracias!',\n fixed_next: 'Llega en la próxima versión.',\n fixed_version: 'Actualiza a v{version}. ¿Ya funciona?',\n fixed: '¿Ya funciona?',\n closed: 'Cerrado.',\n waiting: 'El desarrollador hizo una pregunta: responde abajo.',\n },\n idea: {\n received: 'Gracias por la idea',\n reviewing: 'En consideración',\n fixing: 'En desarrollo',\n shipped: 'Publicado',\n shippedVersion: 'Publicado en v{version}',\n },\n closedReason: {\n duplicate: 'Es igual a un reporte anterior; te avisaremos allí.',\n not_reproducible: 'No pudimos reproducirlo. Responde si vuelve a pasar.',\n wont_fix: 'Decidimos no cambiar esto.',\n working_as_intended: 'Este es el comportamiento esperado.',\n none: 'Cerrado.',\n },\n others: {\n few: 'Tú y algunas personas más reportaron esto',\n many: 'Muchas personas reportaron esto',\n },\n timeline: {\n received: 'Reportaste esto',\n reviewing: 'El desarrollador lo está revisando',\n duplicate_linked: 'Es igual a un reporte existente; te avisaremos allí',\n info_requested: 'El desarrollador preguntó: {text}',\n reporter_comment: 'Tú',\n fix_started: 'Hay una corrección en curso',\n fixed: 'Corregido — llega en la próxima actualización',\n released: 'Publicado en v{version}: actualiza para tenerlo',\n verified: 'Confirmaste que está corregido',\n reopened: 'Reabierto',\n },\n categories: {\n bug: 'Error',\n slow: 'Lento',\n visual: 'Se ve mal',\n confusing: 'Confuso',\n idea: 'Idea',\n },\n ui: {\n yes: 'Sí',\n notYet: 'Todavía no',\n empty: 'Nada todavía: tus reportes aparecerán aquí con novedades.',\n loadError: 'No se pudieron cargar las novedades',\n retry: 'Reintentar',\n sending: 'Enviando…',\n sent: 'Enviado',\n sendFailed: 'Error al enviar',\n noReplies: 'El desarrollador aún no ha respondido.',\n you: 'Tú',\n developer: 'Desarrollador',\n addWords: 'Agrega algunas palabras',\n developerReplied: 'El desarrollador respondió: “{text}”',\n updates: '{n} novedades en tus reportes',\n receipt: 'Enviado. Te avisaremos aquí cuando haya novedades.',\n trackIt: 'Seguirlo',\n done: 'Listo',\n emailOptIn: 'Recibir novedades por correo',\n emailPlaceholder: 'tu@ejemplo.com',\n emailSubmit: 'Enviarme novedades',\n emailCheckInbox: 'Revisa tu correo y toca el enlace para confirmar.',\n emailInvalid: 'Eso no parece un correo electrónico.',\n emailFailed: 'No pudimos activar las novedades por correo. Inténtalo más tarde.',\n toastReplied: 'El desarrollador respondió a tu reporte',\n toastFixed: 'Tu error está corregido',\n view: 'Ver',\n },\n};\n\nconst TH: ReporterCopy = {\n status: {\n received: 'ได้รับแล้ว',\n reviewing: 'กำลังตรวจสอบ',\n reviewing_again: 'กำลังตรวจสอบอีกครั้ง',\n fixing: 'กำลังแก้ไข',\n verified: 'ยืนยันว่าแก้แล้ว',\n fixed_next: 'แก้แล้ว — จะมาในอัปเดตถัดไป',\n fixed_version: 'แก้แล้วใน v{version}',\n fixed: 'แก้แล้ว',\n closed: 'ปิดแล้ว',\n waiting: 'รอคำตอบจากคุณ',\n },\n detail: {\n received: 'เราได้รับรายงานแล้ว',\n reviewing: 'นักพัฒนาใส่ไว้ในรายการแล้ว',\n reviewing_again: 'ขอบคุณ — เราเปิดเรื่องนี้อีกครั้งแล้ว',\n fixing: 'กำลังดำเนินการแก้ไข',\n verified: 'คุณยืนยันการแก้ไขแล้ว ขอบคุณ!',\n fixed_next: 'จะมาในรุ่นถัดไป',\n fixed_version: 'อัปเดตเป็น v{version} ตอนนี้ใช้ได้หรือยัง?',\n fixed: 'ตอนนี้ใช้ได้หรือยัง?',\n closed: 'ปิดแล้ว',\n waiting: 'นักพัฒนามีคำถาม — ตอบด้านล่าง',\n },\n idea: {\n received: 'ขอบคุณสำหรับไอเดีย',\n reviewing: 'กำลังพิจารณา',\n fixing: 'กำลังสร้าง',\n shipped: 'เปิดใช้แล้ว',\n shippedVersion: 'เปิดใช้แล้วใน v{version}',\n },\n closedReason: {\n duplicate: 'เหมือนกับรายงานก่อนหน้า — เราจะแจ้งความคืบหน้าที่นั่น',\n not_reproducible: 'เราทำให้เกิดซ้ำไม่ได้ ตอบกลับหากเกิดขึ้นอีก',\n wont_fix: 'เราตัดสินใจไม่เปลี่ยนส่วนนี้',\n working_as_intended: 'นี่เป็นการทำงานตามที่ตั้งใจไว้',\n none: 'ปิดแล้ว',\n },\n others: {\n few: 'คุณและอีกไม่กี่คนรายงานเรื่องนี้',\n many: 'มีหลายคนรายงานเรื่องนี้',\n },\n timeline: {\n received: 'คุณรายงานเรื่องนี้',\n reviewing: 'นักพัฒนากำลังตรวจสอบ',\n duplicate_linked: 'เหมือนกับรายงานที่มีอยู่ — เราจะแจ้งความคืบหน้าที่นั่น',\n info_requested: 'นักพัฒนาถามว่า: {text}',\n reporter_comment: 'คุณ',\n fix_started: 'กำลังแก้ไข',\n fixed: 'แก้แล้ว — จะมาในอัปเดตถัดไป',\n released: 'เปิดใช้ใน v{version} — อัปเดตเพื่อรับการแก้ไข',\n verified: 'คุณยืนยันว่าแก้แล้ว',\n reopened: 'เปิดอีกครั้ง',\n },\n categories: {\n bug: 'บั๊ก',\n slow: 'ช้า',\n visual: 'แสดงผลผิด',\n confusing: 'สับสน',\n idea: 'ไอเดีย',\n },\n ui: {\n yes: 'ใช่',\n notYet: 'ยังไม่',\n empty: 'ยังไม่มี — รายงานของคุณจะแสดงที่นี่พร้อมความคืบหน้า',\n loadError: 'โหลดความคืบหน้าไม่ได้',\n retry: 'ลองอีกครั้ง',\n sending: 'กำลังส่ง…',\n sent: 'ส่งแล้ว',\n sendFailed: 'ส่งไม่สำเร็จ',\n noReplies: 'ยังไม่มีคำตอบจากนักพัฒนา',\n you: 'คุณ',\n developer: 'นักพัฒนา',\n addWords: 'เพิ่มรายละเอียดอีกเล็กน้อย',\n developerReplied: 'นักพัฒนาตอบว่า: “{text}”',\n updates: 'มีความคืบหน้า {n} รายการในรายงานของคุณ',\n receipt: 'ส่งแล้ว เราจะแจ้งคุณที่นี่เมื่อมีความคืบหน้า',\n trackIt: 'ติดตาม',\n done: 'เสร็จ',\n emailOptIn: 'รับความคืบหน้าทางอีเมล',\n emailPlaceholder: 'you@example.com',\n emailSubmit: 'ส่งความคืบหน้าทางอีเมล',\n emailCheckInbox: 'ตรวจสอบกล่องจดหมายแล้วแตะลิงก์เพื่อยืนยัน',\n emailInvalid: 'ดูเหมือนจะไม่ใช่อีเมลที่ถูกต้อง',\n emailFailed: 'ตั้งค่าการแจ้งทางอีเมลไม่สำเร็จ ลองใหม่ภายหลัง',\n toastReplied: 'นักพัฒนาตอบกลับรายงานของคุณแล้ว',\n toastFixed: 'บั๊กที่คุณรายงานได้รับการแก้ไขแล้ว',\n view: 'ดู',\n },\n};\n\nconst ZH: ReporterCopy = {\n status: {\n received: '已收到',\n reviewing: '处理中',\n reviewing_again: '重新查看中',\n fixing: '修复中',\n verified: '已确认修复',\n fixed_next: '已修复 — 下次更新上线',\n fixed_version: '已在 v{version} 修复',\n fixed: '已修复',\n closed: '已关闭',\n waiting: '等待你的回复',\n },\n detail: {\n received: '我们已收到你的反馈。',\n reviewing: '开发者已把它加入待处理列表。',\n reviewing_again: '谢谢,我们正在重新查看。',\n fixing: '正在修复。',\n verified: '感谢你确认修复!',\n fixed_next: '会在下一个版本中上线。',\n fixed_version: '请更新到 v{version}。问题解决了吗?',\n fixed: '问题解决了吗?',\n closed: '已关闭。',\n waiting: '开发者有问题想问你,请在下方回复。',\n },\n idea: {\n received: '谢谢你的想法',\n reviewing: '考虑中',\n fixing: '开发中',\n shipped: '已上线',\n shippedVersion: '已在 v{version} 上线',\n },\n closedReason: {\n duplicate: '和之前的一条反馈相同,我们会在那条里通知进展。',\n not_reproducible: '我们没能复现。如果再次发生,请回复。',\n wont_fix: '我们决定不修改这一点。',\n working_as_intended: '这是预期的行为。',\n none: '已关闭。',\n },\n others: {\n few: '包括你在内,有几个人反馈了这个问题',\n many: '很多人反馈了这个问题',\n },\n timeline: {\n received: '你提交了反馈',\n reviewing: '开发者正在查看',\n duplicate_linked: '和已有反馈相同,我们会在那条里通知进展',\n info_requested: '开发者的问题:{text}',\n reporter_comment: '你',\n fix_started: '正在修复',\n fixed: '已修复 — 下次更新上线',\n released: '已在 v{version} 发布 — 请更新',\n verified: '已确认修复',\n reopened: '已重新打开',\n },\n categories: {\n bug: '错误',\n slow: '慢',\n visual: '显示问题',\n confusing: '难以理解',\n idea: '想法',\n },\n ui: {\n yes: '是',\n notYet: '还没有',\n empty: '还没有反馈。提交后会在这里显示进展。',\n loadError: '无法加载更新',\n retry: '重试',\n sending: '发送中…',\n sent: '已发送',\n sendFailed: '发送失败',\n noReplies: '开发者还没有回复。',\n you: '你',\n developer: '开发者',\n addWords: '请再多写几个字',\n developerReplied: '开发者回复:“{text}”',\n updates: '你的反馈有 {n} 条更新',\n receipt: '已发送。有进展时会在这里通知你。',\n trackIt: '查看进展',\n done: '完成',\n emailOptIn: '通过邮件接收更新',\n emailPlaceholder: 'you@example.com',\n emailSubmit: '用邮件接收',\n emailCheckInbox: '请点击收件箱中邮件里的链接完成确认。',\n emailInvalid: '邮箱地址格式不正确。',\n emailFailed: '无法设置邮件通知,请稍后再试。',\n toastReplied: '开发者回复了你的反馈',\n toastFixed: '你反馈的问题已修复',\n view: '查看',\n },\n};\n\nconst COPY: Record<ReporterLocale, ReporterCopy> = { en: EN, ja: JA, es: ES, th: TH, zh: ZH };\n\n/** Resolve a BCP-47 tag (`ja-JP`, `es-419`) to a supported locale; English otherwise. */\nexport function resolveReporterLocale(tag: string | null | undefined): ReporterLocale {\n const base = (tag ?? '').toLowerCase().split(/[-_]/)[0];\n return (REPORTER_LOCALES as readonly string[]).includes(base) ? (base as ReporterLocale) : 'en';\n}\n\n/** The full copy table for a locale (unknown tags fall back to English). */\nexport function reporterCopy(locale: string = 'en'): ReporterCopy {\n return COPY[resolveReporterLocale(locale)];\n}\n\n/** Replace `{name}` placeholders; a missing value leaves an empty string. */\nexport function fillReporterTemplate(\n template: string,\n params: Record<string, string | number | null | undefined> = {},\n): string {\n return template.replace(/\\{(\\w+)\\}/g, (_, key: string) => {\n const value = params[key];\n return value === null || value === undefined ? '' : String(value);\n });\n}\n\n/** Fields of a reporter-list row `reporterStatus` reads. All but `status` are optional. */\nexport interface ReporterStatusInput {\n status: string;\n /** True (or a timestamp) while the developer waits for the reporter. */\n awaiting_reporter?: boolean | null;\n awaiting_reporter_at?: string | null;\n fixed_in_version?: string | null;\n closed_reason?: string | null;\n /** Bucketed duplicate count; never a raw number. */\n group_bucket?: 'none' | 'few' | 'many' | null;\n /** `user_category`; \"Feature request\" / \"idea\" selects the idea labels. */\n user_category?: string | null;\n}\n\nexport interface ReporterStatusView {\n key: ReporterStatusKey;\n label: string;\n tone: ReporterTone;\n detail: string;\n /** Show the Yes / Not yet verification buttons. */\n canVerify: boolean;\n /** \"You and a few others reported this\", or null. */\n othersNote: string | null;\n /** `closed_reason = 'spam'`: hide the report from the reporter entirely. */\n hidden: boolean;\n}\n\nconst OPEN_KEYS: ReadonlySet<ReporterStatusKey> = new Set([\n 'received',\n 'reviewing',\n 'reviewing_again',\n 'fixing',\n]);\n\nfunction isCanonical(status: string): status is ReporterCanonicalStatus {\n return (REPORTER_CANONICAL_STATUSES as readonly string[]).includes(status);\n}\n\n/** True when the row is a feature request rather than a bug. */\nexport function isReporterIdea(input: Pick<ReporterStatusInput, 'user_category'>): boolean {\n const cat = (input.user_category ?? '').trim().toLowerCase().replace(/[\\s-]+/g, '_');\n // The web widget and the ingest path send 'feature'; older clients sent\n // 'Feature request'. The server's isFeatureRequest() accepts the same set.\n return cat === 'feature' || cat === 'feature_request' || cat === 'idea';\n}\n\n/**\n * The end-user view of a report. Overlays are checked before the base\n * status: \"Waiting on you\" wins for any open report, and the duplicate-group\n * note is added on top of whatever the base state is.\n */\nexport function reporterStatus(input: ReporterStatusInput, locale: string = 'en'): ReporterStatusView {\n const copy = reporterCopy(locale);\n const status = (input.status ?? '').trim();\n // No raw passthrough: anything the table does not know reads as Received.\n let key: ReporterStatusKey = isCanonical(status) ? BASE_STATE[status] : 'received';\n\n const version = input.fixed_in_version?.trim() || null;\n if ((status === 'fixed' || status === 'resolved') && version) key = 'fixed_version';\n\n const awaiting = Boolean(input.awaiting_reporter) || Boolean(input.awaiting_reporter_at);\n if (awaiting && OPEN_KEYS.has(key)) key = 'waiting';\n\n const idea = isReporterIdea(input);\n let label = copy.status[key];\n let detail = copy.detail[key];\n if (idea) {\n if (key === 'received') label = copy.idea.received;\n else if (key === 'reviewing' || key === 'reviewing_again') label = copy.idea.reviewing;\n else if (key === 'fixing') label = copy.idea.fixing;\n else if (key === 'fixed_version') label = copy.idea.shippedVersion;\n else if (key === 'fixed_next' || key === 'fixed') label = copy.idea.shipped;\n }\n\n const reason = input.closed_reason ?? null;\n if (key === 'closed') {\n detail =\n reason && reason !== 'spam' && reason in copy.closedReason\n ? copy.closedReason[reason as keyof ReporterCopy['closedReason']]\n : copy.closedReason.none;\n }\n\n const bucket = input.group_bucket ?? 'none';\n const othersNote =\n OPEN_KEYS.has(key) || key === 'waiting'\n ? bucket === 'few'\n ? copy.others.few\n : bucket === 'many'\n ? copy.others.many\n : null\n : null;\n\n return {\n key,\n label: fillReporterTemplate(label, { version }),\n tone: TONE[key],\n detail: fillReporterTemplate(detail, { version }),\n canVerify: key === 'fixed_next' || key === 'fixed_version' || key === 'fixed',\n othersNote,\n hidden: key === 'closed' && reason === 'spam',\n };\n}\n\n/** Parameters a timeline template may interpolate. */\nexport interface ReporterTimelineParams {\n /** Verbatim developer text for `comment` and `info_requested`; the reporter's own for `reporter_comment`. */\n text?: string | null;\n version?: string | null;\n closed_reason?: string | null;\n}\n\n/**\n * Render one timeline entry. Pipeline kinds always come from the template for\n * their kind; only the developer's own words (`comment`, `info_requested`) are\n * passed through, verbatim.\n */\nexport function reporterTimelineText(\n kind: ReporterTimelineKind,\n params: ReporterTimelineParams = {},\n locale: string = 'en',\n): string {\n const copy = reporterCopy(locale);\n if (kind === 'comment') return params.text ?? '';\n if (kind === 'closed') {\n const reason = params.closed_reason ?? '';\n return reason && reason !== 'spam' && reason in copy.closedReason\n ? copy.closedReason[reason as keyof ReporterCopy['closedReason']]\n : copy.closedReason.none;\n }\n if (kind === 'reporter_comment') return params.text ?? copy.timeline.reporter_comment;\n return fillReporterTemplate(copy.timeline[kind], { text: params.text, version: params.version });\n}\n\n/** Reporter-facing label for a category chip. */\nexport function reporterCategoryLabel(category: ReporterCategory, locale: string = 'en'): string {\n return reporterCopy(locale).categories[category];\n}\n\n// ---------------------------------------------------------------------------\n// Widget rules shared by web and React Native (Plan 018 §1.1, §2.3, §4.2)\n// ---------------------------------------------------------------------------\n\n/** A few words are enough; an attachment speaks for itself (§1.1). */\nexport const REPORTER_MIN_DESCRIPTION = 8;\n\n/** Characters Send needs: `min` (default 8), or 0 with a screenshot / element attached. */\nexport function reporterRequiredLength(min: number | undefined, hasAttachment: boolean): number {\n if (hasAttachment) return 0;\n return typeof min === 'number' && Number.isFinite(min) && min >= 0 ? Math.floor(min) : REPORTER_MIN_DESCRIPTION;\n}\n\n/** Whether Send is enabled: some text or an attachment, and enough words. */\nexport function reporterCanSend(text: string, hasAttachment: boolean, min?: number): boolean {\n const trimmed = text.trim();\n if (!trimmed && !hasAttachment) return false;\n return trimmed.length >= reporterRequiredLength(min, hasAttachment);\n}\n\n/** Wire category for an optional chip: none → `other` (triage sets the type); Idea → a feature request. */\nexport function reporterChipToReport(\n chip: ReporterCategory | null,\n): { category: 'bug' | 'slow' | 'visual' | 'confusing' | 'other'; userCategory?: string } {\n if (!chip) return { category: 'other' };\n if (chip === 'idea') return { category: 'other', userCategory: 'feature' };\n return { category: chip };\n}\n\n/** One entry of `GET /v1/reporter/reports/:id` → `timeline` (subset the widgets read). */\nexport interface ReporterTimelineEntry {\n kind: string;\n text: string;\n /** Developer- or project-written text: show `text` as is. */\n custom?: boolean;\n body?: string;\n version?: string;\n closed_reason?: string;\n}\n\n/** True for the people talking (developer / reporter), false for pipeline events. */\nexport function isReporterConversation(entry: Pick<ReporterTimelineEntry, 'kind'>): boolean {\n return entry.kind === 'comment' || entry.kind === 'reporter_comment' || entry.kind === 'info_requested';\n}\n\n/** Text for one timeline entry in the reporter's locale; custom wording is shown as is. */\nexport function reporterTimelineEntryText(entry: ReporterTimelineEntry, locale: string = 'en'): string {\n if (entry.custom || !(REPORTER_TIMELINE_KINDS as readonly string[]).includes(entry.kind)) return entry.text;\n return reporterTimelineText(\n entry.kind as ReporterTimelineKind,\n { text: entry.body ?? null, version: entry.version ?? null, closed_reason: entry.closed_reason ?? null },\n locale,\n );\n}\n\nexport const REPORTER_TOAST_INTERVAL_MS = 24 * 60 * 60 * 1000;\n\n/** The next-visit toast: one per session and at most one a day, only with something unread (§4.2). */\nexport function reporterShouldShowToast(input: {\n enabled: boolean;\n unreadTotal: number;\n shownThisSession: boolean;\n lastShownAt: number | null;\n now: number;\n}): boolean {\n if (!input.enabled || input.unreadTotal <= 0 || input.shownThisSession) return false;\n return input.lastShownAt === null || input.now - input.lastShownAt >= REPORTER_TOAST_INTERVAL_MS;\n}\n\n/** \"The developer replied…\", \"Your bug is fixed\", or \"3 updates on your reports\". */\nexport function reporterToastMessage(\n updates: { unread_total: number; latest: Array<{ kind: string | null }> },\n locale: string = 'en',\n): string {\n const ui = reporterCopy(locale).ui;\n const kind = updates.unread_total === 1 ? updates.latest[0]?.kind : null;\n if (kind === 'comment' || kind === 'info_requested') return ui.toastReplied;\n if (kind === 'fixed' || kind === 'released') return ui.toastFixed;\n return ui.updates.replace('{n}', String(updates.unread_total));\n}\n\n/** A typo screen for the email opt-in box; the server validates the address. */\nexport function isPlausibleReporterEmail(raw: string): boolean {\n return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]{2,}$/.test(raw.trim());\n}\n"]}
|