@webmcp-auto-ui/sdk 2.5.38 → 2.5.39
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 +1 -1
- package/src/canvas-to-notebook.ts +55 -2
- package/src/index.ts +3 -2
- package/src/recipes/fence.ts +12 -0
- package/src/recipes/index.ts +1 -0
- package/src/recipes/parse.ts +6 -3
package/package.json
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
* present, snapshot fallback otherwise.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import { pickFence } from './recipes/fence.js';
|
|
8
|
+
|
|
7
9
|
export interface WidgetLineage {
|
|
8
10
|
widgetType: string;
|
|
9
11
|
widgetParams: Record<string, unknown>;
|
|
@@ -138,13 +140,64 @@ export function canvasToNotebookMarkdown(
|
|
|
138
140
|
return fm.join('\n') + '\n' + body.join('\n').replace(/\n+$/, '') + '\n';
|
|
139
141
|
}
|
|
140
142
|
|
|
143
|
+
export interface CanvasToNotebookData {
|
|
144
|
+
title?: string;
|
|
145
|
+
description?: string;
|
|
146
|
+
/** Active MCP servers (name, url) — to seed the notebook widget's data. */
|
|
147
|
+
servers?: Array<{ name: string; url: string }>;
|
|
148
|
+
/** Active bundled WebMCP server ids. */
|
|
149
|
+
webmcpServers?: string[];
|
|
150
|
+
/** Cells ready to feed into a NotebookState. */
|
|
151
|
+
cells: Array<{
|
|
152
|
+
id: string;
|
|
153
|
+
type: 'md' | 'sql' | 'js';
|
|
154
|
+
content: string;
|
|
155
|
+
varname?: string;
|
|
156
|
+
status?: 'idle';
|
|
157
|
+
}>;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** Same logic as canvasToNotebookMarkdown but returns structured cells for direct widget mounting. */
|
|
161
|
+
export function canvasToNotebookCells(
|
|
162
|
+
blocks: CanvasBlock[],
|
|
163
|
+
getLineage: (blockId: string) => WidgetLineage | null,
|
|
164
|
+
opts: CanvasToNotebookOptions = {},
|
|
165
|
+
): CanvasToNotebookData {
|
|
166
|
+
let counter = 0;
|
|
167
|
+
const cells: CanvasToNotebookData['cells'] = [];
|
|
168
|
+
|
|
169
|
+
for (const block of blocks) {
|
|
170
|
+
const raw = lineageToCells(block, getLineage(block.id));
|
|
171
|
+
for (const cell of raw) {
|
|
172
|
+
const entry: CanvasToNotebookData['cells'][number] = {
|
|
173
|
+
id: `c${counter++}`,
|
|
174
|
+
type: cell.kind,
|
|
175
|
+
content: cell.content,
|
|
176
|
+
status: 'idle',
|
|
177
|
+
};
|
|
178
|
+
if (cell.varname !== undefined) entry.varname = cell.varname;
|
|
179
|
+
cells.push(entry);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const out: CanvasToNotebookData = { cells };
|
|
184
|
+
if (opts.title) out.title = opts.title;
|
|
185
|
+
if (opts.description) out.description = opts.description;
|
|
186
|
+
if (opts.servers && opts.servers.length > 0) out.servers = opts.servers;
|
|
187
|
+
if (opts.webmcpServers && opts.webmcpServers.length > 0) out.webmcpServers = opts.webmcpServers;
|
|
188
|
+
return out;
|
|
189
|
+
}
|
|
190
|
+
|
|
141
191
|
function renderCell(cell: NotebookCell): string {
|
|
142
192
|
if (cell.kind === 'md') return cell.content;
|
|
143
193
|
if (cell.kind === 'sql') {
|
|
144
194
|
const meta = cell.varname ? `-- @meta {"varname": "${cell.varname}"}\n` : '';
|
|
145
|
-
|
|
195
|
+
const body = meta + cell.content;
|
|
196
|
+
const fence = pickFence(body);
|
|
197
|
+
return fence + 'sql\n' + body + '\n' + fence;
|
|
146
198
|
}
|
|
147
|
-
|
|
199
|
+
const fence = pickFence(cell.content);
|
|
200
|
+
return fence + 'js\n' + cell.content + '\n' + fence;
|
|
148
201
|
}
|
|
149
202
|
|
|
150
203
|
function pickArrayParamKey(params: Record<string, unknown>): string | null {
|
package/src/index.ts
CHANGED
|
@@ -91,7 +91,7 @@ export type { RemoteMcpRegistryEntry } from './remote-mcp-registry.js';
|
|
|
91
91
|
// import { canvas } from '@webmcp-auto-ui/sdk/canvas'
|
|
92
92
|
|
|
93
93
|
// Recipe runner — markdown-fence parser + JS/TS/SQL/etc executor over MCP
|
|
94
|
-
export { parseBody, runCode, estimateTokens, safeStringify, findCodeParamName, buildToolArgs, parseWidgetDisplayCall } from './recipes/index.js';
|
|
94
|
+
export { parseBody, pickFence, runCode, estimateTokens, safeStringify, findCodeParamName, buildToolArgs, parseWidgetDisplayCall } from './recipes/index.js';
|
|
95
95
|
export type { ParsedSegment, RunResult, RunLog, RunTab, RecipeData } from './recipes/index.js';
|
|
96
96
|
|
|
97
97
|
// Short URL — domain-dependent compact token
|
|
@@ -104,10 +104,11 @@ export { extractSampleData } from './widget-sample-data.js';
|
|
|
104
104
|
export { unwrap } from './unwrap.js';
|
|
105
105
|
|
|
106
106
|
// Canvas → HyperSkill notebook serializer
|
|
107
|
-
export { lineageToCells, canvasToNotebookMarkdown } from './canvas-to-notebook.js';
|
|
107
|
+
export { lineageToCells, canvasToNotebookMarkdown, canvasToNotebookCells } from './canvas-to-notebook.js';
|
|
108
108
|
export type {
|
|
109
109
|
WidgetLineage,
|
|
110
110
|
CanvasToNotebookOptions,
|
|
111
|
+
CanvasToNotebookData,
|
|
111
112
|
NotebookCell,
|
|
112
113
|
CanvasBlock,
|
|
113
114
|
} from './canvas-to-notebook.js';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pick a backtick fence longer than any run of backticks inside `content`.
|
|
3
|
+
* CommonMark rule: a fenced code block opens with N≥3 backticks; the content
|
|
4
|
+
* must not contain a closing fence of the same length, and the closing fence
|
|
5
|
+
* must be at least as long as the opening. Choosing N = max(3, longestRun+1)
|
|
6
|
+
* makes round-trips safe even when the cell content embeds Markdown fences.
|
|
7
|
+
*/
|
|
8
|
+
export function pickFence(content: string): string {
|
|
9
|
+
const runs = content.match(/`+/g) ?? [];
|
|
10
|
+
const longest = runs.reduce((m, r) => Math.max(m, r.length), 0);
|
|
11
|
+
return '`'.repeat(Math.max(3, longest + 1));
|
|
12
|
+
}
|
package/src/recipes/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { parseBody } from './parse.js';
|
|
2
|
+
export { pickFence } from './fence.js';
|
|
2
3
|
export { runCode, estimateTokens, safeStringify, findCodeParamName, buildToolArgs, parseWidgetDisplayCall } from './runner.js';
|
|
3
4
|
export type { ParsedSegment, RunResult, RunLog, RunTab, RecipeData } from './types.js';
|
package/src/recipes/parse.ts
CHANGED
|
@@ -16,14 +16,17 @@ export function parseBody(body: string): ParsedSegment[] {
|
|
|
16
16
|
|
|
17
17
|
const segments: ParsedSegment[] = [];
|
|
18
18
|
// Match fenced code blocks with optional language tag.
|
|
19
|
-
//
|
|
20
|
-
|
|
19
|
+
// Variable-length fence (≥3 backticks, CommonMark §4.5): the closing fence
|
|
20
|
+
// must use the same number of backticks as the opening — captured via \1.
|
|
21
|
+
// This allows cells whose content embeds ``` (e.g. widget('text', { content: "...```js..." }))
|
|
22
|
+
// to be encoded with a longer fence (4+ backticks) without premature closure.
|
|
23
|
+
const re = /(`{3,})([a-zA-Z0-9_+-]*)\r?\n([\s\S]*?)\r?\n\1[ \t]*(?=\r?\n|$)/g;
|
|
21
24
|
|
|
22
25
|
let lastIndex = 0;
|
|
23
26
|
let match: RegExpExecArray | null;
|
|
24
27
|
|
|
25
28
|
while ((match = re.exec(body)) !== null) {
|
|
26
|
-
const [full, langRaw, codeRaw] = match;
|
|
29
|
+
const [full, , langRaw, codeRaw] = match;
|
|
27
30
|
const start = match.index;
|
|
28
31
|
|
|
29
32
|
if (start > lastIndex) {
|