@webmcp-auto-ui/ui 2.5.27 → 2.5.29

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 (76) hide show
  1. package/package.json +18 -5
  2. package/src/agent/LLMSelector.svelte +11 -3
  3. package/src/agent/ModelCacheManager.svelte +359 -0
  4. package/src/index.ts +42 -30
  5. package/src/theme/scale.ts +128 -0
  6. package/src/widgets/WidgetRenderer.svelte +144 -107
  7. package/src/widgets/export-widget.ts +28 -1
  8. package/src/widgets/helpers/safe-image.ts +78 -0
  9. package/src/widgets/notebook/.gitkeep +0 -0
  10. package/src/widgets/notebook/chart-renderer.ts +63 -0
  11. package/src/widgets/notebook/executors/.gitkeep +1 -0
  12. package/src/widgets/notebook/executors/index.ts +4 -0
  13. package/src/widgets/notebook/executors/js-worker.ts +269 -0
  14. package/src/widgets/notebook/executors/sql.ts +206 -0
  15. package/src/widgets/notebook/import-modals.ts +560 -0
  16. package/src/widgets/notebook/left-pane.ts +256 -0
  17. package/src/widgets/notebook/notebook.ts +930 -0
  18. package/src/widgets/notebook/prose.ts +615 -0
  19. package/src/widgets/notebook/recipe-browser.ts +350 -0
  20. package/src/widgets/notebook/recipes/notebook.md +124 -0
  21. package/src/widgets/notebook/resource-extractor.ts +162 -0
  22. package/src/widgets/notebook/share-handlers.ts +222 -0
  23. package/src/widgets/notebook/shared.ts +1633 -0
  24. package/src/widgets/rich/cards.ts +181 -0
  25. package/src/widgets/rich/carousel.ts +319 -0
  26. package/src/widgets/rich/chart-rich.ts +386 -0
  27. package/src/widgets/rich/d3.ts +503 -0
  28. package/src/widgets/rich/data-table.ts +342 -0
  29. package/src/widgets/rich/gallery.ts +350 -0
  30. package/src/widgets/rich/grid-data.ts +173 -0
  31. package/src/widgets/rich/hemicycle.ts +313 -0
  32. package/src/widgets/rich/js-sandbox.ts +122 -0
  33. package/src/widgets/rich/json-viewer.ts +202 -0
  34. package/src/widgets/rich/log.ts +143 -0
  35. package/src/widgets/rich/map.ts +218 -0
  36. package/src/widgets/rich/profile.ts +256 -0
  37. package/src/widgets/rich/sankey.ts +257 -0
  38. package/src/widgets/rich/stat-card.ts +125 -0
  39. package/src/widgets/rich/timeline.ts +179 -0
  40. package/src/widgets/rich/trombinoscope.ts +246 -0
  41. package/src/widgets/simple/actions.ts +89 -0
  42. package/src/widgets/simple/alert.ts +100 -0
  43. package/src/widgets/simple/chart.ts +189 -0
  44. package/src/widgets/simple/code.ts +79 -0
  45. package/src/widgets/simple/kv.ts +68 -0
  46. package/src/widgets/simple/list.ts +89 -0
  47. package/src/widgets/simple/stat.ts +58 -0
  48. package/src/widgets/simple/tags.ts +125 -0
  49. package/src/widgets/simple/text.ts +198 -0
  50. package/src/widgets/SafeImage.svelte +0 -76
  51. package/src/widgets/rich/Cards.svelte +0 -39
  52. package/src/widgets/rich/Carousel.svelte +0 -88
  53. package/src/widgets/rich/Chart.svelte +0 -142
  54. package/src/widgets/rich/D3Widget.svelte +0 -378
  55. package/src/widgets/rich/DataTable.svelte +0 -62
  56. package/src/widgets/rich/Gallery.svelte +0 -94
  57. package/src/widgets/rich/GridData.svelte +0 -44
  58. package/src/widgets/rich/Hemicycle.svelte +0 -78
  59. package/src/widgets/rich/JsSandbox.svelte +0 -51
  60. package/src/widgets/rich/JsonViewer.svelte +0 -42
  61. package/src/widgets/rich/LogViewer.svelte +0 -24
  62. package/src/widgets/rich/MapView.svelte +0 -140
  63. package/src/widgets/rich/ProfileCard.svelte +0 -59
  64. package/src/widgets/rich/Sankey.svelte +0 -56
  65. package/src/widgets/rich/StatCard.svelte +0 -35
  66. package/src/widgets/rich/Timeline.svelte +0 -43
  67. package/src/widgets/rich/Trombinoscope.svelte +0 -48
  68. package/src/widgets/simple/ActionsBlock.svelte +0 -15
  69. package/src/widgets/simple/AlertBlock.svelte +0 -11
  70. package/src/widgets/simple/ChartBlock.svelte +0 -21
  71. package/src/widgets/simple/CodeBlock.svelte +0 -11
  72. package/src/widgets/simple/KVBlock.svelte +0 -16
  73. package/src/widgets/simple/ListBlock.svelte +0 -17
  74. package/src/widgets/simple/StatBlock.svelte +0 -14
  75. package/src/widgets/simple/TagsBlock.svelte +0 -15
  76. package/src/widgets/simple/TextBlock.svelte +0 -122
@@ -0,0 +1,256 @@
1
+ // @ts-nocheck
2
+ // ---------------------------------------------------------------------------
3
+ // Left pane — collapsible resource browser (Chrome-bookmarks-style).
4
+ // Lists recipes + tools per connected MCP server.
5
+ // Click opens the appropriate viewer modal (recipe / tool).
6
+ // Collapsed by default.
7
+ // ---------------------------------------------------------------------------
8
+
9
+ import { callToolViaPostMessage } from '@webmcp-auto-ui/core';
10
+ import { openRecipeViewerModal, openToolViewerModal, type ImportedRecipe } from './import-modals.js';
11
+ import type { NotebookCell, NotebookState, DataServerDescriptor } from './shared.js';
12
+
13
+ export interface LeftPaneHandlers {
14
+ /** Called when the user injects one or more cells from a recipe/tool viewer. */
15
+ onInjectCells: (cells: NotebookCell[]) => void;
16
+ }
17
+
18
+ export interface LeftPaneHandle {
19
+ root: HTMLElement;
20
+ setServers(servers: DataServerDescriptor[]): void;
21
+ toggle(open?: boolean): void;
22
+ destroy(): void;
23
+ }
24
+
25
+ /**
26
+ * Mount a left pane inside `host`. The pane is collapsed by default;
27
+ * click the handle to expand. Returns a handle the widget can use to
28
+ * feed updated server lists or toggle programmatically.
29
+ */
30
+ export function mountLeftPane(
31
+ host: HTMLElement,
32
+ state: NotebookState,
33
+ initialServers: DataServerDescriptor[],
34
+ handlers: LeftPaneHandlers,
35
+ ): LeftPaneHandle {
36
+ injectLeftPaneStyles();
37
+
38
+ const root = document.createElement('aside');
39
+ root.className = 'nb-lp';
40
+ root.innerHTML = `
41
+ <button type="button" class="nb-lp-handle" aria-label="Toggle resources" title="Resources">
42
+ <span class="nb-lp-handle-icon">▸</span>
43
+ <span class="nb-lp-handle-label">Resources</span>
44
+ </button>
45
+ <div class="nb-lp-body" hidden>
46
+ <header class="nb-lp-head">
47
+ <span class="nb-lp-title">Resources</span>
48
+ <button type="button" class="nb-lp-close" aria-label="Close">×</button>
49
+ </header>
50
+ <div class="nb-lp-servers" data-role="servers"></div>
51
+ </div>
52
+ `;
53
+ host.appendChild(root);
54
+
55
+ const handle = root.querySelector('.nb-lp-handle') as HTMLElement;
56
+ const body = root.querySelector('.nb-lp-body') as HTMLElement;
57
+ const closeBtn = root.querySelector('.nb-lp-close') as HTMLElement;
58
+ const serversEl = root.querySelector('[data-role="servers"]') as HTMLElement;
59
+
60
+ let servers: DataServerDescriptor[] = initialServers ?? [];
61
+ // Recipe-body cache per serverName/name key
62
+ const recipeBodyCache = new Map<string, string>();
63
+
64
+ function render() {
65
+ serversEl.innerHTML = '';
66
+ if (!servers.length) {
67
+ serversEl.innerHTML = '<div class="nb-lp-empty">No servers connected.</div>';
68
+ return;
69
+ }
70
+ for (const srv of servers) {
71
+ const section = document.createElement('section');
72
+ section.className = 'nb-lp-srv';
73
+ section.innerHTML = `
74
+ <header class="nb-lp-srv-head">
75
+ <span class="nb-lp-srv-dot"></span>
76
+ <span class="nb-lp-srv-name">${escapeHtml(srv.name)}</span>
77
+ <span class="nb-lp-srv-meta">${(srv.recipes?.length ?? 0)} recipes · ${(srv.tools?.length ?? 0)} tools</span>
78
+ </header>
79
+ <div class="nb-lp-srv-groups">
80
+ ${srv.recipes?.length ? `
81
+ <details class="nb-lp-group" open>
82
+ <summary>📜 Recipes (${srv.recipes.length})</summary>
83
+ <ul class="nb-lp-list" data-role="recipes"></ul>
84
+ </details>
85
+ ` : ''}
86
+ ${srv.tools?.length ? `
87
+ <details class="nb-lp-group">
88
+ <summary>⚙ Tools (${srv.tools.length})</summary>
89
+ <ul class="nb-lp-list" data-role="tools"></ul>
90
+ </details>
91
+ ` : ''}
92
+ </div>
93
+ `;
94
+ const recList = section.querySelector('[data-role="recipes"]') as HTMLElement | null;
95
+ if (recList && srv.recipes) {
96
+ for (const r of srv.recipes) {
97
+ const li = document.createElement('li');
98
+ li.className = 'nb-lp-item';
99
+ li.innerHTML = `
100
+ <span class="nb-lp-item-name">${escapeHtml(r.name)}</span>
101
+ ${r.description ? `<span class="nb-lp-item-desc">${escapeHtml(r.description)}</span>` : ''}
102
+ `;
103
+ li.addEventListener('click', () => onRecipeClick(srv, r));
104
+ recList.appendChild(li);
105
+ }
106
+ }
107
+ const toolList = section.querySelector('[data-role="tools"]') as HTMLElement | null;
108
+ if (toolList && srv.tools) {
109
+ for (const t of srv.tools) {
110
+ const li = document.createElement('li');
111
+ li.className = 'nb-lp-item';
112
+ li.innerHTML = `
113
+ <span class="nb-lp-item-name nb-lp-tool">${escapeHtml(t.name)}</span>
114
+ ${t.description ? `<span class="nb-lp-item-desc">${escapeHtml(t.description)}</span>` : ''}
115
+ `;
116
+ li.addEventListener('click', () => onToolClick(srv, t));
117
+ toolList.appendChild(li);
118
+ }
119
+ }
120
+ serversEl.appendChild(section);
121
+ }
122
+ }
123
+
124
+ async function onRecipeClick(srv: DataServerDescriptor, r: { name: string; description?: string; body?: string }) {
125
+ const imported: ImportedRecipe = {
126
+ name: r.name,
127
+ description: r.description,
128
+ body: r.body,
129
+ serverName: srv.name,
130
+ originalName: r.name,
131
+ };
132
+ const key = srv.name + ':' + r.name;
133
+ if (!imported.body && recipeBodyCache.has(key)) {
134
+ imported.body = recipeBodyCache.get(key);
135
+ }
136
+ if (!imported.body) {
137
+ try {
138
+ const res: any = await callToolViaPostMessage(`${srv.name}_get_recipe`, { name: r.name, id: r.name });
139
+ const text = res?.content?.find?.((c: any) => c.type === 'text')?.text;
140
+ if (text) {
141
+ let body = text;
142
+ try {
143
+ const parsed = JSON.parse(text);
144
+ // Recipe servers return either { content: "..." } (legacy) or
145
+ // { name, description, body, ... } (autoui-style). Pick whichever
146
+ // string field carries the markdown body, in priority order.
147
+ if (parsed && typeof parsed === 'object') {
148
+ if (typeof parsed.body === 'string') body = parsed.body;
149
+ else if (typeof parsed.content === 'string') body = parsed.content;
150
+ else if (typeof parsed.markdown === 'string') body = parsed.markdown;
151
+ }
152
+ } catch { /* not JSON, use raw text */ }
153
+ imported.body = body;
154
+ recipeBodyCache.set(key, body);
155
+ }
156
+ } catch { /* pass empty body to viewer */ }
157
+ }
158
+ openRecipeViewerModal(imported, (cell) => handlers.onInjectCells([cell]));
159
+ }
160
+
161
+ function onToolClick(srv: DataServerDescriptor, t: { name: string; description?: string; inputSchema?: unknown }) {
162
+ openToolViewerModal({ ...t, serverName: srv.name }, (cells) => handlers.onInjectCells(cells));
163
+ }
164
+
165
+ handle.addEventListener('click', () => toggle());
166
+ closeBtn.addEventListener('click', () => toggle(false));
167
+
168
+ function toggle(open?: boolean) {
169
+ const nextOpen = typeof open === 'boolean' ? open : !!body.hidden;
170
+ body.hidden = !nextOpen;
171
+ root.classList.toggle('nb-lp-open', nextOpen);
172
+ (root.querySelector('.nb-lp-handle-icon') as HTMLElement).textContent = nextOpen ? '◂' : '▸';
173
+ }
174
+
175
+ render();
176
+
177
+ return {
178
+ root,
179
+ setServers(next) {
180
+ servers = next ?? [];
181
+ render();
182
+ },
183
+ toggle,
184
+ destroy() {
185
+ root.remove();
186
+ },
187
+ };
188
+ }
189
+
190
+ function escapeHtml(s: string): string {
191
+ return String(s ?? '').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
192
+ }
193
+
194
+ function injectLeftPaneStyles() {
195
+ if (document.getElementById('nb-lp-styles')) return;
196
+ const s = document.createElement('style');
197
+ s.id = 'nb-lp-styles';
198
+ s.textContent = `
199
+ .nb-lp {
200
+ position: relative; display: flex; flex-direction: row; flex-shrink: 0;
201
+ font-family: var(--font-sans, system-ui); font-size: 12px;
202
+ color: var(--color-text1, #111);
203
+ }
204
+ .nb-lp-handle {
205
+ writing-mode: vertical-rl; transform: rotate(180deg);
206
+ background: var(--color-surface2, #f4f4f5);
207
+ border: 1px solid var(--color-border, #e4e4e7); border-radius: 6px;
208
+ padding: 10px 5px; cursor: pointer;
209
+ display: flex; align-items: center; gap: 6px;
210
+ height: fit-content; font-size: 11px; color: var(--color-text2, #666);
211
+ align-self: flex-start; margin-top: 12px; margin-right: 4px;
212
+ }
213
+ .nb-lp-handle:hover { background: var(--color-surface3, #eeeef0); }
214
+ .nb-lp-handle-icon { font-size: 10px; }
215
+ .nb-lp.nb-lp-open .nb-lp-handle { display: none; }
216
+ .nb-lp-body[hidden] { display: none !important; }
217
+ .nb-lp-body {
218
+ width: 260px; border-right: 1px solid var(--color-border, #e4e4e7);
219
+ background: var(--color-surface, #fff);
220
+ display: flex; flex-direction: column; max-height: 100%; overflow: hidden;
221
+ }
222
+ .nb-lp-head {
223
+ display: flex; align-items: center; padding: 10px 12px;
224
+ border-bottom: 1px solid var(--color-border, #e4e4e7);
225
+ }
226
+ .nb-lp-title { flex: 1; font-weight: 600; font-size: 12px; }
227
+ .nb-lp-close { background: none; border: none; cursor: pointer; font-size: 16px; color: var(--color-text2, #666); }
228
+ .nb-lp-servers { overflow-y: auto; padding: 8px 10px 12px; flex: 1; }
229
+ .nb-lp-srv { margin-bottom: 10px; }
230
+ .nb-lp-srv-head {
231
+ display: flex; align-items: center; gap: 6px;
232
+ padding: 6px 2px; font-size: 11px; color: var(--color-text2, #666);
233
+ }
234
+ .nb-lp-srv-dot {
235
+ width: 6px; height: 6px; border-radius: 50%; background: var(--color-accent, #6a55ff);
236
+ flex-shrink: 0;
237
+ }
238
+ .nb-lp-srv-name { font-weight: 600; color: var(--color-text1, #111); }
239
+ .nb-lp-srv-meta { margin-left: auto; font-family: monospace; font-size: 10.5px; }
240
+ .nb-lp-group > summary {
241
+ cursor: pointer; padding: 4px 2px; font-size: 11px;
242
+ color: var(--color-text2, #666); font-family: monospace;
243
+ }
244
+ .nb-lp-list { list-style: none; padding: 0 0 0 6px; margin: 2px 0 6px; }
245
+ .nb-lp-item {
246
+ padding: 5px 8px; border-radius: 4px; cursor: pointer;
247
+ display: flex; flex-direction: column; gap: 2px;
248
+ }
249
+ .nb-lp-item:hover { background: var(--color-surface2, #f4f4f5); }
250
+ .nb-lp-item-name { font-size: 12px; }
251
+ .nb-lp-tool { font-family: monospace; }
252
+ .nb-lp-item-desc { font-size: 10.5px; color: var(--color-text2, #666); }
253
+ .nb-lp-empty { padding: 12px; font-size: 11px; color: var(--color-text2, #666); text-align: center; }
254
+ `;
255
+ document.head.appendChild(s);
256
+ }