@flamingo-stack/openframe-frontend-core 0.0.292 → 0.0.293-snapshot.20260619104458

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 (163) hide show
  1. package/dist/{chunk-6FHO73AP.js → chunk-26PKDALD.js} +7 -79
  2. package/dist/chunk-26PKDALD.js.map +1 -0
  3. package/dist/{chunk-OXOTKEYY.cjs → chunk-4W7NYJ3B.cjs} +23 -23
  4. package/dist/{chunk-OXOTKEYY.cjs.map → chunk-4W7NYJ3B.cjs.map} +1 -1
  5. package/dist/{chunk-KBKZYJRI.cjs → chunk-5E2HOSSH.cjs} +66 -19
  6. package/dist/chunk-5E2HOSSH.cjs.map +1 -0
  7. package/dist/{chunk-CUQH4SHH.js → chunk-6GCI7JOE.js} +2 -2
  8. package/dist/{chunk-2NJ44RTT.cjs → chunk-6LUVYHKD.cjs} +30 -30
  9. package/dist/{chunk-2NJ44RTT.cjs.map → chunk-6LUVYHKD.cjs.map} +1 -1
  10. package/dist/{chunk-E2YXRSDG.js → chunk-6SG25O2N.js} +15 -13
  11. package/dist/chunk-6SG25O2N.js.map +1 -0
  12. package/dist/{chunk-CDJOKNCS.cjs → chunk-73QT66LJ.cjs} +25 -19
  13. package/dist/chunk-73QT66LJ.cjs.map +1 -0
  14. package/dist/{chunk-N6ZM5PYZ.js → chunk-7RIYT7ZH.js} +49 -2
  15. package/dist/chunk-7RIYT7ZH.js.map +1 -0
  16. package/dist/{chunk-FFP2A77V.cjs → chunk-AFKRDSRS.cjs} +12 -12
  17. package/dist/{chunk-FFP2A77V.cjs.map → chunk-AFKRDSRS.cjs.map} +1 -1
  18. package/dist/{chunk-PZZGDS5I.cjs → chunk-B4T3RTFX.cjs} +24 -22
  19. package/dist/chunk-B4T3RTFX.cjs.map +1 -0
  20. package/dist/{chunk-5R5OODNE.cjs → chunk-CLWQ7MHW.cjs} +40 -40
  21. package/dist/{chunk-5R5OODNE.cjs.map → chunk-CLWQ7MHW.cjs.map} +1 -1
  22. package/dist/{chunk-C667P6LZ.js → chunk-DEBURY5R.js} +13 -7
  23. package/dist/{chunk-C667P6LZ.js.map → chunk-DEBURY5R.js.map} +1 -1
  24. package/dist/{chunk-HTYUZXQP.js → chunk-E24HKKIE.js} +5 -5
  25. package/dist/{chunk-VK4B6UGU.js → chunk-E4XABBSU.js} +16 -8
  26. package/dist/{chunk-VK4B6UGU.js.map → chunk-E4XABBSU.js.map} +1 -1
  27. package/dist/{chunk-DUIWR7RQ.js → chunk-EJXHZX2E.js} +3 -3
  28. package/dist/{chunk-5PELVUFT.cjs → chunk-EYEW6PTA.cjs} +44 -36
  29. package/dist/chunk-EYEW6PTA.cjs.map +1 -0
  30. package/dist/{chunk-SLP4KXP6.js → chunk-FQJK446R.js} +8 -2
  31. package/dist/chunk-FQJK446R.js.map +1 -0
  32. package/dist/{chunk-JC5RN7ZS.cjs → chunk-FT4FCV7L.cjs} +6 -6
  33. package/dist/{chunk-JC5RN7ZS.cjs.map → chunk-FT4FCV7L.cjs.map} +1 -1
  34. package/dist/{chunk-ZHNL2IPK.cjs → chunk-J54Z3OCR.cjs} +8 -2
  35. package/dist/chunk-J54Z3OCR.cjs.map +1 -0
  36. package/dist/{chunk-Z6BK4XHH.cjs → chunk-KXCRGTRN.cjs} +10 -82
  37. package/dist/chunk-KXCRGTRN.cjs.map +1 -0
  38. package/dist/{chunk-5KD3S25X.cjs → chunk-LFGGF7OT.cjs} +139 -2
  39. package/dist/chunk-LFGGF7OT.cjs.map +1 -0
  40. package/dist/{chunk-N45M3TK3.js → chunk-NSPOYUBH.js} +2 -2
  41. package/dist/{chunk-TYZEMPPH.js → chunk-OQ6X7ZOC.js} +138 -1
  42. package/dist/chunk-OQ6X7ZOC.js.map +1 -0
  43. package/dist/{chunk-B2U6INNO.js → chunk-QFG4G62D.js} +4 -4
  44. package/dist/{chunk-IXDTNQF4.js → chunk-RT6DJB3C.js} +4 -4
  45. package/dist/{chunk-MDLWEJAV.cjs → chunk-V6YZGLHZ.cjs} +454 -459
  46. package/dist/chunk-V6YZGLHZ.cjs.map +1 -0
  47. package/dist/{chunk-2BMVBPC7.cjs → chunk-YIGPRLQY.cjs} +9 -9
  48. package/dist/{chunk-2BMVBPC7.cjs.map → chunk-YIGPRLQY.cjs.map} +1 -1
  49. package/dist/{chunk-5FK7X3EE.js → chunk-ZYZWD7LS.js} +9 -14
  50. package/dist/chunk-ZYZWD7LS.js.map +1 -0
  51. package/dist/components/chat/entity-cards/roadmap-card.d.ts +7 -1
  52. package/dist/components/chat/entity-cards/roadmap-card.d.ts.map +1 -1
  53. package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts +0 -5
  54. package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts.map +1 -1
  55. package/dist/components/chat/hooks/use-realtime-chunk-processor.d.ts.map +1 -1
  56. package/dist/components/chat/index.cjs +7 -7
  57. package/dist/components/chat/index.js +6 -6
  58. package/dist/components/chat/types/api.types.d.ts +0 -6
  59. package/dist/components/chat/types/api.types.d.ts.map +1 -1
  60. package/dist/components/contact/index.cjs +8 -8
  61. package/dist/components/contact/index.js +7 -7
  62. package/dist/components/docs/index.cjs +6 -6
  63. package/dist/components/docs/index.js +5 -5
  64. package/dist/components/docs/use-document-tree.d.ts.map +1 -1
  65. package/dist/components/embeds/index.cjs +8 -8
  66. package/dist/components/embeds/index.js +7 -7
  67. package/dist/components/faq/faq-section.d.ts.map +1 -1
  68. package/dist/components/faq/index.cjs +8 -8
  69. package/dist/components/faq/index.js +7 -7
  70. package/dist/components/features/index.cjs +7 -7
  71. package/dist/components/features/index.js +6 -6
  72. package/dist/components/index.cjs +214 -193
  73. package/dist/components/index.cjs.map +1 -1
  74. package/dist/components/index.js +50 -29
  75. package/dist/components/index.js.map +1 -1
  76. package/dist/components/navigation/index.cjs +7 -7
  77. package/dist/components/navigation/index.js +6 -6
  78. package/dist/components/onboarding-guides/index.cjs +24 -24
  79. package/dist/components/onboarding-guides/index.js +4 -4
  80. package/dist/components/related-content/index.cjs +8 -8
  81. package/dist/components/related-content/index.js +7 -7
  82. package/dist/components/shared/delivery/delivery-lists.d.ts.map +1 -1
  83. package/dist/components/shared/delivery/delivery-row.d.ts +8 -1
  84. package/dist/components/shared/delivery/delivery-row.d.ts.map +1 -1
  85. package/dist/components/shared/delivery/delivery-table.d.ts.map +1 -1
  86. package/dist/components/shared/roadmap/roadmap-grid.d.ts.map +1 -1
  87. package/dist/components/shared/roadmap/roadmap-view.d.ts.map +1 -1
  88. package/dist/components/tickets/help-center-card.d.ts +7 -1
  89. package/dist/components/tickets/help-center-card.d.ts.map +1 -1
  90. package/dist/components/tickets/help-center-list.d.ts.map +1 -1
  91. package/dist/components/tickets/index.cjs +82 -73
  92. package/dist/components/tickets/index.cjs.map +1 -1
  93. package/dist/components/tickets/index.js +24 -15
  94. package/dist/components/tickets/index.js.map +1 -1
  95. package/dist/components/tickets/ticket-center.d.ts.map +1 -1
  96. package/dist/components/tickets/ticket-row.d.ts +6 -1
  97. package/dist/components/tickets/ticket-row.d.ts.map +1 -1
  98. package/dist/components/ui/index.cjs +7 -7
  99. package/dist/components/ui/index.js +6 -6
  100. package/dist/hooks/index.cjs +5 -3
  101. package/dist/hooks/index.cjs.map +1 -1
  102. package/dist/hooks/index.d.ts +1 -0
  103. package/dist/hooks/index.d.ts.map +1 -1
  104. package/dist/hooks/index.js +4 -2
  105. package/dist/hooks/use-scroll-to-hash.d.ts +17 -0
  106. package/dist/hooks/use-scroll-to-hash.d.ts.map +1 -0
  107. package/dist/index.cjs +19 -7
  108. package/dist/index.cjs.map +1 -1
  109. package/dist/index.js +19 -7
  110. package/dist/utils/dev-sections/dev-section-param-keys.d.ts +10 -0
  111. package/dist/utils/dev-sections/dev-section-param-keys.d.ts.map +1 -1
  112. package/dist/utils/index.cjs +71 -1
  113. package/dist/utils/index.cjs.map +1 -1
  114. package/dist/utils/index.d.ts +2 -1
  115. package/dist/utils/index.d.ts.map +1 -1
  116. package/dist/utils/index.js +67 -2
  117. package/dist/utils/index.js.map +1 -1
  118. package/dist/utils/same-page-hash-nav.d.ts +37 -0
  119. package/dist/utils/same-page-hash-nav.d.ts.map +1 -0
  120. package/dist/utils/source-icons.d.ts.map +1 -1
  121. package/package.json +1 -1
  122. package/src/components/chat/entity-cards/roadmap-card.tsx +8 -1
  123. package/src/components/chat/hooks/use-nats-chat-adapter.ts +0 -8
  124. package/src/components/chat/hooks/use-realtime-chunk-processor.ts +1 -12
  125. package/src/components/chat/types/api.types.ts +0 -6
  126. package/src/components/docs/use-document-tree.ts +45 -3
  127. package/src/components/faq/faq-section.tsx +22 -9
  128. package/src/components/shared/delivery/delivery-lists.tsx +9 -0
  129. package/src/components/shared/delivery/delivery-row.tsx +15 -2
  130. package/src/components/shared/delivery/delivery-table.tsx +7 -1
  131. package/src/components/shared/roadmap/roadmap-grid.tsx +7 -0
  132. package/src/components/shared/roadmap/roadmap-view.tsx +11 -0
  133. package/src/components/tickets/help-center-card.tsx +9 -17
  134. package/src/components/tickets/help-center-list.tsx +13 -0
  135. package/src/components/tickets/ticket-center.tsx +2 -0
  136. package/src/components/tickets/ticket-row.tsx +7 -1
  137. package/src/hooks/index.ts +5 -0
  138. package/src/hooks/use-scroll-to-hash.ts +74 -0
  139. package/src/utils/.source-icons.md +1 -1
  140. package/src/utils/dev-sections/dev-section-param-keys.ts +14 -0
  141. package/src/utils/index.ts +17 -1
  142. package/src/utils/same-page-hash-nav.ts +115 -0
  143. package/src/utils/source-icons.ts +7 -1
  144. package/dist/chunk-5FK7X3EE.js.map +0 -1
  145. package/dist/chunk-5KD3S25X.cjs.map +0 -1
  146. package/dist/chunk-5PELVUFT.cjs.map +0 -1
  147. package/dist/chunk-6FHO73AP.js.map +0 -1
  148. package/dist/chunk-CDJOKNCS.cjs.map +0 -1
  149. package/dist/chunk-E2YXRSDG.js.map +0 -1
  150. package/dist/chunk-KBKZYJRI.cjs.map +0 -1
  151. package/dist/chunk-MDLWEJAV.cjs.map +0 -1
  152. package/dist/chunk-N6ZM5PYZ.js.map +0 -1
  153. package/dist/chunk-PZZGDS5I.cjs.map +0 -1
  154. package/dist/chunk-SLP4KXP6.js.map +0 -1
  155. package/dist/chunk-TYZEMPPH.js.map +0 -1
  156. package/dist/chunk-Z6BK4XHH.cjs.map +0 -1
  157. package/dist/chunk-ZHNL2IPK.cjs.map +0 -1
  158. /package/dist/{chunk-CUQH4SHH.js.map → chunk-6GCI7JOE.js.map} +0 -0
  159. /package/dist/{chunk-HTYUZXQP.js.map → chunk-E24HKKIE.js.map} +0 -0
  160. /package/dist/{chunk-DUIWR7RQ.js.map → chunk-EJXHZX2E.js.map} +0 -0
  161. /package/dist/{chunk-N45M3TK3.js.map → chunk-NSPOYUBH.js.map} +0 -0
  162. /package/dist/{chunk-B2U6INNO.js.map → chunk-QFG4G62D.js.map} +0 -0
  163. /package/dist/{chunk-IXDTNQF4.js.map → chunk-RT6DJB3C.js.map} +0 -0
@@ -0,0 +1,74 @@
1
+ 'use client'
2
+
3
+ import { useEffect } from 'react'
4
+ import { scrollElementIntoView } from '../utils/scroll-into-view'
5
+ import { normalizeHashFragment } from '../utils/same-page-hash-nav'
6
+
7
+ /** ~1s at 60fps — long enough to outlast Radix accordion expand + SWR
8
+ * mount, short enough that a missed anchor doesn't hang the page. */
9
+ const MAX_POLL_FRAMES = 60
10
+
11
+ export interface UseScrollToHashOptions {
12
+ /** Pixels to subtract for sticky chrome. */
13
+ headerOffset?: number
14
+ }
15
+
16
+ /**
17
+ * Scroll the page to `window.location.hash` once `readyDep` resolves
18
+ * to a truthy value. Polls via rAF for ~1s so lazy-mounted rows (Radix
19
+ * accordion, SWR fetch) have time to render. Re-runs on `readyDep`
20
+ * reference change AND on `hashchange` (browser back/forward + the
21
+ * synthetic event `navigateSamePageHash` dispatches).
22
+ *
23
+ * Skipped when `readyDep == null || readyDep === false`. Default
24
+ * `true` makes the hook run on mount for pages whose target is in the
25
+ * initial SSR render.
26
+ */
27
+ export function useScrollToHash(
28
+ readyDep: unknown = true,
29
+ options?: UseScrollToHashOptions,
30
+ ): void {
31
+ const headerOffset = options?.headerOffset ?? 0
32
+ useEffect(() => {
33
+ if (typeof window === 'undefined') return
34
+ if (readyDep === null || readyDep === false) return
35
+ let rafId: number | null = null
36
+ const cancelPoll = () => {
37
+ if (rafId !== null) {
38
+ cancelAnimationFrame(rafId)
39
+ rafId = null
40
+ }
41
+ }
42
+ const tryScrollToHash = () => {
43
+ // `normalizeHashFragment` heals a malformed multi-fragment hash
44
+ // so `getElementById` resolves on deep-link entries that bypass
45
+ // `navigateSamePageHash`'s own normalize.
46
+ const hash = normalizeHashFragment(window.location.hash).slice(1)
47
+ if (!hash) return
48
+ // Cancel any in-flight poll from a prior invocation so two
49
+ // concurrent ticks can't both call scrollElementIntoView.
50
+ cancelPoll()
51
+ let frames = 0
52
+ const tick = () => {
53
+ const el = document.getElementById(hash)
54
+ if (el) {
55
+ rafId = null
56
+ scrollElementIntoView(el, { headerOffset })
57
+ return
58
+ }
59
+ if (frames++ < MAX_POLL_FRAMES) {
60
+ rafId = requestAnimationFrame(tick)
61
+ } else {
62
+ rafId = null
63
+ }
64
+ }
65
+ tick()
66
+ }
67
+ tryScrollToHash()
68
+ window.addEventListener('hashchange', tryScrollToHash)
69
+ return () => {
70
+ window.removeEventListener('hashchange', tryScrollToHash)
71
+ cancelPoll()
72
+ }
73
+ }, [readyDep, headerOffset])
74
+ }
@@ -27,7 +27,7 @@ const iconName = getSourceIconName('github-commits')
27
27
 
28
28
  // Get display label for a chip strip
29
29
  const label = getSourceLabel('hubspot-tickets-anon')
30
- // → 'Known Issues'
30
+ // → 'Tickets'
31
31
 
32
32
  // Map a documentType emitted by the LLM to its table ID
33
33
  const tableId = defaultTableIdForDocumentType('slack_message')
@@ -19,3 +19,17 @@ export const DEV_SECTION_PARAM_KEYS = {
19
19
  /** Delivery (bug-fix / enhancement) task-type filter. */
20
20
  deliveryTaskType: 'task_type',
21
21
  } as const
22
+
23
+ /** Section keys that participate in `<section>-<id>` anchor IDs.
24
+ * The URL composer (`appendSearchAndHash` in hub `dev-section-url.ts`)
25
+ * and the row components (`DeliveryRow`, `RoadmapCard`, `HelpCenterCard`)
26
+ * both call `devSectionAnchorId` so the DOM `id` and the URL hash stay
27
+ * in lockstep — adding a new section means adding the literal here
28
+ * ONCE, not at every render site. */
29
+ export type DevSectionAnchorKind = 'roadmap' | 'delivery' | 'ticket'
30
+
31
+ /** Compose the canonical `<section>-<id>` anchor id used by the dev-center
32
+ * rows + URL composer. */
33
+ export function devSectionAnchorId(section: DevSectionAnchorKind, id: string): string {
34
+ return `${section}-${id}`
35
+ }
@@ -44,7 +44,11 @@ export * from './release-cover'
44
44
  // Dev-center URL param keys — the ONE source for the `?search=` / `?status=` / … keys the
45
45
  // chrome registry writes and the list views read; re-exported so embedders (and the hub's
46
46
  // dev-section-url helper) build deep-links with the same keys instead of a bare literal.
47
- export { DEV_SECTION_PARAM_KEYS } from './dev-sections/dev-section-param-keys'
47
+ export {
48
+ DEV_SECTION_PARAM_KEYS,
49
+ devSectionAnchorId,
50
+ type DevSectionAnchorKind,
51
+ } from './dev-sections/dev-section-param-keys'
48
52
  // Dynamic icon registry — single source of truth lives at
49
53
  // components/chat/utils/icon-registry. Re-exported here so existing
50
54
  // `@flamingo-stack/openframe-frontend-core/utils` callers (hub admin
@@ -246,6 +250,18 @@ export {
246
250
  scrollElementIntoView,
247
251
  } from './scroll-into-view'
248
252
 
253
+ // Same-page hash navigation — owns pushState + synthetic hashchange +
254
+ // anchoring-proof scroll. Pair of `scrollElementIntoView`. Used by the
255
+ // hub's `useUnifiedNav` + chat-runtime `navigate`, AND by every
256
+ // embeddable surface that drives state off the URL hash.
257
+ export {
258
+ navigateSamePageHash,
259
+ normalizeHashFragment,
260
+ STICKY_HEADER_OFFSET_PX,
261
+ HUB_HEADER_OFFSET_PX,
262
+ type NavigateSamePageHashOptions,
263
+ } from './same-page-hash-nav'
264
+
249
265
  // Shared list-API URL builder — the single source for the per-type chat
250
266
  // entity-card fetch shapes. The hub's 12 RAG mapper `listApi` closures
251
267
  // delegate here (byte-parity test guards the migration); embedders wire
@@ -0,0 +1,115 @@
1
+ import { scrollElementIntoView } from './scroll-into-view'
2
+
3
+ /** Pages with a section-nav STRIP on top of the global hub header
4
+ * (dev-center roadmap/delivery/tickets, FAQ category-pill nav).
5
+ * Anchor lands BELOW both layers. */
6
+ export const STICKY_HEADER_OFFSET_PX = 96
7
+
8
+ /** Pages with only the global hub header (docs, blog, vendor detail).
9
+ * Anchor lands BELOW the header bar. */
10
+ export const HUB_HEADER_OFFSET_PX = 80
11
+
12
+ /**
13
+ * Take only the FIRST hash segment from a fragment that may contain extra
14
+ * `#` characters. `'' → ''`, `'#a' → '#a'`, `'#a#b' → '#a'`.
15
+ *
16
+ * No real DOM id contains `#`, so a multi-fragment hash is always a bug at
17
+ * the composer site; `navigateSamePageHash` + `useScrollToHash` both call
18
+ * this so URL bar and `getElementById` stay in sync.
19
+ */
20
+ export function normalizeHashFragment(hash: string): string {
21
+ if (!hash) return ''
22
+ const second = hash.indexOf('#', 1)
23
+ return second < 0 ? hash : hash.slice(0, second)
24
+ }
25
+
26
+ export interface NavigateSamePageHashOptions {
27
+ /** Pixels to subtract for sticky chrome. */
28
+ headerOffset?: number
29
+ /** `'push'` (default) — new history entry; `'replace'` — overwrite
30
+ * current entry (use for TOC-style in-page navigators). */
31
+ history?: 'push' | 'replace'
32
+ }
33
+
34
+ /**
35
+ * Same-page hash navigation primitive: pushState + synthetic `hashchange`
36
+ * + anchoring-proof smooth scroll. Replaces `router.push` for hash CTAs
37
+ * (Next.js suppresses smooth-scroll during navigation; `router.push` on
38
+ * an exact-URL match is a no-op). Returns `true` when the helper claimed
39
+ * the nav (same pathname + search); `false` for cross-page targets so
40
+ * callers fall through to `router.push`.
41
+ *
42
+ * `target` accepts an origin-stripped path (`/x#anchor`) or a bare hash
43
+ * (`#anchor`); bare-hash callers don't need to reconstruct `pathname +
44
+ * search` themselves.
45
+ */
46
+ export function navigateSamePageHash(
47
+ target: string,
48
+ options: NavigateSamePageHashOptions = {},
49
+ ): boolean {
50
+ if (typeof window === 'undefined') return false
51
+ const { headerOffset = 0, history: historyMode = 'push' } = options
52
+ const normalizedTarget =
53
+ target.startsWith('#')
54
+ ? window.location.pathname + window.location.search + target
55
+ : target
56
+ // `new URL(absoluteUrl, base)` ignores `base` per RFC 3986; an absolute
57
+ // cross-origin target sharing pathname/search would otherwise pass the
58
+ // check below and trip pushState's same-origin enforcement. Parse with
59
+ // an explicit base so malformed inputs cleanly fall through.
60
+ let url: URL
61
+ try {
62
+ url = new URL(normalizedTarget, window.location.href)
63
+ } catch {
64
+ return false
65
+ }
66
+ if (
67
+ url.origin !== window.location.origin ||
68
+ url.pathname !== window.location.pathname ||
69
+ url.search !== window.location.search
70
+ ) {
71
+ return false
72
+ }
73
+ const current = window.location.pathname + window.location.search + window.location.hash
74
+ // Heal a malformed multi-fragment hash so the URL bar is clean and
75
+ // `getElementById` resolves. Dev-warn fingers the upstream composer.
76
+ const normalizedHash = normalizeHashFragment(url.hash)
77
+ if (process.env.NODE_ENV === 'development' && normalizedHash !== url.hash) {
78
+ // eslint-disable-next-line no-console
79
+ console.warn(
80
+ `[navigateSamePageHash] malformed fragment "${url.hash}" → normalizing to "${normalizedHash}". Fix the upstream composer.`,
81
+ )
82
+ }
83
+ const next = url.pathname + url.search + normalizedHash
84
+ const id = normalizedHash && normalizedHash !== '#' ? normalizedHash.slice(1) : ''
85
+ // Hash-less targets are only ours on an EXACT URL re-click.
86
+ if (!id && next !== current) return false
87
+ if (next !== current) {
88
+ const oldURL = window.location.href
89
+ if (historyMode === 'replace') {
90
+ window.history.replaceState(null, '', next)
91
+ } else {
92
+ window.history.pushState(null, '', next)
93
+ }
94
+ // Synthetic `hashchange` — `pushState` doesn't fire it (HTML spec),
95
+ // so URL-hash-bound listeners (FAQ auto-expand, etc.) wouldn't react.
96
+ window.dispatchEvent(new HashChangeEvent('hashchange', {
97
+ oldURL,
98
+ newURL: window.location.href,
99
+ }))
100
+ }
101
+ const el = id ? document.getElementById(id) : null
102
+ if (id && !el && process.env.NODE_ENV === 'development') {
103
+ // eslint-disable-next-line no-console
104
+ console.warn(
105
+ `[navigateSamePageHash] anchor "#${id}" not found — scrolling to top.`,
106
+ )
107
+ }
108
+ // Missing anchor → tween to page top. `documentElement` is at 0 by
109
+ // definition, so one tween covers both branches.
110
+ scrollElementIntoView(el ?? document.documentElement, {
111
+ behavior: 'smooth',
112
+ headerOffset,
113
+ })
114
+ return true
115
+ }
@@ -128,7 +128,13 @@ export const SOURCE_LABELS_BY_TABLE: Record<string, string> = {
128
128
 
129
129
  // HubSpot
130
130
  'hubspot-tickets': 'HubSpot Tickets',
131
- 'hubspot-tickets-anon': 'Known Issues',
131
+ // Anon + self share the "Tickets" root so the chip vocabulary is
132
+ // uniform across logged-out (everyone's resolved support tickets,
133
+ // anonymized → "Tickets") and logged-in (user-scoped → "My Tickets")
134
+ // surfaces. The full-PII `hubspot-tickets` entry above is product-hub
135
+ // internal only (admin view), kept as "HubSpot Tickets" to flag the
136
+ // distinct scope.
137
+ 'hubspot-tickets-anon': 'Tickets',
132
138
  'hubspot-tickets-self': 'My Tickets',
133
139
 
134
140
  // Communications