@webmcp-auto-ui/agent 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.
@@ -1,348 +0,0 @@
1
- // @ts-nocheck
2
- // ---------------------------------------------------------------------------
3
- // notebook-editorial — publication-ready layout (observable-like)
4
- // Serif prose + cells in a single ordered list, all drag-and-droppable together.
5
- // Cells alternate freely: md (prose paragraph) / sql / js cells share the flow.
6
- // ---------------------------------------------------------------------------
7
-
8
- import {
9
- createState, injectStyles, mountRunControls, mountHistoryPanel,
10
- setupDnD, deleteCellWithConfirm, restoreCellFromSnapshot, addCell,
11
- autosize, openShareModal, registerHistoryObserver,
12
- buildServersButton,
13
- type NotebookState, type NotebookCell,
14
- } from './shared.js';
15
-
16
- export async function render(container: HTMLElement, data: Record<string, unknown>): Promise<() => void> {
17
- injectStyles();
18
- injectLayoutStyles();
19
-
20
- const state: NotebookState = createState({
21
- id: data.id as string,
22
- title: data.title as string ?? 'Untitled notebook',
23
- mode: (data.mode as any) ?? 'edit',
24
- cells: data.cells as any,
25
- });
26
- const kicker: string = (data.kicker as string) ?? 'untitled';
27
- const forkId: string = (data.forkId as string) ?? (state.id.slice(0, 4) + '·' + state.id.slice(4, 8));
28
-
29
- container.classList.add('nb-root');
30
- container.classList.toggle('nb-view-mode', state.mode === 'view');
31
-
32
- container.innerHTML = `
33
- <div class="nbe-shell">
34
- <div class="nbe-kicker">
35
- <span class="nbe-kicker-label">${escapeHtml(kicker)}</span>
36
- <div class="nb-mode-switch" style="margin-left:auto;">
37
- <button class="nb-mode-edit nb-on">edit</button>
38
- <button class="nb-mode-view">view</button>
39
- </div>
40
- <button class="nb-btn nbe-history-btn">⟲ history</button>
41
- <span class="nbe-servers-slot"></span>
42
- </div>
43
- <input class="nbe-title nb-ed-title" value="${escapeAttr(state.title)}">
44
- <div class="nb-history-panel nbe-history-panel"></div>
45
- <div class="nbe-cells"></div>
46
- <div class="nbe-footer">
47
- <button class="nb-btn nb-add-cell" data-add="md">+ prose</button>
48
- <button class="nb-btn nb-add-cell" data-add="sql">+ sql</button>
49
- <button class="nb-btn nb-add-cell" data-add="js">+ chart</button>
50
- <span class="nbe-fork nbe-share-btn">share · ${escapeHtml(forkId)}</span>
51
- </div>
52
- </div>`;
53
-
54
- const shell = container.querySelector('.nbe-shell') as HTMLElement;
55
- const cellsEl = shell.querySelector('.nbe-cells') as HTMLElement;
56
- const historyPanel = shell.querySelector('.nbe-history-panel') as HTMLElement;
57
-
58
- function renderCells() {
59
- cellsEl.innerHTML = '';
60
- state.cells.forEach((cell) => cellsEl.appendChild(renderCell(cell, state, rerender)));
61
- }
62
-
63
- function rerender() {
64
- mountHistoryPanel(historyPanel, state, (snap) => { restoreCellFromSnapshot(state, snap); rerender(); });
65
- renderCells();
66
- }
67
-
68
- shell.querySelectorAll<HTMLElement>('[data-add]').forEach((btn) => {
69
- btn.addEventListener('click', () => {
70
- const type = btn.dataset.add as any;
71
- addCell(state, type);
72
- rerender();
73
- });
74
- });
75
- (shell.querySelector('.nbe-history-btn') as HTMLElement).addEventListener('click', () => {
76
- historyPanel.classList.toggle('nb-open');
77
- });
78
- (shell.querySelector('.nbe-share-btn') as HTMLElement).addEventListener('click', () => {
79
- openShareModal(state, (fmt) => console.log('[notebook-editorial] share as', fmt, state));
80
- });
81
- (shell.querySelector('.nbe-title') as HTMLInputElement).addEventListener('input', (e) => {
82
- state.title = (e.target as HTMLInputElement).value;
83
- });
84
- const editBtn = shell.querySelector('.nb-mode-edit') as HTMLElement;
85
- const viewBtn = shell.querySelector('.nb-mode-view') as HTMLElement;
86
- editBtn.addEventListener('click', () => {
87
- state.mode = 'edit';
88
- container.classList.remove('nb-view-mode');
89
- editBtn.classList.add('nb-on'); viewBtn.classList.remove('nb-on');
90
- });
91
- viewBtn.addEventListener('click', () => {
92
- state.mode = 'view';
93
- container.classList.add('nb-view-mode');
94
- viewBtn.classList.add('nb-on'); editBtn.classList.remove('nb-on');
95
- });
96
-
97
- buildServersButton(state, shell.querySelector('.nbe-servers-slot') as HTMLElement, data, rerender);
98
-
99
- setupDnD(cellsEl, state, rerender);
100
- const unsubHistory = registerHistoryObserver(() => mountHistoryPanel(historyPanel, state, (snap) => { restoreCellFromSnapshot(state, snap); rerender(); }));
101
-
102
- rerender();
103
- return () => { unsubHistory(); };
104
- }
105
-
106
- // Unified cell rendering: prose (md) and code (sql/js) both live in the same flow.
107
- function renderCell(cell: NotebookCell, state: NotebookState, rerender: () => void): HTMLElement {
108
- const wrap = document.createElement('div');
109
- wrap.className = 'nb-cell-wrapper nbe-cell';
110
- wrap.dataset.id = cell.id;
111
-
112
- const handle = document.createElement('span');
113
- handle.className = 'nb-drag-handle nbe-handle';
114
- handle.draggable = true;
115
- handle.textContent = '⋮⋮';
116
- wrap.appendChild(handle);
117
-
118
- const del = document.createElement('button');
119
- del.className = 'nb-icon-btn nb-danger nbe-del-abs';
120
- del.textContent = '✕';
121
- del.addEventListener('click', () =>
122
- deleteCellWithConfirm(state, cell, (c) => c.type === 'md' ? 'prose paragraph' : `${c.type} cell`, rerender)
123
- );
124
- wrap.appendChild(del);
125
-
126
- if (cell.type === 'md') {
127
- const p = document.createElement('div');
128
- p.className = 'nbe-prose';
129
- p.contentEditable = 'true';
130
- p.innerHTML = cell.content;
131
- p.addEventListener('input', () => { cell.content = p.innerHTML; });
132
- wrap.appendChild(p);
133
- return wrap;
134
- }
135
-
136
- // Code cell: header with run controls FIRST, then code body, then optional output.
137
- const codeCell = document.createElement('div');
138
- codeCell.className = 'nb-code-cell nbe-code-cell';
139
-
140
- const head = document.createElement('div');
141
- head.className = 'nbe-cell-head';
142
- head.innerHTML = `
143
- <span class="nbe-run-controls"></span>
144
- <span class="nbe-type-${cell.type}">${cell.type}</span>
145
- <span class="nbe-meta-info">${cell.lastMs != null ? cell.lastMs + 'ms' : ''}</span>
146
- <div class="nbe-actions">
147
- <button class="nb-icon-btn nb-toggle-src">${cell.hideSource ? '▸ src' : '◂ src'}</button>
148
- <button class="nb-icon-btn nb-toggle-res">${cell.hideResult ? '▸ res' : '◂ res'}</button>
149
- </div>`;
150
- codeCell.appendChild(head);
151
- mountRunControls(head.querySelector('.nbe-run-controls') as HTMLElement, cell, wrap, rerender);
152
-
153
- const body = document.createElement('div');
154
- body.className = 'nbe-code-body' + (cell.hideSource ? ' nbe-hidden' : '');
155
- const ta = document.createElement('textarea');
156
- ta.className = 'nb-code-edit';
157
- ta.value = cell.content;
158
- ta.rows = 1;
159
- ta.spellcheck = false;
160
- ta.addEventListener('input', () => { cell.content = ta.value; autosize(ta); cell.status = 'stale'; });
161
- body.appendChild(ta);
162
- codeCell.appendChild(body);
163
- setTimeout(() => autosize(ta), 0);
164
-
165
- if (cell.type === 'js' && !cell.hideResult) {
166
- const chart = document.createElement('div');
167
- chart.className = 'nbe-chart';
168
- chart.innerHTML = `
169
- <div class="nbe-col"><span class="nbe-val">42</span><div class="nbe-bar" style="height:100%"></div></div>
170
- <div class="nbe-col"><span class="nbe-val">29</span><div class="nbe-bar" style="height:69%;opacity:.84"></div></div>
171
- <div class="nbe-col"><span class="nbe-val">22</span><div class="nbe-bar" style="height:53%;opacity:.7"></div></div>
172
- <div class="nbe-col"><span class="nbe-val">9</span><div class="nbe-bar" style="height:22%;opacity:.56"></div></div>`;
173
- codeCell.appendChild(chart);
174
- const axis = document.createElement('div');
175
- axis.className = 'nbe-chart-axis';
176
- axis.innerHTML = '<span>A</span><span>B</span><span>C</span><span>D</span>';
177
- codeCell.appendChild(axis);
178
- } else if (cell.type === 'sql' && !cell.hideResult) {
179
- const res = document.createElement('div');
180
- res.className = 'nbe-sql-result';
181
- res.innerHTML = `
182
- <table>
183
- <tr><td>row_1</td><td>42</td></tr>
184
- <tr><td>row_2</td><td>29</td></tr>
185
- <tr><td>row_3</td><td>22</td></tr>
186
- <tr><td>row_4</td><td>9</td></tr>
187
- </table>`;
188
- codeCell.appendChild(res);
189
- }
190
-
191
- wrap.appendChild(codeCell);
192
-
193
- (head.querySelector('.nb-toggle-src') as HTMLElement).addEventListener('click', () => { cell.hideSource = !cell.hideSource; rerender(); });
194
- (head.querySelector('.nb-toggle-res') as HTMLElement).addEventListener('click', () => { cell.hideResult = !cell.hideResult; rerender(); });
195
-
196
- return wrap;
197
- }
198
-
199
- function escapeHtml(s: string): string {
200
- return (s ?? '').replace(/[&<>"']/g, (c) => ({ '&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;' }[c]!));
201
- }
202
- function escapeAttr(s: string): string {
203
- return (s ?? '').replace(/"/g, '&quot;');
204
- }
205
-
206
- function injectLayoutStyles(): void {
207
- if (document.getElementById('nbe-styles')) return;
208
- const style = document.createElement('style');
209
- style.id = 'nbe-styles';
210
- style.textContent = `
211
- .nbe-shell {
212
- background: var(--color-surface);
213
- border: 1px solid var(--color-border);
214
- border-radius: 12px;
215
- padding: 36px 44px;
216
- }
217
- .nbe-kicker {
218
- display: flex; align-items: center; gap: 8px;
219
- font-family: var(--font-mono, 'IBM Plex Mono', monospace);
220
- font-size: 11px; color: var(--color-text2);
221
- letter-spacing: 0.1em; text-transform: uppercase;
222
- margin-bottom: 14px;
223
- }
224
- .nbe-kicker-label { flex: 0 0 auto; }
225
- .nbe-title {
226
- font-family: 'EB Garamond', Georgia, serif;
227
- font-size: 30px; font-weight: 500;
228
- margin: 0 0 12px;
229
- letter-spacing: -0.01em; line-height: 1.2;
230
- background: transparent; border: none; outline: none;
231
- color: var(--color-text1);
232
- width: 100%; padding: 2px 4px; border-radius: 3px;
233
- }
234
- .nbe-title:focus { background: var(--color-bg); }
235
- .nbe-history-panel { margin: 0 0 14px; }
236
-
237
- .nbe-cells { display: flex; flex-direction: column; gap: 14px; }
238
- .nbe-cell { position: relative; padding-left: 28px; }
239
- .nbe-handle { position: absolute; left: 0; top: 6px; }
240
- .nbe-del-abs {
241
- position: absolute; top: 4px; right: 4px;
242
- opacity: 0; transition: opacity 0.15s;
243
- }
244
- .nbe-cell:hover .nbe-del-abs { opacity: 0.5; }
245
- .nbe-del-abs:hover { opacity: 1 !important; }
246
-
247
- .nbe-prose {
248
- font-family: 'EB Garamond', Georgia, serif;
249
- font-size: 17px; line-height: 1.7;
250
- color: var(--color-text1);
251
- max-width: 620px;
252
- outline: none;
253
- padding: 2px 4px;
254
- border-radius: 3px;
255
- border: 1px dashed transparent;
256
- }
257
- .nbe-prose:focus { border-color: var(--color-border); background: var(--color-bg); }
258
- .nbe-prose code {
259
- font-family: var(--font-mono, 'IBM Plex Mono', monospace);
260
- font-size: 14px;
261
- background: var(--color-surface2);
262
- padding: 1px 6px; border-radius: 3px;
263
- color: var(--color-accent);
264
- }
265
- .nbe-prose mark {
266
- background: rgba(240,160,80,0.18);
267
- color: var(--color-amber);
268
- padding: 0 4px; border-radius: 2px;
269
- }
270
-
271
- .nbe-code-cell {
272
- background: var(--color-surface2);
273
- border: 1px solid var(--color-border);
274
- border-radius: 8px;
275
- overflow: hidden;
276
- }
277
- .nbe-cell-head {
278
- display: flex; align-items: center; gap: 8px;
279
- padding: 7px 12px;
280
- border-bottom: 1px solid var(--color-border);
281
- font-family: var(--font-mono, 'IBM Plex Mono', monospace);
282
- font-size: 10px; color: var(--color-text2);
283
- letter-spacing: 0.06em;
284
- }
285
- .nbe-type-sql { color: var(--color-accent); text-transform: uppercase; letter-spacing: 0.08em; }
286
- .nbe-type-js { color: var(--color-teal); text-transform: uppercase; letter-spacing: 0.08em; }
287
- .nbe-meta-info { margin-right: auto; }
288
- .nbe-actions { display: flex; gap: 4px; }
289
- .nbe-code-body { padding: 14px 16px; }
290
- .nbe-hidden { display: none !important; }
291
-
292
- .nbe-sql-result {
293
- background: var(--color-bg);
294
- padding: 10px 16px;
295
- border-top: 1px solid var(--color-border);
296
- }
297
- .nbe-sql-result table {
298
- width: 100%; border-collapse: collapse;
299
- font-family: var(--font-mono, 'IBM Plex Mono', monospace);
300
- font-size: 12px; font-variant-numeric: tabular-nums;
301
- }
302
- .nbe-sql-result table td { padding: 3px 0; color: var(--color-text1); }
303
- .nbe-sql-result table td:first-child { color: var(--color-text2); }
304
- .nbe-sql-result table td:last-child { text-align: right; }
305
-
306
- .nbe-chart {
307
- display: flex; align-items: flex-end; gap: 14px;
308
- height: 180px;
309
- padding: 16px;
310
- border-top: 1px solid var(--color-border);
311
- background: var(--color-bg);
312
- }
313
- .nbe-col {
314
- flex: 1; display: flex; flex-direction: column; align-items: center; gap: 5px;
315
- height: 100%; justify-content: flex-end;
316
- }
317
- .nbe-val {
318
- font-family: var(--font-mono, 'IBM Plex Mono', monospace);
319
- font-size: 11px; color: var(--color-text2);
320
- font-variant-numeric: tabular-nums;
321
- }
322
- .nbe-bar { width: 100%; background: var(--color-accent); border-radius: 2px 2px 0 0; }
323
- .nbe-chart-axis {
324
- display: flex; gap: 14px;
325
- padding: 6px 16px 10px;
326
- font-family: var(--font-mono, 'IBM Plex Mono', monospace);
327
- font-size: 11px; color: var(--color-text2);
328
- text-transform: uppercase; letter-spacing: 0.08em;
329
- background: var(--color-bg);
330
- }
331
- .nbe-chart-axis span { flex: 1; text-align: center; }
332
-
333
- .nbe-footer {
334
- display: flex; gap: 8px;
335
- padding-top: 16px; margin-top: 24px;
336
- border-top: 1px solid var(--color-border);
337
- align-items: center;
338
- }
339
- .nbe-fork {
340
- margin-left: auto;
341
- font-family: var(--font-mono, 'IBM Plex Mono', monospace);
342
- font-size: 11px; color: var(--color-text2);
343
- cursor: pointer;
344
- }
345
- .nbe-fork:hover { color: var(--color-accent); }
346
- `;
347
- document.head.appendChild(style);
348
- }
@@ -1,104 +0,0 @@
1
- ---
2
- widget: notebook-compact
3
- description: Reactive minimalist notebook with a left gutter showing cell types and named outputs. Cells chain via variable names (→ rows, → df) and display fresh/stale status. Minimal chrome, ideal for quick data exploration.
4
- schema:
5
- type: object
6
- properties:
7
- id:
8
- type: string
9
- description: Unique notebook identifier (for save/share via hyperskill)
10
- title:
11
- type: string
12
- description: Notebook title
13
- mode:
14
- type: string
15
- enum: [edit, view]
16
- description: edit allows run/edit/reorder; view hides all controls for read-only display
17
- cells:
18
- type: array
19
- description: Ordered list of cells (markdown, sql, js)
20
- items:
21
- type: object
22
- required: [type, content]
23
- properties:
24
- type:
25
- type: string
26
- enum: [md, sql, js]
27
- content:
28
- type: string
29
- description: Cell source (markdown text or code)
30
- name:
31
- type: string
32
- description: Optional cell name
33
- varname:
34
- type: string
35
- description: Named output variable (code cells only), displayed as → varname
36
- hideSource:
37
- type: boolean
38
- hideResult:
39
- type: boolean
40
- ---
41
-
42
- ## When to use
43
-
44
- Use `notebook-compact` when the user wants to explore data quickly with a minimal, reactive interface. Ideal for:
45
- - Iterative SQL / JS exploration where output variables feed each other
46
- - Quick prototyping where chrome should fade into the background
47
- - Hackathon playgrounds where users expect a familiar "type, see, share" flow
48
- - Situations where the reactive dataflow (fresh/stale status) helps the user reason about stale results
49
-
50
- Prefer over `notebook-workspace` when there is no multi-source concern and no publish/dashboard end goal.
51
- Prefer over `notebook-document` when collaboration and comments are not central.
52
- Prefer over `notebook-editorial` when the result is a working session, not a publication.
53
-
54
- ## How to use
55
-
56
- 1. **Create the widget** with a title and 2–4 seed cells:
57
- ```
58
- widget_display({name: "notebook-compact", params: {
59
- title: "Exploration",
60
- cells: [
61
- {type: "md", content: "### Quick look\n\nLet's inspect the data."},
62
- {type: "sql", content: "select * from source limit 10", varname: "rows"},
63
- {type: "js", content: "console.table(rows)"}
64
- ]
65
- }})
66
- ```
67
-
68
- 2. **Name outputs** (varname) on the SQL/JS cells so the gutter displays `→ varname` and downstream cells can reference them.
69
-
70
- 3. **Start markdown cells with a heading** (`### Title`) — the first line renders larger and gives the cell an anchor.
71
-
72
- 4. **Let the user iterate** — they can add cells with `+ md / + sql / + js`, reorder via the drag handle, toggle source/result, and run cells with the green pill button.
73
-
74
- ## Notes
75
-
76
- - The Run button lives at the left of each cell's title row, right after the type label.
77
- - Stop (red pill) appears while running, with a live elapsed timer.
78
- - After a run, Run becomes the replay button (same green pill, re-click to re-run).
79
- - Deletions prompt a confirmation modal and are recorded in the history panel; they can be restored from there.
80
- - `mode: "view"` hides all controls (run, delete, drag, add), making the notebook read-only.
81
-
82
- ## Integration with connected data servers
83
-
84
- If a MCP **data** server is connected (e.g. `tricoteuses`, `metmuseum`), BEFORE seeding cells:
85
-
86
- 1. Call `{server}_list_recipes()` or `{server}_search_recipes(query)` to find recipes that match the user's intent.
87
- 2. Call `{server}_list_tools()` to see available tables/endpoints.
88
- 3. For each high-signal recipe or table, seed ONE cell that demonstrates it: an SQL `SELECT ... LIMIT 10`, a `run_script` call, or a short markdown note.
89
- 4. Pass the server metadata via the `servers:` param so the UI can render the server menu modal:
90
-
91
- ```ts
92
- widget_display({
93
- name: 'notebook-compact',
94
- params: {
95
- title: '...',
96
- cells: [...],
97
- servers: [{ name: 'tricoteuses', url: 'https://...', kind: 'data' }]
98
- }
99
- })
100
- ```
101
-
102
- If NO data server is connected, seed generic markdown-only cells that explain the notebook's purpose and invite the user to connect an MCP server.
103
-
104
- **Filter rule**: only include MCP *data* servers (`kind: 'data'`) in `servers:`. Do NOT include WebMCP UI servers like `autoui` — they don't expose queryable data and would clutter the menu.
@@ -1,100 +0,0 @@
1
- ---
2
- widget: notebook-document
3
- description: Collaborative notebook styled as a shared document. Avatars of editors at the top, inline highlights in prose, optional margin comments next to cells. Minimal cell chrome, reads as a report with conversation around it.
4
- schema:
5
- type: object
6
- properties:
7
- id:
8
- type: string
9
- title:
10
- type: string
11
- mode:
12
- type: string
13
- enum: [edit, view]
14
- cells:
15
- type: array
16
- items:
17
- type: object
18
- required: [type, content]
19
- properties:
20
- type:
21
- type: string
22
- enum: [md, sql, js]
23
- content:
24
- type: string
25
- description: For md cells, HTML is supported (use <mark> for inline highlights).
26
- hideSource:
27
- type: boolean
28
- hideResult:
29
- type: boolean
30
- comment:
31
- type: object
32
- description: Optional margin comment attached to a code cell
33
- properties:
34
- who:
35
- type: string
36
- when:
37
- type: string
38
- description: Relative time shown as-is (e.g. "2m", "just now")
39
- body:
40
- type: string
41
- ---
42
-
43
- ## When to use
44
-
45
- Use `notebook-document` when the notebook is meant to be read and discussed by a team:
46
- - Shared analyses where colleagues leave margin comments on specific cells
47
- - Onboarding guides, knowledge docs, incident retros
48
- - Any context where the notebook should feel like a Google Doc / Notion page, not a developer tool
49
-
50
- Prose cells can contain `<mark>` highlights inline to draw attention to a sentence, and code cells can carry an optional margin comment object showing who commented and when.
51
-
52
- ## How to use
53
-
54
- 1. **Create with prose-heavy seed content** and inline highlights for emphasis:
55
- ```
56
- widget_display({name: "notebook-document", params: {
57
- title: "Weekly review",
58
- cells: [
59
- {type: "md", content: "Here is this week's summary. <mark>Pay attention to this metric.</mark> More context follows."},
60
- {type: "sql", content: "select ...", comment: {who: "reviewer", when: "2m", body: "Can we filter X here?"}},
61
- {type: "js", content: "// visualization"}
62
- ]
63
- }})
64
- ```
65
-
66
- 2. **Attach comments to code cells** by adding a `comment` object. The cell row splits into two columns (cell + comment) only when a comment exists.
67
-
68
- 3. **Keep prose short** — the layout punishes walls of text. Break them into several markdown cells with their own handles.
69
-
70
- ## Notes
71
-
72
- - Every cell (prose or code) has its own drag handle for reordering.
73
- - Comments can only be attached to code cells at creation time in the current widget version; prose cells do not carry comments.
74
- - `<mark>` inside markdown renders with an amber tint; use sparingly.
75
- - The footer shows an "invite · share" link that opens the share modal.
76
- - Like the other notebook widgets, `mode: "view"` removes all editing and running controls.
77
-
78
- ## Integration with connected data servers
79
-
80
- When a MCP **data** server is connected (for instance `tricoteuses` or `metmuseum`), the document should open on real material rather than empty placeholders. Before seeding cells, the agent takes a short discovery pass:
81
-
82
- 1. It calls `{server}_list_recipes()` or `{server}_search_recipes(query)` to locate recipes aligned with the user's topic.
83
- 2. It calls `{server}_list_tools()` to learn which tables and endpoints the server exposes.
84
- 3. For each relevant recipe or table, it seeds a single cell that grounds the document — a short SQL `SELECT ... LIMIT 10`, a `run_script` call, or a prose cell that introduces what the data shows. A margin `comment` on a code cell is a natural way to flag an open question for collaborators.
85
- 4. It passes the server metadata through the `servers:` param so the share/connect affordances know what is in play:
86
-
87
- ```ts
88
- widget_display({
89
- name: 'notebook-document',
90
- params: {
91
- title: '...',
92
- cells: [...],
93
- servers: [{ name: 'tricoteuses', url: 'https://...', kind: 'data' }]
94
- }
95
- })
96
- ```
97
-
98
- When no data server is connected, seed a prose-only skeleton that frames the discussion and invites the reader (or a colleague) to link an MCP server.
99
-
100
- **Filter rule**: only MCP *data* servers (`kind: 'data'`) appear in `servers:`. WebMCP UI servers such as `autoui` are excluded — they carry no queryable data and would clutter the collaborative view.
@@ -1,104 +0,0 @@
1
- ---
2
- widget: notebook-editorial
3
- description: Publication-ready notebook with serif prose and inline cells, all drag-and-droppable in a single ordered flow. Inspired by Observable — cells can be prose paragraphs, sql queries, or js charts, mixed freely in any order to build an article-like narrative.
4
- schema:
5
- type: object
6
- properties:
7
- id:
8
- type: string
9
- title:
10
- type: string
11
- mode:
12
- type: string
13
- enum: [edit, view]
14
- kicker:
15
- type: string
16
- description: Small uppercase label above the title (e.g. "analysis", "memo", "brief"). Defaults to "untitled".
17
- forkId:
18
- type: string
19
- description: Short identifier shown in the footer next to "share" (e.g. "4c7a·9f21"). Defaults to a slice of the notebook id.
20
- cells:
21
- type: array
22
- description: Mixed flow of prose and code cells. All share the same ordering and can be reordered together.
23
- items:
24
- type: object
25
- required: [type, content]
26
- properties:
27
- type:
28
- type: string
29
- enum: [md, sql, js]
30
- description: md = prose paragraph, sql = query cell with table output, js = code cell with chart output
31
- content:
32
- type: string
33
- hideSource:
34
- type: boolean
35
- hideResult:
36
- type: boolean
37
- ---
38
-
39
- ## When to use
40
-
41
- Use `notebook-editorial` when the notebook is meant to be published or shared as a finished artifact:
42
- - Research memos with code appendices visible on demand
43
- - Blog-style writeups mixing narrative and runnable code
44
- - Final deliverables where prose leads and code supports
45
-
46
- The distinguishing feature: prose paragraphs and code cells share a single ordered list, both drag-and-droppable with the same handle. This lets users rearrange the story freely without thinking about "sections".
47
-
48
- ## How to use
49
-
50
- 1. **Start with prose-first seed content** and intersperse code cells:
51
- ```
52
- widget_display({name: "notebook-editorial", params: {
53
- title: "Q3 observations",
54
- kicker: "memo",
55
- cells: [
56
- {type: "md", content: "This memo covers the highlights of last quarter."},
57
- {type: "md", content: "We first look at revenue, then at churn."},
58
- {type: "sql", content: "select * from source limit 10"},
59
- {type: "md", content: "The table above suggests..."},
60
- {type: "js", content: "// render a chart"}
61
- ]
62
- }})
63
- ```
64
-
65
- 2. **Use prose paragraphs as transitions** between code blocks. The layout emphasizes reading flow.
66
-
67
- 3. **Code cells render their result in the editorial style**:
68
- - SQL cells show a minimal mono-spaced table
69
- - JS cells show a chart area with labeled bars
70
-
71
- 4. **Reorder cells freely** — the user can drag a prose paragraph from the bottom to the top, or swap a chart and its introduction, all via the same handle.
72
-
73
- ## Notes
74
-
75
- - The serif font (EB Garamond, with Georgia fallback) applies only to prose content inside this widget — it signals "publication" the moment the user sees it.
76
- - The kicker above the title ("analysis", "memo", "internal") is editorial shorthand; keep it short.
77
- - The footer shows `share · forkId`, which is clickable to open the share modal.
78
- - Run / Stop controls are at the left of each code cell's header, same as the other notebook layouts.
79
- - Unlike the other widgets, `notebook-editorial` does not separate prose and code into different flows — they are the same flow in one list.
80
-
81
- ## Integration with connected data servers
82
-
83
- An editorial piece earns its weight when the prose is anchored to real material. If a MCP **data** server is connected (say `tricoteuses` or `metmuseum`), the agent should — before composing the memo — go and see what the server has to offer:
84
-
85
- 1. Call `{server}_list_recipes()` or `{server}_search_recipes(query)` to find recipes that speak to the subject at hand.
86
- 2. Call `{server}_list_tools()` to survey the available tables and endpoints.
87
- 3. For each recipe or table worth citing, seed one cell that lets the reader touch the evidence — a modest SQL `SELECT ... LIMIT 10`, a `run_script` call, or a prose paragraph that introduces the figure to come. Let prose and code alternate; the editorial flow is built for exactly that.
88
- 4. Pass the server metadata through the `servers:` param so the footer's share affordance and the connect modal reflect the provenance of the piece:
89
-
90
- ```ts
91
- widget_display({
92
- name: 'notebook-editorial',
93
- params: {
94
- title: '...',
95
- kicker: 'memo',
96
- cells: [...],
97
- servers: [{ name: 'tricoteuses', url: 'https://...', kind: 'data' }]
98
- }
99
- })
100
- ```
101
-
102
- When no data server is connected, seed a prose-first skeleton that stakes out the argument and gently invites the reader to connect an MCP server so the memo can take on flesh.
103
-
104
- **Filter rule**: only MCP *data* servers (`kind: 'data'`) belong in `servers:`. WebMCP UI servers like `autoui` are kept out — they hold no queryable material and have no place in the editorial masthead.