@flamingo-stack/openframe-frontend-core 0.0.686 → 0.0.687

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.
Files changed (101) hide show
  1. package/dist/{chunk-QTXDXHMD.cjs → chunk-3ADAUFKK.cjs} +22 -22
  2. package/dist/{chunk-QTXDXHMD.cjs.map → chunk-3ADAUFKK.cjs.map} +1 -1
  3. package/dist/{chunk-2G2CB7ZZ.js → chunk-3IANMZFS.js} +5 -5
  4. package/dist/{chunk-WJX3ZSOE.cjs → chunk-3RNG4MJN.cjs} +33 -33
  5. package/dist/{chunk-WJX3ZSOE.cjs.map → chunk-3RNG4MJN.cjs.map} +1 -1
  6. package/dist/{chunk-ASBBGP36.js → chunk-4GQ7VWAR.js} +5 -5
  7. package/dist/{chunk-II5GVLTN.cjs → chunk-4THS6V3I.cjs} +5 -5
  8. package/dist/{chunk-II5GVLTN.cjs.map → chunk-4THS6V3I.cjs.map} +1 -1
  9. package/dist/{chunk-WFH52OVQ.js → chunk-A2ZP6WX4.js} +5 -5
  10. package/dist/{chunk-U6JC3RS2.js → chunk-APLL2BVR.js} +2 -2
  11. package/dist/{chunk-4LBCUBVL.cjs → chunk-BZKDFMZM.cjs} +45 -45
  12. package/dist/{chunk-4LBCUBVL.cjs.map → chunk-BZKDFMZM.cjs.map} +1 -1
  13. package/dist/{chunk-ZS3YCJYY.cjs → chunk-CDCUUEL4.cjs} +4 -4
  14. package/dist/{chunk-ZS3YCJYY.cjs.map → chunk-CDCUUEL4.cjs.map} +1 -1
  15. package/dist/{chunk-MZAZUSPQ.cjs → chunk-CKQ4ZBDN.cjs} +10 -10
  16. package/dist/{chunk-MZAZUSPQ.cjs.map → chunk-CKQ4ZBDN.cjs.map} +1 -1
  17. package/dist/{chunk-NINO3PI7.js → chunk-DGOUPZJ7.js} +2 -2
  18. package/dist/{chunk-RBCMBRI7.cjs → chunk-DYNWIHFU.cjs} +26 -26
  19. package/dist/{chunk-RBCMBRI7.cjs.map → chunk-DYNWIHFU.cjs.map} +1 -1
  20. package/dist/{chunk-PG65AE3I.js → chunk-ENIABTA5.js} +3 -3
  21. package/dist/{chunk-3NIZ2VLQ.js → chunk-GXKAWMDT.js} +2 -2
  22. package/dist/{chunk-N3I53B5W.cjs → chunk-HHNKNOZU.cjs} +3 -3
  23. package/dist/{chunk-N3I53B5W.cjs.map → chunk-HHNKNOZU.cjs.map} +1 -1
  24. package/dist/{chunk-C5TFPMOM.js → chunk-IR6PWTCL.js} +4 -4
  25. package/dist/{chunk-QOH45QSE.js → chunk-JSXNOXZQ.js} +3 -3
  26. package/dist/{chunk-SI77HF4U.cjs → chunk-LL35WZZS.cjs} +3 -3
  27. package/dist/chunk-LL35WZZS.cjs.map +1 -0
  28. package/dist/{chunk-MHTB67ZH.js → chunk-LROQXUFX.js} +2 -2
  29. package/dist/{chunk-GETQTNRV.cjs → chunk-QRZ5LS6A.cjs} +22 -22
  30. package/dist/{chunk-GETQTNRV.cjs.map → chunk-QRZ5LS6A.cjs.map} +1 -1
  31. package/dist/{chunk-4RJQMOQW.js → chunk-R4MKXLRJ.js} +6 -6
  32. package/dist/{chunk-3DO76UAB.cjs → chunk-RHBFZC6F.cjs} +15 -15
  33. package/dist/{chunk-3DO76UAB.cjs.map → chunk-RHBFZC6F.cjs.map} +1 -1
  34. package/dist/{chunk-RKH7DEDF.cjs → chunk-RVO77GSL.cjs} +10 -10
  35. package/dist/{chunk-RKH7DEDF.cjs.map → chunk-RVO77GSL.cjs.map} +1 -1
  36. package/dist/{chunk-IELOPDTA.js → chunk-S6SGPLA4.js} +3 -3
  37. package/dist/{chunk-LCOMNQLX.js → chunk-SFY2E5DG.js} +3 -3
  38. package/dist/{chunk-JBJZL5PI.js → chunk-SQQQVVV2.js} +3 -3
  39. package/dist/{chunk-YPXXXX6R.js → chunk-SR2C5SJJ.js} +3 -3
  40. package/dist/chunk-SR2C5SJJ.js.map +1 -0
  41. package/dist/{chunk-IBUFFKAK.cjs → chunk-T32BTPL5.cjs} +339 -339
  42. package/dist/{chunk-IBUFFKAK.cjs.map → chunk-T32BTPL5.cjs.map} +1 -1
  43. package/dist/{chunk-RSMAVO2Z.js → chunk-W4CHRA6G.js} +2 -2
  44. package/dist/{chunk-UBXEETWQ.cjs → chunk-WIUDKYAQ.cjs} +71 -71
  45. package/dist/{chunk-UBXEETWQ.cjs.map → chunk-WIUDKYAQ.cjs.map} +1 -1
  46. package/dist/{chunk-P4SBHO7U.cjs → chunk-WMOELYDL.cjs} +10 -10
  47. package/dist/{chunk-P4SBHO7U.cjs.map → chunk-WMOELYDL.cjs.map} +1 -1
  48. package/dist/{chunk-AF4XOURN.cjs → chunk-XEESEFGT.cjs} +3 -3
  49. package/dist/{chunk-AF4XOURN.cjs.map → chunk-XEESEFGT.cjs.map} +1 -1
  50. package/dist/components/case-studies/index.cjs +14 -14
  51. package/dist/components/case-studies/index.js +3 -3
  52. package/dist/components/chat/approval-batch-message.d.ts.map +1 -1
  53. package/dist/components/chat/index.cjs +2 -2
  54. package/dist/components/chat/index.js +1 -1
  55. package/dist/components/chat/tool-execution-display.d.ts.map +1 -1
  56. package/dist/components/contact/index.cjs +4 -4
  57. package/dist/components/contact/index.js +3 -3
  58. package/dist/components/docs/index.cjs +7 -7
  59. package/dist/components/docs/index.js +6 -6
  60. package/dist/components/embeds/index.cjs +4 -4
  61. package/dist/components/embeds/index.js +3 -3
  62. package/dist/components/faq/index.cjs +4 -4
  63. package/dist/components/faq/index.js +3 -3
  64. package/dist/components/features/index.cjs +3 -3
  65. package/dist/components/features/index.js +2 -2
  66. package/dist/components/help-center-pages/index.cjs +73 -73
  67. package/dist/components/help-center-pages/index.js +14 -14
  68. package/dist/components/index.cjs +40 -40
  69. package/dist/components/index.js +13 -13
  70. package/dist/components/meeting-scheduler/index.cjs +25 -25
  71. package/dist/components/meeting-scheduler/index.js +2 -2
  72. package/dist/components/navigation/index.cjs +6 -6
  73. package/dist/components/navigation/index.js +5 -5
  74. package/dist/components/onboarding-guides/index.cjs +6 -6
  75. package/dist/components/onboarding-guides/index.js +5 -5
  76. package/dist/components/tickets/index.cjs +7 -7
  77. package/dist/components/tickets/index.js +6 -6
  78. package/dist/components/ui/index.cjs +3 -3
  79. package/dist/components/ui/index.js +2 -2
  80. package/dist/index.cjs +7 -7
  81. package/dist/index.js +6 -6
  82. package/package.json +1 -1
  83. package/src/components/chat/approval-batch-message.tsx +3 -1
  84. package/src/components/chat/tool-execution-display.tsx +2 -1
  85. package/dist/chunk-SI77HF4U.cjs.map +0 -1
  86. package/dist/chunk-YPXXXX6R.js.map +0 -1
  87. /package/dist/{chunk-2G2CB7ZZ.js.map → chunk-3IANMZFS.js.map} +0 -0
  88. /package/dist/{chunk-ASBBGP36.js.map → chunk-4GQ7VWAR.js.map} +0 -0
  89. /package/dist/{chunk-WFH52OVQ.js.map → chunk-A2ZP6WX4.js.map} +0 -0
  90. /package/dist/{chunk-U6JC3RS2.js.map → chunk-APLL2BVR.js.map} +0 -0
  91. /package/dist/{chunk-NINO3PI7.js.map → chunk-DGOUPZJ7.js.map} +0 -0
  92. /package/dist/{chunk-PG65AE3I.js.map → chunk-ENIABTA5.js.map} +0 -0
  93. /package/dist/{chunk-3NIZ2VLQ.js.map → chunk-GXKAWMDT.js.map} +0 -0
  94. /package/dist/{chunk-C5TFPMOM.js.map → chunk-IR6PWTCL.js.map} +0 -0
  95. /package/dist/{chunk-QOH45QSE.js.map → chunk-JSXNOXZQ.js.map} +0 -0
  96. /package/dist/{chunk-MHTB67ZH.js.map → chunk-LROQXUFX.js.map} +0 -0
  97. /package/dist/{chunk-4RJQMOQW.js.map → chunk-R4MKXLRJ.js.map} +0 -0
  98. /package/dist/{chunk-IELOPDTA.js.map → chunk-S6SGPLA4.js.map} +0 -0
  99. /package/dist/{chunk-LCOMNQLX.js.map → chunk-SFY2E5DG.js.map} +0 -0
  100. /package/dist/{chunk-JBJZL5PI.js.map → chunk-SQQQVVV2.js.map} +0 -0
  101. /package/dist/{chunk-RSMAVO2Z.js.map → chunk-W4CHRA6G.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-II5GVLTN.cjs","../src/components/tickets/ticket-live-provider.tsx","../src/components/tickets/types.ts"],"names":[],"mappings":"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","file":"/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-II5GVLTN.cjs","sourcesContent":[null,"'use client';\n\n/**\n * TicketLiveProvider — the ONE client home for support-ticket realtime.\n *\n * DOMAIN logic only — connection lifecycle (subscribed-confirm, server\n * retry grace, failed-reconnect pacing, hidden-tab suspend/resume) is\n * owned entirely by the common SSE client (`createSseSubscription`);\n * this provider consumes its consolidated `onConnectedChange` signal.\n *\n * Owns:\n * - the unread summary — delivered EXCLUSIVELY by the stream\n * (`ticket-summary` frames on connect + debounced after events) and\n * by `ticket-read` responses. The client holds NO summary fetch\n * path: resync-on-(re)connect is served by the SERVER (every\n * connect pushes a fresh summary);\n * - query invalidation on events (list + open drawer refresh live);\n * - `markRead` with local write-through zeroing for 0-latency,\n * reconciled by the response's server-computed summary;\n * - the ticket-contract `no-stream` (204) retry policy.\n *\n * There is NO polling anywhere and NO summary endpoint. Hosts whose\n * stream endpoint 404s (terminal) or is absent get no unread indication\n * — the indication IS a realtime feature.\n *\n * Invalidation semantics on events — deliberately `invalidateQueries`,\n * NOT `setQueryData` patches: the ticket hooks run `gcTime: 0`, so\n * there is usually no cache entry to patch, and `TicketsCacheSlot`\n * shape rules apply when there is.\n */\n\nimport { useQueryClient } from '@tanstack/react-query';\nimport React from 'react';\nimport { useChatRuntime } from '../../contexts/chat-runtime-context';\nimport { embedAuthedFetch } from '../../utils/embed-authed-fetch';\nimport { createSseSubscription, type SseSubscription, type SseTransportStatus } from '../../utils/sse-subscription';\nimport { useChatIdentity } from '../chat/hooks/use-chat-identity';\nimport type { TicketMessageStreamData, TicketStreamEvent, TicketUnreadSummary } from './types';\n\n// Ticket realtime is its OWN api surface — deliberately NOT under\n// `/api/chat/agent/*` (that prefix is the chat agent's tool surface;\n// ticket streaming/read-receipts are unrelated to chat).\nconst TICKET_STREAM_ENDPOINT = '/api/tickets/stream';\nconst TICKET_READ_ENDPOINT = '/api/tickets/read';\n\nconst EMPTY_SUMMARY: TicketUnreadSummary = { totalUnread: 0, tickets: {}, latestUnreadAt: {} };\n\n/** ISO stamps can carry mixed timezone offsets (server `+00:00` vs an\n * optimistic local `Z`) — lexicographic string comparison mis-orders\n * those, so every stamp comparison goes through parsed epoch ms.\n * Missing/unparseable stamps sort oldest. */\nconst stampMs = (stamp: string | null | undefined): number => {\n if (!stamp) return 0;\n const ms = Date.parse(stamp);\n return Number.isNaN(ms) ? 0 : ms;\n};\n\n/** Trailing debounce for event-burst invalidations (an agent pasting 5\n * messages must not fire 5 list refetches). */\nconst INVALIDATE_DEBOUNCE_MS = 1_500;\n\nexport interface TicketLiveContextValue {\n /** Viewer has a resolved, non-anon chat identity. Surfaces (e.g. the\n * header cell) hide themselves when false — computed here so they\n * don't each spin up their own identity resolution. */\n authed: boolean;\n /** Realtime-subscribed (server-confirmed), not merely HTTP-open. */\n connected: boolean;\n /** Unread total across tickets — drives the header count badge. */\n unreadTotal: number;\n /** Per-ticket unread counts (missing key = 0) — drives row dots.\n * The currently-open ticket is masked to 0. */\n unreadByTicket: Record<string, number>;\n /** The ticket with the NEWEST unread reply (open ticket excluded) —\n * the header cell routes to it via the SSOT deep link\n * (`buildTicketOpenHref`). Null when nothing is unread. */\n nextUnreadTicketId: string | null;\n /** Mark a ticket read: POSTs the receipt, zeroes the ticket locally\n * (0-latency), then applies the response's server-computed summary. */\n markRead: (ticketExternalId: string) => Promise<void>;\n /** Drawer open/close registration — suppresses unread bumps for the\n * open ticket and masks it in the derived map. */\n setOpenTicketId: (ticketExternalId: string | null) => void;\n /** Hint that the viewer just created a ticket — reconnects the stream\n * (fresh ownership set + fresh server-pushed summary). */\n notifyTicketCreated: () => void;\n}\n\nconst TicketLiveContext = React.createContext<TicketLiveContextValue | null>(null);\n\nexport function useOptionalTicketLive(): TicketLiveContextValue | null {\n return React.useContext(TicketLiveContext);\n}\n\nexport function useTicketLive(): TicketLiveContextValue {\n const ctx = React.useContext(TicketLiveContext);\n if (!ctx) {\n throw new Error('useTicketLive must be used within <TicketLiveProvider>');\n }\n return ctx;\n}\n\nfunction isTicketMessageData(value: unknown): value is TicketMessageStreamData {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as TicketMessageStreamData).ticket_external_id === 'string'\n );\n}\n\nfunction isSummaryData(value: unknown): value is TicketUnreadSummary {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as TicketUnreadSummary).totalUnread === 'number' &&\n typeof (value as TicketUnreadSummary).tickets === 'object'\n );\n}\n\nexport interface TicketLiveProviderProps {\n children: React.ReactNode;\n /**\n * Whether ticket realtime is available to this host at all (feature flag,\n * session, entitlement). `false` provides NO context — `useOptionalTicketLive()`\n * reads `null`, `useTicketLive()` throws, and no identity or stream request is\n * made: byte-for-byte what a host got by not mounting this at all.\n *\n * It exists so a host never has to mount this CONDITIONALLY. A host that writes\n * `enabled ? <TicketLiveProvider>{children}</TicketLiveProvider> : <>{children}</>`\n * changes the element TYPE at that position the moment its flag answers, and React\n * tears down and remounts everything below. In openframe-frontend that wrapper sat\n * around the whole `AppLayout`, so every cold load remounted the app shell and the\n * page under it — visibly, as a chat drawer opened by a deep link replaying its\n * open animation. Mount it always and pass the answer here instead.\n *\n * One consequence of mounting it always: a `ChatRuntime` must be above it even\n * while disabled. The identity resolver reads one unconditionally (Rules of\n * Hooks) and throws without it; `enabled` gates the request, not the read.\n */\n enabled?: boolean;\n}\n\nexport function TicketLiveProvider({ children, enabled = true }: TicketLiveProviderProps) {\n const runtime = useChatRuntime();\n const identity = useChatIdentity(enabled);\n const queryClient = useQueryClient();\n\n const streamUrl = runtime?.endpoints.ticketStreamUrl ?? TICKET_STREAM_ENDPOINT;\n const readUrl = runtime?.endpoints.ticketReadUrl ?? TICKET_READ_ENDPOINT;\n\n // Gates the stream below (via `streamEnabled`) as well as every surface that\n // reads `authed` — one predicate, so a disabled provider is also a silent one.\n const authed = enabled && identity.authTier !== 'anon' && !!identity.user?.email;\n\n const [connected, setConnected] = React.useState(false);\n // The SINGLE unread source — written ONLY from server-computed\n // summaries (stream frames + markRead responses) plus the two\n // 0-latency optimistic writes (event bump, markRead zero), both\n // reconciled by the next server summary.\n const [summary, setSummary] = React.useState<TicketUnreadSummary>(EMPTY_SUMMARY);\n // State + ref for the open drawer: state drives the derived map,\n // the ref lets stream callbacks read it without re-subscribing.\n const [openTicketIdState, setOpenTicketIdState] = React.useState<string | null>(null);\n const openTicketIdRef = React.useRef<string | null>(null);\n\n // ---- Debounced (trailing) invalidation of ticket queries on events ----\n const pendingTicketIdsRef = React.useRef<Set<string>>(new Set());\n const invalidateTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);\n const flushInvalidations = React.useCallback(() => {\n invalidateTimerRef.current = null;\n const ids = Array.from(pendingTicketIdsRef.current);\n pendingTicketIdsRef.current.clear();\n void queryClient.invalidateQueries({ queryKey: ['tickets'] });\n for (const id of ids) {\n void queryClient.invalidateQueries({ queryKey: ['ticket-engagements', id] });\n }\n }, [queryClient]);\n const scheduleInvalidation = React.useCallback(\n (ticketId?: string) => {\n if (ticketId) pendingTicketIdsRef.current.add(ticketId);\n if (invalidateTimerRef.current) return;\n invalidateTimerRef.current = setTimeout(flushInvalidations, INVALIDATE_DEBOUNCE_MS);\n },\n [flushInvalidations],\n );\n\n // ---- Optimistic unread writes (reconciled by server summaries) ----\n const bumpUnread = React.useCallback((ticketId: string, createdAt?: string | null) => {\n setSummary(prev => {\n const stamp = createdAt ?? new Date().toISOString();\n const existing = prev.latestUnreadAt[ticketId];\n return {\n totalUnread: prev.totalUnread + 1,\n tickets: { ...prev.tickets, [ticketId]: (prev.tickets[ticketId] ?? 0) + 1 },\n latestUnreadAt: {\n ...prev.latestUnreadAt,\n [ticketId]: existing && stampMs(existing) > stampMs(stamp) ? existing : stamp,\n },\n };\n });\n }, []);\n\n const zeroUnread = React.useCallback((ticketId: string) => {\n setSummary(prev => {\n const current = prev.tickets[ticketId] ?? 0;\n if (current === 0) return prev;\n const tickets = { ...prev.tickets };\n delete tickets[ticketId];\n const latestUnreadAt = { ...prev.latestUnreadAt };\n delete latestUnreadAt[ticketId];\n return { totalUnread: Math.max(0, prev.totalUnread - current), tickets, latestUnreadAt };\n });\n }, []);\n\n // ---- Stream lifecycle (owned by the SSE CLIENT — the provider only\n // consumes its consolidated signals) ----\n const subscriptionRef = React.useRef<SseSubscription | null>(null);\n const noStreamRef = React.useRef(false);\n\n // Stable handler refs so the subscription effect doesn't churn.\n const handlersRef = React.useRef<{\n onEvent: (eventName: string, data: unknown) => void;\n onStatus: (status: SseTransportStatus) => void;\n onConnected: (connected: boolean) => void;\n }>({ onEvent: () => {}, onStatus: () => {}, onConnected: () => {} });\n\n const handleConnected = (isConnected: boolean) => {\n setConnected(isConnected);\n if (isConnected) {\n // Event-gap healing on every (re)connect: the SERVER pushes a\n // fresh summary; the open drawer refetches through the ACL'd path.\n const openId = openTicketIdRef.current;\n if (openId) {\n void queryClient.invalidateQueries({ queryKey: ['ticket-engagements', openId] });\n }\n }\n };\n\n const handleEvent = (eventName: string, data: unknown) => {\n if (eventName === 'ticket-summary') {\n const frame = data as TicketStreamEvent | null;\n if (isSummaryData(frame?.data)) {\n setSummary({\n totalUnread: frame.data.totalUnread,\n tickets: frame.data.tickets ?? {},\n latestUnreadAt: frame.data.latestUnreadAt ?? {},\n });\n }\n return;\n }\n\n if (eventName === 'ticket-resync') {\n // Ownership gained / degraded payload — a fresh ticket-summary\n // frame follows from the server; refresh the query surfaces.\n scheduleInvalidation();\n return;\n }\n\n if (eventName === 'ticket-status') {\n // Meaningful ticket-row change (close/reopen/pipeline move). The\n // server pushes an updated summary right after (closures count as\n // unread server-side); here we just refresh the query surfaces so\n // the status badge flips live.\n const frame = data as TicketStreamEvent | null;\n const ticketId = (frame?.data as { ticket_external_id?: string } | undefined)?.ticket_external_id;\n scheduleInvalidation(ticketId);\n return;\n }\n\n if (eventName === 'ticket-message') {\n const frame = data as TicketStreamEvent | null;\n const payload = frame?.data;\n if (!isTicketMessageData(payload)) {\n scheduleInvalidation();\n return;\n }\n // Always invalidate (own replies from another tab render live);\n // bump unread ONLY on the server-stamped predicate, and never for\n // the ticket whose drawer is open. The server's debounced summary\n // push reconciles moments later.\n scheduleInvalidation(payload.ticket_external_id);\n if (payload.countsAsUnread && payload.ticket_external_id !== openTicketIdRef.current) {\n bumpUnread(payload.ticket_external_id, payload.hubspot_created_at);\n }\n }\n };\n\n const handleStatus = (status: SseTransportStatus) => {\n // The client owns connection lifecycle (confirm timeout, server\n // retry grace, failed-reconnect pacing, hidden-suspend). The only\n // transport status with DOMAIN meaning is the ticket contract's\n // `no-stream` (204 = zero owned tickets): remember it so a domain\n // signal (own create_ticket, window focus) can retry.\n if (status === 'open') noStreamRef.current = false;\n if (status === 'no-stream') noStreamRef.current = true;\n };\n\n // Publish the three handlers into the stable ref. In an unconditional effect\n // rather than in the render body: every caller is an SSE transport callback,\n // which can only fire after a commit, and a render attempt React discards\n // must not be able to leave a handler behind that closes over state the user\n // never saw. Declared BEFORE the subscription effect so it is filled first\n // in the same flush.\n React.useEffect(() => {\n handlersRef.current.onConnected = handleConnected;\n handlersRef.current.onEvent = handleEvent;\n handlersRef.current.onStatus = handleStatus;\n });\n\n // Mount/unmount of the subscription — gated on auth + a usable URL.\n const streamEnabled = authed && !!streamUrl;\n React.useEffect(() => {\n if (!streamEnabled) return undefined;\n if (typeof window === 'undefined') return undefined;\n\n subscriptionRef.current = createSseSubscription({\n url: streamUrl,\n onEvent: (name, data) => handlersRef.current.onEvent(name, data),\n onStatusChange: status => handlersRef.current.onStatus(status),\n onConnectedChange: isConnected => handlersRef.current.onConnected(isConnected),\n // Long-lived per-user stream: suspend while hidden (scale relief),\n // resume + reconnect on visible/online — all inside the client.\n pauseWhenHidden: true,\n });\n\n const retryIfIdle = () => {\n // After a 204 (no-stream), a focus signal is the retry trigger.\n if (noStreamRef.current) {\n noStreamRef.current = false;\n subscriptionRef.current?.reconnectNow();\n }\n };\n window.addEventListener('focus', retryIfIdle);\n\n return () => {\n window.removeEventListener('focus', retryIfIdle);\n if (invalidateTimerRef.current) {\n clearTimeout(invalidateTimerRef.current);\n invalidateTimerRef.current = null;\n }\n subscriptionRef.current?.close();\n subscriptionRef.current = null;\n setConnected(false);\n setSummary(EMPTY_SUMMARY);\n };\n // handlersRef is stable; streamUrl/auth changes remount the stream.\n }, [streamEnabled, streamUrl]);\n\n // ---- Public API ----\n const markRead = React.useCallback(\n async (ticketExternalId: string) => {\n try {\n const response = await embedAuthedFetch(readUrl, {\n method: 'POST',\n body: JSON.stringify({ ticket_id: ticketExternalId }),\n });\n if (!response.ok) return;\n // 0-latency local zero, then reconcile with the response's\n // server-computed summary (THE truth — includes closure\n // accounting and other tickets).\n zeroUnread(ticketExternalId);\n const body = (await response.json().catch(() => null)) as { summary?: TicketUnreadSummary } | null;\n if (isSummaryData(body?.summary)) {\n setSummary({\n totalUnread: body.summary.totalUnread,\n tickets: body.summary.tickets ?? {},\n latestUnreadAt: body.summary.latestUnreadAt ?? {},\n });\n }\n } catch {\n // Non-fatal — the receipt lands on the next markRead; unread\n // state reconciles via the next server summary push.\n }\n },\n [readUrl, zeroUnread],\n );\n\n const setOpenTicketId = React.useCallback((ticketExternalId: string | null) => {\n openTicketIdRef.current = ticketExternalId;\n setOpenTicketIdState(ticketExternalId);\n }, []);\n\n const notifyTicketCreated = React.useCallback(() => {\n // Fresh connect → fresh ownership set + fresh server summary\n // (covers both the no-stream 204 wait state and an already-live\n // stream whose set predates the new ticket).\n noStreamRef.current = false;\n subscriptionRef.current?.reconnectNow();\n }, []);\n\n // Derived unread map — open ticket masked to 0 so a server summary\n // landing inside the markRead debounce window can't transiently\n // re-badge it.\n const value = React.useMemo<TicketLiveContextValue>(() => {\n let unreadByTicket = summary.tickets;\n let unreadTotal = summary.totalUnread;\n if (openTicketIdState && unreadByTicket[openTicketIdState]) {\n const masked = unreadByTicket[openTicketIdState];\n unreadByTicket = { ...unreadByTicket };\n delete unreadByTicket[openTicketIdState];\n unreadTotal = Math.max(0, unreadTotal - masked);\n }\n // Ticket with the NEWEST unread reply (compared as parsed epoch ms\n // via `stampMs` — mixed timezone offsets break string ordering) —\n // header cell routes there. Falls back to any unread key if a\n // timestamp is missing.\n let nextUnreadTicketId: string | null = null;\n let nextStampMs = 0;\n for (const id of Object.keys(unreadByTicket)) {\n const ms = stampMs(summary.latestUnreadAt[id]);\n if (nextUnreadTicketId === null || ms > nextStampMs) {\n nextUnreadTicketId = id;\n nextStampMs = ms;\n }\n }\n return {\n authed,\n connected,\n unreadTotal,\n unreadByTicket,\n nextUnreadTicketId,\n markRead,\n setOpenTicketId,\n notifyTicketCreated,\n };\n }, [summary, authed, connected, openTicketIdState, markRead, setOpenTicketId, notifyTicketCreated]);\n\n return <TicketLiveContext.Provider value={enabled ? value : null}>{children}</TicketLiveContext.Provider>;\n}\n","/**\n * Wire shape of a row returned by `POST /api/chat/agent/find-ticket`.\n * Mirrors the executor's projection at `lib/data/hubspot-tools.ts`\n * (`FIND_TICKET_SELECT` / `FindTicketResult`).\n *\n * Cross-repo duplication is INTENTIONAL: this lib ships independently\n * of the hub, so we can't import `FindTicketResult` from\n * `hubspot-tools.ts` directly. If the server adds a column to\n * `FIND_TICKET_SELECT`, also add it here. The smoke test in §F of the\n * plan covers the happy path; a wire-contract test belongs in the hub.\n *\n * `find-ticket` returns `customer_emails: string[]` (jsonb array), NOT\n * a single `customer_email`. The list is server-self-scoped to the\n * caller's session email; the array is exposed for admin/staff\n * surfaces, which the ticket center doesn't render.\n */\nexport interface TicketData {\n id: string;\n /** HubSpot ticket id (display number, e.g. \"1234\"). */\n external_id: string;\n subject: string | null;\n /** Short (≤400 char) HTML-stripped preview of the ticket body —\n * used for the list-card subtitle when needed. */\n preview: string | null;\n /** Longer (≤4k char) sanitized body. INCLUDES every appended\n * `content_addendum` comment because the `update_ticket` executor\n * reads + re-writes the `content` property server-side with a\n * `---` separator. Render this in the drawer's description block\n * and the user sees both the original message + every comment they\n * (or staff) added. */\n body: string | null;\n /** Canonical OPEN | CLOSED (HubSpot pipeline derived). */\n status: string | null;\n /** Human label like \"New\" / \"Working on it\" / \"Waiting on contact\" /\n * \"Closed\". Drives the badge text; canonical status drives color. */\n pipeline_stage_label: string | null;\n clickup_task_id: string | null;\n /** Snapshot of the linked ClickUp delivery task — populated server-side\n * via the `clickup_tasks` mirror when `clickup_task_id` is set. Drives\n * the \"Linked delivery\" card surface on the ticket drawer (status\n * badge + ClickUp deep link). `null` when no link OR the ClickUp row\n * was deleted / not yet synced. */\n clickup: TicketClickupSummary | null;\n priority: string | null;\n customer_emails: string[];\n customer_company: string | null;\n /** HubSpot contact's display name. Drives the customer attribution\n * on the drawer when the viewer is NOT the customer themselves\n * (admin browsing / multi-contact second viewer). Conversations\n * API messages don't carry per-message sender info on Custom\n * Channels, so this is the only reliable source for \"what's the\n * customer's name.\" */\n customer_name: string | null;\n /** HubSpot owner id of the agent assigned to this ticket. Carried as\n * raw id for debugging; rendering goes through `assignedOwner`. Null\n * when unassigned. */\n assigned_to: string | null;\n /** Resolved assigned-owner profile — name + email + avatar. Populated\n * server-side via `attachOwnerProfiles` which joins through the\n * `hubspot_owners` mirror to `profiles` by email. Drives the\n * \"Assigned to\" attribution in the drawer header. Null when\n * unassigned OR the owner couldn't be resolved (rare — only when\n * the agent was deleted from HubSpot between the ticket update and\n * the next owners reconcile). */\n assignedOwner: TicketAssignedOwner | null;\n hubspot_updated_at: string;\n}\n\n/** Resolved profile of a ticket's assigned agent — surfaced in the\n * drawer header. Subset of the server's `MirroredOwnerProfile`\n * trimmed to just the rendering fields. */\nexport interface TicketAssignedOwner {\n name: string | null;\n email: string | null;\n avatarUrl: string | null;\n}\n\n/** Compact projection of a linked ClickUp task — matches the server's\n * `ClickupSummary` and aligns with `DeliveryItem` so the linked-card\n * on a ticket can render through the same `DeliveryRow` primitive used\n * on `/bug-fixes-and-enhancements`. */\nexport interface TicketClickupSummary {\n /** ClickUp task external_id (e.g. \"86ad4e022\"). Used as the\n * `?focus=<id>` URL param to scroll the public delivery page to\n * this row. */\n external_id: string;\n title: string | null;\n description: string | null;\n /** ClickUp status name — e.g. \"complete\" / \"working\" / \"design approved\"\n * / \"waiting for release\". Used as the badge label. */\n status: string | null;\n /** ClickUp's per-status hex color (e.g. \"#008844\"). Forwarded to the\n * badge so colors match the ClickUp board exactly. */\n status_color: string | null;\n /** Bucket — `'backlog' | 'working' | 'complete' | 'unknown'`. Used as\n * a fallback when status_color is missing. */\n status_category: string | null;\n /** ClickUp custom item label (`'Bug'` / `'Request'`) — drives the\n * type badge (\"BUG-FIX\" / \"ENHANCEMENT\"). */\n task_type: string | null;\n custom_item_id: number | null;\n /** Every ClickUp list the task is associated with. UI joins with \", \". */\n list_names: string[];\n /** Unix-ms timestamps so the row's \"ACTIVE X ago\" subtitle uses the\n * shared `getRelativeTime()` helper. */\n date_opened: number | null;\n date_updated: number | null;\n date_closed: number | null;\n /** Direct https://app.clickup.com/t/<id> deep link. Kept on the wire\n * for admin surfaces; the customer-facing linked card navigates\n * internally instead. */\n clickup_url: string | null;\n /** Composed server-side via the SAME `buildDevSectionUrl` helper the\n * chat-inline delivery card uses. Carries `?search=<id>` so the\n * delivery list filters to that single task on landing. */\n delivery_href: string;\n /** Target platform name for the host's `useNavLink` to decide\n * same-tab vs new-tab on cross-platform links. */\n delivery_target_platform: string;\n /** Release version label set by the delivery team, e.g. \"0.9\" / \"1.0\".\n * Shown beside the status when present. */\n target_version: string | null;\n}\n\n/**\n * Optimistic placeholder a `submitTicket` call prepends to the list\n * BEFORE the server roundtrip resolves. Drawer is hidden until the\n * real id arrives. The wrapper destructures `_optimistic` before\n * forwarding to `<ChatTicketItem>` so the DOM doesn't see an unknown\n * prop.\n */\nexport interface OptimisticTicket extends TicketData {\n _optimistic: true;\n}\n\nexport type AnyTicket = TicketData | OptimisticTicket;\n\nexport function isOptimistic(t: AnyTicket): t is OptimisticTicket {\n return (t as OptimisticTicket)._optimistic === true;\n}\n\n/**\n * Shape of a single `['tickets', …]` TanStack-Query cache slot.\n * Mirrors `FindTicketResponse` in `hooks/use-tickets-list.ts` — kept\n * here because the cache-mutation call sites in `useTicketActions` and\n * `<HelpCenterList>` would otherwise have to redeclare the shape inline.\n *\n * A 2026-05-29 prod regression (`t.map is not a function` on\n * close/reopen) was caused by assuming the cache held a bare\n * `TicketData[]` instead of this wrapper — every helper that calls\n * `queryClient.setQueriesData` / `getQueriesData` on `['tickets']`\n * MUST type the value through this shape and project / reassemble\n * `tickets` explicitly.\n */\nexport interface TicketsCacheSlot {\n tickets?: TicketData[];\n count?: number;\n totalCount?: number;\n page?: number;\n pageSize?: number;\n totalPages?: number;\n scope?: 'self' | 'all';\n}\n\n/**\n * Stable server-side error codes the ticket-action helpers route\n * through `mapTicketActionError`. Anything else is treated as a generic\n * server error.\n *\n * Reply-specific codes (`HUBSPOT_5XX` / `HUBSPOT_400_VALIDATION` /\n * `HUBSPOT_404_THREAD` / `HUBSPOT_REPLY_UNKNOWN`) drive the drawer\n * banner that appears above the composer when a customer reply fails.\n * Distinct from `HUBSPOT_DISCONNECTED` (whole-system) and\n * `TICKET_NOT_FOUND` (terminal-row) — the reply codes are per-attempt\n * + retryable except for `HUBSPOT_404_THREAD`.\n */\nexport type TicketActionErrorCode =\n | 'PROPOSAL_NOT_CLAIMABLE'\n | 'TICKET_NOT_FOUND'\n | 'TICKET_OWNERSHIP_DENIED'\n | 'HUBSPOT_DISCONNECTED'\n | 'RATE_LIMITED'\n | 'INVALID_TOOL_ARGS'\n | 'HUBSPOT_5XX'\n | 'HUBSPOT_400_VALIDATION'\n | 'HUBSPOT_404_THREAD'\n | 'HUBSPOT_REPLY_UNKNOWN'\n | 'UNKNOWN';\n\nexport interface MappedTicketActionError {\n code: TicketActionErrorCode;\n /** Human-readable copy safe to show in a toast. */\n message: string;\n /** When true, the form should disable submit + show the\n * support-down banner. Set only for HUBSPOT_DISCONNECTED. */\n supportSystemDown: boolean;\n /** When true, the helper should remove the affected row optimistically\n * (TICKET_NOT_FOUND). */\n removeRowFromCache: boolean;\n /** Retry hint surfaced from a 429 response. Caller decides whether\n * to mention it in the toast. */\n retryAfterSeconds?: number;\n}\n\n/**\n * Defensive client-side cap on ticket text (initial content + comment\n * addendums). HubSpot Note engagements accept more, but a 100KB paste\n * should fail fast at the UI rather than burning a server round-trip.\n * Both the open-ticket form and the per-row comment textarea import\n * this so a future server-side hardening only touches one place.\n */\nexport const TICKET_TEXT_MAX_CHARS = 5000;\n\n/**\n * Wire contract of the ticket live stream (`GET /api/chat/agent/\n * ticket-stream`, SSE). Producer (the hub route's `mapEvent`) imports\n * THIS type — same producer/consumer SSOT pattern as\n * `ConversationEngagementWire = TicketEngagement`. A schema drift here\n * would be a silent serialization bug; keep one named contract.\n *\n * Frames carry METADATA ONLY — never message bodies. Content is always\n * refetched through the ACL'd `list-engagements` path (one read path).\n *\n * Event names (`eventType`):\n * - `ticket-message` — a message row landed in the mirror for one of\n * the viewer's tickets. `data` carries the metadata.\n * - `ticket-status` — a MEANINGFUL ticket-row change (status /\n * pipeline stage / closure) landed in the mirror. Server-side\n * old-vs-new diffing drops no-op writes (sync stamps). Clients\n * invalidate + refetch the summary (closures count as unread\n * server-side via `closed_at` vs the read receipt).\n * - `ticket-summary` — the server-computed `TicketUnreadSummary`.\n * THE stream is the summary webservice: pushed on connect (server-\n * side resync-on-reconnect) and re-pushed (debounced) after every\n * delivered event. Clients hold NO summary fetch path.\n * - `ticket-resync` — degraded frame: something happened that the\n * server couldn't fully describe (truncated Realtime payload, or\n * the connection's ownership set just gained tickets). A fresh\n * `ticket-summary` follows it; clients invalidate queries.\n *\n * The server also emits `status` frames (`subscribed` / `retrying` /\n * `reconnect_failed`) from the shared SSE utility — clients derive\n * `connected` from those, NOT from the HTTP stream being open.\n */\nexport type TicketStreamEventType = 'ticket-message' | 'ticket-status' | 'ticket-summary' | 'ticket-resync';\n\n/** Metadata payload of a `ticket-message` frame. */\nexport type TicketMessageStreamData = {\n ticket_external_id: string;\n /** Mirror row's HubSpot message id. */\n message_id: string;\n /** Server-stamped via the same direction predicate the wire mapper\n * uses. Clients never re-derive author classification. */\n authorRole: 'customer' | 'support';\n /** Server-stamped via the visibility+direction unread predicate\n * (`countsAsUnread` in the hub's conversations DAL). ONLY frames\n * with `true` may bump client-side unread state. */\n countsAsUnread: boolean;\n hubspot_created_at: string | null;\n};\n\n/** Metadata payload of a `ticket-status` frame. */\nexport type TicketStatusStreamData = {\n ticket_external_id: string;\n /** Canonical OPEN | CLOSED after the change. */\n status: string | null;\n pipeline_stage_label: string | null;\n closed_at: string | null;\n};\n\nexport interface TicketStreamEvent {\n eventType: TicketStreamEventType;\n /** Human-readable label (SSE envelope compat) — clients ignore it. */\n message: string;\n /** `ticket-message` → TicketMessageStreamData; `ticket-status` →\n * TicketStatusStreamData; `ticket-summary` → TicketUnreadSummary;\n * absent on `ticket-resync`. */\n data?: TicketMessageStreamData | TicketStatusStreamData | TicketUnreadSummary;\n}\n\n/**\n * The SINGLE unread source, delivered EXCLUSIVELY over the stream\n * (`ticket-summary` frames on connect + after events) and in\n * `ticket-read` responses. There is no summary fetch endpoint. Header\n * badge total AND per-row dots both read this via the\n * `TicketLiveProvider` map; missing keys mean 0.\n */\nexport type TicketUnreadSummary = {\n totalUnread: number;\n tickets: Record<string, number>;\n /** ISO timestamp of the NEWEST unread message per ticket (same keys as\n * `tickets`). Drives \"route to the most recent update\" on the header\n * cell: the ticket with the max value here is `nextUnreadTicketId`. */\n latestUnreadAt: Record<string, string>;\n};\n\n/**\n * Trailing debounce for `markRead` while the drawer is open and new\n * messages stream in — flushed on drawer close so a fast open/close\n * still persists the receipt. Named beside its sibling cadences so\n * every timing knob lives in one file.\n */\nexport const TICKET_MARK_READ_DEBOUNCE_MS = 2000;\n\n/**\n * Ticket deep-link SSOT — DEFINED in the server-safe param-keys module\n * (`utils/dev-sections/dev-section-param-keys.ts`, beside its sibling\n * `devSectionAnchorId`) because producers span both worlds: the hub's\n * SERVER-side chat-ref builder and the client header cell. Re-exported\n * here so ticket-surface consumers keep one import home.\n */\nexport { TICKET_OPEN_PARAM, buildTicketOpenHref } from '../../utils/dev-sections/dev-section-param-keys';\n\n/**\n * Centralized toast copy. Keep all wording here so QA / localization\n * can find every user-visible string in one file.\n */\nexport const TOAST_COPY = {\n open_success: { title: 'Ticket opened', description: 'We received your message and will follow up shortly.' },\n open_mirror_pending: { title: 'Ticket opened', description: 'Syncing — your ticket will appear momentarily.' },\n close_success: { title: 'Ticket closed' },\n reopen_success: { title: 'Ticket reopened' },\n comment_success: { title: 'Comment added' },\n attach_success: { title: 'Files attached' },\n // Failure variants are constructed dynamically from MappedTicketActionError.\n} as const;\n"]}
1
+ {"version":3,"sources":["/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-4THS6V3I.cjs","../src/components/tickets/ticket-live-provider.tsx","../src/components/tickets/types.ts"],"names":[],"mappings":"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","file":"/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-4THS6V3I.cjs","sourcesContent":[null,"'use client';\n\n/**\n * TicketLiveProvider — the ONE client home for support-ticket realtime.\n *\n * DOMAIN logic only — connection lifecycle (subscribed-confirm, server\n * retry grace, failed-reconnect pacing, hidden-tab suspend/resume) is\n * owned entirely by the common SSE client (`createSseSubscription`);\n * this provider consumes its consolidated `onConnectedChange` signal.\n *\n * Owns:\n * - the unread summary — delivered EXCLUSIVELY by the stream\n * (`ticket-summary` frames on connect + debounced after events) and\n * by `ticket-read` responses. The client holds NO summary fetch\n * path: resync-on-(re)connect is served by the SERVER (every\n * connect pushes a fresh summary);\n * - query invalidation on events (list + open drawer refresh live);\n * - `markRead` with local write-through zeroing for 0-latency,\n * reconciled by the response's server-computed summary;\n * - the ticket-contract `no-stream` (204) retry policy.\n *\n * There is NO polling anywhere and NO summary endpoint. Hosts whose\n * stream endpoint 404s (terminal) or is absent get no unread indication\n * — the indication IS a realtime feature.\n *\n * Invalidation semantics on events — deliberately `invalidateQueries`,\n * NOT `setQueryData` patches: the ticket hooks run `gcTime: 0`, so\n * there is usually no cache entry to patch, and `TicketsCacheSlot`\n * shape rules apply when there is.\n */\n\nimport { useQueryClient } from '@tanstack/react-query';\nimport React from 'react';\nimport { useChatRuntime } from '../../contexts/chat-runtime-context';\nimport { embedAuthedFetch } from '../../utils/embed-authed-fetch';\nimport { createSseSubscription, type SseSubscription, type SseTransportStatus } from '../../utils/sse-subscription';\nimport { useChatIdentity } from '../chat/hooks/use-chat-identity';\nimport type { TicketMessageStreamData, TicketStreamEvent, TicketUnreadSummary } from './types';\n\n// Ticket realtime is its OWN api surface — deliberately NOT under\n// `/api/chat/agent/*` (that prefix is the chat agent's tool surface;\n// ticket streaming/read-receipts are unrelated to chat).\nconst TICKET_STREAM_ENDPOINT = '/api/tickets/stream';\nconst TICKET_READ_ENDPOINT = '/api/tickets/read';\n\nconst EMPTY_SUMMARY: TicketUnreadSummary = { totalUnread: 0, tickets: {}, latestUnreadAt: {} };\n\n/** ISO stamps can carry mixed timezone offsets (server `+00:00` vs an\n * optimistic local `Z`) — lexicographic string comparison mis-orders\n * those, so every stamp comparison goes through parsed epoch ms.\n * Missing/unparseable stamps sort oldest. */\nconst stampMs = (stamp: string | null | undefined): number => {\n if (!stamp) return 0;\n const ms = Date.parse(stamp);\n return Number.isNaN(ms) ? 0 : ms;\n};\n\n/** Trailing debounce for event-burst invalidations (an agent pasting 5\n * messages must not fire 5 list refetches). */\nconst INVALIDATE_DEBOUNCE_MS = 1_500;\n\nexport interface TicketLiveContextValue {\n /** Viewer has a resolved, non-anon chat identity. Surfaces (e.g. the\n * header cell) hide themselves when false — computed here so they\n * don't each spin up their own identity resolution. */\n authed: boolean;\n /** Realtime-subscribed (server-confirmed), not merely HTTP-open. */\n connected: boolean;\n /** Unread total across tickets — drives the header count badge. */\n unreadTotal: number;\n /** Per-ticket unread counts (missing key = 0) — drives row dots.\n * The currently-open ticket is masked to 0. */\n unreadByTicket: Record<string, number>;\n /** The ticket with the NEWEST unread reply (open ticket excluded) —\n * the header cell routes to it via the SSOT deep link\n * (`buildTicketOpenHref`). Null when nothing is unread. */\n nextUnreadTicketId: string | null;\n /** Mark a ticket read: POSTs the receipt, zeroes the ticket locally\n * (0-latency), then applies the response's server-computed summary. */\n markRead: (ticketExternalId: string) => Promise<void>;\n /** Drawer open/close registration — suppresses unread bumps for the\n * open ticket and masks it in the derived map. */\n setOpenTicketId: (ticketExternalId: string | null) => void;\n /** Hint that the viewer just created a ticket — reconnects the stream\n * (fresh ownership set + fresh server-pushed summary). */\n notifyTicketCreated: () => void;\n}\n\nconst TicketLiveContext = React.createContext<TicketLiveContextValue | null>(null);\n\nexport function useOptionalTicketLive(): TicketLiveContextValue | null {\n return React.useContext(TicketLiveContext);\n}\n\nexport function useTicketLive(): TicketLiveContextValue {\n const ctx = React.useContext(TicketLiveContext);\n if (!ctx) {\n throw new Error('useTicketLive must be used within <TicketLiveProvider>');\n }\n return ctx;\n}\n\nfunction isTicketMessageData(value: unknown): value is TicketMessageStreamData {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as TicketMessageStreamData).ticket_external_id === 'string'\n );\n}\n\nfunction isSummaryData(value: unknown): value is TicketUnreadSummary {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as TicketUnreadSummary).totalUnread === 'number' &&\n typeof (value as TicketUnreadSummary).tickets === 'object'\n );\n}\n\nexport interface TicketLiveProviderProps {\n children: React.ReactNode;\n /**\n * Whether ticket realtime is available to this host at all (feature flag,\n * session, entitlement). `false` provides NO context — `useOptionalTicketLive()`\n * reads `null`, `useTicketLive()` throws, and no identity or stream request is\n * made: byte-for-byte what a host got by not mounting this at all.\n *\n * It exists so a host never has to mount this CONDITIONALLY. A host that writes\n * `enabled ? <TicketLiveProvider>{children}</TicketLiveProvider> : <>{children}</>`\n * changes the element TYPE at that position the moment its flag answers, and React\n * tears down and remounts everything below. In openframe-frontend that wrapper sat\n * around the whole `AppLayout`, so every cold load remounted the app shell and the\n * page under it — visibly, as a chat drawer opened by a deep link replaying its\n * open animation. Mount it always and pass the answer here instead.\n *\n * One consequence of mounting it always: a `ChatRuntime` must be above it even\n * while disabled. The identity resolver reads one unconditionally (Rules of\n * Hooks) and throws without it; `enabled` gates the request, not the read.\n */\n enabled?: boolean;\n}\n\nexport function TicketLiveProvider({ children, enabled = true }: TicketLiveProviderProps) {\n const runtime = useChatRuntime();\n const identity = useChatIdentity(enabled);\n const queryClient = useQueryClient();\n\n const streamUrl = runtime?.endpoints.ticketStreamUrl ?? TICKET_STREAM_ENDPOINT;\n const readUrl = runtime?.endpoints.ticketReadUrl ?? TICKET_READ_ENDPOINT;\n\n // Gates the stream below (via `streamEnabled`) as well as every surface that\n // reads `authed` — one predicate, so a disabled provider is also a silent one.\n const authed = enabled && identity.authTier !== 'anon' && !!identity.user?.email;\n\n const [connected, setConnected] = React.useState(false);\n // The SINGLE unread source — written ONLY from server-computed\n // summaries (stream frames + markRead responses) plus the two\n // 0-latency optimistic writes (event bump, markRead zero), both\n // reconciled by the next server summary.\n const [summary, setSummary] = React.useState<TicketUnreadSummary>(EMPTY_SUMMARY);\n // State + ref for the open drawer: state drives the derived map,\n // the ref lets stream callbacks read it without re-subscribing.\n const [openTicketIdState, setOpenTicketIdState] = React.useState<string | null>(null);\n const openTicketIdRef = React.useRef<string | null>(null);\n\n // ---- Debounced (trailing) invalidation of ticket queries on events ----\n const pendingTicketIdsRef = React.useRef<Set<string>>(new Set());\n const invalidateTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);\n const flushInvalidations = React.useCallback(() => {\n invalidateTimerRef.current = null;\n const ids = Array.from(pendingTicketIdsRef.current);\n pendingTicketIdsRef.current.clear();\n void queryClient.invalidateQueries({ queryKey: ['tickets'] });\n for (const id of ids) {\n void queryClient.invalidateQueries({ queryKey: ['ticket-engagements', id] });\n }\n }, [queryClient]);\n const scheduleInvalidation = React.useCallback(\n (ticketId?: string) => {\n if (ticketId) pendingTicketIdsRef.current.add(ticketId);\n if (invalidateTimerRef.current) return;\n invalidateTimerRef.current = setTimeout(flushInvalidations, INVALIDATE_DEBOUNCE_MS);\n },\n [flushInvalidations],\n );\n\n // ---- Optimistic unread writes (reconciled by server summaries) ----\n const bumpUnread = React.useCallback((ticketId: string, createdAt?: string | null) => {\n setSummary(prev => {\n const stamp = createdAt ?? new Date().toISOString();\n const existing = prev.latestUnreadAt[ticketId];\n return {\n totalUnread: prev.totalUnread + 1,\n tickets: { ...prev.tickets, [ticketId]: (prev.tickets[ticketId] ?? 0) + 1 },\n latestUnreadAt: {\n ...prev.latestUnreadAt,\n [ticketId]: existing && stampMs(existing) > stampMs(stamp) ? existing : stamp,\n },\n };\n });\n }, []);\n\n const zeroUnread = React.useCallback((ticketId: string) => {\n setSummary(prev => {\n const current = prev.tickets[ticketId] ?? 0;\n if (current === 0) return prev;\n const tickets = { ...prev.tickets };\n delete tickets[ticketId];\n const latestUnreadAt = { ...prev.latestUnreadAt };\n delete latestUnreadAt[ticketId];\n return { totalUnread: Math.max(0, prev.totalUnread - current), tickets, latestUnreadAt };\n });\n }, []);\n\n // ---- Stream lifecycle (owned by the SSE CLIENT — the provider only\n // consumes its consolidated signals) ----\n const subscriptionRef = React.useRef<SseSubscription | null>(null);\n const noStreamRef = React.useRef(false);\n\n // Stable handler refs so the subscription effect doesn't churn.\n const handlersRef = React.useRef<{\n onEvent: (eventName: string, data: unknown) => void;\n onStatus: (status: SseTransportStatus) => void;\n onConnected: (connected: boolean) => void;\n }>({ onEvent: () => {}, onStatus: () => {}, onConnected: () => {} });\n\n const handleConnected = (isConnected: boolean) => {\n setConnected(isConnected);\n if (isConnected) {\n // Event-gap healing on every (re)connect: the SERVER pushes a\n // fresh summary; the open drawer refetches through the ACL'd path.\n const openId = openTicketIdRef.current;\n if (openId) {\n void queryClient.invalidateQueries({ queryKey: ['ticket-engagements', openId] });\n }\n }\n };\n\n const handleEvent = (eventName: string, data: unknown) => {\n if (eventName === 'ticket-summary') {\n const frame = data as TicketStreamEvent | null;\n if (isSummaryData(frame?.data)) {\n setSummary({\n totalUnread: frame.data.totalUnread,\n tickets: frame.data.tickets ?? {},\n latestUnreadAt: frame.data.latestUnreadAt ?? {},\n });\n }\n return;\n }\n\n if (eventName === 'ticket-resync') {\n // Ownership gained / degraded payload — a fresh ticket-summary\n // frame follows from the server; refresh the query surfaces.\n scheduleInvalidation();\n return;\n }\n\n if (eventName === 'ticket-status') {\n // Meaningful ticket-row change (close/reopen/pipeline move). The\n // server pushes an updated summary right after (closures count as\n // unread server-side); here we just refresh the query surfaces so\n // the status badge flips live.\n const frame = data as TicketStreamEvent | null;\n const ticketId = (frame?.data as { ticket_external_id?: string } | undefined)?.ticket_external_id;\n scheduleInvalidation(ticketId);\n return;\n }\n\n if (eventName === 'ticket-message') {\n const frame = data as TicketStreamEvent | null;\n const payload = frame?.data;\n if (!isTicketMessageData(payload)) {\n scheduleInvalidation();\n return;\n }\n // Always invalidate (own replies from another tab render live);\n // bump unread ONLY on the server-stamped predicate, and never for\n // the ticket whose drawer is open. The server's debounced summary\n // push reconciles moments later.\n scheduleInvalidation(payload.ticket_external_id);\n if (payload.countsAsUnread && payload.ticket_external_id !== openTicketIdRef.current) {\n bumpUnread(payload.ticket_external_id, payload.hubspot_created_at);\n }\n }\n };\n\n const handleStatus = (status: SseTransportStatus) => {\n // The client owns connection lifecycle (confirm timeout, server\n // retry grace, failed-reconnect pacing, hidden-suspend). The only\n // transport status with DOMAIN meaning is the ticket contract's\n // `no-stream` (204 = zero owned tickets): remember it so a domain\n // signal (own create_ticket, window focus) can retry.\n if (status === 'open') noStreamRef.current = false;\n if (status === 'no-stream') noStreamRef.current = true;\n };\n\n // Publish the three handlers into the stable ref. In an unconditional effect\n // rather than in the render body: every caller is an SSE transport callback,\n // which can only fire after a commit, and a render attempt React discards\n // must not be able to leave a handler behind that closes over state the user\n // never saw. Declared BEFORE the subscription effect so it is filled first\n // in the same flush.\n React.useEffect(() => {\n handlersRef.current.onConnected = handleConnected;\n handlersRef.current.onEvent = handleEvent;\n handlersRef.current.onStatus = handleStatus;\n });\n\n // Mount/unmount of the subscription — gated on auth + a usable URL.\n const streamEnabled = authed && !!streamUrl;\n React.useEffect(() => {\n if (!streamEnabled) return undefined;\n if (typeof window === 'undefined') return undefined;\n\n subscriptionRef.current = createSseSubscription({\n url: streamUrl,\n onEvent: (name, data) => handlersRef.current.onEvent(name, data),\n onStatusChange: status => handlersRef.current.onStatus(status),\n onConnectedChange: isConnected => handlersRef.current.onConnected(isConnected),\n // Long-lived per-user stream: suspend while hidden (scale relief),\n // resume + reconnect on visible/online — all inside the client.\n pauseWhenHidden: true,\n });\n\n const retryIfIdle = () => {\n // After a 204 (no-stream), a focus signal is the retry trigger.\n if (noStreamRef.current) {\n noStreamRef.current = false;\n subscriptionRef.current?.reconnectNow();\n }\n };\n window.addEventListener('focus', retryIfIdle);\n\n return () => {\n window.removeEventListener('focus', retryIfIdle);\n if (invalidateTimerRef.current) {\n clearTimeout(invalidateTimerRef.current);\n invalidateTimerRef.current = null;\n }\n subscriptionRef.current?.close();\n subscriptionRef.current = null;\n setConnected(false);\n setSummary(EMPTY_SUMMARY);\n };\n // handlersRef is stable; streamUrl/auth changes remount the stream.\n }, [streamEnabled, streamUrl]);\n\n // ---- Public API ----\n const markRead = React.useCallback(\n async (ticketExternalId: string) => {\n try {\n const response = await embedAuthedFetch(readUrl, {\n method: 'POST',\n body: JSON.stringify({ ticket_id: ticketExternalId }),\n });\n if (!response.ok) return;\n // 0-latency local zero, then reconcile with the response's\n // server-computed summary (THE truth — includes closure\n // accounting and other tickets).\n zeroUnread(ticketExternalId);\n const body = (await response.json().catch(() => null)) as { summary?: TicketUnreadSummary } | null;\n if (isSummaryData(body?.summary)) {\n setSummary({\n totalUnread: body.summary.totalUnread,\n tickets: body.summary.tickets ?? {},\n latestUnreadAt: body.summary.latestUnreadAt ?? {},\n });\n }\n } catch {\n // Non-fatal — the receipt lands on the next markRead; unread\n // state reconciles via the next server summary push.\n }\n },\n [readUrl, zeroUnread],\n );\n\n const setOpenTicketId = React.useCallback((ticketExternalId: string | null) => {\n openTicketIdRef.current = ticketExternalId;\n setOpenTicketIdState(ticketExternalId);\n }, []);\n\n const notifyTicketCreated = React.useCallback(() => {\n // Fresh connect → fresh ownership set + fresh server summary\n // (covers both the no-stream 204 wait state and an already-live\n // stream whose set predates the new ticket).\n noStreamRef.current = false;\n subscriptionRef.current?.reconnectNow();\n }, []);\n\n // Derived unread map — open ticket masked to 0 so a server summary\n // landing inside the markRead debounce window can't transiently\n // re-badge it.\n const value = React.useMemo<TicketLiveContextValue>(() => {\n let unreadByTicket = summary.tickets;\n let unreadTotal = summary.totalUnread;\n if (openTicketIdState && unreadByTicket[openTicketIdState]) {\n const masked = unreadByTicket[openTicketIdState];\n unreadByTicket = { ...unreadByTicket };\n delete unreadByTicket[openTicketIdState];\n unreadTotal = Math.max(0, unreadTotal - masked);\n }\n // Ticket with the NEWEST unread reply (compared as parsed epoch ms\n // via `stampMs` — mixed timezone offsets break string ordering) —\n // header cell routes there. Falls back to any unread key if a\n // timestamp is missing.\n let nextUnreadTicketId: string | null = null;\n let nextStampMs = 0;\n for (const id of Object.keys(unreadByTicket)) {\n const ms = stampMs(summary.latestUnreadAt[id]);\n if (nextUnreadTicketId === null || ms > nextStampMs) {\n nextUnreadTicketId = id;\n nextStampMs = ms;\n }\n }\n return {\n authed,\n connected,\n unreadTotal,\n unreadByTicket,\n nextUnreadTicketId,\n markRead,\n setOpenTicketId,\n notifyTicketCreated,\n };\n }, [summary, authed, connected, openTicketIdState, markRead, setOpenTicketId, notifyTicketCreated]);\n\n return <TicketLiveContext.Provider value={enabled ? value : null}>{children}</TicketLiveContext.Provider>;\n}\n","/**\n * Wire shape of a row returned by `POST /api/chat/agent/find-ticket`.\n * Mirrors the executor's projection at `lib/data/hubspot-tools.ts`\n * (`FIND_TICKET_SELECT` / `FindTicketResult`).\n *\n * Cross-repo duplication is INTENTIONAL: this lib ships independently\n * of the hub, so we can't import `FindTicketResult` from\n * `hubspot-tools.ts` directly. If the server adds a column to\n * `FIND_TICKET_SELECT`, also add it here. The smoke test in §F of the\n * plan covers the happy path; a wire-contract test belongs in the hub.\n *\n * `find-ticket` returns `customer_emails: string[]` (jsonb array), NOT\n * a single `customer_email`. The list is server-self-scoped to the\n * caller's session email; the array is exposed for admin/staff\n * surfaces, which the ticket center doesn't render.\n */\nexport interface TicketData {\n id: string;\n /** HubSpot ticket id (display number, e.g. \"1234\"). */\n external_id: string;\n subject: string | null;\n /** Short (≤400 char) HTML-stripped preview of the ticket body —\n * used for the list-card subtitle when needed. */\n preview: string | null;\n /** Longer (≤4k char) sanitized body. INCLUDES every appended\n * `content_addendum` comment because the `update_ticket` executor\n * reads + re-writes the `content` property server-side with a\n * `---` separator. Render this in the drawer's description block\n * and the user sees both the original message + every comment they\n * (or staff) added. */\n body: string | null;\n /** Canonical OPEN | CLOSED (HubSpot pipeline derived). */\n status: string | null;\n /** Human label like \"New\" / \"Working on it\" / \"Waiting on contact\" /\n * \"Closed\". Drives the badge text; canonical status drives color. */\n pipeline_stage_label: string | null;\n clickup_task_id: string | null;\n /** Snapshot of the linked ClickUp delivery task — populated server-side\n * via the `clickup_tasks` mirror when `clickup_task_id` is set. Drives\n * the \"Linked delivery\" card surface on the ticket drawer (status\n * badge + ClickUp deep link). `null` when no link OR the ClickUp row\n * was deleted / not yet synced. */\n clickup: TicketClickupSummary | null;\n priority: string | null;\n customer_emails: string[];\n customer_company: string | null;\n /** HubSpot contact's display name. Drives the customer attribution\n * on the drawer when the viewer is NOT the customer themselves\n * (admin browsing / multi-contact second viewer). Conversations\n * API messages don't carry per-message sender info on Custom\n * Channels, so this is the only reliable source for \"what's the\n * customer's name.\" */\n customer_name: string | null;\n /** HubSpot owner id of the agent assigned to this ticket. Carried as\n * raw id for debugging; rendering goes through `assignedOwner`. Null\n * when unassigned. */\n assigned_to: string | null;\n /** Resolved assigned-owner profile — name + email + avatar. Populated\n * server-side via `attachOwnerProfiles` which joins through the\n * `hubspot_owners` mirror to `profiles` by email. Drives the\n * \"Assigned to\" attribution in the drawer header. Null when\n * unassigned OR the owner couldn't be resolved (rare — only when\n * the agent was deleted from HubSpot between the ticket update and\n * the next owners reconcile). */\n assignedOwner: TicketAssignedOwner | null;\n hubspot_updated_at: string;\n}\n\n/** Resolved profile of a ticket's assigned agent — surfaced in the\n * drawer header. Subset of the server's `MirroredOwnerProfile`\n * trimmed to just the rendering fields. */\nexport interface TicketAssignedOwner {\n name: string | null;\n email: string | null;\n avatarUrl: string | null;\n}\n\n/** Compact projection of a linked ClickUp task — matches the server's\n * `ClickupSummary` and aligns with `DeliveryItem` so the linked-card\n * on a ticket can render through the same `DeliveryRow` primitive used\n * on `/bug-fixes-and-enhancements`. */\nexport interface TicketClickupSummary {\n /** ClickUp task external_id (e.g. \"86ad4e022\"). Used as the\n * `?focus=<id>` URL param to scroll the public delivery page to\n * this row. */\n external_id: string;\n title: string | null;\n description: string | null;\n /** ClickUp status name — e.g. \"complete\" / \"working\" / \"design approved\"\n * / \"waiting for release\". Used as the badge label. */\n status: string | null;\n /** ClickUp's per-status hex color (e.g. \"#008844\"). Forwarded to the\n * badge so colors match the ClickUp board exactly. */\n status_color: string | null;\n /** Bucket — `'backlog' | 'working' | 'complete' | 'unknown'`. Used as\n * a fallback when status_color is missing. */\n status_category: string | null;\n /** ClickUp custom item label (`'Bug'` / `'Request'`) — drives the\n * type badge (\"BUG-FIX\" / \"ENHANCEMENT\"). */\n task_type: string | null;\n custom_item_id: number | null;\n /** Every ClickUp list the task is associated with. UI joins with \", \". */\n list_names: string[];\n /** Unix-ms timestamps so the row's \"ACTIVE X ago\" subtitle uses the\n * shared `getRelativeTime()` helper. */\n date_opened: number | null;\n date_updated: number | null;\n date_closed: number | null;\n /** Direct https://app.clickup.com/t/<id> deep link. Kept on the wire\n * for admin surfaces; the customer-facing linked card navigates\n * internally instead. */\n clickup_url: string | null;\n /** Composed server-side via the SAME `buildDevSectionUrl` helper the\n * chat-inline delivery card uses. Carries `?search=<id>` so the\n * delivery list filters to that single task on landing. */\n delivery_href: string;\n /** Target platform name for the host's `useNavLink` to decide\n * same-tab vs new-tab on cross-platform links. */\n delivery_target_platform: string;\n /** Release version label set by the delivery team, e.g. \"0.9\" / \"1.0\".\n * Shown beside the status when present. */\n target_version: string | null;\n}\n\n/**\n * Optimistic placeholder a `submitTicket` call prepends to the list\n * BEFORE the server roundtrip resolves. Drawer is hidden until the\n * real id arrives. The wrapper destructures `_optimistic` before\n * forwarding to `<ChatTicketItem>` so the DOM doesn't see an unknown\n * prop.\n */\nexport interface OptimisticTicket extends TicketData {\n _optimistic: true;\n}\n\nexport type AnyTicket = TicketData | OptimisticTicket;\n\nexport function isOptimistic(t: AnyTicket): t is OptimisticTicket {\n return (t as OptimisticTicket)._optimistic === true;\n}\n\n/**\n * Shape of a single `['tickets', …]` TanStack-Query cache slot.\n * Mirrors `FindTicketResponse` in `hooks/use-tickets-list.ts` — kept\n * here because the cache-mutation call sites in `useTicketActions` and\n * `<HelpCenterList>` would otherwise have to redeclare the shape inline.\n *\n * A 2026-05-29 prod regression (`t.map is not a function` on\n * close/reopen) was caused by assuming the cache held a bare\n * `TicketData[]` instead of this wrapper — every helper that calls\n * `queryClient.setQueriesData` / `getQueriesData` on `['tickets']`\n * MUST type the value through this shape and project / reassemble\n * `tickets` explicitly.\n */\nexport interface TicketsCacheSlot {\n tickets?: TicketData[];\n count?: number;\n totalCount?: number;\n page?: number;\n pageSize?: number;\n totalPages?: number;\n scope?: 'self' | 'all';\n}\n\n/**\n * Stable server-side error codes the ticket-action helpers route\n * through `mapTicketActionError`. Anything else is treated as a generic\n * server error.\n *\n * Reply-specific codes (`HUBSPOT_5XX` / `HUBSPOT_400_VALIDATION` /\n * `HUBSPOT_404_THREAD` / `HUBSPOT_REPLY_UNKNOWN`) drive the drawer\n * banner that appears above the composer when a customer reply fails.\n * Distinct from `HUBSPOT_DISCONNECTED` (whole-system) and\n * `TICKET_NOT_FOUND` (terminal-row) — the reply codes are per-attempt\n * + retryable except for `HUBSPOT_404_THREAD`.\n */\nexport type TicketActionErrorCode =\n | 'PROPOSAL_NOT_CLAIMABLE'\n | 'TICKET_NOT_FOUND'\n | 'TICKET_OWNERSHIP_DENIED'\n | 'HUBSPOT_DISCONNECTED'\n | 'RATE_LIMITED'\n | 'INVALID_TOOL_ARGS'\n | 'HUBSPOT_5XX'\n | 'HUBSPOT_400_VALIDATION'\n | 'HUBSPOT_404_THREAD'\n | 'HUBSPOT_REPLY_UNKNOWN'\n | 'UNKNOWN';\n\nexport interface MappedTicketActionError {\n code: TicketActionErrorCode;\n /** Human-readable copy safe to show in a toast. */\n message: string;\n /** When true, the form should disable submit + show the\n * support-down banner. Set only for HUBSPOT_DISCONNECTED. */\n supportSystemDown: boolean;\n /** When true, the helper should remove the affected row optimistically\n * (TICKET_NOT_FOUND). */\n removeRowFromCache: boolean;\n /** Retry hint surfaced from a 429 response. Caller decides whether\n * to mention it in the toast. */\n retryAfterSeconds?: number;\n}\n\n/**\n * Defensive client-side cap on ticket text (initial content + comment\n * addendums). HubSpot Note engagements accept more, but a 100KB paste\n * should fail fast at the UI rather than burning a server round-trip.\n * Both the open-ticket form and the per-row comment textarea import\n * this so a future server-side hardening only touches one place.\n */\nexport const TICKET_TEXT_MAX_CHARS = 5000;\n\n/**\n * Wire contract of the ticket live stream (`GET /api/chat/agent/\n * ticket-stream`, SSE). Producer (the hub route's `mapEvent`) imports\n * THIS type — same producer/consumer SSOT pattern as\n * `ConversationEngagementWire = TicketEngagement`. A schema drift here\n * would be a silent serialization bug; keep one named contract.\n *\n * Frames carry METADATA ONLY — never message bodies. Content is always\n * refetched through the ACL'd `list-engagements` path (one read path).\n *\n * Event names (`eventType`):\n * - `ticket-message` — a message row landed in the mirror for one of\n * the viewer's tickets. `data` carries the metadata.\n * - `ticket-status` — a MEANINGFUL ticket-row change (status /\n * pipeline stage / closure) landed in the mirror. Server-side\n * old-vs-new diffing drops no-op writes (sync stamps). Clients\n * invalidate + refetch the summary (closures count as unread\n * server-side via `closed_at` vs the read receipt).\n * - `ticket-summary` — the server-computed `TicketUnreadSummary`.\n * THE stream is the summary webservice: pushed on connect (server-\n * side resync-on-reconnect) and re-pushed (debounced) after every\n * delivered event. Clients hold NO summary fetch path.\n * - `ticket-resync` — degraded frame: something happened that the\n * server couldn't fully describe (truncated Realtime payload, or\n * the connection's ownership set just gained tickets). A fresh\n * `ticket-summary` follows it; clients invalidate queries.\n *\n * The server also emits `status` frames (`subscribed` / `retrying` /\n * `reconnect_failed`) from the shared SSE utility — clients derive\n * `connected` from those, NOT from the HTTP stream being open.\n */\nexport type TicketStreamEventType = 'ticket-message' | 'ticket-status' | 'ticket-summary' | 'ticket-resync';\n\n/** Metadata payload of a `ticket-message` frame. */\nexport type TicketMessageStreamData = {\n ticket_external_id: string;\n /** Mirror row's HubSpot message id. */\n message_id: string;\n /** Server-stamped via the same direction predicate the wire mapper\n * uses. Clients never re-derive author classification. */\n authorRole: 'customer' | 'support';\n /** Server-stamped via the visibility+direction unread predicate\n * (`countsAsUnread` in the hub's conversations DAL). ONLY frames\n * with `true` may bump client-side unread state. */\n countsAsUnread: boolean;\n hubspot_created_at: string | null;\n};\n\n/** Metadata payload of a `ticket-status` frame. */\nexport type TicketStatusStreamData = {\n ticket_external_id: string;\n /** Canonical OPEN | CLOSED after the change. */\n status: string | null;\n pipeline_stage_label: string | null;\n closed_at: string | null;\n};\n\nexport interface TicketStreamEvent {\n eventType: TicketStreamEventType;\n /** Human-readable label (SSE envelope compat) — clients ignore it. */\n message: string;\n /** `ticket-message` → TicketMessageStreamData; `ticket-status` →\n * TicketStatusStreamData; `ticket-summary` → TicketUnreadSummary;\n * absent on `ticket-resync`. */\n data?: TicketMessageStreamData | TicketStatusStreamData | TicketUnreadSummary;\n}\n\n/**\n * The SINGLE unread source, delivered EXCLUSIVELY over the stream\n * (`ticket-summary` frames on connect + after events) and in\n * `ticket-read` responses. There is no summary fetch endpoint. Header\n * badge total AND per-row dots both read this via the\n * `TicketLiveProvider` map; missing keys mean 0.\n */\nexport type TicketUnreadSummary = {\n totalUnread: number;\n tickets: Record<string, number>;\n /** ISO timestamp of the NEWEST unread message per ticket (same keys as\n * `tickets`). Drives \"route to the most recent update\" on the header\n * cell: the ticket with the max value here is `nextUnreadTicketId`. */\n latestUnreadAt: Record<string, string>;\n};\n\n/**\n * Trailing debounce for `markRead` while the drawer is open and new\n * messages stream in — flushed on drawer close so a fast open/close\n * still persists the receipt. Named beside its sibling cadences so\n * every timing knob lives in one file.\n */\nexport const TICKET_MARK_READ_DEBOUNCE_MS = 2000;\n\n/**\n * Ticket deep-link SSOT — DEFINED in the server-safe param-keys module\n * (`utils/dev-sections/dev-section-param-keys.ts`, beside its sibling\n * `devSectionAnchorId`) because producers span both worlds: the hub's\n * SERVER-side chat-ref builder and the client header cell. Re-exported\n * here so ticket-surface consumers keep one import home.\n */\nexport { TICKET_OPEN_PARAM, buildTicketOpenHref } from '../../utils/dev-sections/dev-section-param-keys';\n\n/**\n * Centralized toast copy. Keep all wording here so QA / localization\n * can find every user-visible string in one file.\n */\nexport const TOAST_COPY = {\n open_success: { title: 'Ticket opened', description: 'We received your message and will follow up shortly.' },\n open_mirror_pending: { title: 'Ticket opened', description: 'Syncing — your ticket will appear momentarily.' },\n close_success: { title: 'Ticket closed' },\n reopen_success: { title: 'Ticket reopened' },\n comment_success: { title: 'Comment added' },\n attach_success: { title: 'Files attached' },\n // Failure variants are constructed dynamically from MappedTicketActionError.\n} as const;\n"]}
@@ -3,16 +3,16 @@ import {
3
3
  FileDownloadCard,
4
4
  GoogleSheetsViewer,
5
5
  PdfViewer
6
- } from "./chunk-3NIZ2VLQ.js";
6
+ } from "./chunk-GXKAWMDT.js";
7
7
  import {
8
8
  MobileNavigationDropdown,
9
9
  MultiLevelNavigation,
10
10
  StickySectionNav
11
- } from "./chunk-NINO3PI7.js";
11
+ } from "./chunk-DGOUPZJ7.js";
12
12
  import {
13
13
  DocSearchBar,
14
14
  useDocSearch
15
- } from "./chunk-JBJZL5PI.js";
15
+ } from "./chunk-SQQQVVV2.js";
16
16
  import {
17
17
  CategorySidebarSkeleton,
18
18
  DEFAULT_FOLDER_INDEX_FILE,
@@ -22,7 +22,7 @@ import {
22
22
  findDocNodeByPath,
23
23
  getDocAncestorNodeIds,
24
24
  stripFolderIndexFromPath
25
- } from "./chunk-MHTB67ZH.js";
25
+ } from "./chunk-LROQXUFX.js";
26
26
  import {
27
27
  contentFetch
28
28
  } from "./chunk-UAKWSR3U.js";
@@ -1098,4 +1098,4 @@ export {
1098
1098
  EmbedSkeleton,
1099
1099
  DocsHubPage
1100
1100
  };
1101
- //# sourceMappingURL=chunk-WFH52OVQ.js.map
1101
+ //# sourceMappingURL=chunk-A2ZP6WX4.js.map
@@ -10,7 +10,7 @@ import {
10
10
  StatusFilterComponent,
11
11
  TASK_TYPE_LABELS,
12
12
  TASK_TYPE_TEXT_COLORS
13
- } from "./chunk-MHTB67ZH.js";
13
+ } from "./chunk-LROQXUFX.js";
14
14
  import {
15
15
  usePathname,
16
16
  useRouter,
@@ -302,4 +302,4 @@ export {
302
302
  DevCardRowSkeletonList,
303
303
  DeliveryRow
304
304
  };
305
- //# sourceMappingURL=chunk-U6JC3RS2.js.map
305
+ //# sourceMappingURL=chunk-APLL2BVR.js.map
@@ -5,13 +5,13 @@ var _chunkBOM6MMWBcjs = require('./chunk-BOM6MMWB.cjs');
5
5
 
6
6
 
7
7
 
8
- var _chunkMZAZUSPQcjs = require('./chunk-MZAZUSPQ.cjs');
8
+ var _chunkCKQ4ZBDNcjs = require('./chunk-CKQ4ZBDN.cjs');
9
9
 
10
10
 
11
11
  var _chunkNMXL34Z2cjs = require('./chunk-NMXL34Z2.cjs');
12
12
 
13
13
 
14
- var _chunkAF4XOURNcjs = require('./chunk-AF4XOURN.cjs');
14
+ var _chunkXEESEFGTcjs = require('./chunk-XEESEFGT.cjs');
15
15
 
16
16
 
17
17
  var _chunkDMXI5D2Qcjs = require('./chunk-DMXI5D2Q.cjs');
@@ -30,7 +30,7 @@ var _chunkDMXI5D2Qcjs = require('./chunk-DMXI5D2Q.cjs');
30
30
 
31
31
 
32
32
 
33
- var _chunkIBUFFKAKcjs = require('./chunk-IBUFFKAK.cjs');
33
+ var _chunkT32BTPL5cjs = require('./chunk-T32BTPL5.cjs');
34
34
 
35
35
 
36
36
 
@@ -46,7 +46,7 @@ var _chunkJGVJRUGBcjs = require('./chunk-JGVJRUGB.cjs');
46
46
 
47
47
 
48
48
 
49
- var _chunkSI77HF4Ucjs = require('./chunk-SI77HF4U.cjs');
49
+ var _chunkLL35WZZScjs = require('./chunk-LL35WZZS.cjs');
50
50
 
51
51
 
52
52
  var _chunkIJUQQTMVcjs = require('./chunk-IJUQQTMV.cjs');
@@ -89,8 +89,8 @@ var _chunkX7CEWD6Vcjs = require('./chunk-X7CEWD6V.cjs');
89
89
  // src/components/shared/product-release/product-releases-view.tsx
90
90
  var _jsxruntime = require('react/jsx-runtime');
91
91
  var DEFAULT_ENDPOINT = "/api/releases";
92
- var DEFAULT_SEARCH_PARAM_KEY = _chunkIBUFFKAKcjs.DEV_SECTION_PARAM_KEYS.search;
93
- var DEFAULT_STATUS_PARAM_KEY = _chunkIBUFFKAKcjs.DEV_SECTION_PARAM_KEYS.releaseStatus;
92
+ var DEFAULT_SEARCH_PARAM_KEY = _chunkT32BTPL5cjs.DEV_SECTION_PARAM_KEYS.search;
93
+ var DEFAULT_STATUS_PARAM_KEY = _chunkT32BTPL5cjs.DEV_SECTION_PARAM_KEYS.releaseStatus;
94
94
  var DEFAULT_PAGE_PARAM_KEY = "page";
95
95
  function ReleaseRow({
96
96
  release,
@@ -99,7 +99,7 @@ function ReleaseRow({
99
99
  }) {
100
100
  const runtime = _chunkU2YLMFJKcjs.useChatRuntime.call(void 0, );
101
101
  const router = _chunkSADRZ4YIcjs.useRouter.call(void 0, );
102
- const cta = _chunkIBUFFKAKcjs.resolveContentHref.call(void 0, _optionalChain([runtime, 'optionalAccess', _2 => _2.composeContentUrl]), {
102
+ const cta = _chunkT32BTPL5cjs.resolveContentHref.call(void 0, _optionalChain([runtime, 'optionalAccess', _2 => _2.composeContentUrl]), {
103
103
  type: "product_release",
104
104
  slug: release.slug,
105
105
  basePath,
@@ -107,9 +107,9 @@ function ReleaseRow({
107
107
  });
108
108
  const { target, rel } = _chunkQNWQQ4RTcjs.useEntityCardLink.call(void 0, { href: cta.href, targetPlatform: cta.targetPlatform });
109
109
  const onClick = (e) => {
110
- if (e.defaultPrevented || _chunkSI77HF4Ucjs.isModifierClick.call(void 0, e) || target === "_blank") return;
110
+ if (e.defaultPrevented || _chunkLL35WZZScjs.isModifierClick.call(void 0, e) || target === "_blank") return;
111
111
  e.preventDefault();
112
- _chunkSI77HF4Ucjs.executeNavigationImperative.call(void 0, {
112
+ _chunkLL35WZZScjs.executeNavigationImperative.call(void 0, {
113
113
  runtime,
114
114
  href: cta.href,
115
115
  targetPlatform: cta.targetPlatform,
@@ -171,10 +171,10 @@ function ProductReleasesView({
171
171
  router.replace(`${pathname}?${params.toString()}`, { scroll: false });
172
172
  };
173
173
  if (error) {
174
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: _chunkX7CEWD6Vcjs.cn.call(void 0, "w-full", className), children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkIBUFFKAKcjs.LoadError, { message: "Failed to load releases.", onRetry: reload }) });
174
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: _chunkX7CEWD6Vcjs.cn.call(void 0, "w-full", className), children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkT32BTPL5cjs.LoadError, { message: "Failed to load releases.", onRetry: reload }) });
175
175
  }
176
176
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: _chunkX7CEWD6Vcjs.cn.call(void 0, "flex w-full flex-col gap-[40px]", className), children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "min-h-[600px]", children: showEmpty ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "flex h-[600px] items-center justify-center", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
177
- _chunkMZAZUSPQcjs.ListEmptyState,
177
+ _chunkCKQ4ZBDNcjs.ListEmptyState,
178
178
  {
179
179
  isFiltered: hasActiveFilters,
180
180
  filtered: {
@@ -251,7 +251,7 @@ function MediaGalleryStrip({ items, className }) {
251
251
  mediaItem.id || index
252
252
  );
253
253
  }
254
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: tileClass, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkSI77HF4Ucjs.Video, { url: mediaItem.media_url, layout: "native" }) }, mediaItem.id || index);
254
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: tileClass, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLL35WZZScjs.Video, { url: mediaItem.media_url, layout: "native" }) }, mediaItem.id || index);
255
255
  }) }),
256
256
  galleryImages.length > 0 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
257
257
  _chunkQNWQQ4RTcjs.ImageGalleryModal,
@@ -269,7 +269,7 @@ function MediaGalleryStrip({ items, className }) {
269
269
  var _lucidereact = require('lucide-react');
270
270
 
271
271
 
272
- var DefaultMarkdownRenderer = _chunkIBUFFKAKcjs.RichMarkdownRenderer;
272
+ var DefaultMarkdownRenderer = _chunkT32BTPL5cjs.RichMarkdownRenderer;
273
273
  var DEFAULT_ENDPOINT2 = "/api/releases";
274
274
  function ReleaseDetailPage({
275
275
  authorHref,
@@ -287,8 +287,8 @@ function ReleaseDetailPage({
287
287
  shell = true
288
288
  }) {
289
289
  const router = _chunkSADRZ4YIcjs.useRouter.call(void 0, );
290
- const captions = _chunkSI77HF4Ucjs.useCaptions.call(void 0, );
291
- const renderShell = (node) => shell ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkIBUFFKAKcjs.PageShell, { children: node }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "page-shell-content", children: node });
290
+ const captions = _chunkLL35WZZScjs.useCaptions.call(void 0, );
291
+ const renderShell = (node) => shell ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkT32BTPL5cjs.PageShell, { children: node }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "page-shell-content", children: node });
292
292
  const seed = initialData == null ? void 0 : initialData;
293
293
  const {
294
294
  data: release,
@@ -347,7 +347,7 @@ function ReleaseDetailPage({
347
347
  return renderShell(
348
348
  // Match the loaded page's top offset (TitleBlock's
349
349
  // `pt-[var(--spacing-system-l)]`) so content doesn't jump on load.
350
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "pt-[var(--spacing-system-l)]", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkAF4XOURNcjs.DetailPageSkeleton, { bare: true, metadataColumns: 4, showImageGallery: true }) })
350
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "pt-[var(--spacing-system-l)]", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkXEESEFGTcjs.DetailPageSkeleton, { bare: true, metadataColumns: 4, showImageGallery: true }) })
351
351
  );
352
352
  }
353
353
  if (error || !release) {
@@ -386,7 +386,7 @@ function ReleaseDetailPage({
386
386
  const improvements = release.improvements;
387
387
  return renderShell(
388
388
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
389
- _chunkIBUFFKAKcjs.PageLayout,
389
+ _chunkT32BTPL5cjs.PageLayout,
390
390
  {
391
391
  title: releaseTitle,
392
392
  subtitle: `Version: ${releaseVersion}`,
@@ -395,7 +395,7 @@ function ReleaseDetailPage({
395
395
  backButton: showBackButton ? { label: backLabel, onClick: () => router.push(backHref) } : void 0,
396
396
  children: [
397
397
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "space-y-6 md:space-y-8", children: [
398
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkIBUFFKAKcjs.EntityTagBadges, { tags: release.product_release_tags }),
398
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkT32BTPL5cjs.EntityTagBadges, { tags: release.product_release_tags }),
399
399
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "grid w-full grid-cols-1 overflow-hidden rounded-md border border-ods-border md:grid-cols-4", children: [
400
400
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "flex flex-col gap-3 border-b border-ods-border bg-ods-card p-4 md:border-b-0 md:border-r", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex flex-col gap-0", children: [
401
401
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "text-ods-text-primary text-h4", children: releaseType.toLocaleUpperCase() }),
@@ -437,9 +437,9 @@ function ReleaseDetailPage({
437
437
  highlightCaptionsUrl
438
438
  }
439
439
  ) : /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
440
- youtubeUrl && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkSI77HF4Ucjs.Video, { kind: "youtube", url: youtubeUrl, title: `${releaseTitle} - Video`, layout: "native" }),
440
+ youtubeUrl && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLL35WZZScjs.Video, { kind: "youtube", url: youtubeUrl, title: `${releaseTitle} - Video`, layout: "native" }),
441
441
  !youtubeUrl && mainVideoUrl && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
442
- _chunkSI77HF4Ucjs.Video,
442
+ _chunkLL35WZZScjs.Video,
443
443
  {
444
444
  url: mainVideoUrl,
445
445
  srtContent: _optionalChain([release, 'optionalAccess', _9 => _9.srt_content]),
@@ -448,7 +448,7 @@ function ReleaseDetailPage({
448
448
  }
449
449
  ),
450
450
  highlightVideoUrl && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
451
- _chunkSI77HF4Ucjs.Video,
451
+ _chunkLL35WZZScjs.Video,
452
452
  {
453
453
  url: highlightVideoUrl,
454
454
  poster: highlightVideoThumbnail,
@@ -466,7 +466,7 @@ function ReleaseDetailPage({
466
466
  ] })
467
467
  ] }) }) }),
468
468
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
469
- _chunkIBUFFKAKcjs.ReleaseChangelogSection,
469
+ _chunkT32BTPL5cjs.ReleaseChangelogSection,
470
470
  {
471
471
  title: "Breaking Changes",
472
472
  entries: breakingChanges || [],
@@ -477,7 +477,7 @@ function ReleaseDetailPage({
477
477
  }
478
478
  ),
479
479
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
480
- _chunkIBUFFKAKcjs.ReleaseChangelogSection,
480
+ _chunkT32BTPL5cjs.ReleaseChangelogSection,
481
481
  {
482
482
  title: "Features Added",
483
483
  entries: featuresAdded || [],
@@ -487,7 +487,7 @@ function ReleaseDetailPage({
487
487
  }
488
488
  ),
489
489
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
490
- _chunkIBUFFKAKcjs.ReleaseChangelogSection,
490
+ _chunkT32BTPL5cjs.ReleaseChangelogSection,
491
491
  {
492
492
  title: "Bugs Fixed",
493
493
  entries: bugFixed || [],
@@ -497,7 +497,7 @@ function ReleaseDetailPage({
497
497
  }
498
498
  ),
499
499
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
500
- _chunkIBUFFKAKcjs.ReleaseChangelogSection,
500
+ _chunkT32BTPL5cjs.ReleaseChangelogSection,
501
501
  {
502
502
  title: "Improvements",
503
503
  entries: improvements || [],
@@ -523,7 +523,7 @@ function ReleaseDetailPage({
523
523
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "tracking-[-0.28px] text-ods-text-secondary text-h5", children: "Related Enhancements and Bug-fixes" }),
524
524
  (() => {
525
525
  const deliveryCount = deliveryData ? deliveryData.completed.length + deliveryData.inProgress.length : 0;
526
- return deliveryLoading ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DeliverySection, { data: deliveryData, isLoading: deliveryLoading }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkIBUFFKAKcjs.FadePreview, { hiddenCount: deliveryCount - 1, collapsedHeight: 300, resetKey: deliveryCount, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "space-y-4", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DeliverySection, { data: deliveryData, isLoading: false }) }) });
526
+ return deliveryLoading ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DeliverySection, { data: deliveryData, isLoading: deliveryLoading }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkT32BTPL5cjs.FadePreview, { hiddenCount: deliveryCount - 1, collapsedHeight: 300, resetKey: deliveryCount, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "space-y-4", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DeliverySection, { data: deliveryData, isLoading: false }) }) });
527
527
  })()
528
528
  ] }),
529
529
  (_optionalChain([githubReleases, 'optionalAccess', _10 => _10.length]) || _optionalChain([knowledgeBaseLinks, 'optionalAccess', _11 => _11.length]) || migrationGuideUrl || documentationUrl) && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "w-full space-y-1", children: [
@@ -602,7 +602,7 @@ function ReleaseDetailPage({
602
602
  // src/components/shared/product-release/release-detail-skeleton.tsx
603
603
 
604
604
  function ReleaseDetailSkeleton() {
605
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkAF4XOURNcjs.DetailPageSkeleton, { metadataColumns: 4, showImageGallery: true });
605
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkXEESEFGTcjs.DetailPageSkeleton, { metadataColumns: 4, showImageGallery: true });
606
606
  }
607
607
 
608
608
  // src/components/shared/roadmap/use-roadmap-voting.ts
@@ -625,7 +625,7 @@ function useRoadmapVoting(options = {}) {
625
625
  const { getSignals } = _chunkXF5TJ4P2cjs.useHumanitySignals.call(void 0, );
626
626
  const [votes, setVotes] = _react.useState.call(void 0, NO_VOTES);
627
627
  const [loadedKey, setLoadedKey] = _react.useState.call(void 0, null);
628
- const hydrated = _chunkSI77HF4Ucjs.useIsHydrated.call(void 0, );
628
+ const hydrated = _chunkLL35WZZScjs.useIsHydrated.call(void 0, );
629
629
  if (hydrated && loadedKey !== storageKey) {
630
630
  setLoadedKey(storageKey);
631
631
  setVotes(readStoredVotes(storageKey));
@@ -777,7 +777,7 @@ function RoadmapGridSingle({
777
777
  _chunkQNWQQ4RTcjs.RoadmapCard,
778
778
  {
779
779
  item,
780
- id: _chunkIBUFFKAKcjs.devSectionAnchorId.call(void 0, "roadmap", item.id),
780
+ id: _chunkT32BTPL5cjs.devSectionAnchorId.call(void 0, "roadmap", item.id),
781
781
  userVote: getVote(item.id),
782
782
  onVote: (voteType) => onVote(item.id, voteType),
783
783
  isVoting: votingTasks.has(item.id)
@@ -893,7 +893,7 @@ function RoadmapGrid({
893
893
  );
894
894
  }
895
895
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
896
- _chunkIBUFFKAKcjs.Accordion,
896
+ _chunkT32BTPL5cjs.Accordion,
897
897
  {
898
898
  type: "multiple",
899
899
  value: expandedQuarters,
@@ -903,13 +903,13 @@ function RoadmapGrid({
903
903
  const itemCount = _optionalChain([itemsByQuarter, 'access', _20 => _20[quarter], 'optionalAccess', _21 => _21.length]) || 0;
904
904
  const isExpanded = expandedQuarters.includes(quarter);
905
905
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
906
- _chunkIBUFFKAKcjs.AccordionItem,
906
+ _chunkT32BTPL5cjs.AccordionItem,
907
907
  {
908
908
  value: quarter,
909
909
  id: `quarter-${quarter.replace(/\s+/g, "-").toLowerCase()}`,
910
910
  className: "border-0",
911
911
  children: [
912
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkIBUFFKAKcjs.AccordionTrigger, { className: "w-full p-0 hover:no-underline [&>svg]:ml-auto [&>svg]:h-5 [&>svg]:w-5 [&>svg]:shrink-0 [&>svg]:text-ods-text-secondary", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-center gap-3", children: [
912
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkT32BTPL5cjs.AccordionTrigger, { className: "w-full p-0 hover:no-underline [&>svg]:ml-auto [&>svg]:h-5 [&>svg]:w-5 [&>svg]:shrink-0 [&>svg]:text-ods-text-secondary", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-center gap-3", children: [
913
913
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
914
914
  "h3",
915
915
  {
@@ -939,7 +939,7 @@ function RoadmapGrid({
939
939
  }
940
940
  )
941
941
  ] }) }),
942
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkIBUFFKAKcjs.AccordionContent, { className: "overflow-hidden pb-0 pt-4 data-[state=closed]:animate-none data-[state=open]:animate-none", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
942
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkT32BTPL5cjs.AccordionContent, { className: "overflow-hidden pb-0 pt-4 data-[state=closed]:animate-none data-[state=open]:animate-none", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
943
943
  RoadmapGridSingle,
944
944
  {
945
945
  items: itemsByQuarter[quarter],
@@ -991,8 +991,8 @@ function RoadmapGridSkeleton({ count = 4, showLeftMargin = true }) {
991
991
 
992
992
 
993
993
  var DEFAULT_ENDPOINT3 = "/api/roadmap";
994
- var DEFAULT_SEARCH_PARAM_KEY2 = _chunkIBUFFKAKcjs.DEV_SECTION_PARAM_KEYS.search;
995
- var DEFAULT_STATUS_PARAM_KEY2 = _chunkIBUFFKAKcjs.DEV_SECTION_PARAM_KEYS.status;
994
+ var DEFAULT_SEARCH_PARAM_KEY2 = _chunkT32BTPL5cjs.DEV_SECTION_PARAM_KEYS.search;
995
+ var DEFAULT_STATUS_PARAM_KEY2 = _chunkT32BTPL5cjs.DEV_SECTION_PARAM_KEYS.status;
996
996
  function RoadmapView({
997
997
  endpoint = DEFAULT_ENDPOINT3,
998
998
  initialItems,
@@ -1015,7 +1015,7 @@ function RoadmapView({
1015
1015
  const items = _nullishCoalesce(_optionalChain([data, 'optionalAccess', _22 => _22.items]), () => ( []));
1016
1016
  _chunkXF5TJ4P2cjs.useScrollToHash.call(void 0, data, { headerOffset: _chunkIJUQQTMVcjs.STICKY_HEADER_OFFSET_PX });
1017
1017
  if (error) {
1018
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkIBUFFKAKcjs.LoadError, { message: "Failed to load roadmap.", onRetry: reload });
1018
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkT32BTPL5cjs.LoadError, { message: "Failed to load roadmap.", onRetry: reload });
1019
1019
  }
1020
1020
  if (isLoading && !data) {
1021
1021
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, RoadmapGridSkeleton, { showLeftMargin });
@@ -1068,7 +1068,7 @@ function DeliveryTable({ items, isLoading = false }) {
1068
1068
  // → `#delivery-<external_id>`; `useScrollToHash` in
1069
1069
  // `delivery-lists.tsx` finds the row by id and scrolls. The
1070
1070
  // outer wrapper here ONLY exists for the row separators.
1071
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "border-b border-ods-border last:border-b-0", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkMZAZUSPQcjs.DeliveryRow, { item, id: _chunkIBUFFKAKcjs.devSectionAnchorId.call(void 0, "delivery", item.id) }) }, item.id)
1071
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "border-b border-ods-border last:border-b-0", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkCKQ4ZBDNcjs.DeliveryRow, { item, id: _chunkT32BTPL5cjs.devSectionAnchorId.call(void 0, "delivery", item.id) }) }, item.id)
1072
1072
  )) }) });
1073
1073
  }
1074
1074
 
@@ -1077,8 +1077,8 @@ function DeliveryTable({ items, isLoading = false }) {
1077
1077
 
1078
1078
  var DEFAULT_COMPLETED_ENDPOINT = "/api/delivery/completed";
1079
1079
  var DEFAULT_IN_PROGRESS_ENDPOINT = "/api/delivery/in-progress";
1080
- var DEFAULT_SEARCH_PARAM_KEY3 = _chunkIBUFFKAKcjs.DEV_SECTION_PARAM_KEYS.search;
1081
- var DEFAULT_TASK_TYPE_PARAM_KEY = _chunkIBUFFKAKcjs.DEV_SECTION_PARAM_KEYS.deliveryTaskType;
1080
+ var DEFAULT_SEARCH_PARAM_KEY3 = _chunkT32BTPL5cjs.DEV_SECTION_PARAM_KEYS.search;
1081
+ var DEFAULT_TASK_TYPE_PARAM_KEY = _chunkT32BTPL5cjs.DEV_SECTION_PARAM_KEYS.deliveryTaskType;
1082
1082
  function DeliveryLists({
1083
1083
  completedApiEndpoint = DEFAULT_COMPLETED_ENDPOINT,
1084
1084
  inProgressApiEndpoint = DEFAULT_IN_PROGRESS_ENDPOINT,
@@ -1145,11 +1145,11 @@ function DeliveryLists({
1145
1145
  const hasActiveFilters = searchQuery !== "" || taskTypeFilter !== "all";
1146
1146
  const hasResults = showCompleted && filteredCompleted.length > 0 || showInProgress && filteredInProgress.length > 0;
1147
1147
  if (error) {
1148
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "w-full", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkIBUFFKAKcjs.LoadError, { message: error, onRetry: () => window.location.reload() }) });
1148
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "w-full", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkT32BTPL5cjs.LoadError, { message: error, onRetry: () => window.location.reload() }) });
1149
1149
  }
1150
1150
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex w-full flex-col gap-[40px]", children: [
1151
1151
  !isLoading && !hasResults && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1152
- _chunkMZAZUSPQcjs.ListEmptyState,
1152
+ _chunkCKQ4ZBDNcjs.ListEmptyState,
1153
1153
  {
1154
1154
  isFiltered: hasActiveFilters,
1155
1155
  filtered: {
@@ -1303,7 +1303,7 @@ function LegalDocumentPage({
1303
1303
  initialData = null,
1304
1304
  initialLastUpdatedLabel = null,
1305
1305
  apiEndpoint,
1306
- MarkdownRenderer = _chunkIBUFFKAKcjs.RichMarkdownRenderer,
1306
+ MarkdownRenderer = _chunkT32BTPL5cjs.RichMarkdownRenderer,
1307
1307
  backButton,
1308
1308
  shell = true
1309
1309
  }) {
@@ -1316,7 +1316,7 @@ function LegalDocumentPage({
1316
1316
  const fallbackLastUpdatedLabel = _optionalChain([data, 'optionalAccess', _27 => _27.lastSynced]) != null ? _chunkQNWQQ4RTcjs.formatLegalDate.call(void 0, data.lastSynced) : null;
1317
1317
  const effectiveLastUpdatedLabel = _nullishCoalesce(initialLastUpdatedLabel, () => ( fallbackLastUpdatedLabel));
1318
1318
  const subtitle = effectiveLastUpdatedLabel ? `Last Updated: ${effectiveLastUpdatedLabel}` : fallbackDescription;
1319
- const inner = /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkIBUFFKAKcjs.PageLayout, { title, subtitle, backButton: backCfg, titleSize: "h1", titleWrap: true, children: [
1319
+ const inner = /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkT32BTPL5cjs.PageLayout, { title, subtitle, backButton: backCfg, titleSize: "h1", titleWrap: true, children: [
1320
1320
  _optionalChain([data, 'optionalAccess', _28 => _28.sourceFile]) && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "p", { className: "text-ods-text-secondary opacity-75 text-h6", children: [
1321
1321
  "Source: ",
1322
1322
  data.sourceFile
@@ -1356,7 +1356,7 @@ function LegalDocumentPage({
1356
1356
  ] })
1357
1357
  ] }) }) }) }) })
1358
1358
  ] });
1359
- return shell ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkIBUFFKAKcjs.PageShell, { children: inner }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "page-shell-content", children: inner });
1359
+ return shell ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkT32BTPL5cjs.PageShell, { children: inner }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "page-shell-content", children: inner });
1360
1360
  }
1361
1361
 
1362
1362
 
@@ -1373,4 +1373,4 @@ function LegalDocumentPage({
1373
1373
 
1374
1374
 
1375
1375
  exports.ProductReleasesView = ProductReleasesView; exports.MediaGalleryStrip = MediaGalleryStrip; exports.ReleaseDetailPage = ReleaseDetailPage; exports.ReleaseDetailSkeleton = ReleaseDetailSkeleton; exports.useRoadmapVoting = useRoadmapVoting; exports.RoadmapGrid = RoadmapGrid; exports.RoadmapGridSkeleton = RoadmapGridSkeleton; exports.RoadmapView = RoadmapView; exports.DeliveryTable = DeliveryTable; exports.DeliveryLists = DeliveryLists; exports.useLegalDocs = useLegalDocs; exports.LegalDocumentPage = LegalDocumentPage;
1376
- //# sourceMappingURL=chunk-4LBCUBVL.cjs.map
1376
+ //# sourceMappingURL=chunk-BZKDFMZM.cjs.map