@luckydraw/blex 0.1.0

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 (97) hide show
  1. package/README.md +298 -0
  2. package/dist/blex-chart.min.global.js +23 -0
  3. package/dist/blex-chart.min.global.js.map +1 -0
  4. package/dist/blex.min.global.js +105 -0
  5. package/dist/blex.min.global.js.map +1 -0
  6. package/dist/esm/base-renderer.d.ts +28 -0
  7. package/dist/esm/base-renderer.d.ts.map +1 -0
  8. package/dist/esm/calendar-HUZDQQN2.js +314 -0
  9. package/dist/esm/calendar-HUZDQQN2.js.map +1 -0
  10. package/dist/esm/chart/index.d.ts +14 -0
  11. package/dist/esm/chart/index.d.ts.map +1 -0
  12. package/dist/esm/chart/index.js +3 -0
  13. package/dist/esm/chart/index.js.map +1 -0
  14. package/dist/esm/chart-2QT47W6E.js +38 -0
  15. package/dist/esm/chart-2QT47W6E.js.map +1 -0
  16. package/dist/esm/chunk-ODHF7VXV.js +74 -0
  17. package/dist/esm/chunk-ODHF7VXV.js.map +1 -0
  18. package/dist/esm/chunk-PBRPD4A5.js +91 -0
  19. package/dist/esm/chunk-PBRPD4A5.js.map +1 -0
  20. package/dist/esm/chunk-ZKSJGHJI.js +1573 -0
  21. package/dist/esm/chunk-ZKSJGHJI.js.map +1 -0
  22. package/dist/esm/defaults.d.ts +6 -0
  23. package/dist/esm/defaults.d.ts.map +1 -0
  24. package/dist/esm/fallback.d.ts +11 -0
  25. package/dist/esm/fallback.d.ts.map +1 -0
  26. package/dist/esm/gallery-ISM7FZA3.js +130 -0
  27. package/dist/esm/gallery-ISM7FZA3.js.map +1 -0
  28. package/dist/esm/index.d.ts +8 -0
  29. package/dist/esm/index.d.ts.map +1 -0
  30. package/dist/esm/index.js +91 -0
  31. package/dist/esm/index.js.map +1 -0
  32. package/dist/esm/kanban-XUXVTRX2.js +126 -0
  33. package/dist/esm/kanban-XUXVTRX2.js.map +1 -0
  34. package/dist/esm/mermaid-W3HX2CK2.js +130 -0
  35. package/dist/esm/mermaid-W3HX2CK2.js.map +1 -0
  36. package/dist/esm/placeholder.d.ts +8 -0
  37. package/dist/esm/placeholder.d.ts.map +1 -0
  38. package/dist/esm/plugin.d.ts +34 -0
  39. package/dist/esm/plugin.d.ts.map +1 -0
  40. package/dist/esm/react/chart.d.ts +12 -0
  41. package/dist/esm/react/chart.d.ts.map +1 -0
  42. package/dist/esm/react/chart.js +50 -0
  43. package/dist/esm/react/chart.js.map +1 -0
  44. package/dist/esm/react/index.d.ts +13 -0
  45. package/dist/esm/react/index.d.ts.map +1 -0
  46. package/dist/esm/react/index.js +66 -0
  47. package/dist/esm/react/index.js.map +1 -0
  48. package/dist/esm/registry.d.ts +46 -0
  49. package/dist/esm/registry.d.ts.map +1 -0
  50. package/dist/esm/render.d.ts +13 -0
  51. package/dist/esm/render.d.ts.map +1 -0
  52. package/dist/esm/renderers/calendar.d.ts +34 -0
  53. package/dist/esm/renderers/calendar.d.ts.map +1 -0
  54. package/dist/esm/renderers/chart.d.ts +12 -0
  55. package/dist/esm/renderers/chart.d.ts.map +1 -0
  56. package/dist/esm/renderers/code.d.ts +19 -0
  57. package/dist/esm/renderers/code.d.ts.map +1 -0
  58. package/dist/esm/renderers/confirm.d.ts +13 -0
  59. package/dist/esm/renderers/confirm.d.ts.map +1 -0
  60. package/dist/esm/renderers/diff.d.ts +24 -0
  61. package/dist/esm/renderers/diff.d.ts.map +1 -0
  62. package/dist/esm/renderers/file-tree.d.ts +24 -0
  63. package/dist/esm/renderers/file-tree.d.ts.map +1 -0
  64. package/dist/esm/renderers/form.d.ts +27 -0
  65. package/dist/esm/renderers/form.d.ts.map +1 -0
  66. package/dist/esm/renderers/gallery.d.ts +24 -0
  67. package/dist/esm/renderers/gallery.d.ts.map +1 -0
  68. package/dist/esm/renderers/image.d.ts +17 -0
  69. package/dist/esm/renderers/image.d.ts.map +1 -0
  70. package/dist/esm/renderers/kanban.d.ts +29 -0
  71. package/dist/esm/renderers/kanban.d.ts.map +1 -0
  72. package/dist/esm/renderers/layout.d.ts +17 -0
  73. package/dist/esm/renderers/layout.d.ts.map +1 -0
  74. package/dist/esm/renderers/mermaid.d.ts +19 -0
  75. package/dist/esm/renderers/mermaid.d.ts.map +1 -0
  76. package/dist/esm/renderers/metric.d.ts +16 -0
  77. package/dist/esm/renderers/metric.d.ts.map +1 -0
  78. package/dist/esm/renderers/poll.d.ts +17 -0
  79. package/dist/esm/renderers/poll.d.ts.map +1 -0
  80. package/dist/esm/renderers/progress.d.ts +18 -0
  81. package/dist/esm/renderers/progress.d.ts.map +1 -0
  82. package/dist/esm/renderers/status.d.ts +17 -0
  83. package/dist/esm/renderers/status.d.ts.map +1 -0
  84. package/dist/esm/renderers/svg.d.ts +13 -0
  85. package/dist/esm/renderers/svg.d.ts.map +1 -0
  86. package/dist/esm/renderers/table.d.ts +20 -0
  87. package/dist/esm/renderers/table.d.ts.map +1 -0
  88. package/dist/esm/renderers/terminal.d.ts +16 -0
  89. package/dist/esm/renderers/terminal.d.ts.map +1 -0
  90. package/dist/esm/renderers/timeline.d.ts +20 -0
  91. package/dist/esm/renderers/timeline.d.ts.map +1 -0
  92. package/dist/esm/theme.d.ts +10 -0
  93. package/dist/esm/theme.d.ts.map +1 -0
  94. package/dist/esm/types-C42V92x6.d.ts +75 -0
  95. package/dist/esm/types.d.ts +81 -0
  96. package/dist/esm/types.d.ts.map +1 -0
  97. package/package.json +82 -0
package/README.md ADDED
@@ -0,0 +1,298 @@
1
+ # @luckydraw/blex
2
+
3
+ A TypeScript library for rendering interactive content blocks from JSON. Framework-agnostic, streaming-aware, with lazy-loaded heavy renderers.
4
+
5
+ Used by **cumulus** (AI chat) to display rich content inline in conversations, and by **plastic** (app platform) for charts, dashboards, and application view components.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install @luckydraw/blex
11
+ ```
12
+
13
+ ## Distribution
14
+
15
+ Two build outputs from the same source:
16
+
17
+ - **ESM** — `import { renderBlock } from '@luckydraw/blex'` — for bundled consumers (plastic, janus)
18
+ - **IIFE** — `<script src="blex.min.js">` → `window.Blex` — for cumulus widget (no build step)
19
+ - **Chart standalone** — `blex-chart.min.js` / `import { renderChart } from '@luckydraw/blex/chart'`
20
+
21
+ Target: ~11KB core (gzipped), heavy renderers lazy-loaded on first use.
22
+
23
+ ## Quick Start
24
+
25
+ ```typescript
26
+ import { renderBlock, registerBlockType } from '@luckydraw/blex';
27
+
28
+ // Render a block into a container
29
+ const handle = await renderBlock(
30
+ { type: 'confirm', id: 'c1', data: { message: 'Deploy to production?' } },
31
+ document.getElementById('container')
32
+ );
33
+
34
+ // Listen for interactions
35
+ handle.onInteraction((interaction) => {
36
+ console.log(interaction.type); // 'click'
37
+ console.log(interaction.serialized); // 'User confirmed: Deploy to production?'
38
+ });
39
+
40
+ // Clean up
41
+ handle.destroy();
42
+ ```
43
+
44
+ ### Chart Subpath Export
45
+
46
+ A standalone chart API shared between cumulus and plastic. Fully independent — no side effects, no DOMPurify, no registry setup:
47
+
48
+ ```typescript
49
+ import { renderChart } from '@luckydraw/blex/chart';
50
+
51
+ const chart = renderChart(container, {
52
+ type: 'bar',
53
+ data: {
54
+ labels: ['Q1', 'Q2', 'Q3', 'Q4'],
55
+ datasets: [{ label: 'Revenue', values: [10, 25, 15, 30] }]
56
+ },
57
+ options: { title: 'Quarterly Revenue', legend: true }
58
+ });
59
+
60
+ chart.update({ data: { labels: ['Q1', 'Q2'], datasets: [{ label: 'Revenue', values: [10, 25] }] } });
61
+ chart.destroy();
62
+ ```
63
+
64
+ ## API
65
+
66
+ ### `renderBlock(block, container, options?): Promise<BlockHandle>`
67
+
68
+ Resolves the block type from the registry, loads the renderer (lazy if needed), renders into the container, and wires up interaction callbacks.
69
+
70
+ Options:
71
+ - `onReady?: () => void` — called when the block finishes initial render
72
+
73
+ ### `renderPlaceholder(type, container): HTMLElement`
74
+
75
+ Renders a typed skeleton/spinner for a block that hasn't finished streaming yet. Returns the placeholder element for later replacement. Used by cumulus while buffering `~~~blex:TYPE` fence content.
76
+
77
+ ### `registerBlockType(type, renderer): void`
78
+
79
+ Register a custom block type renderer. Overrides built-in renderers if the type matches.
80
+
81
+ ```typescript
82
+ registerBlockType('my-widget', {
83
+ render(block, container) { /* ... */ },
84
+ update(block) { /* streaming updates */ },
85
+ onInteraction(callback) { /* wire up events */ },
86
+ destroy() { /* cleanup */ }
87
+ });
88
+ ```
89
+
90
+ ## Types
91
+
92
+ ```typescript
93
+ interface ContentBlock<T = unknown> {
94
+ type: string;
95
+ data: T;
96
+ id: string; // unique per block instance, stable across re-renders
97
+ streaming?: boolean; // true while data is still arriving
98
+ }
99
+
100
+ interface BlockRenderer<T = unknown> {
101
+ render(block: ContentBlock<T>, container: HTMLElement): void;
102
+ update?(block: ContentBlock<T>): void;
103
+ onInteraction?(callback: (interaction: BlockInteraction) => void): void;
104
+ destroy(): void;
105
+ }
106
+
107
+ interface BlockHandle {
108
+ update(block: ContentBlock): void;
109
+ onInteraction(callback: (interaction: BlockInteraction) => void): void;
110
+ destroy(): void;
111
+ }
112
+
113
+ interface BlockInteraction {
114
+ blockId: string;
115
+ type: string; // 'select', 'click', 'submit', 'apply', 'reject', 'move'
116
+ payload: unknown;
117
+ serialized: string; // pre-formatted text for chat input injection
118
+ }
119
+ ```
120
+
121
+ ### Chart Types
122
+
123
+ ```typescript
124
+ interface ChartConfig {
125
+ type: 'bar' | 'line' | 'pie' | 'doughnut';
126
+ data: { labels: string[]; datasets: DataSet[] };
127
+ options?: { title?: string; legend?: boolean; responsive?: boolean };
128
+ }
129
+
130
+ interface DataSet {
131
+ label: string;
132
+ values: number[];
133
+ color?: string;
134
+ }
135
+
136
+ interface ChartInstance {
137
+ update(config: Partial<ChartConfig>): void;
138
+ destroy(): void;
139
+ }
140
+ ```
141
+
142
+ ## Built-in Block Types
143
+
144
+ ### Core Blocks (bundled, <5KB each)
145
+
146
+ | Type | Description | Interaction Types |
147
+ |------|-------------|-------------------|
148
+ | `confirm` | Yes/No/Cancel buttons | `click` |
149
+ | `poll` | Single/multi choice with optional write-in | `submit` |
150
+ | `status` | Key/value pairs with status indicators (ok/warning/error) | — |
151
+ | `metric` | Single big number with label and trend indicator | — |
152
+ | `image` | URL/base64 image with caption, zoom/download | `click` |
153
+ | `svg` | Sanitized inline SVG with download/copy | `click` |
154
+ | `table` | Sortable columns, row/cell/column selection | `select` |
155
+ | `code` | Syntax-highlighted code with line selection | `select` |
156
+ | `diff` | Unified or side-by-side diff | `apply`, `reject` |
157
+ | `file-tree` | Expandable directory tree | `select` |
158
+ | `form` | JSON schema → dynamic form | `submit` |
159
+ | `progress` | Live-updating step tracker | — |
160
+ | `terminal` | Collapsible command output with exit code | `click` |
161
+ | `timeline` | Vertical event timeline with timestamps | — |
162
+
163
+ ### Lazy-loaded Blocks
164
+
165
+ | Type | Description | Loaded Size |
166
+ |------|-------------|-------------|
167
+ | `mermaid` | Diagram rendering via mermaid.js | ~200KB |
168
+ | `chart` | Bar/line/pie/doughnut via chart library | ~60KB |
169
+ | `kanban` | Drag-and-drop columns via HTML5 DnD | ~8KB |
170
+ | `calendar` | Month/week/day event views | ~12KB |
171
+ | `gallery` | Image grid/carousel with lazy loading | ~5KB |
172
+
173
+ Heavy renderers are loaded on first use. Subsequent renders of the same type reuse the loaded module.
174
+
175
+ ### Unknown Types
176
+
177
+ Any unrecognized block type renders as raw JSON inside a collapsible `<details>` element, so content is never lost.
178
+
179
+ ## Theming
180
+
181
+ blex uses CSS custom properties with sensible defaults. Consumers override them to match their theme:
182
+
183
+ ```css
184
+ /* blex defaults — override in your app */
185
+ --blex-bg: #ffffff;
186
+ --blex-text: #1a1a1a;
187
+ --blex-border: #e5e5e5;
188
+ --blex-accent: #3b82f6;
189
+ --blex-success: #22c55e;
190
+ --blex-warning: #f59e0b;
191
+ --blex-error: #ef4444;
192
+ --blex-chart-bg: var(--blex-bg);
193
+ --blex-chart-text: var(--blex-text);
194
+ --blex-animation-duration: 200ms;
195
+ ```
196
+
197
+ **Dark mode:** Set `data-theme="dark"` on a parent element. blex adjusts defaults automatically.
198
+
199
+ **Test mode:** Set `data-test-mode="true"` or `--blex-animation-duration: 0ms` to disable all animations for puppet/browser testing.
200
+
201
+ ## Streaming
202
+
203
+ Renderers that implement `update()` receive incremental data without a full destroy/re-render cycle. The `streaming` flag on `ContentBlock` indicates data is still arriving.
204
+
205
+ ```typescript
206
+ const handle = await renderBlock(
207
+ { type: 'table', id: 't1', data: { rows: [] }, streaming: true },
208
+ container
209
+ );
210
+
211
+ // As data arrives:
212
+ handle.update({ type: 'table', id: 't1', data: { rows: newRows }, streaming: true });
213
+
214
+ // When complete:
215
+ handle.update({ type: 'table', id: 't1', data: { rows: allRows }, streaming: false });
216
+ ```
217
+
218
+ When `streaming: false` (e.g., historical message replay), blocks render immediately with no transition/animation.
219
+
220
+ ## Interaction Serialization
221
+
222
+ Every `BlockInteraction` includes a `serialized` field — a pre-formatted text string suitable for injecting into a chat input. Each block type controls its own serialization format.
223
+
224
+ ```typescript
225
+ // Table selection might serialize as:
226
+ {
227
+ blockId: 't1',
228
+ type: 'select',
229
+ payload: { rows: [0, 2], columns: ['name', 'status'] },
230
+ serialized: 'Selected from table:\n| name | status |\n| Alice | active |\n| Charlie | pending |'
231
+ }
232
+ ```
233
+
234
+ Consumers receive interactions via the `BlockHandle.onInteraction()` callback and can forward `serialized` text directly to the chat input.
235
+
236
+ ## Cleanup Contract
237
+
238
+ `destroy()` is a hard guarantee: after calling it, **zero references remain**. This includes:
239
+
240
+ - Event listeners (click, resize, keyboard)
241
+ - Animation frames (RAF callbacks)
242
+ - Canvas contexts
243
+ - Resize/intersection/mutation observers
244
+ - Timers (setTimeout, setInterval)
245
+ - Pending lazy-load promises
246
+ - DOM references
247
+
248
+ Enforced via `BaseRenderer` abstract class with cleanup tracking. All built-in renderers extend it.
249
+
250
+ ## Design Decisions
251
+
252
+ - **Vanilla JS/TS** — no React dependency. Consumers wrap as needed.
253
+ - **Dual output** — ESM for bundled consumers, IIFE (`window.Blex`) for cumulus widget.
254
+ - **DOMPurify** — the one required runtime dependency, used for SVG/HTML sanitization.
255
+ - **Lazy loading** — heavy renderers (mermaid, chart.js, kanban, calendar, gallery) loaded on first use.
256
+ - **Subpath export** — `@luckydraw/blex/chart` is fully independent: no side effects, no registry, no DOMPurify. Just `renderChart()` + Chart.js lazy load.
257
+ - **CSS custom properties** — `--blex-*` variables for theming. No hardcoded colors.
258
+ - **`data-testid`** — on all interactive elements for puppet/browser automation.
259
+ - **BaseRenderer** — abstract class enforcing `destroy()` cleanup contract.
260
+
261
+ ## Package Structure
262
+
263
+ ```
264
+ src/
265
+ ├── index.ts # main entry: renderBlock, registerBlockType, renderPlaceholder
266
+ ├── types.ts # ContentBlock, BlockRenderer, BlockInteraction, BlockHandle
267
+ ├── registry.ts # block type registry + lazy loader
268
+ ├── base-renderer.ts # BaseRenderer abstract class with cleanup tracking
269
+ ├── fallback.ts # unknown type fallback renderer
270
+ ├── placeholder.ts # streaming skeleton/placeholder utility
271
+ ├── theme.ts # CSS custom property defaults + dark mode
272
+ ├── chart/
273
+ │ └── index.ts # subpath export: renderChart (fully independent)
274
+ └── renderers/
275
+ ├── confirm.ts
276
+ ├── poll.ts
277
+ ├── status.ts
278
+ ├── metric.ts
279
+ ├── image.ts
280
+ ├── mermaid.ts # lazy-loaded
281
+ ├── svg.ts
282
+ ├── table.ts
283
+ ├── chart.ts # lazy-loaded, wraps chart/index.ts
284
+ ├── code.ts
285
+ ├── diff.ts
286
+ ├── file-tree.ts
287
+ ├── form.ts
288
+ ├── progress.ts
289
+ ├── terminal.ts
290
+ ├── timeline.ts
291
+ ├── kanban.ts # lazy-loaded
292
+ ├── calendar.ts # lazy-loaded
293
+ └── gallery.ts # lazy-loaded
294
+ ```
295
+
296
+ ## License
297
+
298
+ MIT