@flamingo-stack/openframe-frontend-core 0.0.210-snapshot.20260528032637 → 0.0.210

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 (72) hide show
  1. package/dist/{chunk-P5EE2VJX.cjs → chunk-6RZYJICV.cjs} +1 -1
  2. package/dist/chunk-6RZYJICV.cjs.map +1 -0
  3. package/dist/{chunk-ZG2YY5E7.js → chunk-7L4DWM7P.js} +1 -1
  4. package/dist/chunk-7L4DWM7P.js.map +1 -0
  5. package/dist/{chunk-QKFBZLIR.js → chunk-ATEUJQKU.js} +2 -2
  6. package/dist/{chunk-VTUIMMHO.cjs → chunk-MDTIOPVS.cjs} +24 -24
  7. package/dist/{chunk-VTUIMMHO.cjs.map → chunk-MDTIOPVS.cjs.map} +1 -1
  8. package/dist/{chunk-3E5ANY55.js → chunk-R5RNRH62.js} +5 -14
  9. package/dist/chunk-R5RNRH62.js.map +1 -0
  10. package/dist/{chunk-WI76ZUBE.cjs → chunk-TWKPYZNQ.cjs} +17 -26
  11. package/dist/chunk-TWKPYZNQ.cjs.map +1 -0
  12. package/dist/{chunk-ZFBLC5GV.cjs → chunk-VBFOCTMD.cjs} +17 -17
  13. package/dist/{chunk-ZFBLC5GV.cjs.map → chunk-VBFOCTMD.cjs.map} +1 -1
  14. package/dist/{chunk-5BNWGK6D.js → chunk-WJBPLMBX.js} +2 -2
  15. package/dist/components/chat/hooks/use-chat-identity.d.ts +3 -3
  16. package/dist/components/chat/hooks/use-chat-identity.d.ts.map +1 -1
  17. package/dist/components/chat/index.cjs +3 -3
  18. package/dist/components/chat/index.js +2 -2
  19. package/dist/components/contact/index.cjs +4 -4
  20. package/dist/components/contact/index.js +3 -3
  21. package/dist/components/features/index.cjs +3 -3
  22. package/dist/components/features/index.js +2 -2
  23. package/dist/components/footer-waitlist-button.d.ts +2 -21
  24. package/dist/components/footer-waitlist-button.d.ts.map +1 -1
  25. package/dist/components/index.cjs +91 -87
  26. package/dist/components/index.cjs.map +1 -1
  27. package/dist/components/index.js +16 -12
  28. package/dist/components/index.js.map +1 -1
  29. package/dist/components/navigation/index.cjs +3 -3
  30. package/dist/components/navigation/index.js +2 -2
  31. package/dist/components/navigation/sticky-section-nav.d.ts.map +1 -1
  32. package/dist/components/tickets/help-center-card.d.ts.map +1 -1
  33. package/dist/components/tickets/index.cjs +98 -139
  34. package/dist/components/tickets/index.cjs.map +1 -1
  35. package/dist/components/tickets/index.js +54 -95
  36. package/dist/components/tickets/index.js.map +1 -1
  37. package/dist/components/tickets/ticket-row.d.ts.map +1 -1
  38. package/dist/components/ui/index.cjs +3 -3
  39. package/dist/components/ui/index.js +2 -2
  40. package/dist/contexts/chat-runtime-context.d.ts +3 -6
  41. package/dist/contexts/chat-runtime-context.d.ts.map +1 -1
  42. package/dist/contexts/index.cjs +2 -2
  43. package/dist/contexts/index.js +1 -1
  44. package/dist/index.cjs +3 -5
  45. package/dist/index.cjs.map +1 -1
  46. package/dist/index.js +2 -4
  47. package/dist/utils/index.cjs +0 -10
  48. package/dist/utils/index.cjs.map +1 -1
  49. package/dist/utils/index.d.ts +0 -1
  50. package/dist/utils/index.d.ts.map +1 -1
  51. package/dist/utils/index.js +1 -10
  52. package/dist/utils/index.js.map +1 -1
  53. package/package.json +1 -1
  54. package/src/components/chat/hooks/use-chat-identity.ts +7 -8
  55. package/src/components/footer-waitlist-button.tsx +16 -33
  56. package/src/components/navigation/sticky-section-nav.tsx +4 -6
  57. package/src/components/tickets/help-center-card.tsx +1 -55
  58. package/src/components/tickets/help-center-list.tsx +1 -9
  59. package/src/components/tickets/ticket-detail-drawer.tsx +4 -19
  60. package/src/components/tickets/ticket-row.tsx +19 -30
  61. package/src/contexts/chat-runtime-context.tsx +3 -6
  62. package/src/stories/EmbeddableChat.stories.tsx +1 -1
  63. package/src/utils/index.ts +0 -12
  64. package/dist/chunk-3E5ANY55.js.map +0 -1
  65. package/dist/chunk-P5EE2VJX.cjs.map +0 -1
  66. package/dist/chunk-WI76ZUBE.cjs.map +0 -1
  67. package/dist/chunk-ZG2YY5E7.js.map +0 -1
  68. package/dist/utils/scroll-into-view.d.ts +0 -63
  69. package/dist/utils/scroll-into-view.d.ts.map +0 -1
  70. package/src/utils/scroll-into-view.ts +0 -74
  71. /package/dist/{chunk-QKFBZLIR.js.map → chunk-ATEUJQKU.js.map} +0 -0
  72. /package/dist/{chunk-5BNWGK6D.js.map → chunk-WJBPLMBX.js.map} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flamingo-stack/openframe-frontend-core",
3
- "version": "0.0.210-snapshot.20260528032637",
3
+ "version": "0.0.210",
4
4
  "description": "Shared design system and components for all Flamingo platforms",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -8,9 +8,8 @@
8
8
  * (attachment button, drawer composer, /tickets gate) on chat-side
9
9
  * identity tiers WITHOUT sending a chat message first.
10
10
  *
11
- * Server-side parity: the host's identity endpoint (hub default
12
- * `/api/auth/identity`, override via `runtime.endpoints.identityUrl`)
13
- * runs the same 3-tier `requireChatAuth` chain the chat itself uses.
11
+ * Server-side parity: the route at `/api/chat/identity` runs the same
12
+ * 3-tier `requireChatAuth` chain the chat itself uses.
14
13
  * `attachmentsEnabled` is computed server-side as
15
14
  * `authTier !== 'anon' AND isSelfScopedSource(source)` — single
16
15
  * source of truth, consumers don't combine the fields themselves.
@@ -28,7 +27,7 @@
28
27
  * 3. The fetch is cheap and short — no perf justification for a
29
28
  * cache layer
30
29
  *
31
- * Endpoint URL: read from `useRequiredChatRuntime().endpoints.identityUrl`
30
+ * Endpoint URL: read from `useRequiredChatRuntime().endpoints.chatIdentityUrl`
32
31
  * so embedded apps with their own reverse-proxy topology can override.
33
32
  */
34
33
 
@@ -38,9 +37,9 @@ import { chatAuthedFetch } from '../utils/chat-authed-fetch'
38
37
  import { getChatProxyAuth } from '../utils/chat-proxy-auth-storage'
39
38
 
40
39
  /**
41
- * Wire-shape for the identity route response. Mirrors the hub's
42
- * `ChatIdentityResponse` (in `app/api/auth/identity/route.ts`)
43
- * kept in sync there. Lib-side declaration so the chat panel can
40
+ * Wire-shape for the `/api/chat/identity` route response. Mirrors
41
+ * the hub's `ChatIdentityResponse` (in `app/api/chat/identity/route.ts`)
42
+ * kept in sync there. Lib-side declaration so the chat panel can
44
43
  * compile without depending on hub-internal types.
45
44
  */
46
45
  export interface ChatIdentityResponse {
@@ -88,7 +87,7 @@ const ANON_DEFAULTS: ChatIdentityResponse = {
88
87
 
89
88
  export function useChatIdentity(): ChatIdentitySurface {
90
89
  const runtime = useRequiredChatRuntime()
91
- const url = runtime.endpoints.identityUrl
90
+ const url = runtime.endpoints.chatIdentityUrl
92
91
  // `getChatProxyAuth()` reads localStorage every render. If the user
93
92
  // pastes bearer creds mid-session (via the `/debug` creds bar),
94
93
  // their email arrives here and the effect's dep changes → refetch.
@@ -1,7 +1,6 @@
1
1
  'use client';
2
2
 
3
- import { useRouter } from '../embed-shims/next-navigation';
4
- import { useChatRuntime } from '../contexts/chat-runtime-context';
3
+ import { usePathname, useRouter } from '../embed-shims/next-navigation';
5
4
  import { useCallback } from 'react';
6
5
  import { OpenFrameLogo } from './icons';
7
6
  import { Button } from './ui/button';
@@ -12,43 +11,27 @@ export interface FooterWaitlistButtonProps {
12
11
 
13
12
  /**
14
13
  * Small wrapper around JoinWaitlistButton for use inside the footer.
15
- *
16
- * Routes through the host's unified-navigation hook
17
- * (`runtime.navigation.navigate`) when a `ChatRuntimeContext` is
18
- * mounted — that's the same path EVERY other in-app navigation
19
- * surface uses (source chips, inline cards, search-autocomplete,
20
- * action cards). One rule, one decision tree across the whole app.
21
- * The hub's `HubRuntimeProvider` wires `navigate` to its `useUnifiedNav`
22
- * helper, so this button picks up cross-platform new-tab decisions,
23
- * same-URL re-scroll handling, embed-mode short-circuiting, and any
24
- * future host-side nav rules for free.
25
- *
26
- * Falls back to the embed-shim's `router.push` when no runtime is
27
- * mounted (third-party embedders who haven't set up
28
- * `ChatRuntimeContext` — the lib stays usable without forcing them
29
- * to wire the full chat-runtime).
30
- *
31
- * Target URL: `/waitlist#top`. `#top` is the canonical "scroll to
32
- * page top" anchor — the destination page has an explicit
33
- * `<div id="top">` at the top of `<main>` so native browser anchor
34
- * scroll works in every browser regardless of the HTML5 magic-anchor
35
- * behavior.
14
+ * Provides a default click handler that scrolls/focuses the wait-list form
15
+ * if the user is already on /waitlist; otherwise navigates to it.
36
16
  */
37
17
  export function FooterWaitlistButton({ className }: FooterWaitlistButtonProps) {
38
18
  const router = useRouter();
39
- const runtime = useChatRuntime();
19
+ const pathname = usePathname();
40
20
 
41
21
  const handleClick = useCallback(() => {
42
- const href = '/waitlist#top';
43
- // Prefer the host's unified-nav callback (hub-wired
44
- // `useUnifiedNav`). Falls back to the embed-shim's router when
45
- // the host hasn't provided one.
46
- if (runtime?.navigation?.navigate) {
47
- const handled = runtime.navigation.navigate({ href });
48
- if (handled) return;
22
+ if (pathname?.startsWith('/waitlist')) {
23
+ const anchor = document.getElementById('waitlist-form');
24
+ if (anchor) {
25
+ anchor.scrollIntoView({ behavior: 'smooth', block: 'center' } as any);
26
+ setTimeout(() => {
27
+ const input = anchor.querySelector('input[type="email"]') as HTMLInputElement | null;
28
+ input?.focus();
29
+ }, 400);
30
+ return;
31
+ }
49
32
  }
50
- router.push(href);
51
- }, [router, runtime]);
33
+ router.push('/waitlist#waitlist-form');
34
+ }, [pathname, router]);
52
35
 
53
36
  return (
54
37
  <Button
@@ -2,7 +2,6 @@
2
2
 
3
3
  import React, { useEffect, useState, useCallback, useRef } from 'react'
4
4
  import { cn } from '../../utils'
5
- import { scrollElementIntoView } from '../../utils/scroll-into-view'
6
5
 
7
6
  export interface StickyNavSection {
8
7
  id: string
@@ -98,10 +97,7 @@ export function useSectionNavigation(
98
97
  const isScrollingFromClick = useRef(false)
99
98
  const { offset = 100 } = options || {}
100
99
 
101
- // Handle click - scroll to the element via the canonical helper.
102
- // The `offset` prop maps to `headerOffset` (sticky chrome above the
103
- // section nav); same smooth-scroll mechanics every other anchor
104
- // surface in the app uses.
100
+ // Handle click - just scroll to the element
105
101
  const handleSectionClick = useCallback((sectionId: string) => {
106
102
  const targetElement = document.getElementById(sectionId)
107
103
  if (!targetElement) return
@@ -110,7 +106,9 @@ export function useSectionNavigation(
110
106
  isScrollingFromClick.current = true
111
107
  setActiveSection(sectionId)
112
108
 
113
- scrollElementIntoView(targetElement, { headerOffset: offset })
109
+ // Scroll to element
110
+ const top = targetElement.offsetTop - offset
111
+ window.scrollTo({ top, behavior: 'smooth' })
114
112
 
115
113
  // Allow scroll spy again after scroll completes
116
114
  setTimeout(() => {
@@ -15,10 +15,8 @@
15
15
  * is a SIBLING of the toggle button, not nested inside it.
16
16
  */
17
17
 
18
- import { useCallback, useRef } from 'react'
19
18
  import { StatusBadge, type StatusBadgeProps } from '../ui'
20
19
  import { formatRelativeTime } from '../../utils/date-utils'
21
- import { scrollElementIntoView } from '../../utils/scroll-into-view'
22
20
  import { getStatusColorScheme } from '../chat/utils/agent-status-message'
23
21
  import { DevCardRowContent } from '../shared/dev-section/dev-card-row'
24
22
  import {
@@ -28,23 +26,6 @@ import {
28
26
  import type { AnyTicket } from './types'
29
27
  import { isOptimistic } from './types'
30
28
 
31
- /** Sticky page-chrome offset, applied two ways from this ONE constant:
32
- *
33
- * 1. As `scrollMarginTop` inline style on the wrapper — so any
34
- * anchor-driven or `scrollIntoView()`-driven scroll (browser
35
- * `#hash` navigation, Tab-focus into the card) lands BELOW the
36
- * sticky header.
37
- * 2. As `headerOffset` passed to `scrollElementIntoView(...)` — for
38
- * the click-to-expand `window.scrollTo` path, which pre-computes
39
- * its target pixel and ignores CSS `scroll-margin-top`.
40
- *
41
- * Single source of truth: change 96 here and BOTH paths follow. The
42
- * previous code combined a `scroll-mt-24` (=96px) Tailwind class
43
- * with this constant — two declarations, one comment binding them,
44
- * drift hazard. Now there's nothing to keep in sync.
45
- */
46
- const STICKY_HEADER_OFFSET_PX = 96
47
-
48
29
  export interface HelpCenterCardProps {
49
30
  ticket: AnyTicket
50
31
  expanded: boolean
@@ -91,39 +72,6 @@ export function HelpCenterCard({
91
72
  const isExpandable = !optimistic
92
73
  const isExpanded = expanded && isExpandable
93
74
 
94
- // Scroll-on-click — delegates to the canonical `scrollElementIntoView`
95
- // helper with a cross-row layout-shift `adjustTargetY` callback. The
96
- // helper owns the smooth-scroll mechanics + sticky-chrome offset; we
97
- // pass the consumer-specific knowledge ("a sibling drawer above me
98
- // is about to collapse — subtract its height from the target Y").
99
- //
100
- // Cross-row gotcha: if ANOTHER row above this one is currently
101
- // expanded, its drawer collapses simultaneously with our toggle.
102
- // The collapse shrinks the page above our row → our final Y is
103
- // HIGHER than the current `rect.top`. By pre-subtracting the
104
- // collapsing drawer's height we land at the post-shift position
105
- // cleanly, without scrollIntoView's mid-animation drift.
106
- const rowRef = useRef<HTMLDivElement | null>(null)
107
- const handleClick = useCallback(() => {
108
- onToggle(ticket.id)
109
- scrollElementIntoView(rowRef.current, {
110
- headerOffset: STICKY_HEADER_OFFSET_PX,
111
- adjustTargetY: (raw) => {
112
- if (!rowRef.current) return raw
113
- const expandedDrawer = document.querySelector(
114
- 'div[id^="help-center-drawer-"]',
115
- )
116
- if (!(expandedDrawer instanceof HTMLElement)) return raw
117
- const drawerRect = expandedDrawer.getBoundingClientRect()
118
- const myRect = rowRef.current.getBoundingClientRect()
119
- // Only adjust when the drawer is ABOVE us. Drawers below us
120
- // don't shift our position when they collapse.
121
- if (drawerRect.bottom > myRect.top) return raw
122
- return raw - drawerRect.height
123
- },
124
- })
125
- }, [onToggle, ticket.id])
126
-
127
75
  const rightBadges = (
128
76
  <>
129
77
  <StatusBadge
@@ -145,14 +93,12 @@ export function HelpCenterCard({
145
93
 
146
94
  return (
147
95
  <div
148
- ref={rowRef}
149
- style={{ scrollMarginTop: STICKY_HEADER_OFFSET_PX }}
150
96
  className={`border-b border-ods-border last:border-b-0 ${optimistic ? 'opacity-60' : ''}`}
151
97
  aria-busy={optimistic || undefined}
152
98
  >
153
99
  <button
154
100
  type="button"
155
- onClick={isExpandable ? handleClick : undefined}
101
+ onClick={isExpandable ? () => onToggle(ticket.id) : undefined}
156
102
  disabled={!isExpandable}
157
103
  aria-expanded={isExpandable ? isExpanded : undefined}
158
104
  aria-controls={isExpanded ? `help-center-drawer-${ticket.id}` : undefined}
@@ -264,15 +264,7 @@ function HelpCenterListAuthed({
264
264
  />
265
265
  )
266
266
  ) : (
267
- // `overflow-clip` (NOT `overflow-hidden`) — both visually
268
- // clip the rounded corners, but `hidden` makes the element
269
- // a "scroll container" per CSSOM spec, which causes
270
- // `scrollIntoView` calls inside (`<HelpCenterCard>` click
271
- // handlers) to try scrolling THIS div (can't, overflow
272
- // hidden) instead of bubbling up to the window. `clip`
273
- // keeps the visual clip but NOT the scroll-container
274
- // status, so click-to-scroll actually moves the page.
275
- <div className="bg-ods-card border border-ods-border rounded-[6px] overflow-clip w-full">
267
+ <div className="bg-ods-card border border-ods-border rounded-[6px] overflow-hidden w-full">
276
268
  {merged.map((ticket) => (
277
269
  <HelpCenterCard
278
270
  key={ticket.id}
@@ -212,16 +212,9 @@ function TicketTimelinePanel({ ticket }: { ticket: AnyTicket }) {
212
212
  return (
213
213
  <div className="bg-ods-card border border-ods-border rounded-[6px] overflow-hidden w-full">
214
214
  {/* Customer-authored description + any legacy `---`-joined
215
- comments. Always rendered ABOVE the engagement timeline as
216
- "Original message" because the server's intake-burst filter
217
- (see `filterCustomerVisibleTimeline` in
218
- `hubspot-conversations-utils.ts`) drops the customer's first
219
- message from engagements when it was part of the HubSpot
220
- Custom Channel bot intake — bodyTurns IS the canonical
221
- original for those tickets. For tickets created without bot
222
- intake (admin-created, email channel) bodyTurns shows the
223
- manually-entered description and engagements show subsequent
224
- replies — same flow, no duplication. */}
215
+ comments. Original message gets a special role label; legacy
216
+ updates get "Update N" or "Resolution". Timestamp matches the
217
+ ticket's creation time when available. */}
225
218
  {bodyTurns.map((turn, i) => {
226
219
  const isResolution = turn.startsWith('[Resolution]')
227
220
  const role =
@@ -322,19 +315,11 @@ function TicketTimelinePanel({ ticket }: { ticket: AnyTicket }) {
322
315
  avatarSrc = undefined
323
316
  }
324
317
 
325
- // Role label: every engagement is a customer-visible
326
- // Conversations message (customer ↔ agent on the Custom
327
- // Channel). There are no internal Notes on this surface
328
- // anymore — the read path explicitly filters them. So
329
- // "Reply" for BOTH sides. The previous "Note" label for
330
- // support bubbles was a legacy artifact from when Notes
331
- // were rendered and made customers think their support
332
- // engineer was leaving internal comments on their ticket.
333
318
  return (
334
319
  <ConversationCardRow
335
320
  key={eng.id}
336
321
  author={author}
337
- role="Reply"
322
+ role={isCustomer ? 'Reply' : 'Note'}
338
323
  avatarSrc={avatarSrc}
339
324
  timestamp={eng.createdAt}
340
325
  body={stripAttachmentsPreamble(eng.body ?? '')}
@@ -18,7 +18,7 @@
18
18
  * rendered only when this row is the expanded one.
19
19
  */
20
20
 
21
- import { useCallback, useRef } from 'react'
21
+ import { useEffect, useRef } from 'react'
22
22
  import {
23
23
  Collapsible,
24
24
  CollapsibleContent,
@@ -28,7 +28,6 @@ import {
28
28
  type ChatTicketItemData,
29
29
  } from '../chat/entity-cards/chat-ticket-item'
30
30
  import { formatRelativeTime } from '../../utils/date-utils'
31
- import { scrollElementIntoView } from '../../utils/scroll-into-view'
32
31
  import {
33
32
  TicketDetailDrawer,
34
33
  type TicketDetailDrawerProps,
@@ -65,35 +64,25 @@ export function TicketRow({
65
64
  // arrived yet, so action targets would be undefined.
66
65
  const optimistic = isOptimistic(ticket)
67
66
 
68
- // Scroll the clicked card to the top of the viewport. Every click
69
- // scrolls first-click expansion, same-row re-click, cross-row
70
- // switch. The clicked card lands at the top.
67
+ // Scroll the row's summary tile to the top of the viewport when the
68
+ // user expands it. Without this, expanding a row near the bottom of
69
+ // the viewport leaves most of the drawer (timeline + composer)
70
+ // hidden — the user has to scroll manually to see what they just
71
+ // opened.
71
72
  //
72
- // Cross-row gotcha: if ANOTHER row above this one is currently
73
- // expanded, its drawer is about to collapse simultaneously with our
74
- // toggle. We pre-subtract its height from the target Y so the
75
- // smooth-scroll lands at the FINAL post-collapse position cleanly.
76
- // Same pattern as `<HelpCenterCard>` — the only diff is the drawer
77
- // id prefix (`ticket-drawer-` vs `help-center-drawer-`).
73
+ // Uses smooth-scroll + `block: 'start'` so the tile lands at the top
74
+ // edge. Two RAFs let the Collapsible's height animation start before
75
+ // we measure the row position, otherwise the scroll lands at the
76
+ // pre-expansion position.
78
77
  const rowRef = useRef<HTMLDivElement | null>(null)
79
- const handleClick = useCallback(() => {
80
- onToggle(ticket.id)
81
- scrollElementIntoView(rowRef.current, {
82
- adjustTargetY: (raw) => {
83
- if (!rowRef.current) return raw
84
- const expandedDrawer = document.querySelector(
85
- 'div[id^="ticket-drawer-"]',
86
- )
87
- if (!(expandedDrawer instanceof HTMLElement)) return raw
88
- const drawerRect = expandedDrawer.getBoundingClientRect()
89
- const myRect = rowRef.current.getBoundingClientRect()
90
- // Only adjust when the drawer is ABOVE us. Drawers below
91
- // don't shift our position when they collapse.
92
- if (drawerRect.bottom > myRect.top) return raw
93
- return raw - drawerRect.height
94
- },
78
+ useEffect(() => {
79
+ if (!expanded || optimistic) return
80
+ requestAnimationFrame(() => {
81
+ requestAnimationFrame(() => {
82
+ rowRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' })
83
+ })
95
84
  })
96
- }, [onToggle, ticket.id])
85
+ }, [expanded, optimistic])
97
86
 
98
87
  const tileData: ChatTicketItemData = {
99
88
  id: ticket.id,
@@ -123,14 +112,14 @@ export function TicketRow({
123
112
  }
124
113
 
125
114
  return (
126
- <div ref={rowRef} className="scroll-mt-24">
115
+ <div ref={rowRef} className="scroll-mt-4">
127
116
  <Collapsible
128
117
  open={expanded && !optimistic}
129
118
  className="border-b border-ods-border last:border-b-0"
130
119
  >
131
120
  <ChatTicketItem
132
121
  ticket={tileData}
133
- onClick={optimistic ? undefined : handleClick}
122
+ onClick={optimistic ? undefined : onToggle}
134
123
  aria-expanded={expanded && !optimistic}
135
124
  aria-controls={`ticket-drawer-${ticket.id}`}
136
125
  />
@@ -62,15 +62,12 @@ export interface ChatRuntime {
62
62
  * relative `/api/storage/view/chat-attachments/` is sufficient
63
63
  * (same-origin); embedders supply an absolute hub URL so the
64
64
  * browser can fetch cross-origin.
65
- * - `identityUrl` — GET endpoint the `useChatIdentity` hook
65
+ * - `chatIdentityUrl` — GET endpoint the `useChatIdentity` hook
66
66
  * hits to learn the `{authTier, source, attachmentsEnabled}`
67
- * capability bag for the current session. Used beyond chat
68
- * (tickets / contact form / any embedded surface that needs
69
- * to identify the proxied customer), so the name has no
70
- * "chat" prefix even though the consuming hook still does. */
67
+ * capability bag for the current session. */
71
68
  attachmentUploadUrl: string
72
69
  attachmentViewUrlPrefix: string
73
- identityUrl: string
70
+ chatIdentityUrl: string
74
71
  /** Optional URL prefix for the image proxy (`<prefix>?url=<external>`).
75
72
  * When unset, lib's `getProxiedImageUrl` returns the original URL
76
73
  * unchanged. Hub default: '/api/image-proxy'. Embedders that don't
@@ -35,7 +35,7 @@ function createMockRuntime(): ChatRuntime {
35
35
  buildListUrl: () => null,
36
36
  attachmentUploadUrl: '/__story__/upload',
37
37
  attachmentViewUrlPrefix: '/__story__/view/',
38
- identityUrl: '/__story__/identity',
38
+ chatIdentityUrl: '/__story__/identity',
39
39
  },
40
40
  navigation: {
41
41
  mode: 'embed',
@@ -200,15 +200,3 @@ export { fetchPriorityProp, type FetchPriorityValue } from './fetch-priority'
200
200
  // server-safe (no JSX, no contexts/* imports); imported by route-page
201
201
  // `metadata` exports + the shared `<DevSectionView>` chrome.
202
202
  export * from './dev-sections'
203
-
204
- // Canonical "smooth scroll element into view with sticky-chrome
205
- // offset" helper. Single source of truth across the lib + hub for
206
- // pre-computed-target scrolling — see `scroll-into-view.ts` for the
207
- // rationale (TL;DR: window.scrollTo({top, behavior:'smooth'}) with a
208
- // pre-computed pixel value avoids the mid-animation jitter that
209
- // `element.scrollIntoView()` produces when layout shifts during the
210
- // scroll).
211
- export {
212
- type ScrollElementIntoViewOptions,
213
- scrollElementIntoView,
214
- } from './scroll-into-view'