@webmcp-auto-ui/ui 2.5.29 → 2.5.31
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
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
addImportedCells, registerExecutor, collectDataServers,
|
|
12
12
|
autosize, openShareModal, registerHistoryObserver,
|
|
13
13
|
renderCellLogs,
|
|
14
|
-
createPublishControls, autoConnectFrontmatterServers,
|
|
14
|
+
createPublishControls, autoConnectFrontmatterServers, bootstrapMcpBridge,
|
|
15
15
|
createRuntimeOverlay, effectiveResult, cellRuntimeStatus,
|
|
16
16
|
lastRefreshedAt, bootstrapLiveRefresh, fmtRelTime, preserveScrollAround,
|
|
17
17
|
type NotebookState, type NotebookCell, type CellResult, type CellExecContext,
|
|
@@ -289,6 +289,10 @@ export async function render(container: HTMLElement, data: Record<string, unknow
|
|
|
289
289
|
// Auto-connect data servers declared in the recipe frontmatter (data.servers)
|
|
290
290
|
autoConnectFrontmatterServers(data, () => pane.setServers(collectDataServers(data)));
|
|
291
291
|
|
|
292
|
+
// Start a persistent MCP bridge so the sql executor can find tools in edit mode
|
|
293
|
+
// too (not just when live-refresh is running in view mode).
|
|
294
|
+
const mcpBridgeCleanup = bootstrapMcpBridge({ data, MultiMcpBridgeCtor: MultiMcpBridge as any });
|
|
295
|
+
|
|
292
296
|
// Keep pane servers in sync with canvas changes
|
|
293
297
|
let canvasUnsub: (() => void) | null = null;
|
|
294
298
|
try {
|
|
@@ -315,6 +319,7 @@ export async function render(container: HTMLElement, data: Record<string, unknow
|
|
|
315
319
|
pane.destroy();
|
|
316
320
|
publishCleanup();
|
|
317
321
|
liveCleanup?.();
|
|
322
|
+
mcpBridgeCleanup();
|
|
318
323
|
};
|
|
319
324
|
}
|
|
320
325
|
|
|
@@ -207,12 +207,15 @@ function walk(node: Node): void {
|
|
|
207
207
|
// via turndown on input (debounced) and at blur.
|
|
208
208
|
// ---------------------------------------------------------------------------
|
|
209
209
|
|
|
210
|
-
//
|
|
211
|
-
|
|
212
|
-
|
|
210
|
+
// turndown is loaded lazily (browser-only). Top-level import breaks SSR because
|
|
211
|
+
// turndown's CJS internals use require() which throws in ESM scope.
|
|
213
212
|
let _td: any = null;
|
|
214
|
-
function
|
|
213
|
+
async function ensureTd(): Promise<any> {
|
|
215
214
|
if (_td) return _td;
|
|
215
|
+
if (typeof window === 'undefined') return null;
|
|
216
|
+
// @ts-ignore — turndown ships its own types but we stay ts-nocheck here
|
|
217
|
+
const mod = await import('turndown');
|
|
218
|
+
const TurndownService = mod.default || mod;
|
|
216
219
|
_td = new TurndownService({
|
|
217
220
|
headingStyle: 'atx',
|
|
218
221
|
hr: '---',
|
|
@@ -230,8 +233,11 @@ function td(): any {
|
|
|
230
233
|
return _td;
|
|
231
234
|
}
|
|
232
235
|
|
|
233
|
-
function htmlToMd(html: string): string {
|
|
234
|
-
try {
|
|
236
|
+
async function htmlToMd(html: string): Promise<string> {
|
|
237
|
+
try {
|
|
238
|
+
const t = await ensureTd();
|
|
239
|
+
return t ? t.turndown(html || '') : '';
|
|
240
|
+
} catch { return ''; }
|
|
235
241
|
}
|
|
236
242
|
|
|
237
243
|
function ensureToolbarStyles(): void {
|
|
@@ -442,9 +448,9 @@ export function mountEditableProse(opts: {
|
|
|
442
448
|
flushToMd();
|
|
443
449
|
}, 400);
|
|
444
450
|
};
|
|
445
|
-
const flushToMd = () => {
|
|
451
|
+
const flushToMd = async () => {
|
|
446
452
|
const html = host.innerHTML;
|
|
447
|
-
const md = htmlToMd(html);
|
|
453
|
+
const md = await htmlToMd(html);
|
|
448
454
|
opts.setContent(md);
|
|
449
455
|
opts.onChange?.();
|
|
450
456
|
updateEmptyState(host);
|
|
@@ -491,10 +497,11 @@ export function mountEditableProse(opts: {
|
|
|
491
497
|
if (html) {
|
|
492
498
|
e.preventDefault();
|
|
493
499
|
// Strip inline styles by routing via turndown → re-render via our MD pipeline
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
500
|
+
htmlToMd(html).then((md) => {
|
|
501
|
+
const cleanHtml = renderProse(md);
|
|
502
|
+
document.execCommand('insertHTML', false, cleanHtml);
|
|
503
|
+
scheduleSync();
|
|
504
|
+
});
|
|
498
505
|
} else if (text) {
|
|
499
506
|
// Plain text paste — default behaviour fine, but still trigger sync
|
|
500
507
|
scheduleSync();
|
|
@@ -128,8 +128,19 @@ export function preserveScrollAround(anchor: HTMLElement): () => void {
|
|
|
128
128
|
}
|
|
129
129
|
const winY = window.scrollY;
|
|
130
130
|
const saved = scrollParents.map((el) => el.scrollTop);
|
|
131
|
+
// Only capture the active cell if the user was actually editing
|
|
132
|
+
// (textarea or contentEditable). Clicking a button or label should
|
|
133
|
+
// NOT trigger a post-rerender refocus, which would scroll the page
|
|
134
|
+
// to that cell's input.
|
|
131
135
|
const activeEl = document.activeElement as HTMLElement | null;
|
|
132
|
-
const
|
|
136
|
+
const isEditing = !!activeEl && (
|
|
137
|
+
activeEl.tagName === 'TEXTAREA' ||
|
|
138
|
+
activeEl.tagName === 'INPUT' ||
|
|
139
|
+
activeEl.isContentEditable
|
|
140
|
+
);
|
|
141
|
+
const activeCellId = isEditing
|
|
142
|
+
? activeEl?.closest<HTMLElement>('[data-cell-id]')?.dataset.cellId ?? null
|
|
143
|
+
: null;
|
|
133
144
|
|
|
134
145
|
return () => {
|
|
135
146
|
requestAnimationFrame(() => {
|
|
@@ -844,6 +855,35 @@ export function createPublishControls(state: NotebookState, opts: PublishControl
|
|
|
844
855
|
};
|
|
845
856
|
}
|
|
846
857
|
|
|
858
|
+
/**
|
|
859
|
+
* Start a persistent MCP bridge that connects declared data servers and keeps
|
|
860
|
+
* their tool/recipe metadata populated on the canvas store. Independent of
|
|
861
|
+
* live-refresh — this runs even in edit mode, so the sql executor can discover
|
|
862
|
+
* `*_query_sql` tools as soon as the bridge is connected.
|
|
863
|
+
*
|
|
864
|
+
* Returns a cleanup function. Caller is expected to start this once at mount
|
|
865
|
+
* and call cleanup on unmount.
|
|
866
|
+
*/
|
|
867
|
+
export function bootstrapMcpBridge(opts: {
|
|
868
|
+
data: Record<string, unknown>;
|
|
869
|
+
MultiMcpBridgeCtor: new (opts: { getCanvas: () => unknown }) => {
|
|
870
|
+
start(): void;
|
|
871
|
+
stop(): void;
|
|
872
|
+
};
|
|
873
|
+
}): () => void {
|
|
874
|
+
try {
|
|
875
|
+
autoConnectFrontmatterServers(opts.data);
|
|
876
|
+
const canvas: unknown = (globalThis as { __canvasVanilla?: unknown; canvasVanilla?: unknown })
|
|
877
|
+
.__canvasVanilla ?? (globalThis as { canvasVanilla?: unknown }).canvasVanilla;
|
|
878
|
+
if (!canvas) return () => { /* no-op */ };
|
|
879
|
+
const bridge = new opts.MultiMcpBridgeCtor({ getCanvas: () => canvas });
|
|
880
|
+
bridge.start();
|
|
881
|
+
return () => { try { bridge.stop(); } catch { /* ignore */ } };
|
|
882
|
+
} catch {
|
|
883
|
+
return () => { /* no-op */ };
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
|
|
847
887
|
/**
|
|
848
888
|
* Auto-connect any data servers declared in recipe frontmatter (`data.servers`)
|
|
849
889
|
* to the shared canvas store. No-op / no-throw if the canvas store is absent.
|