@papermap/papermap 1.1.0 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +936 -35
- package/dist/index.d.ts +936 -35
- package/dist/index.js +13040 -4956
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +12998 -4921
- package/dist/index.mjs.map +1 -1
- package/package.json +11 -1
- package/readme.md +673 -23
- package/styles.css +24 -3
package/readme.md
CHANGED
|
@@ -8,20 +8,53 @@ For **server-side setup** (HMAC auth, dashboards per tenant, embed tokens, API e
|
|
|
8
8
|
|
|
9
9
|
## Main components
|
|
10
10
|
|
|
11
|
-
- **`PaperChat`** — Full AI chat assistant with streaming, conversation history, and chart generation.
|
|
11
|
+
- **`PaperChat`** — Full AI chat assistant with streaming, conversation history, and chart generation. Supports four layouts: **default** (floating toolbar + modal assistant), **side** (fixed side panel for sheet-style pages), **floating** (chart preview overlay), and **dialog** (tawk.to-style floating launcher + popup chat widget).
|
|
12
12
|
- **`PaperCard`** — Standalone chart card with toolbar actions. Use `variant="streaming"` for an embedded chart + conversation dialog (without the floating assistant).
|
|
13
|
-
- **`
|
|
13
|
+
- **`PaperInsight`** — "Quick Insights" panel: streams up to 3 AI-generated insights (highlight / trends / recommendation) for a dashboard + date range.
|
|
14
|
+
- **`PaperBoard`** — Responsive grid layout of chart cards, toolbar (screenshot, generate dashboard, theme), optional chat assistant + `PaperInsight` tile, and controlled or uncontrolled layouts.
|
|
14
15
|
|
|
15
16
|
```tsx
|
|
16
|
-
import { PaperChat, PaperCard, PaperBoard } from '@papermap/papermap'
|
|
17
|
+
import { PaperChat, PaperCard, PaperInsight, PaperBoard } from '@papermap/papermap'
|
|
17
18
|
|
|
18
|
-
//
|
|
19
|
+
// Default: floating toolbar + expandable assistant (dashboard-style)
|
|
19
20
|
<PaperChat
|
|
20
21
|
token="your-base64-api-token"
|
|
21
22
|
workspaceId="your-workspace-id"
|
|
22
23
|
dashboardId="your-dashboard-id"
|
|
23
24
|
/>
|
|
24
25
|
|
|
26
|
+
// Side panel (controlled open state; typical for sheet / narrow layouts)
|
|
27
|
+
<PaperChat
|
|
28
|
+
variant="side"
|
|
29
|
+
token="..."
|
|
30
|
+
workspaceId="..."
|
|
31
|
+
dashboardId="..."
|
|
32
|
+
open={sideOpen}
|
|
33
|
+
onOpenChange={setSideOpen}
|
|
34
|
+
/>
|
|
35
|
+
|
|
36
|
+
// Floating chart overlay (preview over your content)
|
|
37
|
+
<PaperChat
|
|
38
|
+
variant="floating"
|
|
39
|
+
token="..."
|
|
40
|
+
workspaceId="..."
|
|
41
|
+
dashboardId="..."
|
|
42
|
+
open={overlayOpen}
|
|
43
|
+
onOpenChange={setOverlayOpen}
|
|
44
|
+
/>
|
|
45
|
+
|
|
46
|
+
// Dialog (live-chat widget: floating launcher + popup; outside-click is ignored,
|
|
47
|
+
// closes only via the X button or by clicking the launcher again)
|
|
48
|
+
<PaperChat
|
|
49
|
+
variant="dialog"
|
|
50
|
+
token="..."
|
|
51
|
+
workspaceId="..."
|
|
52
|
+
dashboardId="..."
|
|
53
|
+
title="Support"
|
|
54
|
+
subtitle="Ask anything about your data"
|
|
55
|
+
launcherPosition="bottom-right"
|
|
56
|
+
/>
|
|
57
|
+
|
|
25
58
|
// Standalone chart card
|
|
26
59
|
<PaperCard
|
|
27
60
|
token="your-base64-api-token"
|
|
@@ -39,12 +72,22 @@ import { PaperChat, PaperCard, PaperBoard } from '@papermap/papermap'
|
|
|
39
72
|
chartId="your-chart-id" // optional; card shows empty state if omitted
|
|
40
73
|
/>
|
|
41
74
|
|
|
42
|
-
//
|
|
75
|
+
// Standalone Quick Insights panel (streams 3 AI insights for the period)
|
|
76
|
+
<PaperInsight
|
|
77
|
+
token="your-base64-api-token"
|
|
78
|
+
workspaceId="your-workspace-id"
|
|
79
|
+
dashboardId="your-dashboard-id"
|
|
80
|
+
startDate="2026-04-25"
|
|
81
|
+
endDate="2026-05-25"
|
|
82
|
+
/>
|
|
83
|
+
|
|
84
|
+
// Full grid dashboard (wraps chart cards + optional PaperChat + insight tile)
|
|
43
85
|
<PaperBoard
|
|
44
86
|
token="..."
|
|
45
87
|
workspaceId="..."
|
|
46
88
|
dashboardId="..."
|
|
47
89
|
showChatAssistant
|
|
90
|
+
showInsights
|
|
48
91
|
/>
|
|
49
92
|
```
|
|
50
93
|
|
|
@@ -125,21 +168,125 @@ function App() {
|
|
|
125
168
|
|
|
126
169
|
#### `PaperChat` props
|
|
127
170
|
|
|
128
|
-
| Prop
|
|
129
|
-
|
|
|
130
|
-
| `
|
|
131
|
-
| `
|
|
132
|
-
| `
|
|
133
|
-
| `
|
|
134
|
-
| `
|
|
135
|
-
| `placeholder`
|
|
136
|
-
| `shortcutKey`
|
|
137
|
-
| `autoFade`
|
|
138
|
-
| `fadeDelay`
|
|
139
|
-
| `className`
|
|
171
|
+
| Prop | Type | Required | Default | Description |
|
|
172
|
+
| --------------------------- | -------------------------------------------------------------- | ----------- | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
173
|
+
| `variant` | `'default' \| 'side' \| 'floating' \| 'dialog'` | No | `'default'` | Layout: floating dashboard chat, fixed side panel, chart overlay, or popup widget. |
|
|
174
|
+
| `token` | `string` | Yes\* | -- | Base64-encoded API key token |
|
|
175
|
+
| `workspaceId` | `string` | Yes\* | -- | Workspace ID |
|
|
176
|
+
| `dashboardId` | `string` | Yes\* | -- | Dashboard ID |
|
|
177
|
+
| `apiUrl` | `string` | No | `https://dataapi.papermap.ai` | API base URL |
|
|
178
|
+
| `placeholder` | `string` | No | `"Ask anything..."` | Input placeholder (default variant) |
|
|
179
|
+
| `shortcutKey` | `string` | No | `"k"` | Focus shortcut (Cmd/Ctrl + key, default variant) |
|
|
180
|
+
| `autoFade` | `boolean` | No | `false` | Fade toolbar after inactivity (default variant) |
|
|
181
|
+
| `fadeDelay` | `number` | No | `5000` | Milliseconds before auto-fade |
|
|
182
|
+
| `className` | `string` | No | -- | Extra CSS class on toolbar container (default variant) |
|
|
183
|
+
| `showToolbar` | `boolean` | No | `true` | Show the bottom chat toolbar (default variant) |
|
|
184
|
+
| `hideChartArea` | `boolean` | No | `false` | Hides chart preview regions where applicable |
|
|
185
|
+
| `onToolbarHeightChange` | `(height: number) => void` | No | -- | Fired when toolbar height changes (e.g. multiline input) |
|
|
186
|
+
| `onAssistantClose` | `() => void` | No | -- | Fired when the floating assistant panel closes (default variant) |
|
|
187
|
+
| `initialChatScroll` | `'top' \| 'bottom'` | No | -- | Conversation scroll anchoring; `top` matches main-app onboarding |
|
|
188
|
+
| `isViewer` | `boolean` | No | -- | Hides bookmark actions (viewer workspaces); use provider when nested |
|
|
189
|
+
| `showAvatars` | `boolean` | No | see below | Hide message-row avatars when `false`. When omitted on **`variant="default"`**, `ChatAssistant` shows avatars whenever the layout is not inline (`assistantInline` in the store, default `false`). **`variant="side"`** and **`variant="dialog"`** default to showing avatars (`true`). |
|
|
190
|
+
| `avatars` | `PaperChatAvatars` | No | -- | Replace the user / assistant / “thinking” icons in each message row (see [Chat assistant customization](#chat-assistant-customization)). |
|
|
191
|
+
| `assistantInline` | `boolean` | No | -- | Inline assistant layout; for `variant="side"` defaults to `true` |
|
|
192
|
+
| `open` | `boolean` | Yes† / Yes‡ | -- | Surface open: **`side`** rail, **`floating`** overlay, **`dialog`** modal (see `onOpenChange`) |
|
|
193
|
+
| `onOpenChange` | `(open: boolean) => void` | Yes† / Yes‡ | -- | Toggle for **`side`**, **`floating`**, and **`dialog`** |
|
|
194
|
+
| `onChartVisibilityChange` | `(visible: boolean) => void` | No | -- | Sheet: chart overlay visibility |
|
|
195
|
+
| `chartOverlayPortalRef` | `RefObject<HTMLDivElement \| null>` | No | -- | Optional portal target for chart overlay |
|
|
196
|
+
| `onPendingSheetEditsChange` | `(edits: { edit_session_id?: string }[]) => void` | No | -- | Sheet: pending edit sessions |
|
|
197
|
+
| `clearPendingSheetEditsRef` | `MutableRefObject<(() => void) \| null>` | No | -- | Ref to clear pending edits |
|
|
198
|
+
| `visible` | `boolean` | No | -- | **Deprecated.** Use **`open`** for `variant="floating"`. |
|
|
199
|
+
| `onClose` | `() => void` | No | -- | **Deprecated.** Use **`onOpenChange(false)`** for `variant="floating"`; still called after `onOpenChange` on close if both are passed. |
|
|
200
|
+
| `historyOpen` | `boolean` | No | -- | Show chart history in overlay instead of chat column |
|
|
201
|
+
| `onCloseHistory` | `() => void` | No | -- | Close history sub-view in overlay |
|
|
202
|
+
| `title` | `string` | No | `"Chat"` | Dialog header title (`variant="dialog"`) |
|
|
203
|
+
| `subtitle` | `string` | No | ask-anything hint | Dialog header subtitle (`variant="dialog"`) |
|
|
204
|
+
| `showLauncher` | `boolean` | No | `true` | Render the floating launcher (`variant="dialog"`) |
|
|
205
|
+
| `launcherAriaLabel` | `string` | No | `"Open chat"` | Accessible label for the launcher (`variant="dialog"`) |
|
|
206
|
+
| `launcherPosition` | `'bottom-right' \| 'bottom-left' \| 'top-right' \| 'top-left'` | No | `'bottom-right'` | Viewport corner for the launcher (`variant="dialog"`) |
|
|
207
|
+
| `dialogTitle` | `string` | No | -- | **Deprecated.** Use **`title`**. |
|
|
208
|
+
| `dialogSubtitle` | `string` | No | -- | **Deprecated.** Use **`subtitle`**. |
|
|
209
|
+
| `dialogOpen` | `boolean` | No | -- | **Deprecated.** Use **`open`** for controlled dialog. |
|
|
210
|
+
| `onDialogOpenChange` | `(open: boolean) => void` | No | -- | **Deprecated.** Use **`onOpenChange`**. |
|
|
211
|
+
| `showDialogLauncher` | `boolean` | No | -- | **Deprecated.** Use **`showLauncher`**. |
|
|
212
|
+
| `dialogLauncherAriaLabel` | `string` | No | -- | **Deprecated.** Use **`launcherAriaLabel`**. |
|
|
213
|
+
| `dialogLauncherPosition` | `'bottom-right' \| 'bottom-left' \| 'top-right' \| 'top-left'` | No | -- | **Deprecated.** Use **`launcherPosition`**. |
|
|
140
214
|
|
|
141
215
|
\*Omit on the component when values come from `PapermapConfigProvider` / `PapermapProvider`.
|
|
142
216
|
|
|
217
|
+
†Required when `variant` is `"side"`. ‡For `variant="floating"`, pass **`open`** + **`onOpenChange`** (or the deprecated **`visible`** + **`onClose`**).
|
|
218
|
+
|
|
219
|
+
#### Chat assistant customization
|
|
220
|
+
|
|
221
|
+
The transcript UI is implemented by **`ChatAssistant`** (also exported for custom layouts). **`PaperChat`** forwards branding-related props on every variant except **`floating`** (that variant is chart-first and does not expose these).
|
|
222
|
+
|
|
223
|
+
##### Avatars (`PaperChatAvatars`)
|
|
224
|
+
|
|
225
|
+
Import the type and pass an `avatars` object on **`PaperChat`**, **`PaperChatSidePanel`** / **`ChatSidePanel`**, or **`PaperChatDialogInner`**:
|
|
226
|
+
|
|
227
|
+
```tsx
|
|
228
|
+
import { PaperChat, type PaperChatAvatars } from '@papermap/papermap'
|
|
229
|
+
|
|
230
|
+
const avatars: PaperChatAvatars = {
|
|
231
|
+
user: <span className="text-xs font-bold text-primary-foreground">ME</span>,
|
|
232
|
+
assistant: <img src="/your-product-mark.svg" alt="" className="h-4 w-4" />,
|
|
233
|
+
thinking: <img src="/your-spinner-or-mark.svg" alt="" className="h-4 w-4" />,
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
<PaperChat
|
|
237
|
+
token="..."
|
|
238
|
+
workspaceId="..."
|
|
239
|
+
dashboardId="..."
|
|
240
|
+
avatars={avatars}
|
|
241
|
+
/>
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Each slot replaces **only the inner icon**. The outer **32px circular** affordance (background colors, alignment) stays the same—size custom icons for parity with the defaults.
|
|
245
|
+
|
|
246
|
+
##### Showing or hiding avatars
|
|
247
|
+
|
|
248
|
+
Set **`showAvatars={false}`** for a denser transcript without avatar columns.
|
|
249
|
+
|
|
250
|
+
When **`showAvatars` is omitted** on **`variant="default"`**, visibility is handled inside **`ChatAssistant`**: avatars show unless the assistant is in **inline** column mode (`assistantInline` in the store). With **`variant="side"`** or **`variant="dialog"`**, omitting **`showAvatars`** defaults to **`true`**.
|
|
251
|
+
|
|
252
|
+
If you compose **`ChatAssistant`** yourself with **`inline`** (compact column layout), avatars are **hidden by default** unless you pass **`showAvatars={true}`**—matching the main app’s compact sidebar behavior.
|
|
253
|
+
|
|
254
|
+
##### Dialog variant: floating launcher “button”
|
|
255
|
+
|
|
256
|
+
For **`variant="dialog"`**, the launcher is a fixed circular control portaled to **`document.body`** (MessageSquare icon). You can:
|
|
257
|
+
|
|
258
|
+
- Move it with **`launcherPosition`** (`bottom-right` \| `bottom-left` \| `top-right` \| `top-left`).
|
|
259
|
+
- Change the accessible name with **`launcherAriaLabel`** (also used as the native **`title`** tooltip on the launcher).
|
|
260
|
+
- Hide it with **`showLauncher={false}`** and drive the popup yourself via **`open`** / **`onOpenChange`** from your own button elsewhere in the host UI.
|
|
261
|
+
|
|
262
|
+
Legacy names (`dialogLauncherPosition`, `dialogLauncherAriaLabel`, `showDialogLauncher`, `dialogOpen`, `onDialogOpenChange`, `dialogTitle`, `dialogSubtitle`) still work; **`open`**, **`title`**, and the other canonical props take precedence when both are set.
|
|
263
|
+
|
|
264
|
+
##### Side variant: custom open trigger
|
|
265
|
+
|
|
266
|
+
When **`variant="side"`** and the panel is closed, pass **`renderSidePanelOpenTrigger`** to render your own control. It receives **`{ open, show, toggle }`** so you can wire a menu item, FAB, or icon button while **`open`** / **`onOpenChange`** remain the source of truth.
|
|
267
|
+
|
|
268
|
+
```tsx
|
|
269
|
+
<PaperChat
|
|
270
|
+
variant="side"
|
|
271
|
+
token="..."
|
|
272
|
+
workspaceId="..."
|
|
273
|
+
dashboardId="..."
|
|
274
|
+
open={open}
|
|
275
|
+
onOpenChange={setOpen}
|
|
276
|
+
renderSidePanelOpenTrigger={({ toggle }) => (
|
|
277
|
+
<button type="button" className="your-menu-styles" onClick={toggle}>
|
|
278
|
+
Open assistant
|
|
279
|
+
</button>
|
|
280
|
+
)}
|
|
281
|
+
/>
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
##### Advanced composition
|
|
285
|
+
|
|
286
|
+
Use **`PapermapProvider`** (or **`PapermapConfigProvider`**) and render **`ChatAssistant`** with **`avatars`**, **`showAvatars`**, **`inline`**, and **`hideChartArea`** as needed—the same props **`PaperChat`** uses internally.
|
|
287
|
+
|
|
288
|
+
Prefer **`PaperChat`** with `variant` for a single integration path. If you already wrap subtrees in `PapermapProvider`, you can compose **`PaperChatSidePanel`** or **`PaperChatFloatingChartOverlay`** directly (they reuse the parent store when present, or create a provider when not).
|
|
289
|
+
|
|
143
290
|
#### `PaperCard` props
|
|
144
291
|
|
|
145
292
|
| Prop | Type | Required | Default | Description |
|
|
@@ -193,6 +340,36 @@ const chart: TChartResponse = {
|
|
|
193
340
|
/>
|
|
194
341
|
```
|
|
195
342
|
|
|
343
|
+
#### `PaperInsight` props
|
|
344
|
+
|
|
345
|
+
| Prop | Type | Required | Default | Description |
|
|
346
|
+
| ------------------------- | -------------------------------------------------- | -------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
347
|
+
| `token` | `string` | Yes\* | -- | Base64-encoded API key token |
|
|
348
|
+
| `workspaceId` | `string` | Yes\* | -- | Workspace ID |
|
|
349
|
+
| `dashboardId` | `string` | Yes\* | -- | Dashboard ID |
|
|
350
|
+
| `apiUrl` | `string` | No | `https://dataapi.papermap.ai` | API base URL |
|
|
351
|
+
| `startDate` | `Date \| string` (`yyyy-MM-dd`) | Yes | -- | Inclusive start of the insight window |
|
|
352
|
+
| `endDate` | `Date \| string` (`yyyy-MM-dd`) | Yes | -- | Inclusive end of the insight window |
|
|
353
|
+
| `theme` | `'light' \| 'dark' \| 'system'` | No | -- | Scoped color scheme (independent of host page) |
|
|
354
|
+
| `dashboardTheme` | `DashboardTheme \| null` | No | -- | Themed mark color + module surface (matches main app workspace theme) |
|
|
355
|
+
| `isEditMode` | `boolean` | No | `false` | Disable interactions while a parent grid is in layout-edit mode |
|
|
356
|
+
| `periodPreset` | `InsightsPeriodPreset` | No | -- | Show the period dropdown (`last_3_months \| this_week \| this_month \| this_year \| custom`). Hide by omitting. |
|
|
357
|
+
| `onPeriodPresetChange` | `(preset: InsightsPeriodPreset) => void` | No | -- | Fired when the user picks a preset |
|
|
358
|
+
| `onRangeChange` | `(start: Date, end: Date) => void` | No | -- | Required for the inline custom-range scrubber to function — the host owns the dates and updates them when this fires |
|
|
359
|
+
| `showCustomRangeScrubber` | `boolean` | No | `true` | Render the inline `TimelineRange` scrubber when `periodPreset === 'custom'`. Set `false` when a parent (e.g. `PaperBoard`) renders its own. |
|
|
360
|
+
| `onExplore` | `(chatId: string) => void` | No | (opens chat assistant) | Per-row "Explore in Chat" arrow. Defaults to calling `openPapermapChatAssistant` when a store is mounted. |
|
|
361
|
+
| `onInsight` | `(insight: DashboardInsight) => void` | No | -- | Fired once per `insight` SSE event |
|
|
362
|
+
| `onComplete` | `(result: DashboardInsightsResult) => void` | No | -- | Fired when the server emits `insights_complete` |
|
|
363
|
+
| `onError` | `(error: Error) => void` | No | -- | Fired on transport / `error` SSE event |
|
|
364
|
+
| `className` | `string` | No | -- | Root element class |
|
|
365
|
+
| `style` | `CSSProperties` | No | -- | Root element inline style |
|
|
366
|
+
| `classNames` | `Partial<Record<PaperInsightSlot, string>>` | No | -- | Per-slot class overrides (`header`, `title`, `dateLabel`, `refreshButton`, `row`, `body`, `footer`, …) |
|
|
367
|
+
| `styles` | `Partial<Record<PaperInsightSlot, CSSProperties>>` | No | -- | Per-slot inline style overrides |
|
|
368
|
+
|
|
369
|
+
\*Omit on the component when values come from `PapermapProvider`.
|
|
370
|
+
|
|
371
|
+
The SSE call is `POST /api/v1/analytics/dashboards/:dashboardId/insights/stream` with body `{ start_date, end_date }`. The component renders the panel's chrome (Activity icon, "Quick Insights" title, animated `TickSpinner` + cycling "Thinking… / Crunching… / Cooking…", optional period dropdown, Refresh button) and up to 3 streamed insights with metric chips and an "Explore in Chat" arrow.
|
|
372
|
+
|
|
196
373
|
#### `PaperBoard` props (high level)
|
|
197
374
|
|
|
198
375
|
| Prop | Notes |
|
|
@@ -202,6 +379,10 @@ const chart: TChartResponse = {
|
|
|
202
379
|
| `layouts` / `onLayoutsChange` | Controlled grid layouts per breakpoint. |
|
|
203
380
|
| `isEditMode`, `editLayout`, `isViewer` | Edit vs view behavior. |
|
|
204
381
|
| `showToolbar`, `showScreenshot`, `showGenerateDashboard`, `showHeader`, `showChatAssistant` | Feature toggles. |
|
|
382
|
+
| `showInsights` | Mount the `PaperInsight` tile at the top of the grid (mirrors main app `TimelineInsights`). |
|
|
383
|
+
| `insightsPeriodPreset`, `onInsightsPeriodPresetChange` | Controlled period for the insight tile. Defaults internally to `'last_3_months'`. |
|
|
384
|
+
| `insightsStartDate`, `insightsEndDate` | Controlled range overrides (use with `'custom'` to drive the dashboard `TimelineRange` scrubber from the host). |
|
|
385
|
+
| `onInsightExplore` | Per-row "Explore in Chat" arrow. Defaults to opening the floating chat assistant when one is mounted. |
|
|
205
386
|
| `variant` | `'default' \| 'streaming'` for embedded chart cards (matches `PaperCard`). |
|
|
206
387
|
| `dashboardTheme`, `onDashboardThemeChange`, `persistWorkspaceTheme`, `renderThemeModal` | Theming; built-in modal uses `ThemeCustomizationSettings`. |
|
|
207
388
|
| Callbacks | `onEditChart`, `onDeleteChart`, `onPinChange`, `onGenerateDashboard`, `onTakeScreenshot`, `onThemeModalOpen`, etc. |
|
|
@@ -234,9 +415,477 @@ export function EmbeddedStreamingChart() {
|
|
|
234
415
|
|
|
235
416
|
---
|
|
236
417
|
|
|
237
|
-
##
|
|
418
|
+
## Examples
|
|
419
|
+
|
|
420
|
+
Every example below has a matching Storybook story — run `npm run storybook` (http://localhost:3001) to see it live. Replace the `'...'` placeholders with your own `token` / `workspaceId` / `dashboardId`, or supply them once via `PapermapConfigProvider` / `PapermapProvider`. Controlled examples use React `useState`.
|
|
421
|
+
|
|
422
|
+
### PaperChat
|
|
423
|
+
|
|
424
|
+
#### Auto-fading toolbar
|
|
425
|
+
|
|
426
|
+
Fade the floating toolbar after a period of inactivity.
|
|
427
|
+
|
|
428
|
+
```tsx
|
|
429
|
+
<PaperChat token="..." workspaceId="..." dashboardId="..." autoFade fadeDelay={3000} />
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
#### Custom placeholder and focus shortcut
|
|
433
|
+
|
|
434
|
+
```tsx
|
|
435
|
+
<PaperChat
|
|
436
|
+
token="..."
|
|
437
|
+
workspaceId="..."
|
|
438
|
+
dashboardId="..."
|
|
439
|
+
placeholder="Ask about your data..."
|
|
440
|
+
shortcutKey="j"
|
|
441
|
+
/>
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
#### Side panel (controlled)
|
|
445
|
+
|
|
446
|
+
Fixed-width rail for sheet-style pages. `open` / `onOpenChange` are required.
|
|
447
|
+
|
|
448
|
+
```tsx
|
|
449
|
+
function SidePanelExample() {
|
|
450
|
+
const [open, setOpen] = useState(true)
|
|
451
|
+
return (
|
|
452
|
+
<div className="flex h-screen min-h-0">
|
|
453
|
+
<PaperChat
|
|
454
|
+
variant="side"
|
|
455
|
+
token="..."
|
|
456
|
+
workspaceId="..."
|
|
457
|
+
dashboardId="..."
|
|
458
|
+
sidePosition="left"
|
|
459
|
+
sideContentInsetMode="auto"
|
|
460
|
+
open={open}
|
|
461
|
+
onOpenChange={setOpen}
|
|
462
|
+
className="h-full min-h-0 shrink-0"
|
|
463
|
+
/>
|
|
464
|
+
<main className="min-h-0 min-w-0 flex-1 overflow-auto">Your page</main>
|
|
465
|
+
</div>
|
|
466
|
+
)
|
|
467
|
+
}
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
`sideContentInsetMode="auto"` places the panel in a flex row beside your main column; `"none"` keeps it viewport-fixed and overlays content. `sidePosition` docks it `left` or `right`.
|
|
471
|
+
|
|
472
|
+
#### Floating chart overlay
|
|
473
|
+
|
|
474
|
+
```tsx
|
|
475
|
+
function FloatingExample() {
|
|
476
|
+
const [open, setOpen] = useState(true)
|
|
477
|
+
return (
|
|
478
|
+
<PaperChat
|
|
479
|
+
variant="floating"
|
|
480
|
+
token="..."
|
|
481
|
+
workspaceId="..."
|
|
482
|
+
dashboardId="..."
|
|
483
|
+
open={open}
|
|
484
|
+
onOpenChange={setOpen}
|
|
485
|
+
/>
|
|
486
|
+
)
|
|
487
|
+
}
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
#### Dialog widget (floating launcher)
|
|
491
|
+
|
|
492
|
+
```tsx
|
|
493
|
+
<PaperChat
|
|
494
|
+
variant="dialog"
|
|
495
|
+
token="..."
|
|
496
|
+
workspaceId="..."
|
|
497
|
+
dashboardId="..."
|
|
498
|
+
title="Ask Alan"
|
|
499
|
+
subtitle="Continue your conversation about this report"
|
|
500
|
+
launcherAriaLabel="Open chat"
|
|
501
|
+
launcherPosition="bottom-right"
|
|
502
|
+
/>
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
#### Dialog widget (controlled, no launcher)
|
|
506
|
+
|
|
507
|
+
Hide the built-in launcher and drive the popup from your own button.
|
|
508
|
+
|
|
509
|
+
```tsx
|
|
510
|
+
function ControlledDialog() {
|
|
511
|
+
const [open, setOpen] = useState(false)
|
|
512
|
+
return (
|
|
513
|
+
<>
|
|
514
|
+
<button type="button" onClick={() => setOpen(true)}>
|
|
515
|
+
Open chat
|
|
516
|
+
</button>
|
|
517
|
+
<PaperChat
|
|
518
|
+
variant="dialog"
|
|
519
|
+
token="..."
|
|
520
|
+
workspaceId="..."
|
|
521
|
+
dashboardId="..."
|
|
522
|
+
title="Support"
|
|
523
|
+
open={open}
|
|
524
|
+
onOpenChange={setOpen}
|
|
525
|
+
showLauncher={false}
|
|
526
|
+
/>
|
|
527
|
+
</>
|
|
528
|
+
)
|
|
529
|
+
}
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
### PaperCard
|
|
533
|
+
|
|
534
|
+
These examples pass a preloaded `chart` (a `TChartResponse`) — see [Pre-loaded chart](#papercard-props) above for the shape.
|
|
535
|
+
|
|
536
|
+
#### Streaming card with a preloaded chart
|
|
537
|
+
|
|
538
|
+
`variant="streaming"` with `chart` data skips the API round-trip.
|
|
539
|
+
|
|
540
|
+
```tsx
|
|
541
|
+
<PaperCard token="..." workspaceId="..." dashboardId="..." variant="streaming" chart={chart} />
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
#### Standalone streaming card (no chart yet)
|
|
545
|
+
|
|
546
|
+
Pass a stable `id` and no `chartId` — the card shows an empty state and does not call the chart API until a link exists.
|
|
547
|
+
|
|
548
|
+
```tsx
|
|
549
|
+
<PaperCard
|
|
550
|
+
token="..."
|
|
551
|
+
workspaceId="..."
|
|
552
|
+
dashboardId="..."
|
|
553
|
+
variant="streaming"
|
|
554
|
+
id="my-card-1"
|
|
555
|
+
onSave={(chart) => console.log('Saved', chart)}
|
|
556
|
+
/>
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
#### Wide table card
|
|
560
|
+
|
|
561
|
+
```tsx
|
|
562
|
+
<PaperCard token="..." workspaceId="..." dashboardId="..." chart={tableChart} wide />
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
#### Showing the variant selector
|
|
566
|
+
|
|
567
|
+
The chart variation combobox is hidden by default (`hideVariants` is `true`).
|
|
568
|
+
|
|
569
|
+
```tsx
|
|
570
|
+
<PaperCard token="..." workspaceId="..." dashboardId="..." chart={chart} hideVariants={false} />
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
#### Scoped dashboard theme
|
|
574
|
+
|
|
575
|
+
```tsx
|
|
576
|
+
<PaperCard
|
|
577
|
+
token="..."
|
|
578
|
+
workspaceId="..."
|
|
579
|
+
dashboardId="..."
|
|
580
|
+
chart={chart}
|
|
581
|
+
dashboardTheme={{
|
|
582
|
+
primary: '#0f766e',
|
|
583
|
+
secondary: '#64748b',
|
|
584
|
+
interactive: '#14b8a6',
|
|
585
|
+
container: '#f0fdfa',
|
|
586
|
+
module: '#ffffff',
|
|
587
|
+
accent: '#ccfbf1',
|
|
588
|
+
outline: '#99f6e4',
|
|
589
|
+
dialog: '#ffffff',
|
|
590
|
+
fontFamily: 'Inter',
|
|
591
|
+
borderRadius: 0.75,
|
|
592
|
+
}}
|
|
593
|
+
/>
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
#### Streaming lifecycle hooks
|
|
597
|
+
|
|
598
|
+
Refetch host state after a chart is pinned or the embedded assistant closes.
|
|
599
|
+
|
|
600
|
+
```tsx
|
|
601
|
+
<PaperCard
|
|
602
|
+
token="..."
|
|
603
|
+
workspaceId="..."
|
|
604
|
+
dashboardId="..."
|
|
605
|
+
variant="streaming"
|
|
606
|
+
chart={chart}
|
|
607
|
+
onPinnedToDashboard={(chart) => console.log('Pinned', chart)}
|
|
608
|
+
onAssistantClosed={() => console.log('Assistant closed')}
|
|
609
|
+
/>
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
### PaperInsight
|
|
613
|
+
|
|
614
|
+
#### Minimal — fixed date range
|
|
615
|
+
|
|
616
|
+
```tsx
|
|
617
|
+
<PaperInsight
|
|
618
|
+
token="..."
|
|
619
|
+
workspaceId="..."
|
|
620
|
+
dashboardId="..."
|
|
621
|
+
startDate="2026-04-25"
|
|
622
|
+
endDate="2026-05-25"
|
|
623
|
+
/>
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
#### With period dropdown + custom-range scrubber
|
|
627
|
+
|
|
628
|
+
When `periodPreset === 'custom'` the inline `TimelineRange` scrubber renders between the header and the insight rows; dragging it fires `onRangeChange`.
|
|
629
|
+
|
|
630
|
+
```tsx
|
|
631
|
+
import {
|
|
632
|
+
PaperInsight,
|
|
633
|
+
computeInsightsPeriodRange,
|
|
634
|
+
type InsightsPeriodPreset,
|
|
635
|
+
type InsightsPeriodMenuPreset,
|
|
636
|
+
} from '@papermap/papermap'
|
|
637
|
+
|
|
638
|
+
function MyInsights() {
|
|
639
|
+
const [preset, setPreset] = useState<InsightsPeriodPreset>('last_3_months')
|
|
640
|
+
const [range, setRange] = useState(() => computeInsightsPeriodRange('last_3_months'))
|
|
641
|
+
|
|
642
|
+
return (
|
|
643
|
+
<PaperInsight
|
|
644
|
+
token="..."
|
|
645
|
+
workspaceId="..."
|
|
646
|
+
dashboardId="..."
|
|
647
|
+
startDate={range.start}
|
|
648
|
+
endDate={range.end}
|
|
649
|
+
periodPreset={preset}
|
|
650
|
+
onPeriodPresetChange={(next) => {
|
|
651
|
+
setPreset(next)
|
|
652
|
+
if (next !== 'custom') {
|
|
653
|
+
setRange(computeInsightsPeriodRange(next as InsightsPeriodMenuPreset))
|
|
654
|
+
}
|
|
655
|
+
}}
|
|
656
|
+
onRangeChange={(start, end) => {
|
|
657
|
+
setRange({ start, end })
|
|
658
|
+
setPreset('custom')
|
|
659
|
+
}}
|
|
660
|
+
/>
|
|
661
|
+
)
|
|
662
|
+
}
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
#### Wire "Explore in Chat" to the floating assistant
|
|
666
|
+
|
|
667
|
+
`PaperInsight` defaults `onExplore` to `openPapermapChatAssistant(storeApi, { editChartId: chatId })` whenever a `PapermapProvider` is mounted. Override to route somewhere else (e.g. your own router).
|
|
668
|
+
|
|
669
|
+
```tsx
|
|
670
|
+
<PaperInsight
|
|
671
|
+
token="..."
|
|
672
|
+
workspaceId="..."
|
|
673
|
+
dashboardId="..."
|
|
674
|
+
startDate="2026-04-25"
|
|
675
|
+
endDate="2026-05-25"
|
|
676
|
+
onExplore={(chatId) => router.push(`/dashboard?conversation_id=${chatId}`)}
|
|
677
|
+
/>
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
#### Custom styling via slots
|
|
681
|
+
|
|
682
|
+
```tsx
|
|
683
|
+
<PaperInsight
|
|
684
|
+
token="..."
|
|
685
|
+
workspaceId="..."
|
|
686
|
+
dashboardId="..."
|
|
687
|
+
startDate="2026-04-25"
|
|
688
|
+
endDate="2026-05-25"
|
|
689
|
+
className="rounded-2xl shadow-xl"
|
|
690
|
+
classNames={{
|
|
691
|
+
title: 'font-serif text-amber-200',
|
|
692
|
+
refreshButton: 'bg-amber-500 text-black hover:bg-amber-400',
|
|
693
|
+
row: 'hover:bg-amber-500/10',
|
|
694
|
+
}}
|
|
695
|
+
styles={{
|
|
696
|
+
body: { paddingInline: 24 },
|
|
697
|
+
}}
|
|
698
|
+
/>
|
|
699
|
+
```
|
|
700
|
+
|
|
701
|
+
#### Listen to stream events (analytics)
|
|
702
|
+
|
|
703
|
+
```tsx
|
|
704
|
+
<PaperInsight
|
|
705
|
+
token="..."
|
|
706
|
+
workspaceId="..."
|
|
707
|
+
dashboardId="..."
|
|
708
|
+
startDate="2026-04-25"
|
|
709
|
+
endDate="2026-05-25"
|
|
710
|
+
onInsight={(insight) => track('insight_received', { type: insight.insightType })}
|
|
711
|
+
onComplete={(result) => track('insights_complete', { count: result.insights.length })}
|
|
712
|
+
onError={(err) => Sentry.captureException(err)}
|
|
713
|
+
/>
|
|
714
|
+
```
|
|
715
|
+
|
|
716
|
+
---
|
|
717
|
+
|
|
718
|
+
### PaperBoard
|
|
719
|
+
|
|
720
|
+
#### Default streaming grid
|
|
721
|
+
|
|
722
|
+
```tsx
|
|
723
|
+
<PaperBoard token="..." workspaceId="..." dashboardId="..." variant="streaming" showToolbar />
|
|
724
|
+
```
|
|
238
725
|
|
|
239
|
-
|
|
726
|
+
#### With app chrome and chat assistant
|
|
727
|
+
|
|
728
|
+
`showAppChrome` adds the dashboard header + tab bar (off by default).
|
|
729
|
+
|
|
730
|
+
```tsx
|
|
731
|
+
<PaperBoard
|
|
732
|
+
token="..."
|
|
733
|
+
workspaceId="..."
|
|
734
|
+
dashboardId="..."
|
|
735
|
+
variant="streaming"
|
|
736
|
+
showAppChrome
|
|
737
|
+
showToolbar
|
|
738
|
+
showChatAssistant
|
|
739
|
+
/>
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
#### Embed mode (no app chrome)
|
|
743
|
+
|
|
744
|
+
```tsx
|
|
745
|
+
<PaperBoard
|
|
746
|
+
token="..."
|
|
747
|
+
workspaceId="..."
|
|
748
|
+
dashboardId="..."
|
|
749
|
+
variant="streaming"
|
|
750
|
+
showAppChrome={false}
|
|
751
|
+
showToolbar
|
|
752
|
+
/>
|
|
753
|
+
```
|
|
754
|
+
|
|
755
|
+
#### Static charts without fetching
|
|
756
|
+
|
|
757
|
+
`enableFetch={false}` renders the supplied `charts` + `layouts` and skips the API. `layouts` is keyed per breakpoint (`lg` / `md` / `sm` / `xs` / `xxs`).
|
|
758
|
+
|
|
759
|
+
```tsx
|
|
760
|
+
<PaperBoard
|
|
761
|
+
token="..."
|
|
762
|
+
workspaceId="..."
|
|
763
|
+
dashboardId="..."
|
|
764
|
+
variant="streaming"
|
|
765
|
+
enableFetch={false}
|
|
766
|
+
charts={charts}
|
|
767
|
+
layouts={layouts}
|
|
768
|
+
showToolbar
|
|
769
|
+
showHeader={false}
|
|
770
|
+
/>
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
#### Minimal toolbar
|
|
774
|
+
|
|
775
|
+
```tsx
|
|
776
|
+
<PaperBoard
|
|
777
|
+
token="..."
|
|
778
|
+
workspaceId="..."
|
|
779
|
+
dashboardId="..."
|
|
780
|
+
variant="streaming"
|
|
781
|
+
showToolbar
|
|
782
|
+
showScreenshot={false}
|
|
783
|
+
showGenerateDashboard={false}
|
|
784
|
+
/>
|
|
785
|
+
```
|
|
786
|
+
|
|
787
|
+
#### Dashboard tour control
|
|
788
|
+
|
|
789
|
+
Adds an opt-in tour button to the header (does not auto-start).
|
|
790
|
+
|
|
791
|
+
```tsx
|
|
792
|
+
<PaperBoard
|
|
793
|
+
token="..."
|
|
794
|
+
workspaceId="..."
|
|
795
|
+
dashboardId="..."
|
|
796
|
+
variant="streaming"
|
|
797
|
+
showToolbar
|
|
798
|
+
showDashboardTour
|
|
799
|
+
autoStartDashboardTour={false}
|
|
800
|
+
/>
|
|
801
|
+
```
|
|
802
|
+
|
|
803
|
+
#### With the Quick Insights tile
|
|
804
|
+
|
|
805
|
+
`showInsights` mounts the `PaperInsight` tile at the top of the grid and renders the dashboard-level `TimelineRange` scrubber between the header and the grid when the user picks `Custom range`. The insight tile and the scrubber share the same preset / range state internally.
|
|
806
|
+
|
|
807
|
+
```tsx
|
|
808
|
+
<PaperBoard
|
|
809
|
+
token="..."
|
|
810
|
+
workspaceId="..."
|
|
811
|
+
dashboardId="..."
|
|
812
|
+
variant="streaming"
|
|
813
|
+
showToolbar
|
|
814
|
+
showChatAssistant
|
|
815
|
+
showInsights
|
|
816
|
+
/>
|
|
817
|
+
```
|
|
818
|
+
|
|
819
|
+
Pass a controlled preset / range if the host owns the dashboard timeline:
|
|
820
|
+
|
|
821
|
+
```tsx
|
|
822
|
+
import {
|
|
823
|
+
PaperBoard,
|
|
824
|
+
computeInsightsPeriodRange,
|
|
825
|
+
type InsightsPeriodPreset,
|
|
826
|
+
type InsightsPeriodMenuPreset,
|
|
827
|
+
} from '@papermap/papermap'
|
|
828
|
+
|
|
829
|
+
function MyDashboard() {
|
|
830
|
+
const [preset, setPreset] = useState<InsightsPeriodPreset>('last_3_months')
|
|
831
|
+
const [range, setRange] = useState(() => computeInsightsPeriodRange('last_3_months'))
|
|
832
|
+
|
|
833
|
+
return (
|
|
834
|
+
<PaperBoard
|
|
835
|
+
token="..."
|
|
836
|
+
workspaceId="..."
|
|
837
|
+
dashboardId="..."
|
|
838
|
+
showInsights
|
|
839
|
+
insightsPeriodPreset={preset}
|
|
840
|
+
onInsightsPeriodPresetChange={(next) => {
|
|
841
|
+
setPreset(next)
|
|
842
|
+
if (next !== 'custom') {
|
|
843
|
+
setRange(computeInsightsPeriodRange(next as InsightsPeriodMenuPreset))
|
|
844
|
+
}
|
|
845
|
+
}}
|
|
846
|
+
insightsStartDate={range.start}
|
|
847
|
+
insightsEndDate={range.end}
|
|
848
|
+
onInsightExplore={(chatId) => router.push(`?conversation_id=${chatId}`)}
|
|
849
|
+
/>
|
|
850
|
+
)
|
|
851
|
+
}
|
|
852
|
+
```
|
|
853
|
+
|
|
854
|
+
---
|
|
855
|
+
|
|
856
|
+
<!-- ## Advanced exports
|
|
857
|
+
|
|
858
|
+
For custom layouts, the package also exports:
|
|
859
|
+
|
|
860
|
+
- **Composition:** `ChatAssistant`, `PaperChatSidePanel` / `ChatSidePanel` (alias), `PaperChatFloatingChartOverlay` / `FloatingChartOverlay` (alias), `StreamingChartDialog`, `StreamingChatPanel`, `StreamingTimeline`, `ChartView`, `DataTable`, dashboard panels (`RecentConversations`, `ChartHistory`, `ToolCallDisplay`, …).
|
|
861
|
+
- **Store:** `usePapermapStore`, `usePapermapStoreApi`, `createPapermapStore`, `PapermapProvider`, `CHAT_MODAL_TAB`, and **`openPapermapChatAssistant(storeApi, options?)`** — opens/focuses the assistant and optionally primes edit mode from a chart (`editChartId`, `chart`, `suppressOpen` for embedded streaming UIs).
|
|
862
|
+
- **Hooks:** `useAnalyticsStream`, `useKeyboardShortcuts`, `useAutoResize`, `useAutoFade`, `useTour`, …
|
|
863
|
+
- **API:** `createApiClient`, `decodeToken`, `buildAuthHeaders`, `papermapQueryKeys`
|
|
864
|
+
- **Insights:**
|
|
865
|
+
- **Component:** `PaperInsight`, slot types (`PaperInsightSlot`, `PaperInsightClassNames`, `PaperInsightStyles`)
|
|
866
|
+
- **Service:** `streamDashboardInsights`, `normalizeInsightType`, `orderInsightsForDisplay`, `formatInsightDate`, `parseInsightMetricToTrend` — and types `DashboardInsight`, `DashboardInsightType`, `DashboardInsightsResult`, `StreamDashboardInsightsOptions`.
|
|
867
|
+
- **Period helpers:** `computeInsightsPeriodRange`, `formatInsightsDateLabel`, `INSIGHTS_PERIOD_LABELS`, `INSIGHTS_PERIOD_SELECT_OPTIONS`, types `InsightsPeriodPreset`, `InsightsPeriodMenuPreset`.
|
|
868
|
+
- **Grid layout:** `PAPER_INSIGHT_GRID_ID`, `ensurePaperInsightLayoutItem(layouts, gridId, defaults?)` — inject the insight slot into custom `PaperBoardLayouts`.
|
|
869
|
+
- **Types:** streaming (`TimelineEvent`, `ToolCall`, …), charts (`TChartResponse`, …), messages
|
|
870
|
+
- **Theming:** `themePresets`, `defaultTheme`, `ThemeCustomizationSettings`
|
|
871
|
+
- **Persistence:** chart card `id` ↔ backend chat id helpers (`getChartCardIdLink`, `resolveChartFetchChatId`, …)
|
|
872
|
+
- **Platform (hosts / extensions):** `PAPERMAP_SDK_VERSION`, `PAPERMAP_PLUGIN_CONTRACT_VERSION`, `checkPluginPeerCompatibility`, `createPluginContext`, `createPapermapObservability`, and related types -->
|
|
873
|
+
|
|
874
|
+
All paths use the package entry: `@papermap/papermap`.
|
|
875
|
+
|
|
876
|
+
---
|
|
877
|
+
|
|
878
|
+
<!--
|
|
879
|
+
## Local development
|
|
880
|
+
|
|
881
|
+
```bash
|
|
882
|
+
npm install
|
|
883
|
+
npm run storybook # UI at http://localhost:3001
|
|
884
|
+
npm run validate # lint, TypeScript, tests
|
|
885
|
+
npm run test:unit # Vitest project `unit` (default `*.test`, excludes `*.integration.test`)
|
|
886
|
+
npm run test:integration # Vitest project `integration` (`*.integration.test`, longer timeout)
|
|
887
|
+
npm run build # dist + bundled styles.css
|
|
888
|
+
```
|
|
240
889
|
|
|
241
890
|
---
|
|
242
891
|
|
|
@@ -293,12 +942,13 @@ The library decodes it and sends these headers on API requests:
|
|
|
293
942
|
|
|
294
943
|
### What users see in `PaperChat`
|
|
295
944
|
|
|
296
|
-
- Floating input bar (portaled to `document.body`), Cmd/Ctrl+
|
|
297
|
-
-
|
|
298
|
-
-
|
|
945
|
+
- **Default:** Floating input bar (portaled to `document.body`), Cmd/Ctrl+shortcut to focus, Escape to dismiss; expanding input, backdrop, conversation panel, stop during load, new chat, scroll and history pagination; recent conversations, per-chat chart history, feedback, optional execution view, model selector, and toolbar actions.
|
|
946
|
+
- **Side:** Fixed-width panel with the same assistant and composer, suited to sheet-style pages; chart can open in an overlay with optional history mode.
|
|
947
|
+
- **Floating:** Chart-first overlay with maximize, screenshot, and conversation alongside the preview.
|
|
948
|
+
- **Dialog:** Floating circular launcher button + popup anchored to its corner (`bottom-right` by default). The popup keeps a fixed header (title/subtitle + close `X`) and composer with the message list scrolling internally — the dialog itself never overflows the viewport. Outside-click and `Escape` are intentionally ignored: the dialog only closes via the explicit close button or by clicking the launcher again. Layered above page content (`z-[102]`) with the launcher remaining clickable underneath (overlay is `pointer-events-none`). Responsive sizing (`min(100vw - 1.5rem, 472px)` wide, `min(80vh, 640px)` tall) keeps it usable on mobile and desktop.
|
|
299
949
|
|
|
300
950
|
---
|
|
301
951
|
|
|
302
952
|
## Roadmap
|
|
303
953
|
|
|
304
|
-
Additional embeddable surfaces (for example richer explorers) will follow the same pattern: self-contained components with `token` / `workspaceId` / `dashboardId` and optional callbacks, exported from `@papermap/papermap`.
|
|
954
|
+
Additional embeddable surfaces (for example richer explorers) will follow the same pattern: self-contained components with `token` / `workspaceId` / `dashboardId` and optional callbacks, exported from `@papermap/papermap`. -->
|