@nowline/mcp 0.6.0 → 0.7.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.
- package/dist/capabilities.d.ts +9 -0
- package/dist/capabilities.d.ts.map +1 -0
- package/dist/capabilities.js +17 -0
- package/dist/capabilities.js.map +1 -0
- package/dist/generated/resources.d.ts +2 -0
- package/dist/generated/resources.d.ts.map +1 -1
- package/dist/generated/resources.js +6 -1
- package/dist/generated/resources.js.map +1 -1
- package/dist/generated/ui-bundle.d.ts +3 -0
- package/dist/generated/ui-bundle.d.ts.map +1 -0
- package/dist/generated/ui-bundle.js +10 -0
- package/dist/generated/ui-bundle.js.map +1 -0
- package/dist/index.js +63 -12
- package/dist/index.js.map +1 -1
- package/dist/prompts.d.ts +3 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +143 -0
- package/dist/prompts.js.map +1 -0
- package/dist/schemas.d.ts +96 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/schemas.js +65 -0
- package/dist/schemas.js.map +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +304 -41
- package/dist/server.js.map +1 -1
- package/dist/ui/entry.d.ts +2 -0
- package/dist/ui/entry.d.ts.map +1 -0
- package/dist/ui/entry.js +149 -0
- package/dist/ui/entry.js.map +1 -0
- package/package.json +11 -6
- package/src/capabilities.ts +25 -0
- package/src/generated/resources.ts +7 -1
- package/src/generated/ui-bundle.ts +10 -0
- package/src/index.ts +75 -13
- package/src/prompts.ts +172 -0
- package/src/schemas.ts +79 -0
- package/src/server.ts +406 -39
- package/src/ui/entry.ts +169 -0
package/src/ui/entry.ts
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
// Browser entry bundled into the MCP Apps in-chat live preview.
|
|
2
|
+
//
|
|
3
|
+
// Unlike the VS Code webview entry — which receives pre-rendered SVG from
|
|
4
|
+
// the extension host over postMessage — this entry runs standalone inside
|
|
5
|
+
// the MCP host's sandboxed iframe. It reads the .nowline source the server
|
|
6
|
+
// injected as a JSON <script> (#nl-preview-data), renders it to SVG in the
|
|
7
|
+
// browser via `renderSource` (@nowline/browser, the same parse → layout →
|
|
8
|
+
// render pipeline the embed CDN ships), and mounts the shared preview
|
|
9
|
+
// viewport via `mountPreview` (@nowline/preview-shell). No host transport
|
|
10
|
+
// is assumed, so the preview works in any MCP Apps host that renders the
|
|
11
|
+
// embedded text/html resource.
|
|
12
|
+
//
|
|
13
|
+
// Mirrors packages/vscode-extension/src/preview/webview/entry.ts; the seam
|
|
14
|
+
// is that this entry owns the render (no host editor feeding it SVG), so
|
|
15
|
+
// theme / now / show-links changes re-render in-place via renderSource.
|
|
16
|
+
|
|
17
|
+
// Runs in a browser-like iframe, not Node. Pull in the DOM lib only here
|
|
18
|
+
// (the rest of @nowline/mcp is Node) — mirrors the VS Code webview entry.
|
|
19
|
+
/// <reference lib="dom" />
|
|
20
|
+
|
|
21
|
+
import { renderSource } from '@nowline/browser';
|
|
22
|
+
import {
|
|
23
|
+
mountPreview,
|
|
24
|
+
type NowOverride,
|
|
25
|
+
type PreviewHandle,
|
|
26
|
+
type ThemeOverride,
|
|
27
|
+
} from '@nowline/preview-shell';
|
|
28
|
+
|
|
29
|
+
/** Server-injected render inputs (see buildPreviewHtml in server.ts). */
|
|
30
|
+
interface PreviewPayload {
|
|
31
|
+
source: string;
|
|
32
|
+
theme?: string;
|
|
33
|
+
now?: string;
|
|
34
|
+
width?: number;
|
|
35
|
+
locale?: string;
|
|
36
|
+
showLinks?: boolean;
|
|
37
|
+
showMinimap?: boolean;
|
|
38
|
+
initialFit?: 'fitPage' | 'fitWidth' | 'actual';
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
type DiagramTheme = 'light' | 'dark' | 'grayscale';
|
|
42
|
+
|
|
43
|
+
function readPayload(): PreviewPayload | undefined {
|
|
44
|
+
const el = document.getElementById('nl-preview-data');
|
|
45
|
+
if (!el?.textContent) return undefined;
|
|
46
|
+
try {
|
|
47
|
+
return JSON.parse(el.textContent) as PreviewPayload;
|
|
48
|
+
} catch {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Coerce a raw theme token to the shell's ThemeOverride (defaults to Auto). */
|
|
54
|
+
function toThemeOverride(theme: string | undefined): ThemeOverride {
|
|
55
|
+
switch (theme) {
|
|
56
|
+
case 'light':
|
|
57
|
+
case 'dark':
|
|
58
|
+
case 'grayscale':
|
|
59
|
+
case 'greyscale':
|
|
60
|
+
case 'auto':
|
|
61
|
+
return theme;
|
|
62
|
+
default:
|
|
63
|
+
return 'auto';
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Map a shell ThemeOverride to a renderer ThemeName (Auto → renderer default). */
|
|
68
|
+
function toDiagramTheme(theme: ThemeOverride): DiagramTheme | undefined {
|
|
69
|
+
switch (theme) {
|
|
70
|
+
case 'light':
|
|
71
|
+
return 'light';
|
|
72
|
+
case 'dark':
|
|
73
|
+
return 'dark';
|
|
74
|
+
case 'grayscale':
|
|
75
|
+
case 'greyscale':
|
|
76
|
+
return 'grayscale';
|
|
77
|
+
default:
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Map a shell NowOverride to the renderSource `today` input. */
|
|
83
|
+
function toToday(now: NowOverride): Date | string | null | undefined {
|
|
84
|
+
if (now === 'today') return undefined;
|
|
85
|
+
if (now === 'hide' || now === 'none') return null;
|
|
86
|
+
return now;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function bootstrap(): void {
|
|
90
|
+
const root = document.getElementById('nl-preview-root');
|
|
91
|
+
if (!root) {
|
|
92
|
+
console.error('nowline mcp preview: #nl-preview-root missing');
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const payload = readPayload();
|
|
96
|
+
if (!payload || typeof payload.source !== 'string') {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const { source, width, locale } = payload;
|
|
100
|
+
|
|
101
|
+
// Live view state — seeded from the server payload, mutated by the
|
|
102
|
+
// shell's view-options menu, and read on every re-render.
|
|
103
|
+
const current = {
|
|
104
|
+
theme: toThemeOverride(payload.theme),
|
|
105
|
+
now: (payload.now ?? 'today') as NowOverride,
|
|
106
|
+
showLinks: payload.showLinks !== false,
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
let handle: PreviewHandle | undefined;
|
|
110
|
+
|
|
111
|
+
async function render(): Promise<void> {
|
|
112
|
+
if (!handle) return;
|
|
113
|
+
try {
|
|
114
|
+
const result = await renderSource(source, {
|
|
115
|
+
theme: toDiagramTheme(current.theme),
|
|
116
|
+
today: toToday(current.now),
|
|
117
|
+
width,
|
|
118
|
+
locale,
|
|
119
|
+
showLinks: current.showLinks,
|
|
120
|
+
});
|
|
121
|
+
if (result.kind === 'svg') {
|
|
122
|
+
handle.setSvg(result.svg);
|
|
123
|
+
handle.setDiagnostics(result.warnings);
|
|
124
|
+
} else {
|
|
125
|
+
handle.setDiagnostics(result.diagnostics);
|
|
126
|
+
}
|
|
127
|
+
} catch (err) {
|
|
128
|
+
handle.setFatal(err instanceof Error ? err.message : String(err));
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
handle = mountPreview(root, {
|
|
133
|
+
themeControl: 'show',
|
|
134
|
+
locale,
|
|
135
|
+
initialFit: payload.initialFit,
|
|
136
|
+
showMinimap: payload.showMinimap,
|
|
137
|
+
viewBaseline: {
|
|
138
|
+
theme: current.theme,
|
|
139
|
+
now: current.now,
|
|
140
|
+
showLinks: current.showLinks,
|
|
141
|
+
},
|
|
142
|
+
onViewOptions: (overrides) => {
|
|
143
|
+
if (overrides.theme !== undefined) current.theme = overrides.theme;
|
|
144
|
+
if (overrides.now !== undefined) current.now = overrides.now;
|
|
145
|
+
if (overrides.showLinks !== undefined) current.showLinks = overrides.showLinks;
|
|
146
|
+
void render();
|
|
147
|
+
},
|
|
148
|
+
onSave: (req) => {
|
|
149
|
+
// Best-effort in-iframe download; the host's iframe sandbox may
|
|
150
|
+
// block it, in which case the copy actions remain available.
|
|
151
|
+
try {
|
|
152
|
+
const type = req.format === 'png' ? 'image/png' : 'image/svg+xml';
|
|
153
|
+
const blob = new Blob([req.body as BlobPart], { type });
|
|
154
|
+
const url = URL.createObjectURL(blob);
|
|
155
|
+
const a = document.createElement('a');
|
|
156
|
+
a.href = url;
|
|
157
|
+
a.download = `roadmap.${req.format}`;
|
|
158
|
+
a.click();
|
|
159
|
+
setTimeout(() => URL.revokeObjectURL(url), 1000);
|
|
160
|
+
} catch {
|
|
161
|
+
// Download blocked by the sandbox — nothing to do.
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
void render();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
bootstrap();
|