@mario.andreschak/mcp-filesystem 3.46.1 → 3.46.2

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 (2) hide show
  1. package/dist/resources.js +498 -498
  2. package/package.json +6 -1
package/dist/resources.js CHANGED
@@ -165,272 +165,272 @@ export function filesystemReadResource(uri) {
165
165
  * the sandbox proxy, which relays to the FLUJO host bridge). Kept dependency-
166
166
  * free and backtick-free so it embeds cleanly in this template string.
167
167
  */
168
- const FILESYSTEM_APP_HTML = `<!doctype html>
169
- <html>
170
- <head>
171
- <meta charset="utf-8" />
172
- <style>
173
- :root { --bg:#fff; --fg:#1a1a1a; --muted:#666; --border:#e0e0e0; --accent:#1565c0; --hover:#f5f5f5; }
174
- [data-theme="dark"] { --bg:#1e1e1e; --fg:#e8e8e8; --muted:#9e9e9e; --border:#3a3a3a; --accent:#64b5f6; --hover:#2a2a2a; }
175
- * { box-sizing: border-box; }
176
- html,body { margin:0; padding:0; }
177
- body { font: 13px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif; background:var(--bg); color:var(--fg); }
178
- #wrap { padding:10px; }
179
- .bar { display:flex; align-items:center; gap:6px; margin-bottom:8px; flex-wrap:wrap; }
180
- .bar button, .bar label { font:inherit; cursor:pointer; border:1px solid var(--border); background:var(--bg); color:var(--fg); border-radius:5px; padding:3px 9px; }
181
- .bar button:hover, .bar label:hover { background:var(--hover); }
182
- .path { font-family:ui-monospace,SFMono-Regular,Menlo,monospace; color:var(--muted); word-break:break-all; flex:1 1 200px; }
183
- ul { list-style:none; margin:0; padding:0; border:1px solid var(--border); border-radius:6px; overflow:hidden; }
184
- li { display:flex; align-items:center; gap:8px; padding:5px 10px; cursor:pointer; border-bottom:1px solid var(--border); }
185
- li:last-child { border-bottom:none; }
186
- li:hover { background:var(--hover); }
187
- .ico { width:16px; text-align:center; }
188
- .nm { flex:1; word-break:break-all; }
189
- .sz { color:var(--muted); font-size:11px; }
190
- .dl { border:1px solid var(--border); background:var(--bg); color:var(--fg); border-radius:4px; padding:1px 7px; font-size:11px; cursor:pointer; }
191
- .sel { border-color:var(--accent); color:var(--accent); font-weight:600; }
192
- .msg { color:var(--muted); padding:8px 2px; }
193
- .err { color:#d32f2f; }
194
- pre { background:var(--hover); border:1px solid var(--border); border-radius:6px; padding:10px; max-height:320px; overflow:auto; white-space:pre-wrap; word-break:break-word; margin:8px 0 0; }
195
- h4 { margin:10px 0 4px; }
196
- </style>
197
- </head>
198
- <body>
199
- <div id="wrap">
200
- <div class="bar">
201
- <button id="up" title="Parent directory">&#8593; Up</button>
202
- <button id="refresh" title="Refresh">&#8635;</button>
203
- <label>&#8593; Upload<input id="upload" type="file" style="display:none" /></label>
204
- <span class="path" id="path"></span>
205
- </div>
206
- <div id="list"></div>
207
- <div id="preview"></div>
208
- </div>
209
- <script>
210
- (function () {
211
- var parentWin = window.parent;
212
- var idc = 1;
213
- var pending = {};
214
- var cwd = ".";
215
-
216
- function post(msg) { parentWin.postMessage(msg, "*"); }
217
- function rpc(method, params) {
218
- return new Promise(function (resolve, reject) {
219
- var id = idc++;
220
- pending[id] = { resolve: resolve, reject: reject };
221
- post({ jsonrpc: "2.0", id: id, method: method, params: params || {} });
222
- });
223
- }
224
- function notify(method, params) { post({ jsonrpc: "2.0", method: method, params: params || {} }); }
225
-
226
- function applyTheme(ctx) {
227
- var theme = ctx && ctx.theme ? ctx.theme : "light";
228
- document.documentElement.setAttribute("data-theme", theme === "dark" ? "dark" : "light");
229
- }
230
-
231
- function sendSize() {
232
- try {
233
- var h = Math.ceil(document.documentElement.getBoundingClientRect().height);
234
- notify("ui/notifications/size-changed", { width: 0, height: h });
235
- } catch (e) {}
236
- }
237
-
238
- function payloadOf(result) {
239
- if (!result) return null;
240
- if (result.structuredContent) return result.structuredContent;
241
- try {
242
- var t = result.content && result.content[0] && result.content[0].text;
243
- return t ? JSON.parse(t) : null;
244
- } catch (e) { return null; }
245
- }
246
-
247
- function parentPath(p) {
248
- var norm = p.replace(/[\\\\]+/g, "/").replace(/\\/+$/, "");
249
- var i = norm.lastIndexOf("/");
250
- if (i <= 0) return norm;
251
- return norm.slice(0, i);
252
- }
253
- function joinPath(base, name) {
254
- var norm = base.replace(/[\\\\]+/g, "/").replace(/\\/+$/, "");
255
- return norm + "/" + name;
256
- }
257
-
258
- var listEl = document.getElementById("list");
259
- var pathEl = document.getElementById("path");
260
- var previewEl = document.getElementById("preview");
261
-
262
- function setMsg(el, text, isErr) {
263
- el.innerHTML = "";
264
- var d = document.createElement("div");
265
- d.className = "msg" + (isErr ? " err" : "");
266
- d.textContent = text;
267
- el.appendChild(d);
268
- sendSize();
269
- }
270
-
271
- async function navigate(path) {
272
- previewEl.innerHTML = "";
273
- setMsg(listEl, "Loading " + path + " ...");
274
- try {
275
- var res = await rpc("tools/call", { name: "list_dir", arguments: { path: path } });
276
- if (res && res.isError) {
277
- var errMsg = (payloadOf(res) || {}).error || "list_dir failed";
278
- // Detect the unconfigured-roots scenario and provide actionable guidance.
279
- if (errMsg.indexOf("outside the configured filesystem roots") !== -1) {
280
- errMsg = "No filesystem roots configured. "
281
- + "To browse files: go to MCP Manager → Filesystem → Roots and add a directory, "
282
- + "or set the FLUJO_FS_ROOTS environment variable. "
283
- + "In Docker, also add a host bind-mount to docker-compose.yml.";
284
- }
285
- throw new Error(errMsg);
286
- }
287
- var data = payloadOf(res) || {};
288
- cwd = data.path || path;
289
- pathEl.textContent = cwd;
290
- renderEntries(data.entries || []);
291
- } catch (e) {
292
- setMsg(listEl, "Error: " + (e && e.message ? e.message : e), true);
293
- }
294
- }
295
-
296
- function renderEntries(entries) {
297
- entries.sort(function (a, b) {
298
- var ad = a.type === "directory" ? 0 : 1, bd = b.type === "directory" ? 0 : 1;
299
- if (ad !== bd) return ad - bd;
300
- return String(a.name).localeCompare(String(b.name));
301
- });
302
- listEl.innerHTML = "";
303
- if (!entries.length) { setMsg(listEl, "(empty directory)"); return; }
304
- var ul = document.createElement("ul");
305
- entries.forEach(function (ent) {
306
- var li = document.createElement("li");
307
- var isDir = ent.type === "directory";
308
- var ico = document.createElement("span"); ico.className = "ico"; ico.textContent = isDir ? "\\uD83D\\uDCC1" : "\\uD83D\\uDCC4";
309
- var nm = document.createElement("span"); nm.className = "nm"; nm.textContent = ent.name;
310
- li.appendChild(ico); li.appendChild(nm);
311
- if (!isDir) {
312
- var sz = document.createElement("span"); sz.className = "sz"; sz.textContent = fmtSize(ent.size); li.appendChild(sz);
313
- var sel = document.createElement("button"); sel.className = "dl sel"; sel.textContent = "Select";
314
- sel.onclick = function (e) { e.stopPropagation(); selectFile(joinPath(cwd, ent.name)); };
315
- li.appendChild(sel);
316
- var dl = document.createElement("button"); dl.className = "dl"; dl.textContent = "Download";
317
- dl.onclick = function (e) { e.stopPropagation(); downloadFile(joinPath(cwd, ent.name), ent.name); };
318
- li.appendChild(dl);
319
- li.onclick = function () { preview(joinPath(cwd, ent.name), ent.name); };
320
- } else {
321
- li.onclick = function () { navigate(joinPath(cwd, ent.name)); };
322
- }
323
- ul.appendChild(li);
324
- });
325
- listEl.appendChild(ul);
326
- sendSize();
327
- }
328
-
329
- function fmtSize(n) {
330
- if (typeof n !== "number") return "";
331
- if (n < 1024) return n + " B";
332
- if (n < 1048576) return (n / 1024).toFixed(1) + " KB";
333
- return (n / 1048576).toFixed(1) + " MB";
334
- }
335
-
336
- async function preview(path, name) {
337
- previewEl.innerHTML = "";
338
- var h = document.createElement("h4"); h.textContent = name; previewEl.appendChild(h);
339
- var pre = document.createElement("pre"); pre.textContent = "Loading..."; previewEl.appendChild(pre);
340
- sendSize();
341
- try {
342
- var res = await rpc("tools/call", { name: "read_file", arguments: { path: path } });
343
- if (res && res.isError) throw new Error((payloadOf(res) || {}).error || "read_file failed");
344
- var data = payloadOf(res) || {};
345
- pre.textContent = typeof data.content === "string" ? data.content : "(no text content)";
346
- } catch (e) {
347
- pre.textContent = "Error: " + (e && e.message ? e.message : e);
348
- pre.className = "err";
349
- }
350
- sendSize();
351
- }
352
-
353
- function selectFile(path) {
354
- // The picker action: hand the chosen path back to the assistant as a user
355
- // message (ui/message). The host injects it into the conversation, which
356
- // resumes the waiting model.
357
- rpc("ui/message", { role: "user", content: [{ type: "text", text: "Selected file: " + path }] })
358
- .then(function () { setMsg(previewEl, "Selected " + path + " — sent to the assistant."); })
359
- .catch(function (e) { setMsg(previewEl, "Select failed: " + (e && e.message ? e.message : e), true); });
360
- }
361
-
362
- async function downloadFile(path, name) {
363
- try {
364
- var res = await rpc("tools/call", { name: "read_file", arguments: { path: path } });
365
- if (res && res.isError) throw new Error((payloadOf(res) || {}).error || "read_file failed");
366
- var data = payloadOf(res) || {};
367
- await rpc("ui/download-file", {
368
- contents: [{ type: "resource", resource: { uri: "file:///" + name, mimeType: "text/plain", text: String(data.content == null ? "" : data.content) } }]
369
- });
370
- } catch (e) {
371
- setMsg(previewEl, "Download failed: " + (e && e.message ? e.message : e), true);
372
- }
373
- }
374
-
375
- document.getElementById("upload").onchange = function (ev) {
376
- var file = ev.target.files && ev.target.files[0];
377
- if (!file) return;
378
- var reader = new FileReader();
379
- reader.onload = async function () {
380
- try {
381
- await rpc("tools/call", { name: "write_file", arguments: { path: joinPath(cwd, file.name), content: String(reader.result) } });
382
- navigate(cwd);
383
- } catch (e) {
384
- setMsg(previewEl, "Upload failed: " + (e && e.message ? e.message : e), true);
385
- }
386
- };
387
- reader.readAsText(file);
388
- ev.target.value = "";
389
- };
390
- document.getElementById("up").onclick = function () { navigate(parentPath(cwd)); };
391
- document.getElementById("refresh").onclick = function () { navigate(cwd); };
392
-
393
- // --- MCP Apps message handling ---
394
- window.addEventListener("message", function (event) {
395
- var m = event.data;
396
- if (!m || m.jsonrpc !== "2.0") return;
397
- if (m.id !== undefined && (m.result !== undefined || m.error !== undefined)) {
398
- var p = pending[m.id];
399
- if (!p) return;
400
- delete pending[m.id];
401
- if (m.error) p.reject(new Error(m.error.message || "RPC error"));
402
- else p.resolve(m.result);
403
- return;
404
- }
405
- // Host-initiated requests/notifications.
406
- if (m.method === "ui/notifications/host-context-changed") { applyTheme(m.params); return; }
407
- if (m.method === "ui/notifications/tool-input") {
408
- var args = m.params && m.params.arguments;
409
- if (args && typeof args.path === "string" && !started) { started = true; navigate(args.path); }
410
- return;
411
- }
412
- if (m.method === "ping" && m.id !== undefined) { post({ jsonrpc: "2.0", id: m.id, result: {} }); return; }
413
- if (m.method === "ui/resource-teardown" && m.id !== undefined) { post({ jsonrpc: "2.0", id: m.id, result: {} }); return; }
414
- });
415
-
416
- var started = false;
417
-
418
- // Handshake: initialize -> initialized -> initial navigation.
419
- rpc("ui/initialize", {
420
- appInfo: { name: "filesystem-browser", version: "1.0.0" },
421
- appCapabilities: { availableDisplayModes: ["inline", "fullscreen", "pip"] },
422
- protocolVersion: "${MCP_APPS_PROTOCOL_VERSION}"
423
- }).then(function (result) {
424
- applyTheme(result && result.hostContext);
425
- notify("ui/notifications/initialized", {});
426
- // If no tool-input arrives shortly, start at the data directory (".").
427
- setTimeout(function () { if (!started) { started = true; navigate("."); } }, 400);
428
- }).catch(function (e) {
429
- setMsg(listEl, "Failed to initialize app: " + (e && e.message ? e.message : e), true);
430
- });
431
- })();
432
- </script>
433
- </body>
168
+ const FILESYSTEM_APP_HTML = `<!doctype html>
169
+ <html>
170
+ <head>
171
+ <meta charset="utf-8" />
172
+ <style>
173
+ :root { --bg:#fff; --fg:#1a1a1a; --muted:#666; --border:#e0e0e0; --accent:#1565c0; --hover:#f5f5f5; }
174
+ [data-theme="dark"] { --bg:#1e1e1e; --fg:#e8e8e8; --muted:#9e9e9e; --border:#3a3a3a; --accent:#64b5f6; --hover:#2a2a2a; }
175
+ * { box-sizing: border-box; }
176
+ html,body { margin:0; padding:0; }
177
+ body { font: 13px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif; background:var(--bg); color:var(--fg); }
178
+ #wrap { padding:10px; }
179
+ .bar { display:flex; align-items:center; gap:6px; margin-bottom:8px; flex-wrap:wrap; }
180
+ .bar button, .bar label { font:inherit; cursor:pointer; border:1px solid var(--border); background:var(--bg); color:var(--fg); border-radius:5px; padding:3px 9px; }
181
+ .bar button:hover, .bar label:hover { background:var(--hover); }
182
+ .path { font-family:ui-monospace,SFMono-Regular,Menlo,monospace; color:var(--muted); word-break:break-all; flex:1 1 200px; }
183
+ ul { list-style:none; margin:0; padding:0; border:1px solid var(--border); border-radius:6px; overflow:hidden; }
184
+ li { display:flex; align-items:center; gap:8px; padding:5px 10px; cursor:pointer; border-bottom:1px solid var(--border); }
185
+ li:last-child { border-bottom:none; }
186
+ li:hover { background:var(--hover); }
187
+ .ico { width:16px; text-align:center; }
188
+ .nm { flex:1; word-break:break-all; }
189
+ .sz { color:var(--muted); font-size:11px; }
190
+ .dl { border:1px solid var(--border); background:var(--bg); color:var(--fg); border-radius:4px; padding:1px 7px; font-size:11px; cursor:pointer; }
191
+ .sel { border-color:var(--accent); color:var(--accent); font-weight:600; }
192
+ .msg { color:var(--muted); padding:8px 2px; }
193
+ .err { color:#d32f2f; }
194
+ pre { background:var(--hover); border:1px solid var(--border); border-radius:6px; padding:10px; max-height:320px; overflow:auto; white-space:pre-wrap; word-break:break-word; margin:8px 0 0; }
195
+ h4 { margin:10px 0 4px; }
196
+ </style>
197
+ </head>
198
+ <body>
199
+ <div id="wrap">
200
+ <div class="bar">
201
+ <button id="up" title="Parent directory">&#8593; Up</button>
202
+ <button id="refresh" title="Refresh">&#8635;</button>
203
+ <label>&#8593; Upload<input id="upload" type="file" style="display:none" /></label>
204
+ <span class="path" id="path"></span>
205
+ </div>
206
+ <div id="list"></div>
207
+ <div id="preview"></div>
208
+ </div>
209
+ <script>
210
+ (function () {
211
+ var parentWin = window.parent;
212
+ var idc = 1;
213
+ var pending = {};
214
+ var cwd = ".";
215
+
216
+ function post(msg) { parentWin.postMessage(msg, "*"); }
217
+ function rpc(method, params) {
218
+ return new Promise(function (resolve, reject) {
219
+ var id = idc++;
220
+ pending[id] = { resolve: resolve, reject: reject };
221
+ post({ jsonrpc: "2.0", id: id, method: method, params: params || {} });
222
+ });
223
+ }
224
+ function notify(method, params) { post({ jsonrpc: "2.0", method: method, params: params || {} }); }
225
+
226
+ function applyTheme(ctx) {
227
+ var theme = ctx && ctx.theme ? ctx.theme : "light";
228
+ document.documentElement.setAttribute("data-theme", theme === "dark" ? "dark" : "light");
229
+ }
230
+
231
+ function sendSize() {
232
+ try {
233
+ var h = Math.ceil(document.documentElement.getBoundingClientRect().height);
234
+ notify("ui/notifications/size-changed", { width: 0, height: h });
235
+ } catch (e) {}
236
+ }
237
+
238
+ function payloadOf(result) {
239
+ if (!result) return null;
240
+ if (result.structuredContent) return result.structuredContent;
241
+ try {
242
+ var t = result.content && result.content[0] && result.content[0].text;
243
+ return t ? JSON.parse(t) : null;
244
+ } catch (e) { return null; }
245
+ }
246
+
247
+ function parentPath(p) {
248
+ var norm = p.replace(/[\\\\]+/g, "/").replace(/\\/+$/, "");
249
+ var i = norm.lastIndexOf("/");
250
+ if (i <= 0) return norm;
251
+ return norm.slice(0, i);
252
+ }
253
+ function joinPath(base, name) {
254
+ var norm = base.replace(/[\\\\]+/g, "/").replace(/\\/+$/, "");
255
+ return norm + "/" + name;
256
+ }
257
+
258
+ var listEl = document.getElementById("list");
259
+ var pathEl = document.getElementById("path");
260
+ var previewEl = document.getElementById("preview");
261
+
262
+ function setMsg(el, text, isErr) {
263
+ el.innerHTML = "";
264
+ var d = document.createElement("div");
265
+ d.className = "msg" + (isErr ? " err" : "");
266
+ d.textContent = text;
267
+ el.appendChild(d);
268
+ sendSize();
269
+ }
270
+
271
+ async function navigate(path) {
272
+ previewEl.innerHTML = "";
273
+ setMsg(listEl, "Loading " + path + " ...");
274
+ try {
275
+ var res = await rpc("tools/call", { name: "list_dir", arguments: { path: path } });
276
+ if (res && res.isError) {
277
+ var errMsg = (payloadOf(res) || {}).error || "list_dir failed";
278
+ // Detect the unconfigured-roots scenario and provide actionable guidance.
279
+ if (errMsg.indexOf("outside the configured filesystem roots") !== -1) {
280
+ errMsg = "No filesystem roots configured. "
281
+ + "To browse files: go to MCP Manager → Filesystem → Roots and add a directory, "
282
+ + "or set the FLUJO_FS_ROOTS environment variable. "
283
+ + "In Docker, also add a host bind-mount to docker-compose.yml.";
284
+ }
285
+ throw new Error(errMsg);
286
+ }
287
+ var data = payloadOf(res) || {};
288
+ cwd = data.path || path;
289
+ pathEl.textContent = cwd;
290
+ renderEntries(data.entries || []);
291
+ } catch (e) {
292
+ setMsg(listEl, "Error: " + (e && e.message ? e.message : e), true);
293
+ }
294
+ }
295
+
296
+ function renderEntries(entries) {
297
+ entries.sort(function (a, b) {
298
+ var ad = a.type === "directory" ? 0 : 1, bd = b.type === "directory" ? 0 : 1;
299
+ if (ad !== bd) return ad - bd;
300
+ return String(a.name).localeCompare(String(b.name));
301
+ });
302
+ listEl.innerHTML = "";
303
+ if (!entries.length) { setMsg(listEl, "(empty directory)"); return; }
304
+ var ul = document.createElement("ul");
305
+ entries.forEach(function (ent) {
306
+ var li = document.createElement("li");
307
+ var isDir = ent.type === "directory";
308
+ var ico = document.createElement("span"); ico.className = "ico"; ico.textContent = isDir ? "\\uD83D\\uDCC1" : "\\uD83D\\uDCC4";
309
+ var nm = document.createElement("span"); nm.className = "nm"; nm.textContent = ent.name;
310
+ li.appendChild(ico); li.appendChild(nm);
311
+ if (!isDir) {
312
+ var sz = document.createElement("span"); sz.className = "sz"; sz.textContent = fmtSize(ent.size); li.appendChild(sz);
313
+ var sel = document.createElement("button"); sel.className = "dl sel"; sel.textContent = "Select";
314
+ sel.onclick = function (e) { e.stopPropagation(); selectFile(joinPath(cwd, ent.name)); };
315
+ li.appendChild(sel);
316
+ var dl = document.createElement("button"); dl.className = "dl"; dl.textContent = "Download";
317
+ dl.onclick = function (e) { e.stopPropagation(); downloadFile(joinPath(cwd, ent.name), ent.name); };
318
+ li.appendChild(dl);
319
+ li.onclick = function () { preview(joinPath(cwd, ent.name), ent.name); };
320
+ } else {
321
+ li.onclick = function () { navigate(joinPath(cwd, ent.name)); };
322
+ }
323
+ ul.appendChild(li);
324
+ });
325
+ listEl.appendChild(ul);
326
+ sendSize();
327
+ }
328
+
329
+ function fmtSize(n) {
330
+ if (typeof n !== "number") return "";
331
+ if (n < 1024) return n + " B";
332
+ if (n < 1048576) return (n / 1024).toFixed(1) + " KB";
333
+ return (n / 1048576).toFixed(1) + " MB";
334
+ }
335
+
336
+ async function preview(path, name) {
337
+ previewEl.innerHTML = "";
338
+ var h = document.createElement("h4"); h.textContent = name; previewEl.appendChild(h);
339
+ var pre = document.createElement("pre"); pre.textContent = "Loading..."; previewEl.appendChild(pre);
340
+ sendSize();
341
+ try {
342
+ var res = await rpc("tools/call", { name: "read_file", arguments: { path: path } });
343
+ if (res && res.isError) throw new Error((payloadOf(res) || {}).error || "read_file failed");
344
+ var data = payloadOf(res) || {};
345
+ pre.textContent = typeof data.content === "string" ? data.content : "(no text content)";
346
+ } catch (e) {
347
+ pre.textContent = "Error: " + (e && e.message ? e.message : e);
348
+ pre.className = "err";
349
+ }
350
+ sendSize();
351
+ }
352
+
353
+ function selectFile(path) {
354
+ // The picker action: hand the chosen path back to the assistant as a user
355
+ // message (ui/message). The host injects it into the conversation, which
356
+ // resumes the waiting model.
357
+ rpc("ui/message", { role: "user", content: [{ type: "text", text: "Selected file: " + path }] })
358
+ .then(function () { setMsg(previewEl, "Selected " + path + " — sent to the assistant."); })
359
+ .catch(function (e) { setMsg(previewEl, "Select failed: " + (e && e.message ? e.message : e), true); });
360
+ }
361
+
362
+ async function downloadFile(path, name) {
363
+ try {
364
+ var res = await rpc("tools/call", { name: "read_file", arguments: { path: path } });
365
+ if (res && res.isError) throw new Error((payloadOf(res) || {}).error || "read_file failed");
366
+ var data = payloadOf(res) || {};
367
+ await rpc("ui/download-file", {
368
+ contents: [{ type: "resource", resource: { uri: "file:///" + name, mimeType: "text/plain", text: String(data.content == null ? "" : data.content) } }]
369
+ });
370
+ } catch (e) {
371
+ setMsg(previewEl, "Download failed: " + (e && e.message ? e.message : e), true);
372
+ }
373
+ }
374
+
375
+ document.getElementById("upload").onchange = function (ev) {
376
+ var file = ev.target.files && ev.target.files[0];
377
+ if (!file) return;
378
+ var reader = new FileReader();
379
+ reader.onload = async function () {
380
+ try {
381
+ await rpc("tools/call", { name: "write_file", arguments: { path: joinPath(cwd, file.name), content: String(reader.result) } });
382
+ navigate(cwd);
383
+ } catch (e) {
384
+ setMsg(previewEl, "Upload failed: " + (e && e.message ? e.message : e), true);
385
+ }
386
+ };
387
+ reader.readAsText(file);
388
+ ev.target.value = "";
389
+ };
390
+ document.getElementById("up").onclick = function () { navigate(parentPath(cwd)); };
391
+ document.getElementById("refresh").onclick = function () { navigate(cwd); };
392
+
393
+ // --- MCP Apps message handling ---
394
+ window.addEventListener("message", function (event) {
395
+ var m = event.data;
396
+ if (!m || m.jsonrpc !== "2.0") return;
397
+ if (m.id !== undefined && (m.result !== undefined || m.error !== undefined)) {
398
+ var p = pending[m.id];
399
+ if (!p) return;
400
+ delete pending[m.id];
401
+ if (m.error) p.reject(new Error(m.error.message || "RPC error"));
402
+ else p.resolve(m.result);
403
+ return;
404
+ }
405
+ // Host-initiated requests/notifications.
406
+ if (m.method === "ui/notifications/host-context-changed") { applyTheme(m.params); return; }
407
+ if (m.method === "ui/notifications/tool-input") {
408
+ var args = m.params && m.params.arguments;
409
+ if (args && typeof args.path === "string" && !started) { started = true; navigate(args.path); }
410
+ return;
411
+ }
412
+ if (m.method === "ping" && m.id !== undefined) { post({ jsonrpc: "2.0", id: m.id, result: {} }); return; }
413
+ if (m.method === "ui/resource-teardown" && m.id !== undefined) { post({ jsonrpc: "2.0", id: m.id, result: {} }); return; }
414
+ });
415
+
416
+ var started = false;
417
+
418
+ // Handshake: initialize -> initialized -> initial navigation.
419
+ rpc("ui/initialize", {
420
+ appInfo: { name: "filesystem-browser", version: "1.0.0" },
421
+ appCapabilities: { availableDisplayModes: ["inline", "fullscreen", "pip"] },
422
+ protocolVersion: "${MCP_APPS_PROTOCOL_VERSION}"
423
+ }).then(function (result) {
424
+ applyTheme(result && result.hostContext);
425
+ notify("ui/notifications/initialized", {});
426
+ // If no tool-input arrives shortly, start at the data directory (".").
427
+ setTimeout(function () { if (!started) { started = true; navigate("."); } }, 400);
428
+ }).catch(function (e) {
429
+ setMsg(listEl, "Failed to initialize app: " + (e && e.message ? e.message : e), true);
430
+ });
431
+ })();
432
+ </script>
433
+ </body>
434
434
  </html>`;
435
435
  /**
436
436
  * #216 — the docked diff canvas View. Dependency-free, backtick-free MCP Apps
@@ -440,237 +440,237 @@ const FILESYSTEM_APP_HTML = `<!doctype html>
440
440
  * Each `write_file` / `edit_file` result appends a change card; re-feeds for the
441
441
  * same docked tab update it in place (no new iframe).
442
442
  */
443
- const DEVCANVAS_DIFF_HTML = `<!doctype html>
444
- <html>
445
- <head>
446
- <meta charset="utf-8" />
447
- <style>
448
- :root { --bg:#fff; --fg:#1a1a1a; --muted:#666; --border:#d0d7de; --accent:#1565c0; --add:#e6ffed; --addln:#22863a; --del:#ffebe9; --delln:#cf222e; --hover:#f6f8fa; --gutter:#f6f8fa; }
449
- [data-theme="dark"] { --bg:#1e1e1e; --fg:#e8e8e8; --muted:#9e9e9e; --border:#3a3a3a; --accent:#64b5f6; --add:#0f2f18; --addln:#7bd88f; --del:#3b1518; --delln:#ff7b72; --hover:#2a2a2a; --gutter:#252525; }
450
- * { box-sizing: border-box; }
451
- html,body { margin:0; padding:0; height:100%; }
452
- body { font: 13px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif; background:var(--bg); color:var(--fg); }
453
- #wrap { padding:10px; }
454
- h3 { margin:0 0 8px; font-size:14px; }
455
- .empty { color:var(--muted); padding:10px 2px; }
456
- .card { border:1px solid var(--border); border-radius:6px; margin-bottom:8px; overflow:hidden; }
457
- .card h4 { margin:0; padding:6px 10px; background:var(--hover); font-size:12px; font-family:ui-monospace,SFMono-Regular,Menlo,monospace; word-break:break-all; display:flex; gap:8px; align-items:center; }
458
- .badge { font-size:11px; padding:1px 6px; border-radius:10px; border:1px solid var(--border); color:var(--muted); }
459
- .stat { font-size:11px; color:var(--addln); }
460
- pre { margin:0; padding:8px 10px; max-height:320px; overflow:auto; white-space:pre-wrap; word-break:break-word; background:var(--bg); }
461
- .diff-section { border-top:1px solid var(--border); }
462
- .diff-section:first-of-type { border-top:0; }
463
- .diff-label { padding:4px 8px; color:var(--muted); font-size:11px; background:var(--hover); border-bottom:1px solid var(--border); }
464
- .diff-grid { display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr); min-height:34px; overflow:auto; }
465
- .pane { min-width:0; overflow:auto; font:12px/1.5 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace; }
466
- .pane + .pane { border-left:1px solid var(--border); }
467
- .pane-title { position:sticky; top:0; z-index:1; padding:4px 8px; font:600 11px/1.4 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif; background:var(--gutter); border-bottom:1px solid var(--border); }
468
- .line { display:grid; grid-template-columns:42px minmax(max-content,1fr); min-height:18px; }
469
- .line-no { padding:0 7px; text-align:right; color:var(--muted); background:var(--gutter); user-select:none; border-right:1px solid var(--border); }
470
- .line-code { padding:0 8px; white-space:pre; }
471
- .removed { background:var(--del); color:var(--delln); }
472
- .added { background:var(--add); color:var(--addln); }
473
- .empty-side { padding:12px; color:var(--muted); font-style:italic; }
474
- @media (max-width:700px) { .diff-grid { grid-template-columns:1fr; } .pane + .pane { border-left:0; border-top:1px solid var(--border); } }
475
- </style>
476
- </head>
477
- <body>
478
- <div id="wrap">
479
- <h3>Diff canvas</h3>
480
- <div id="log"><div class="empty">Waiting for file writes / edits...</div></div>
481
- </div>
482
- <script>
483
- (function () {
484
- var parentWin = window.parent;
485
- var logEl = document.getElementById("log");
486
- var changes = [];
487
- var seen = 0;
488
-
489
- function post(msg) { parentWin.postMessage(msg, "*"); }
490
- function notify(method, params) { post({ jsonrpc: "2.0", method: method, params: params || {} }); }
491
-
492
- function applyTheme(ctx) {
493
- var theme = ctx && ctx.theme ? ctx.theme : "light";
494
- document.documentElement.setAttribute("data-theme", theme === "dark" ? "dark" : "light");
495
- }
496
- function sendSize() {
497
- try {
498
- var h = Math.ceil(document.documentElement.getBoundingClientRect().height);
499
- notify("ui/notifications/size-changed", { width: 0, height: h });
500
- } catch (e) {}
501
- }
502
- function payloadOf(result) {
503
- if (!result) return null;
504
- if (result.structuredContent) return result.structuredContent;
505
- try {
506
- var t = result.content && result.content[0] && result.content[0].text;
507
- return t ? JSON.parse(t) : null;
508
- } catch (e) { return null; }
509
- }
510
-
511
- function render() {
512
- if (!changes.length) { logEl.innerHTML = '<div class="empty">Waiting for file writes / edits...</div>'; sendSize(); return; }
513
- logEl.innerHTML = "";
514
- for (var i = changes.length - 1; i >= 0; i--) {
515
- var c = changes[i];
516
- var card = document.createElement("div"); card.className = "card";
517
- var h = document.createElement("h4");
518
- var nm = document.createElement("span"); nm.textContent = c.path || "(unknown path)"; nm.style.flex = "1";
519
- h.appendChild(nm);
520
- if (c.mode) { var b = document.createElement("span"); b.className = "badge"; b.textContent = c.mode; h.appendChild(b); }
521
- if (c.stat) { var s = document.createElement("span"); s.className = "stat"; s.textContent = c.stat; h.appendChild(s); }
522
- card.appendChild(h);
523
- if (Array.isArray(c.comparisons) && c.comparisons.length) {
524
- c.comparisons.forEach(function (comparison, index) {
525
- renderSideBySide(card, comparison.oldText, comparison.newText, comparison.label || (c.comparisons.length > 1 ? "Edit " + (index + 1) : ""));
526
- });
527
- } else if (c.diffText) {
528
- renderUnifiedDiff(card, c.diffText);
529
- } else if (c.body) {
530
- renderSideBySide(card, "", c.body, "New content");
531
- }
532
- logEl.appendChild(card);
533
- }
534
- sendSize();
535
- }
536
-
537
- function textLines(value) {
538
- if (typeof value !== "string" || value.length === 0) return [];
539
- return value.replace(/\\r\\n?/g, "\\n").split("\\n");
540
- }
541
-
542
- function appendPane(grid, title, lines, kind) {
543
- var pane = document.createElement("div"); pane.className = "pane";
544
- var paneTitle = document.createElement("div"); paneTitle.className = "pane-title"; paneTitle.textContent = title; pane.appendChild(paneTitle);
545
- if (!lines.length) {
546
- var empty = document.createElement("div"); empty.className = "empty-side"; empty.textContent = "(empty / not captured)"; pane.appendChild(empty);
547
- } else {
548
- lines.forEach(function (text, index) {
549
- var row = document.createElement("div"); row.className = "line " + kind;
550
- var no = document.createElement("span"); no.className = "line-no"; no.textContent = String(index + 1);
551
- var code = document.createElement("span"); code.className = "line-code"; code.textContent = text || " ";
552
- row.appendChild(no); row.appendChild(code); pane.appendChild(row);
553
- });
554
- }
555
- grid.appendChild(pane);
556
- }
557
-
558
- function renderSideBySide(card, oldText, newText, label) {
559
- var section = document.createElement("div"); section.className = "diff-section";
560
- if (label) { var caption = document.createElement("div"); caption.className = "diff-label"; caption.textContent = label; section.appendChild(caption); }
561
- var grid = document.createElement("div"); grid.className = "diff-grid";
562
- appendPane(grid, "Before", textLines(oldText), "removed");
563
- appendPane(grid, "After", textLines(newText), "added");
564
- section.appendChild(grid); card.appendChild(section);
565
- }
566
-
567
- function renderUnifiedDiff(card, diffText) {
568
- var oldLines = [], newLines = [];
569
- textLines(diffText).forEach(function (line) {
570
- if (line.indexOf("--- ") === 0 || line.indexOf("+++ ") === 0 || line.indexOf("\\\\ No newline") === 0) return;
571
- if (line.indexOf("@@") === 0) { oldLines.push(line); newLines.push(line); return; }
572
- if (line.charAt(0) === "-") { oldLines.push(line.slice(1)); return; }
573
- if (line.charAt(0) === "+") { newLines.push(line.slice(1)); return; }
574
- var context = line.charAt(0) === " " ? line.slice(1) : line;
575
- oldLines.push(context); newLines.push(context);
576
- });
577
- renderSideBySide(card, oldLines.join("\\n"), newLines.join("\\n"), "Unified diff");
578
- }
579
-
580
- function addFromResult(payload, args) {
581
- if (!payload && !args) return;
582
- var p = payload || {};
583
- var a = args || {};
584
- if (p.snapshotDiff) {
585
- var sd = p.snapshotDiff;
586
- var files = Array.isArray(sd.changedFiles) ? sd.changedFiles : [];
587
- var change = {
588
- path: (sd.nodeName || sd.nodeId || "Snapshot") + " — " + (sd.root || ""),
589
- mode: "snapshot",
590
- stat: files.length + " file(s)",
591
- body: files.map(function (f) { return (f.status || "?") + " " + (f.path || ""); }).join("\\n")
592
- + (sd.resourceUri ? "\\n\\nPatch resource: " + sd.resourceUri : "")
593
- };
594
- changes.push(change);
595
- render();
596
- if (sd.resourceUri) {
597
- rpc("resources/read", { uri: sd.resourceUri }).then(function (result) {
598
- var contents = result && result.contents;
599
- var text = contents && contents[0] && contents[0].text;
600
- if (typeof text === "string" && text.length) {
601
- change.diffText = text;
602
- render();
603
- }
604
- }).catch(function () {});
605
- }
606
- return;
607
- }
608
- var change = { path: p.path || a.path || "", mode: p.mode || (a.edits ? "edit" : (a.diff ? "diff" : undefined)), stat: "", body: "", diffText: "", comparisons: [] };
609
- if (p.diff && (typeof p.diff.added === "number" || typeof p.diff.removed === "number")) {
610
- change.stat = "+" + (p.diff.added || 0) + " -" + (p.diff.removed || 0);
611
- } else if (typeof p.bytesWritten === "number") {
612
- change.stat = p.bytesWritten + " B";
613
- } else if (typeof p.editsApplied === "number") {
614
- change.stat = p.editsApplied + " edit(s)";
615
- } else if (typeof p.applied === "number") {
616
- change.stat = p.applied + " edit(s)";
617
- }
618
- if (Array.isArray(a.edits)) {
619
- change.comparisons = a.edits.slice(0, 50).map(function (edit, index) {
620
- return {
621
- oldText: typeof edit.oldText === "string" ? edit.oldText.slice(0, 12000) : "",
622
- newText: typeof edit.newText === "string" ? edit.newText.slice(0, 12000) : "",
623
- label: "Edit " + (index + 1) + (edit.startLine ? " near line " + edit.startLine : "")
624
- };
625
- });
626
- } else if (typeof a.diff === "string") change.diffText = a.diff.slice(0, 30000);
627
- else if (typeof a.content === "string") change.body = a.content.slice(0, 30000);
628
- changes.push(change);
629
- render();
630
- }
631
-
632
- var lastArgs = null;
633
- window.addEventListener("message", function (event) {
634
- var m = event.data;
635
- if (!m || m.jsonrpc !== "2.0") return;
636
- if (m.method === "ui/notifications/host-context-changed") { applyTheme(m.params); return; }
637
- if (m.method === "ui/notifications/tool-input") { lastArgs = m.params && m.params.arguments; return; }
638
- if (m.method === "ui/notifications/tool-result") {
639
- addFromResult(payloadOf(m.params && m.params.result ? m.params.result : m.params), lastArgs);
640
- lastArgs = null;
641
- return;
642
- }
643
- if (m.method === "ping" && m.id !== undefined) { post({ jsonrpc: "2.0", id: m.id, result: {} }); return; }
644
- if (m.method === "ui/resource-teardown" && m.id !== undefined) { post({ jsonrpc: "2.0", id: m.id, result: {} }); return; }
645
- });
646
-
647
- // Handshake: advertise pip so the host can dock this app.
648
- var idc = 1; var pending = {};
649
- function rpc(method, params) {
650
- return new Promise(function (resolve, reject) {
651
- var id = idc++; pending[id] = { resolve: resolve, reject: reject };
652
- post({ jsonrpc: "2.0", id: id, method: method, params: params || {} });
653
- });
654
- }
655
- window.addEventListener("message", function (event) {
656
- var m = event.data;
657
- if (!m || m.jsonrpc !== "2.0") return;
658
- if (m.id !== undefined && (m.result !== undefined || m.error !== undefined)) {
659
- var pr = pending[m.id]; if (!pr) return; delete pending[m.id];
660
- if (m.error) pr.reject(new Error(m.error.message || "RPC error")); else pr.resolve(m.result);
661
- }
662
- });
663
- rpc("ui/initialize", {
664
- appInfo: { name: "devcanvas-diff", version: "1.0.0" },
665
- appCapabilities: { availableDisplayModes: ["inline", "fullscreen", "pip"] },
666
- protocolVersion: "${MCP_APPS_PROTOCOL_VERSION}"
667
- }).then(function (result) {
668
- applyTheme(result && result.hostContext);
669
- notify("ui/notifications/initialized", {});
670
- sendSize();
671
- }).catch(function () { sendSize(); });
672
- })();
673
- </script>
674
- </body>
443
+ const DEVCANVAS_DIFF_HTML = `<!doctype html>
444
+ <html>
445
+ <head>
446
+ <meta charset="utf-8" />
447
+ <style>
448
+ :root { --bg:#fff; --fg:#1a1a1a; --muted:#666; --border:#d0d7de; --accent:#1565c0; --add:#e6ffed; --addln:#22863a; --del:#ffebe9; --delln:#cf222e; --hover:#f6f8fa; --gutter:#f6f8fa; }
449
+ [data-theme="dark"] { --bg:#1e1e1e; --fg:#e8e8e8; --muted:#9e9e9e; --border:#3a3a3a; --accent:#64b5f6; --add:#0f2f18; --addln:#7bd88f; --del:#3b1518; --delln:#ff7b72; --hover:#2a2a2a; --gutter:#252525; }
450
+ * { box-sizing: border-box; }
451
+ html,body { margin:0; padding:0; height:100%; }
452
+ body { font: 13px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif; background:var(--bg); color:var(--fg); }
453
+ #wrap { padding:10px; }
454
+ h3 { margin:0 0 8px; font-size:14px; }
455
+ .empty { color:var(--muted); padding:10px 2px; }
456
+ .card { border:1px solid var(--border); border-radius:6px; margin-bottom:8px; overflow:hidden; }
457
+ .card h4 { margin:0; padding:6px 10px; background:var(--hover); font-size:12px; font-family:ui-monospace,SFMono-Regular,Menlo,monospace; word-break:break-all; display:flex; gap:8px; align-items:center; }
458
+ .badge { font-size:11px; padding:1px 6px; border-radius:10px; border:1px solid var(--border); color:var(--muted); }
459
+ .stat { font-size:11px; color:var(--addln); }
460
+ pre { margin:0; padding:8px 10px; max-height:320px; overflow:auto; white-space:pre-wrap; word-break:break-word; background:var(--bg); }
461
+ .diff-section { border-top:1px solid var(--border); }
462
+ .diff-section:first-of-type { border-top:0; }
463
+ .diff-label { padding:4px 8px; color:var(--muted); font-size:11px; background:var(--hover); border-bottom:1px solid var(--border); }
464
+ .diff-grid { display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr); min-height:34px; overflow:auto; }
465
+ .pane { min-width:0; overflow:auto; font:12px/1.5 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace; }
466
+ .pane + .pane { border-left:1px solid var(--border); }
467
+ .pane-title { position:sticky; top:0; z-index:1; padding:4px 8px; font:600 11px/1.4 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif; background:var(--gutter); border-bottom:1px solid var(--border); }
468
+ .line { display:grid; grid-template-columns:42px minmax(max-content,1fr); min-height:18px; }
469
+ .line-no { padding:0 7px; text-align:right; color:var(--muted); background:var(--gutter); user-select:none; border-right:1px solid var(--border); }
470
+ .line-code { padding:0 8px; white-space:pre; }
471
+ .removed { background:var(--del); color:var(--delln); }
472
+ .added { background:var(--add); color:var(--addln); }
473
+ .empty-side { padding:12px; color:var(--muted); font-style:italic; }
474
+ @media (max-width:700px) { .diff-grid { grid-template-columns:1fr; } .pane + .pane { border-left:0; border-top:1px solid var(--border); } }
475
+ </style>
476
+ </head>
477
+ <body>
478
+ <div id="wrap">
479
+ <h3>Diff canvas</h3>
480
+ <div id="log"><div class="empty">Waiting for file writes / edits...</div></div>
481
+ </div>
482
+ <script>
483
+ (function () {
484
+ var parentWin = window.parent;
485
+ var logEl = document.getElementById("log");
486
+ var changes = [];
487
+ var seen = 0;
488
+
489
+ function post(msg) { parentWin.postMessage(msg, "*"); }
490
+ function notify(method, params) { post({ jsonrpc: "2.0", method: method, params: params || {} }); }
491
+
492
+ function applyTheme(ctx) {
493
+ var theme = ctx && ctx.theme ? ctx.theme : "light";
494
+ document.documentElement.setAttribute("data-theme", theme === "dark" ? "dark" : "light");
495
+ }
496
+ function sendSize() {
497
+ try {
498
+ var h = Math.ceil(document.documentElement.getBoundingClientRect().height);
499
+ notify("ui/notifications/size-changed", { width: 0, height: h });
500
+ } catch (e) {}
501
+ }
502
+ function payloadOf(result) {
503
+ if (!result) return null;
504
+ if (result.structuredContent) return result.structuredContent;
505
+ try {
506
+ var t = result.content && result.content[0] && result.content[0].text;
507
+ return t ? JSON.parse(t) : null;
508
+ } catch (e) { return null; }
509
+ }
510
+
511
+ function render() {
512
+ if (!changes.length) { logEl.innerHTML = '<div class="empty">Waiting for file writes / edits...</div>'; sendSize(); return; }
513
+ logEl.innerHTML = "";
514
+ for (var i = changes.length - 1; i >= 0; i--) {
515
+ var c = changes[i];
516
+ var card = document.createElement("div"); card.className = "card";
517
+ var h = document.createElement("h4");
518
+ var nm = document.createElement("span"); nm.textContent = c.path || "(unknown path)"; nm.style.flex = "1";
519
+ h.appendChild(nm);
520
+ if (c.mode) { var b = document.createElement("span"); b.className = "badge"; b.textContent = c.mode; h.appendChild(b); }
521
+ if (c.stat) { var s = document.createElement("span"); s.className = "stat"; s.textContent = c.stat; h.appendChild(s); }
522
+ card.appendChild(h);
523
+ if (Array.isArray(c.comparisons) && c.comparisons.length) {
524
+ c.comparisons.forEach(function (comparison, index) {
525
+ renderSideBySide(card, comparison.oldText, comparison.newText, comparison.label || (c.comparisons.length > 1 ? "Edit " + (index + 1) : ""));
526
+ });
527
+ } else if (c.diffText) {
528
+ renderUnifiedDiff(card, c.diffText);
529
+ } else if (c.body) {
530
+ renderSideBySide(card, "", c.body, "New content");
531
+ }
532
+ logEl.appendChild(card);
533
+ }
534
+ sendSize();
535
+ }
536
+
537
+ function textLines(value) {
538
+ if (typeof value !== "string" || value.length === 0) return [];
539
+ return value.replace(/\\r\\n?/g, "\\n").split("\\n");
540
+ }
541
+
542
+ function appendPane(grid, title, lines, kind) {
543
+ var pane = document.createElement("div"); pane.className = "pane";
544
+ var paneTitle = document.createElement("div"); paneTitle.className = "pane-title"; paneTitle.textContent = title; pane.appendChild(paneTitle);
545
+ if (!lines.length) {
546
+ var empty = document.createElement("div"); empty.className = "empty-side"; empty.textContent = "(empty / not captured)"; pane.appendChild(empty);
547
+ } else {
548
+ lines.forEach(function (text, index) {
549
+ var row = document.createElement("div"); row.className = "line " + kind;
550
+ var no = document.createElement("span"); no.className = "line-no"; no.textContent = String(index + 1);
551
+ var code = document.createElement("span"); code.className = "line-code"; code.textContent = text || " ";
552
+ row.appendChild(no); row.appendChild(code); pane.appendChild(row);
553
+ });
554
+ }
555
+ grid.appendChild(pane);
556
+ }
557
+
558
+ function renderSideBySide(card, oldText, newText, label) {
559
+ var section = document.createElement("div"); section.className = "diff-section";
560
+ if (label) { var caption = document.createElement("div"); caption.className = "diff-label"; caption.textContent = label; section.appendChild(caption); }
561
+ var grid = document.createElement("div"); grid.className = "diff-grid";
562
+ appendPane(grid, "Before", textLines(oldText), "removed");
563
+ appendPane(grid, "After", textLines(newText), "added");
564
+ section.appendChild(grid); card.appendChild(section);
565
+ }
566
+
567
+ function renderUnifiedDiff(card, diffText) {
568
+ var oldLines = [], newLines = [];
569
+ textLines(diffText).forEach(function (line) {
570
+ if (line.indexOf("--- ") === 0 || line.indexOf("+++ ") === 0 || line.indexOf("\\\\ No newline") === 0) return;
571
+ if (line.indexOf("@@") === 0) { oldLines.push(line); newLines.push(line); return; }
572
+ if (line.charAt(0) === "-") { oldLines.push(line.slice(1)); return; }
573
+ if (line.charAt(0) === "+") { newLines.push(line.slice(1)); return; }
574
+ var context = line.charAt(0) === " " ? line.slice(1) : line;
575
+ oldLines.push(context); newLines.push(context);
576
+ });
577
+ renderSideBySide(card, oldLines.join("\\n"), newLines.join("\\n"), "Unified diff");
578
+ }
579
+
580
+ function addFromResult(payload, args) {
581
+ if (!payload && !args) return;
582
+ var p = payload || {};
583
+ var a = args || {};
584
+ if (p.snapshotDiff) {
585
+ var sd = p.snapshotDiff;
586
+ var files = Array.isArray(sd.changedFiles) ? sd.changedFiles : [];
587
+ var change = {
588
+ path: (sd.nodeName || sd.nodeId || "Snapshot") + " — " + (sd.root || ""),
589
+ mode: "snapshot",
590
+ stat: files.length + " file(s)",
591
+ body: files.map(function (f) { return (f.status || "?") + " " + (f.path || ""); }).join("\\n")
592
+ + (sd.resourceUri ? "\\n\\nPatch resource: " + sd.resourceUri : "")
593
+ };
594
+ changes.push(change);
595
+ render();
596
+ if (sd.resourceUri) {
597
+ rpc("resources/read", { uri: sd.resourceUri }).then(function (result) {
598
+ var contents = result && result.contents;
599
+ var text = contents && contents[0] && contents[0].text;
600
+ if (typeof text === "string" && text.length) {
601
+ change.diffText = text;
602
+ render();
603
+ }
604
+ }).catch(function () {});
605
+ }
606
+ return;
607
+ }
608
+ var change = { path: p.path || a.path || "", mode: p.mode || (a.edits ? "edit" : (a.diff ? "diff" : undefined)), stat: "", body: "", diffText: "", comparisons: [] };
609
+ if (p.diff && (typeof p.diff.added === "number" || typeof p.diff.removed === "number")) {
610
+ change.stat = "+" + (p.diff.added || 0) + " -" + (p.diff.removed || 0);
611
+ } else if (typeof p.bytesWritten === "number") {
612
+ change.stat = p.bytesWritten + " B";
613
+ } else if (typeof p.editsApplied === "number") {
614
+ change.stat = p.editsApplied + " edit(s)";
615
+ } else if (typeof p.applied === "number") {
616
+ change.stat = p.applied + " edit(s)";
617
+ }
618
+ if (Array.isArray(a.edits)) {
619
+ change.comparisons = a.edits.slice(0, 50).map(function (edit, index) {
620
+ return {
621
+ oldText: typeof edit.oldText === "string" ? edit.oldText.slice(0, 12000) : "",
622
+ newText: typeof edit.newText === "string" ? edit.newText.slice(0, 12000) : "",
623
+ label: "Edit " + (index + 1) + (edit.startLine ? " near line " + edit.startLine : "")
624
+ };
625
+ });
626
+ } else if (typeof a.diff === "string") change.diffText = a.diff.slice(0, 30000);
627
+ else if (typeof a.content === "string") change.body = a.content.slice(0, 30000);
628
+ changes.push(change);
629
+ render();
630
+ }
631
+
632
+ var lastArgs = null;
633
+ window.addEventListener("message", function (event) {
634
+ var m = event.data;
635
+ if (!m || m.jsonrpc !== "2.0") return;
636
+ if (m.method === "ui/notifications/host-context-changed") { applyTheme(m.params); return; }
637
+ if (m.method === "ui/notifications/tool-input") { lastArgs = m.params && m.params.arguments; return; }
638
+ if (m.method === "ui/notifications/tool-result") {
639
+ addFromResult(payloadOf(m.params && m.params.result ? m.params.result : m.params), lastArgs);
640
+ lastArgs = null;
641
+ return;
642
+ }
643
+ if (m.method === "ping" && m.id !== undefined) { post({ jsonrpc: "2.0", id: m.id, result: {} }); return; }
644
+ if (m.method === "ui/resource-teardown" && m.id !== undefined) { post({ jsonrpc: "2.0", id: m.id, result: {} }); return; }
645
+ });
646
+
647
+ // Handshake: advertise pip so the host can dock this app.
648
+ var idc = 1; var pending = {};
649
+ function rpc(method, params) {
650
+ return new Promise(function (resolve, reject) {
651
+ var id = idc++; pending[id] = { resolve: resolve, reject: reject };
652
+ post({ jsonrpc: "2.0", id: id, method: method, params: params || {} });
653
+ });
654
+ }
655
+ window.addEventListener("message", function (event) {
656
+ var m = event.data;
657
+ if (!m || m.jsonrpc !== "2.0") return;
658
+ if (m.id !== undefined && (m.result !== undefined || m.error !== undefined)) {
659
+ var pr = pending[m.id]; if (!pr) return; delete pending[m.id];
660
+ if (m.error) pr.reject(new Error(m.error.message || "RPC error")); else pr.resolve(m.result);
661
+ }
662
+ });
663
+ rpc("ui/initialize", {
664
+ appInfo: { name: "devcanvas-diff", version: "1.0.0" },
665
+ appCapabilities: { availableDisplayModes: ["inline", "fullscreen", "pip"] },
666
+ protocolVersion: "${MCP_APPS_PROTOCOL_VERSION}"
667
+ }).then(function (result) {
668
+ applyTheme(result && result.hostContext);
669
+ notify("ui/notifications/initialized", {});
670
+ sendSize();
671
+ }).catch(function () { sendSize(); });
672
+ })();
673
+ </script>
674
+ </body>
675
675
  </html>`;
676
676
  //# sourceMappingURL=resources.js.map
package/package.json CHANGED
@@ -1,6 +1,11 @@
1
1
  {
2
2
  "name": "@mario.andreschak/mcp-filesystem",
3
- "version": "3.46.1",
3
+ "version": "3.46.2",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "git+https://github.com/mario-andreschak/FLUJO.git",
7
+ "directory": "mcp-servers/filesystem"
8
+ },
4
9
  "description": "FLUJO's standalone confined filesystem MCP server",
5
10
  "type": "module",
6
11
  "main": "dist/index.js",