@papermap/papermap 1.0.1

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/package.json ADDED
@@ -0,0 +1,122 @@
1
+ {
2
+ "name": "@papermap/papermap",
3
+ "version": "1.0.1",
4
+ "description": "Embeddable AI chat bar and UI components from the Papermap data analytics platform",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ },
14
+ "./styles.css": "./styles.css"
15
+ },
16
+ "style": "./styles.css",
17
+ "sideEffects": [
18
+ "*.css"
19
+ ],
20
+ "files": [
21
+ "dist",
22
+ "styles.css"
23
+ ],
24
+ "scripts": {
25
+ "build": "tsup && npm run build:styles",
26
+ "build:styles": "cp src/styles.css styles.css",
27
+ "dev": "tsup --watch",
28
+ "storybook": "storybook dev -p 3001",
29
+ "build-storybook": "storybook build",
30
+ "lint": "npx eslint src --ext .ts,.tsx",
31
+ "ts-check": "tsc --noEmit",
32
+ "format": "prettier --write \"src/**/*.{ts,tsx}\"",
33
+ "format:check": "prettier --check \"src/**/*.{ts,tsx}\"",
34
+ "validate": "npm run lint && npm run ts-check",
35
+ "validate:watch": "npm run validate -- --watch",
36
+ "clean": "rm -rf dist styles.css",
37
+ "prepare": "husky || true",
38
+ "changeset": "changeset",
39
+ "version-packages": "changeset version",
40
+ "release": "npm run build && changeset publish"
41
+ },
42
+ "lint-staged": {
43
+ "*.{ts,tsx}": [
44
+ "prettier --write",
45
+ "eslint --fix"
46
+ ]
47
+ },
48
+ "peerDependencies": {
49
+ "react": ">=18",
50
+ "react-dom": ">=18"
51
+ },
52
+ "dependencies": {
53
+ "@radix-ui/react-collapsible": "^1.1.12",
54
+ "@radix-ui/react-dialog": "^1.1.15",
55
+ "@radix-ui/react-dropdown-menu": "^2.1.16",
56
+ "@radix-ui/react-hover-card": "^1.1.15",
57
+ "@radix-ui/react-icons": "^1.3.2",
58
+ "@radix-ui/react-select": "^2.2.6",
59
+ "@radix-ui/react-separator": "^1.1.8",
60
+ "@radix-ui/react-slider": "^1.3.6",
61
+ "@radix-ui/react-slot": "^1.2.4",
62
+ "@radix-ui/react-switch": "^1.2.6",
63
+ "@radix-ui/react-toast": "^1.2.15",
64
+ "@radix-ui/react-tooltip": "^1.2.8",
65
+ "@tanstack/react-query": "^5.22.2",
66
+ "@tanstack/react-table": "^8.21.3",
67
+ "axios": "1.7.9",
68
+ "class-variance-authority": "^0.7.1",
69
+ "clsx": "^2.1.1",
70
+ "framer-motion": "^11.17.0",
71
+ "html2canvas": "^1.4.1",
72
+ "lottie-react": "^2.4.1",
73
+ "lucide-react": "^0.460.0",
74
+ "react-grid-layout": "^1.4.4",
75
+ "react-markdown": "^10.1.0",
76
+ "react-resizable": "^3.0.5",
77
+ "react-syntax-highlighter": "^16.1.1",
78
+ "recharts": "^2.15.0",
79
+ "rehype-raw": "^7.0.0",
80
+ "remark-gfm": "^4.0.1",
81
+ "tailwind-merge": "^2.6.0",
82
+ "zustand": "^5.0.11"
83
+ },
84
+ "devDependencies": {
85
+ "@changesets/cli": "^2.29.7",
86
+ "@storybook/addon-essentials": "^8.6.14",
87
+ "@storybook/blocks": "^8.6.14",
88
+ "@storybook/react": "^8.6.18",
89
+ "@storybook/react-vite": "^8.6.18",
90
+ "@types/react": "^18.3.18",
91
+ "@types/react-dom": "^18.3.5",
92
+ "@types/react-syntax-highlighter": "^15.5.13",
93
+ "@typescript-eslint/eslint-plugin": "^8.19.1",
94
+ "@typescript-eslint/parser": "^8.19.1",
95
+ "@vitejs/plugin-react": "^4.7.0",
96
+ "autoprefixer": "^10.4.27",
97
+ "eslint": "^8.57.0",
98
+ "eslint-config-prettier": "^9.1.0",
99
+ "eslint-plugin-prettier": "^5.2.1",
100
+ "eslint-plugin-react": "^7.37.4",
101
+ "eslint-plugin-react-hooks": "^5.1.0",
102
+ "husky": "^9.1.7",
103
+ "lint-staged": "^15.3.0",
104
+ "playwright": "^1.58.2",
105
+ "postcss": "^8.5.8",
106
+ "prettier": "^3.4.2",
107
+ "react": "^18.3.1",
108
+ "react-dom": "^18.3.1",
109
+ "storybook": "^8.6.18",
110
+ "tailwindcss": "^3.4.19",
111
+ "tailwindcss-animate": "^1.0.7",
112
+ "tsup": "^8.4.0",
113
+ "typescript": "^5.7.3",
114
+ "vite": "^6.4.1"
115
+ },
116
+ "engines": {
117
+ "node": ">=18"
118
+ },
119
+ "publishConfig": {
120
+ "access": "public"
121
+ }
122
+ }
package/readme.md ADDED
@@ -0,0 +1,296 @@
1
+ # Papermap
2
+
3
+ Embeddable AI-powered components from the [Papermap](https://www.papermap.ai) data analytics platform. This is a standalone package -- **completely independent** from the main Papermap app. Services, SSE streaming, and UI are all self-contained; the two repos do not link or depend on each other.
4
+
5
+ Two main components are available:
6
+
7
+ - **`PapermapChat`** -- Full AI chat assistant with streaming, conversation history, and chart generation.
8
+ - **`PapermapChartCard`** -- Standalone chart card for embedding individual charts with toolbar actions.
9
+ - **`PapermapChartCard` (streaming variant)** -- Chart card variant that expands into an embedded chart + conversation dialog (without opening the floating assistant UI).
10
+
11
+ ```tsx
12
+ import { PapermapChat, PapermapChartCard } from 'papermap'
13
+
14
+ // AI chat assistant
15
+ <PapermapChat
16
+ token="your-base64-api-token"
17
+ workspaceId="your-workspace-id"
18
+ dashboardId="your-dashboard-id"
19
+ />
20
+
21
+ // Standalone chart card
22
+ <PapermapChartCard
23
+ token="your-base64-api-token"
24
+ workspaceId="your-workspace-id"
25
+ dashboardId="your-dashboard-id"
26
+ chartId="your-chart-id"
27
+ />
28
+
29
+ // Streaming chart card (embedded dialog + chat)
30
+ <PapermapChartCard
31
+ token="your-base64-api-token"
32
+ workspaceId="your-workspace-id"
33
+ dashboardId="your-dashboard-id"
34
+ variant="streaming"
35
+ chartId="your-chart-id" // optional; card stays visible with empty state if omitted
36
+ />
37
+ ```
38
+
39
+ Both components handle everything internally -- API calls, authentication, state, and the full UI.
40
+
41
+ ---
42
+
43
+ ## Quick Start
44
+
45
+ ### 1. Install
46
+
47
+ ```bash
48
+ npm install papermap
49
+ # or
50
+ pnpm add papermap
51
+ ```
52
+
53
+ ### 2. Import styles once
54
+
55
+ ```tsx
56
+ import 'papermap/styles.css'
57
+ ```
58
+
59
+ ### 3. Pick one integration mode
60
+
61
+ #### A) Recommended: one-time provider at app root
62
+
63
+ ```tsx
64
+ import { PapermapConfigProvider, PapermapChat, PapermapChartCard } from 'papermap'
65
+
66
+ function App() {
67
+ return (
68
+ <PapermapConfigProvider
69
+ token="your-base64-api-token"
70
+ workspaceId="your-workspace-id"
71
+ dashboardId="your-dashboard-id"
72
+ // optional: apiUrl="https://dataapi.papermap.ai"
73
+ >
74
+ <PapermapChat />
75
+ <PapermapChartCard chartId="existing-chat-id" />
76
+ </PapermapConfigProvider>
77
+ )
78
+ }
79
+ ```
80
+
81
+ #### B) Drop-in standalone components (no provider required)
82
+
83
+ ```tsx
84
+ import { PapermapChat, PapermapChartCard } from 'papermap'
85
+
86
+ function App() {
87
+ return (
88
+ <>
89
+ <PapermapChat
90
+ token="your-base64-api-token"
91
+ workspaceId="your-workspace-id"
92
+ dashboardId="your-dashboard-id"
93
+ />
94
+
95
+ <PapermapChartCard
96
+ token="your-base64-api-token"
97
+ workspaceId="your-workspace-id"
98
+ dashboardId="your-dashboard-id"
99
+ chartId="existing-chat-id"
100
+ />
101
+ </>
102
+ )
103
+ }
104
+ ```
105
+
106
+ ### 4. Props
107
+
108
+ #### `PapermapChat` Props
109
+
110
+ | Prop | Type | Required | Default | Description |
111
+ | ------------- | ---------------------- | -------- | --------------------------------- | ------------------------------------ |
112
+ | `token` | `string` | Yes | -- | Base64-encoded API key token |
113
+ | `workspaceId` | `string` | Yes | -- | Workspace ID |
114
+ | `dashboardId` | `string` | Yes | -- | Dashboard ID |
115
+ | `apiUrl` | `string` | No | `https://dataapi.papermap.ai` | API base URL |
116
+ | `theme` | `'light' \| 'dark'` | No | -- | Force light or dark theme |
117
+ | `placeholder` | `string` | No | `"Ask anything..."` | Input placeholder text |
118
+ | `shortcutKey` | `string` | No | `"k"` | Keyboard shortcut (Cmd/Ctrl + key) |
119
+ | `autoFade` | `boolean` | No | `false` | Fade toolbar after inactivity |
120
+ | `fadeDelay` | `number` | No | `5000` | Milliseconds before auto-fade |
121
+ | `className` | `string` | No | -- | Extra CSS class on toolbar container |
122
+
123
+ #### `PapermapChartCard` Props
124
+
125
+ | Prop | Type | Required | Default | Description |
126
+ | -------------- | ---------------------------------------- | -------- | --------------------------------- | --------------------------------------------------------------------- |
127
+ | `token` | `string` | Yes | -- | Base64-encoded API key token |
128
+ | `workspaceId` | `string` | Yes | -- | Workspace ID |
129
+ | `dashboardId` | `string` | Yes | -- | Dashboard ID |
130
+ | `apiUrl` | `string` | No | `https://dataapi.papermap.ai` | API base URL |
131
+ | `chartId` | `string` | No | -- | Chat ID to fetch the latest chart for |
132
+ | `chart` | `TChartResponse` | No | -- | Pre-loaded chart data (skips API fetch) |
133
+ | `theme` | `'light' \| 'dark'` | No | -- | Force light or dark theme |
134
+ | `onEditClick` | `(chartId: string) => void` | No | -- | Called when the edit button is clicked |
135
+ | `onDelete` | `(chartId: string) => void` | No | -- | Called when chart deletion is confirmed |
136
+ | `onPinChange` | `(chartId: string, pinned: boolean) => void` | No | -- | Called when pin state changes |
137
+ | `wide` | `boolean` | No | `false` | Wide mode for table charts |
138
+ | `hideVariants` | `boolean` | No | `true` | Hide the chart variation selector |
139
+ | `showToolbar` | `boolean` | No | `true` | Show toolbar with maximize/edit/delete buttons |
140
+ | `className` | `string` | No | -- | Extra CSS class on the card container |
141
+ | `variant` | `'default' \| 'streaming'` | No | `'default'` | When `'streaming'`, edit opens an embedded dialog + chat experience |
142
+
143
+ **Usage with pre-loaded data:**
144
+
145
+ ```tsx
146
+ import { PapermapChartCard } from 'papermap'
147
+ import type { TChartResponse } from 'papermap'
148
+
149
+ const chart: TChartResponse = {
150
+ llm_data_chat_id: 'my-chart-id',
151
+ name: 'Revenue',
152
+ meta: { title: 'Revenue by Quarter', variant: 'default' },
153
+ pin: false,
154
+ chart_response: {
155
+ chart_type: 'bar',
156
+ data: [
157
+ { quarter: 'Q1', revenue: 42000 },
158
+ { quarter: 'Q2', revenue: 55000 },
159
+ ],
160
+ response_type: 'chart',
161
+ schema_hints: { x_key: 'quarter', y_key: 'revenue', label_key: 'quarter' },
162
+ visualization_config: { colors: ['#3b82f6'], width: 600, height: 400 },
163
+ },
164
+ }
165
+
166
+ <PapermapChartCard
167
+ token="..."
168
+ workspaceId="..."
169
+ dashboardId="..."
170
+ chart={chart}
171
+ onEditClick={(id) => console.log('Edit', id)}
172
+ onDelete={(id) => console.log('Delete', id)}
173
+ />
174
+ ```
175
+
176
+ **Usage with API fetch:**
177
+
178
+ ```tsx
179
+ <PapermapChartCard
180
+ token="..."
181
+ workspaceId="..."
182
+ dashboardId="..."
183
+ chartId="existing-chat-id"
184
+ />
185
+ ```
186
+
187
+ ### Streaming chart card variant
188
+
189
+ When `variant="streaming"`, clicking the edit icon opens an **embedded** expanded view (`StreamingChartDialog`) with:
190
+ - Chart + conversation side-by-side (chat always available)
191
+ - SSE streaming support
192
+ - Save-to-dashboard + maximize chart actions
193
+ - Audit Log (execution replay) on assistant messages
194
+
195
+ ```tsx
196
+ import { PapermapChartCard } from 'papermap'
197
+
198
+ export function EmbeddedStreamingChart() {
199
+ return (
200
+ <div className="h-[420px] w-[520px]">
201
+ <PapermapChartCard
202
+ token="..."
203
+ workspaceId="..."
204
+ dashboardId="..."
205
+ variant="streaming"
206
+ chartId="existing-chat-id" // optional; without it the card shows a create-chart empty state
207
+ />
208
+ </div>
209
+ )
210
+ }
211
+ ```
212
+
213
+ **Supported chart types:** `bar`, `line`, `area`, `pie`, `radar`, `scatter`, `table`, `tile` -- matching the main Papermap app.
214
+
215
+ ---
216
+
217
+ ## Troubleshooting
218
+
219
+ ### "Cannot find module 'papermap/styles.css'"
220
+ - Make sure you are on a version that exports `./styles.css`.
221
+ - Reinstall dependencies after upgrading the package.
222
+
223
+ ### "Papermap... requires token/workspaceId/dashboardId"
224
+ - Provide connection values once via `PapermapConfigProvider`, or
225
+ - Pass them directly to each standalone component.
226
+
227
+ ### Requests are hitting the wrong backend
228
+ - Set `apiUrl` explicitly in `PapermapConfigProvider` (or per component).
229
+ - Default base URL is `https://dataapi.papermap.ai`.
230
+
231
+ ### Charts do not appear in `PapermapChartCard`
232
+ - Pass a valid backend `chartId` (chat id), or
233
+ - Provide preloaded `chart` data directly.
234
+
235
+ ## How It Works
236
+
237
+ ### Authentication
238
+
239
+ The `token` prop is a base64-encoded JSON:
240
+
241
+ ```json
242
+ {
243
+ "api_key_id": "ak_...",
244
+ "workspace_id": "...",
245
+ "valid_until": 1773246356,
246
+ "signature": "88b34e..."
247
+ }
248
+ ```
249
+
250
+ The component decodes it and sets these headers on every API request:
251
+
252
+ - `X-API-Key-ID`
253
+ - `X-Workspace-ID`
254
+ - `X-Valid-Until`
255
+ - `X-Signature`
256
+ - `X-Tenant-ID: da-app`
257
+
258
+ ### Data Flow
259
+
260
+ 1. User types a message and hits Enter
261
+ 2. Component creates a chat via `POST /api/v1/analytics/chats` (if first message)
262
+ 3. Message is sent via `POST /api/v1/analytics/charts/stream` with SSE enabled
263
+ 4. SSE connection opens to `POST /api/v1/analytics/requests/stream` for real-time updates (agent thoughts, tool calls, phases)
264
+ 5. HTTP response serves as fallback if SSE fails
265
+ 6. Conversation history is loaded via `GET /api/v1/analytics/chats/{id}/conversations`
266
+ 7. Recent chat history for a dashboard is loaded via `GET /api/v1/analytics/chats-history`
267
+ 8. Full chart history for a chat is loaded via `GET /api/v1/analytics/chats/{id}/get-all-charts`
268
+
269
+ ### What the User Sees
270
+
271
+ - A floating input bar fixed at the bottom-center (portaled to `document.body`)
272
+ - Cmd/Ctrl+K to focus, Escape to dismiss
273
+ - Single-line input that expands to multiline when text overflows
274
+ - Backdrop overlay + conversation panel when focused
275
+ - Submit button (arrow-up) becomes stop button (square) during loading
276
+ - New Chat button to start fresh conversations
277
+ - Auto-scroll, pagination on scroll-up for history
278
+ - Recent conversations panel with infinite scroll + delete actions
279
+ - Per-chat chart history panel, including pinning and meta updates
280
+ - Inline feedback (thumbs up / down) on responses, with optional branching to new chats
281
+ - Optional streaming view that shows real-time phases, thoughts, and tool calls (toggled via "View Execution")
282
+ - Model selector and search-the-web actions integrated into the toolbar
283
+
284
+ ---
285
+
286
+ ---
287
+
288
+ ## Future Growth
289
+
290
+ This package is designed to grow. Current exports:
291
+
292
+ ```tsx
293
+ import { PapermapChat, PapermapChartCard } from 'papermap'
294
+ ```
295
+
296
+ Future embeddings (e.g. `Dashboard`, `DataExplorer`) will follow the same pattern -- self-contained components with `token`/`workspaceId`/`dashboardId` props and optional callbacks.
package/styles.css ADDED
@@ -0,0 +1,114 @@
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+
5
+ @layer base {
6
+ :root {
7
+ --background: 0 0% 100%;
8
+ --foreground: 240 10% 3.9%;
9
+ --card: 0 0% 100%;
10
+ --card-foreground: 240 10% 3.9%;
11
+ --popover: 0 0% 100%;
12
+ --popover-foreground: 240 10% 3.9%;
13
+ --primary: 240 5.9% 10%;
14
+ --primary-foreground: 0 0% 98%;
15
+ --secondary: 240 4.8% 95.9%;
16
+ --secondary-foreground: 240 5.9% 10%;
17
+ --muted: 240 4.8% 95.9%;
18
+ --muted-foreground: 240 3.8% 46.1%;
19
+ --accent: 240 4.8% 95.9%;
20
+ --accent-foreground: 240 5.9% 10%;
21
+ --destructive: 0 84.2% 60.2%;
22
+ --destructive-foreground: 0 0% 98%;
23
+ --border: 240 5.9% 90%;
24
+ --input: 240 5.9% 90%;
25
+ --ring: 240 5.9% 10%;
26
+ --radius: 0.5rem;
27
+ }
28
+
29
+ .dark {
30
+ --background: 240 10% 3.9%;
31
+ --foreground: 0 0% 98%;
32
+ --card: 240 10% 3.9%;
33
+ --card-foreground: 0 0% 98%;
34
+ --popover: 240 10% 3.9%;
35
+ --popover-foreground: 0 0% 98%;
36
+ --primary: 0 0% 98%;
37
+ --primary-foreground: 240 5.9% 10%;
38
+ --secondary: 240 3.7% 15.9%;
39
+ --secondary-foreground: 0 0% 98%;
40
+ --muted: 240 3.7% 15.9%;
41
+ --muted-foreground: 240 5% 64.9%;
42
+ --accent: 240 3.7% 15.9%;
43
+ --accent-foreground: 0 0% 98%;
44
+ --destructive: 0 62.8% 30.6%;
45
+ --destructive-foreground: 0 0% 98%;
46
+ --border: 240 3.7% 15.9%;
47
+ --input: 240 3.7% 15.9%;
48
+ --ring: 240 4.9% 83.9%;
49
+ }
50
+ }
51
+
52
+ @layer base {
53
+ * {
54
+ @apply border-border;
55
+ }
56
+ body {
57
+ @apply bg-background text-foreground;
58
+ }
59
+ }
60
+
61
+ @layer utilities {
62
+ .scrollbar-hide {
63
+ -ms-overflow-style: none;
64
+ scrollbar-width: none;
65
+ }
66
+ .scrollbar-hide::-webkit-scrollbar {
67
+ display: none !important;
68
+ }
69
+
70
+ .scrollbar-hover-only {
71
+ scrollbar-width: thin;
72
+ scrollbar-color: transparent transparent;
73
+ transition: scrollbar-color 0.2s ease;
74
+ }
75
+
76
+ .scrollbar-hover-only::-webkit-scrollbar {
77
+ width: 6px;
78
+ height: 6px;
79
+ }
80
+
81
+ .scrollbar-hover-only::-webkit-scrollbar-track {
82
+ background: transparent;
83
+ }
84
+
85
+ .scrollbar-hover-only::-webkit-scrollbar-thumb {
86
+ background-color: transparent;
87
+ border-radius: 4px;
88
+ transition: background-color 0.2s ease;
89
+ }
90
+
91
+ .scrollbar-hover-only:hover::-webkit-scrollbar-thumb {
92
+ background-color: rgba(150, 150, 150, 0.4);
93
+ }
94
+
95
+ .scrollbar-hover-only:hover::-webkit-scrollbar-thumb:hover {
96
+ background-color: rgba(150, 150, 150, 0.7);
97
+ }
98
+
99
+ .scrollbar-hover-only:hover {
100
+ scrollbar-color: rgba(150, 150, 150, 0.4) transparent;
101
+ }
102
+
103
+ .dark .scrollbar-hover-only:hover::-webkit-scrollbar-thumb {
104
+ background-color: rgba(200, 200, 200, 0.4);
105
+ }
106
+
107
+ .dark .scrollbar-hover-only:hover::-webkit-scrollbar-thumb:hover {
108
+ background-color: rgba(200, 200, 200, 0.7);
109
+ }
110
+
111
+ .dark .scrollbar-hover-only:hover {
112
+ scrollbar-color: rgba(200, 200, 200, 0.4) transparent;
113
+ }
114
+ }