ai-or-die 0.1.85 → 0.1.87
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/artifact-review.js +94 -1
- package/src/artifact-sdk-client.js +495 -64
- package/src/public/app.js +138 -19
- package/src/public/artifact-panel.js +445 -33
- package/src/public/components/artifact-panel.css +124 -6
- package/src/public/index.html +12 -0
- package/src/public/join-repaint.js +36 -0
- package/src/public/session-manager.js +33 -0
- package/src/public/terminal-snapshot-cache.js +295 -0
- package/src/server.js +57 -1
package/package.json
CHANGED
package/src/artifact-review.js
CHANGED
|
@@ -267,6 +267,91 @@ function safeJson(value) {
|
|
|
267
267
|
return JSON.stringify(value).replace(/<\//g, '<\\/');
|
|
268
268
|
}
|
|
269
269
|
|
|
270
|
+
function isMarkdownFile(file) {
|
|
271
|
+
const lower = String(file || '').toLowerCase();
|
|
272
|
+
return lower.endsWith('.md') || lower.endsWith('.markdown');
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Build a self-contained HTML shell that renders markdown SOURCE through the
|
|
276
|
+
// existing client renderer (window.markdownRender.renderInto). This is the
|
|
277
|
+
// fallback path for /view when an artifact is markdown rather than HTML — most
|
|
278
|
+
// plans arrive already-HTML, but a markdown plan must NEVER be shown as raw
|
|
279
|
+
// bytes. The shell is later passed through injectLavishSdk so the annotation
|
|
280
|
+
// SDK loads on top.
|
|
281
|
+
//
|
|
282
|
+
// Absolute script paths are load-bearing: injectLavishSdk inserts a
|
|
283
|
+
// <base href="/api/artifact/:id/asset/..."> and a path-relative
|
|
284
|
+
// "markdown-render.js" would resolve against that asset base (404). The renderer
|
|
285
|
+
// itself lazy-loads /vendor/marked.min.js + /vendor/purify.min.js with absolute
|
|
286
|
+
// paths too, so they survive the base. All three are served by the pre-auth
|
|
287
|
+
// express.static mount, so the sandboxed iframe loads them same-origin without a
|
|
288
|
+
// token.
|
|
289
|
+
function markdownArtifactShell(source, options) {
|
|
290
|
+
options = options || {};
|
|
291
|
+
const title = options.title ? String(options.title) : 'Markdown artifact';
|
|
292
|
+
const src = source == null ? '' : String(source);
|
|
293
|
+
return [
|
|
294
|
+
'<!doctype html>',
|
|
295
|
+
'<html lang="en">',
|
|
296
|
+
'<head>',
|
|
297
|
+
'<meta charset="utf-8">',
|
|
298
|
+
'<meta name="viewport" content="width=device-width, initial-scale=1">',
|
|
299
|
+
'<title>' + escapeAttr(title) + '</title>',
|
|
300
|
+
'<style>',
|
|
301
|
+
':root{color-scheme:light}',
|
|
302
|
+
'html,body{margin:0;padding:0;background:#fbfbfa;color:#1f2328}',
|
|
303
|
+
'body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Serif",Georgia,serif;',
|
|
304
|
+
'font-size:16px;line-height:1.65;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}',
|
|
305
|
+
'.md-reading-column{max-width:760px;margin:0 auto;padding:40px 28px 96px;box-sizing:border-box}',
|
|
306
|
+
'.fb-markdown-rendered,.fb-md-fallback{overflow-wrap:break-word;word-break:break-word}',
|
|
307
|
+
'.fb-markdown-rendered h1,.fb-markdown-rendered h2,.fb-markdown-rendered h3{line-height:1.25;font-weight:650;margin:1.6em 0 .6em}',
|
|
308
|
+
'.fb-markdown-rendered h1{font-size:1.9em;border-bottom:1px solid #e2e2df;padding-bottom:.3em}',
|
|
309
|
+
'.fb-markdown-rendered h2{font-size:1.45em;border-bottom:1px solid #e8e8e5;padding-bottom:.25em}',
|
|
310
|
+
'.fb-markdown-rendered h3{font-size:1.2em}',
|
|
311
|
+
'.fb-markdown-rendered p,.fb-markdown-rendered ul,.fb-markdown-rendered ol{margin:.7em 0}',
|
|
312
|
+
'.fb-markdown-rendered code,.fb-markdown-rendered pre{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,"Liberation Mono",monospace}',
|
|
313
|
+
'.fb-markdown-rendered code{background:#f1f1ef;border-radius:4px;padding:.15em .35em;font-size:.88em}',
|
|
314
|
+
'.fb-markdown-rendered pre{background:#f6f6f4;border:1px solid #e6e6e3;border-radius:8px;padding:14px 16px;overflow:auto;line-height:1.5}',
|
|
315
|
+
'.fb-markdown-rendered pre code{background:none;padding:0;font-size:.86em}',
|
|
316
|
+
'.fb-markdown-rendered blockquote{margin:.8em 0;padding:.1em 1em;border-left:3px solid #d7d7d3;color:#56595f}',
|
|
317
|
+
'.fb-markdown-rendered table{border-collapse:collapse;margin:1em 0;display:block;overflow:auto}',
|
|
318
|
+
'.fb-markdown-rendered th,.fb-markdown-rendered td{border:1px solid #e2e2df;padding:6px 12px}',
|
|
319
|
+
'.fb-markdown-rendered img{max-width:100%}',
|
|
320
|
+
'.fb-markdown-rendered a{color:#0b66c3}',
|
|
321
|
+
'.fb-md-loading{color:#8a8d92;font-size:14px}',
|
|
322
|
+
'.fb-md-feature-unavailable{color:#9a6b00;background:#fff7e0;border:1px solid #f0dca0;border-radius:6px;padding:8px 10px;font-size:13px;margin:0 0 10px}',
|
|
323
|
+
'.fb-md-fallback pre{white-space:pre-wrap;word-break:break-word;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:13px;background:#f6f6f4;border:1px solid #e6e6e3;border-radius:8px;padding:14px 16px;margin:0}',
|
|
324
|
+
'</style>',
|
|
325
|
+
'</head>',
|
|
326
|
+
'<body>',
|
|
327
|
+
'<main class="md-reading-column"><div id="md-artifact-root" class="fb-md-loading">Rendering markdown...</div></main>',
|
|
328
|
+
'<script type="application/json" id="md-artifact-source">' + safeJson(src) + '</script>',
|
|
329
|
+
'<script src="/markdown-render.js"></script>',
|
|
330
|
+
'<script>(function(){',
|
|
331
|
+
'function boot(){',
|
|
332
|
+
'var root=document.getElementById("md-artifact-root");',
|
|
333
|
+
'var raw=document.getElementById("md-artifact-source");',
|
|
334
|
+
'var source="";try{source=JSON.parse(raw.textContent||\'""\');}catch(e){source=raw.textContent||"";}',
|
|
335
|
+
'if(!root)return;',
|
|
336
|
+
'if(window.markdownRender&&typeof window.markdownRender.renderInto==="function"){',
|
|
337
|
+
'window.markdownRender.renderInto(root,source,{enableMermaid:true,enableKatex:true}).catch(function(){renderRaw(root,source);});',
|
|
338
|
+
'}else{renderRaw(root,source);}',
|
|
339
|
+
'}',
|
|
340
|
+
'function renderRaw(root,source){',
|
|
341
|
+
'while(root.firstChild)root.removeChild(root.firstChild);',
|
|
342
|
+
'root.className="fb-md-fallback";',
|
|
343
|
+
'var note=document.createElement("div");note.className="fb-md-feature-unavailable";',
|
|
344
|
+
'note.textContent="Markdown renderer unavailable; showing source.";',
|
|
345
|
+
'var pre=document.createElement("pre");pre.textContent=source;',
|
|
346
|
+
'root.appendChild(note);root.appendChild(pre);',
|
|
347
|
+
'}',
|
|
348
|
+
'if(document.readyState==="loading"){document.addEventListener("DOMContentLoaded",boot,{once:true});}else{boot();}',
|
|
349
|
+
'})();</script>',
|
|
350
|
+
'</body>',
|
|
351
|
+
'</html>',
|
|
352
|
+
].join('\n');
|
|
353
|
+
}
|
|
354
|
+
|
|
270
355
|
function injectLavishSdk(html, options) {
|
|
271
356
|
options = options || {};
|
|
272
357
|
const sessionId = options.sessionId;
|
|
@@ -574,8 +659,14 @@ function createArtifactReviewRouter(options) {
|
|
|
574
659
|
try {
|
|
575
660
|
const stat = fs.statSync(validation.path);
|
|
576
661
|
if (!stat.isFile()) return res.status(400).json({ error: 'file must be a regular file' });
|
|
577
|
-
|
|
662
|
+
const raw = fs.readFileSync(validation.path, 'utf8');
|
|
578
663
|
review.file = validation.path;
|
|
664
|
+
// Markdown FALLBACK: a .md/.markdown artifact is wrapped in a self-contained
|
|
665
|
+
// renderer shell (never shown as raw bytes). HTML and everything else keep
|
|
666
|
+
// the raw path. Both then flow through injectLavishSdk so annotation works.
|
|
667
|
+
html = isMarkdownFile(validation.path)
|
|
668
|
+
? markdownArtifactShell(raw, { title: path.basename(validation.path) })
|
|
669
|
+
: raw;
|
|
579
670
|
} catch (err) {
|
|
580
671
|
if (err && err.code === 'ENOENT') return res.status(404).json({ error: 'file not found' });
|
|
581
672
|
return res.status(500).json({ error: 'read failed', message: err.message });
|
|
@@ -834,5 +925,7 @@ module.exports = {
|
|
|
834
925
|
createArtifactReviewRouter,
|
|
835
926
|
feedbackHasData,
|
|
836
927
|
injectLavishSdk,
|
|
928
|
+
isMarkdownFile,
|
|
929
|
+
markdownArtifactShell,
|
|
837
930
|
resolveArtifactAsset,
|
|
838
931
|
};
|