clay-server 2.5.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.
Files changed (87) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +281 -0
  3. package/bin/cli.js +2385 -0
  4. package/lib/cli-sessions.js +270 -0
  5. package/lib/config.js +237 -0
  6. package/lib/daemon.js +489 -0
  7. package/lib/ipc.js +112 -0
  8. package/lib/notes.js +120 -0
  9. package/lib/pages.js +664 -0
  10. package/lib/project.js +1433 -0
  11. package/lib/public/app.js +2795 -0
  12. package/lib/public/apple-touch-icon-dark.png +0 -0
  13. package/lib/public/apple-touch-icon.png +0 -0
  14. package/lib/public/css/base.css +264 -0
  15. package/lib/public/css/diff.css +128 -0
  16. package/lib/public/css/filebrowser.css +1114 -0
  17. package/lib/public/css/highlight.css +144 -0
  18. package/lib/public/css/icon-strip.css +296 -0
  19. package/lib/public/css/input.css +573 -0
  20. package/lib/public/css/menus.css +856 -0
  21. package/lib/public/css/messages.css +1445 -0
  22. package/lib/public/css/mobile-nav.css +354 -0
  23. package/lib/public/css/overlays.css +697 -0
  24. package/lib/public/css/rewind.css +505 -0
  25. package/lib/public/css/server-settings.css +761 -0
  26. package/lib/public/css/sidebar.css +936 -0
  27. package/lib/public/css/sticky-notes.css +358 -0
  28. package/lib/public/css/title-bar.css +314 -0
  29. package/lib/public/favicon-dark.svg +1 -0
  30. package/lib/public/favicon.svg +1 -0
  31. package/lib/public/icon-192-dark.png +0 -0
  32. package/lib/public/icon-192.png +0 -0
  33. package/lib/public/icon-512-dark.png +0 -0
  34. package/lib/public/icon-512.png +0 -0
  35. package/lib/public/icon-mono.svg +1 -0
  36. package/lib/public/index.html +762 -0
  37. package/lib/public/manifest.json +27 -0
  38. package/lib/public/modules/diff.js +398 -0
  39. package/lib/public/modules/events.js +21 -0
  40. package/lib/public/modules/filebrowser.js +1411 -0
  41. package/lib/public/modules/fileicons.js +172 -0
  42. package/lib/public/modules/icons.js +54 -0
  43. package/lib/public/modules/input.js +584 -0
  44. package/lib/public/modules/markdown.js +356 -0
  45. package/lib/public/modules/notifications.js +649 -0
  46. package/lib/public/modules/qrcode.js +70 -0
  47. package/lib/public/modules/rewind.js +345 -0
  48. package/lib/public/modules/server-settings.js +510 -0
  49. package/lib/public/modules/sidebar.js +1083 -0
  50. package/lib/public/modules/state.js +3 -0
  51. package/lib/public/modules/sticky-notes.js +688 -0
  52. package/lib/public/modules/terminal.js +697 -0
  53. package/lib/public/modules/theme.js +738 -0
  54. package/lib/public/modules/tools.js +1608 -0
  55. package/lib/public/modules/utils.js +56 -0
  56. package/lib/public/style.css +15 -0
  57. package/lib/public/sw.js +75 -0
  58. package/lib/push.js +124 -0
  59. package/lib/sdk-bridge.js +989 -0
  60. package/lib/server.js +582 -0
  61. package/lib/sessions.js +424 -0
  62. package/lib/terminal-manager.js +187 -0
  63. package/lib/terminal.js +24 -0
  64. package/lib/themes/ayu-light.json +9 -0
  65. package/lib/themes/catppuccin-latte.json +9 -0
  66. package/lib/themes/catppuccin-mocha.json +9 -0
  67. package/lib/themes/clay-light.json +10 -0
  68. package/lib/themes/clay.json +10 -0
  69. package/lib/themes/dracula.json +9 -0
  70. package/lib/themes/everforest-light.json +9 -0
  71. package/lib/themes/everforest.json +9 -0
  72. package/lib/themes/github-light.json +9 -0
  73. package/lib/themes/gruvbox-dark.json +9 -0
  74. package/lib/themes/gruvbox-light.json +9 -0
  75. package/lib/themes/monokai.json +9 -0
  76. package/lib/themes/nord-light.json +9 -0
  77. package/lib/themes/nord.json +9 -0
  78. package/lib/themes/one-dark.json +9 -0
  79. package/lib/themes/one-light.json +9 -0
  80. package/lib/themes/rose-pine-dawn.json +9 -0
  81. package/lib/themes/rose-pine.json +9 -0
  82. package/lib/themes/solarized-dark.json +9 -0
  83. package/lib/themes/solarized-light.json +9 -0
  84. package/lib/themes/tokyo-night-light.json +9 -0
  85. package/lib/themes/tokyo-night.json +9 -0
  86. package/lib/updater.js +97 -0
  87. package/package.json +47 -0
@@ -0,0 +1,356 @@
1
+ import { copyToClipboard, escapeHtml } from './utils.js';
2
+ import { refreshIcons } from './icons.js';
3
+ import { getMermaidThemeVars } from './theme.js';
4
+
5
+ // Initialize markdown parser
6
+ marked.use({ gfm: true, breaks: false });
7
+
8
+ // Initialize mermaid
9
+ mermaid.initialize({
10
+ startOnLoad: false,
11
+ theme: "dark",
12
+ themeVariables: getMermaidThemeVars()
13
+ });
14
+
15
+ export function updateMermaidTheme(vars) {
16
+ mermaid.initialize({
17
+ startOnLoad: false,
18
+ theme: "dark",
19
+ themeVariables: vars
20
+ });
21
+ }
22
+
23
+ var mermaidIdCounter = 0;
24
+
25
+ export function renderMarkdown(text) {
26
+ return DOMPurify.sanitize(marked.parse(text));
27
+ }
28
+
29
+ export function highlightCodeBlocks(el) {
30
+ el.querySelectorAll("pre code:not(.hljs):not(.language-mermaid)").forEach(function (block) {
31
+ hljs.highlightElement(block);
32
+ });
33
+ el.querySelectorAll("pre:not(.has-copy-btn):not([data-mermaid-processed])").forEach(function (pre) {
34
+ // Skip non-content code blocks (tool details, diffs, etc.)
35
+ if (!pre.querySelector("code")) return;
36
+ pre.classList.add("has-copy-btn");
37
+ pre.style.position = "relative";
38
+ var btn = document.createElement("button");
39
+ btn.className = "code-copy-btn";
40
+ btn.title = "Copy";
41
+ btn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>';
42
+ btn.addEventListener("click", function (e) {
43
+ e.stopPropagation();
44
+ var code = pre.querySelector("code");
45
+ var text = code ? code.textContent : pre.textContent;
46
+ copyToClipboard(text).then(function () {
47
+ btn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>';
48
+ setTimeout(function () {
49
+ btn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>';
50
+ }, 1500);
51
+ });
52
+ });
53
+ pre.appendChild(btn);
54
+ });
55
+ }
56
+
57
+ export function renderMermaidBlocks(el) {
58
+ var blocks = el.querySelectorAll("pre code.language-mermaid");
59
+ blocks.forEach(function (codeEl) {
60
+ var pre = codeEl.parentElement;
61
+ if (!pre || pre.dataset.mermaidProcessed) return;
62
+ pre.dataset.mermaidProcessed = "true";
63
+
64
+ var source = codeEl.textContent;
65
+ if (!source || !source.trim()) return;
66
+
67
+ var id = "mermaid-" + (++mermaidIdCounter);
68
+ var container = document.createElement("div");
69
+ container.className = "mermaid-diagram";
70
+
71
+ try {
72
+ mermaid.render(id, source.trim()).then(function (result) {
73
+ container.innerHTML = result.svg;
74
+ container.addEventListener("click", function () {
75
+ showMermaidModal(container.innerHTML);
76
+ });
77
+ if (pre.parentNode) pre.parentNode.replaceChild(container, pre);
78
+ }).catch(function (err) {
79
+ pre.classList.add("mermaid-error");
80
+ var errHint = document.createElement("div");
81
+ errHint.className = "mermaid-error-hint";
82
+ errHint.textContent = "Diagram render failed";
83
+ if (pre.parentNode) pre.parentNode.insertBefore(errHint, pre.nextSibling);
84
+ var errDiv = document.getElementById("d" + id);
85
+ if (errDiv) errDiv.remove();
86
+ });
87
+ } catch (err) {
88
+ pre.classList.add("mermaid-error");
89
+ }
90
+ });
91
+ }
92
+
93
+ export function showMermaidModal(svgHtml) {
94
+ var modal = document.getElementById("mermaid-modal");
95
+ var body = document.getElementById("mermaid-modal-body");
96
+ if (!modal || !body) return;
97
+ body.innerHTML = svgHtml;
98
+ modal.classList.remove("hidden");
99
+ refreshIcons();
100
+
101
+ var dlBtn = document.getElementById("mermaid-download-btn");
102
+ dlBtn.onclick = function () {
103
+ downloadMermaidPng(body.querySelector("svg"));
104
+ };
105
+ }
106
+
107
+ export function closeMermaidModal() {
108
+ var modal = document.getElementById("mermaid-modal");
109
+ if (modal) modal.classList.add("hidden");
110
+ }
111
+
112
+ export function downloadMermaidPng(svgEl) {
113
+ if (!svgEl) return;
114
+ var svgClone = svgEl.cloneNode(true);
115
+ // Ensure dimensions
116
+ var bbox = svgEl.getBoundingClientRect();
117
+ var scale = 2; // 2x for retina quality
118
+ var w = bbox.width * scale;
119
+ var h = bbox.height * scale;
120
+ svgClone.setAttribute("width", w);
121
+ svgClone.setAttribute("height", h);
122
+
123
+ var serializer = new XMLSerializer();
124
+ var svgStr = serializer.serializeToString(svgClone);
125
+ var svgBlob = new Blob([svgStr], { type: "image/svg+xml;charset=utf-8" });
126
+ var url = URL.createObjectURL(svgBlob);
127
+
128
+ var img = new Image();
129
+ img.onload = function () {
130
+ var canvas = document.createElement("canvas");
131
+ canvas.width = w;
132
+ canvas.height = h;
133
+ var ctx = canvas.getContext("2d");
134
+ // Dark background
135
+ ctx.fillStyle = "#1E1D1A";
136
+ ctx.fillRect(0, 0, w, h);
137
+ ctx.drawImage(img, 0, 0, w, h);
138
+ URL.revokeObjectURL(url);
139
+
140
+ canvas.toBlob(function (blob) {
141
+ var a = document.createElement("a");
142
+ a.href = URL.createObjectURL(blob);
143
+ a.download = "diagram.png";
144
+ a.click();
145
+ URL.revokeObjectURL(a.href);
146
+ }, "image/png");
147
+ };
148
+ img.src = url;
149
+ }
150
+
151
+ // --- PDF Export ---
152
+
153
+ export function svgToPngDataUrl(svgEl) {
154
+ return new Promise(function (resolve, reject) {
155
+ var svgClone = svgEl.cloneNode(true);
156
+ var bbox = svgEl.getBoundingClientRect();
157
+ var scale = 2;
158
+ var w = Math.max(bbox.width, 1) * scale;
159
+ var h = Math.max(bbox.height, 1) * scale;
160
+ svgClone.setAttribute("width", w);
161
+ svgClone.setAttribute("height", h);
162
+
163
+ var serializer = new XMLSerializer();
164
+ var svgStr = serializer.serializeToString(svgClone);
165
+ var svgBlob = new Blob([svgStr], { type: "image/svg+xml;charset=utf-8" });
166
+ var url = URL.createObjectURL(svgBlob);
167
+
168
+ var img = new Image();
169
+ img.onload = function () {
170
+ var canvas = document.createElement("canvas");
171
+ canvas.width = w;
172
+ canvas.height = h;
173
+ var ctx = canvas.getContext("2d");
174
+ ctx.fillStyle = "#ffffff";
175
+ ctx.fillRect(0, 0, w, h);
176
+ ctx.drawImage(img, 0, 0, w, h);
177
+ URL.revokeObjectURL(url);
178
+ resolve(canvas.toDataURL("image/png"));
179
+ };
180
+ img.onerror = function () {
181
+ URL.revokeObjectURL(url);
182
+ reject(new Error("SVG image load failed"));
183
+ };
184
+ img.src = url;
185
+ });
186
+ }
187
+
188
+ export function exportMarkdownAsPdf(markdownEl, filename) {
189
+ // Open popup synchronously during click event to satisfy browser popup policies
190
+ var popup = window.open("", "_blank", "width=900,height=700");
191
+ if (!popup) {
192
+ alert("PDF export blocked: please allow popups for this site.");
193
+ return Promise.resolve();
194
+ }
195
+
196
+ popup.document.write("<!DOCTYPE html><html><head><title>Preparing PDF\u2026</title></head><body><p style=\"font-family:sans-serif;padding:32px;color:#555\">Preparing PDF, please wait\u2026</p></body></html>");
197
+ popup.document.close();
198
+
199
+ // Collect all mermaid diagrams and their SVGs
200
+ var diagrams = markdownEl.querySelectorAll(".mermaid-diagram");
201
+ var svgEls = [];
202
+ for (var i = 0; i < diagrams.length; i++) {
203
+ svgEls.push(diagrams[i].querySelector("svg"));
204
+ }
205
+
206
+ var pngPromises = [];
207
+ for (var j = 0; j < svgEls.length; j++) {
208
+ if (svgEls[j]) {
209
+ pngPromises.push(svgToPngDataUrl(svgEls[j]));
210
+ } else {
211
+ pngPromises.push(Promise.resolve(null));
212
+ }
213
+ }
214
+
215
+ return Promise.all(pngPromises).then(function (dataUrls) {
216
+ // Deep-clone the markdown container
217
+ var clone = markdownEl.cloneNode(true);
218
+
219
+ // Remove copy buttons
220
+ var copyBtns = clone.querySelectorAll(".code-copy-btn");
221
+ for (var k = 0; k < copyBtns.length; k++) {
222
+ copyBtns[k].remove();
223
+ }
224
+
225
+ // Replace mermaid diagram divs with <img> elements
226
+ var clonedDiagrams = clone.querySelectorAll(".mermaid-diagram");
227
+ for (var m = 0; m < clonedDiagrams.length; m++) {
228
+ if (dataUrls[m]) {
229
+ var imgEl = document.createElement("img");
230
+ imgEl.src = dataUrls[m];
231
+ imgEl.className = "pdf-mermaid-img";
232
+ clonedDiagrams[m].parentNode.replaceChild(imgEl, clonedDiagrams[m]);
233
+ } else {
234
+ var errEl = document.createElement("p");
235
+ errEl.textContent = "[Diagram could not be rendered]";
236
+ clonedDiagrams[m].parentNode.replaceChild(errEl, clonedDiagrams[m]);
237
+ }
238
+ }
239
+
240
+ // Make relative image src absolute so popup can load them
241
+ var cloneImgs = clone.querySelectorAll("img:not(.pdf-mermaid-img)");
242
+ for (var n = 0; n < cloneImgs.length; n++) {
243
+ var src = cloneImgs[n].getAttribute("src");
244
+ if (src && !src.startsWith("http://") && !src.startsWith("https://") && !src.startsWith("data:")) {
245
+ cloneImgs[n].src = window.location.origin + "/" + src.replace(/^\//, "");
246
+ }
247
+ }
248
+
249
+ var contentHtml = clone.innerHTML;
250
+ var title = (filename || "document").replace(/.*\//, "");
251
+
252
+ popup.document.open();
253
+ popup.document.write(buildPrintHtml(title, contentHtml));
254
+ popup.document.close();
255
+
256
+ popup.onload = function () {
257
+ // Wait for web fonts (Pretendard, Roboto Mono) before printing
258
+ popup.document.fonts.ready.then(function () {
259
+ popup.focus();
260
+ popup.print();
261
+ if (typeof popup.onafterprint !== "undefined") {
262
+ popup.onafterprint = function () { popup.close(); };
263
+ } else {
264
+ setTimeout(function () { popup.close(); }, 1000);
265
+ }
266
+ });
267
+ };
268
+ }).catch(function (err) {
269
+ popup.close();
270
+ throw err;
271
+ });
272
+ }
273
+
274
+ function buildPrintHtml(title, contentHtml) {
275
+ return "<!DOCTYPE html>\n" +
276
+ "<html lang=\"ko\"><head>\n" +
277
+ "<meta charset=\"UTF-8\">\n" +
278
+ "<title>" + escapeHtml(title) + "</title>\n" +
279
+ "<link rel=\"preconnect\" href=\"https://cdn.jsdelivr.net\">\n" +
280
+ "<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard.min.css\">\n" +
281
+ "<link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n" +
282
+ "<link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n" +
283
+ "<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,400;0,500;1,400&display=swap\">\n" +
284
+ "<style>\n" + getPrintCss() + "\n</style>\n" +
285
+ "</head><body>\n" +
286
+ "<div class=\"file-viewer-markdown\">" + contentHtml + "</div>\n" +
287
+ "</body></html>";
288
+ }
289
+
290
+ function getPrintCss() {
291
+ return [
292
+ /* MS Word defaults: 2.54cm (1in) margins, 11pt, 115% line-height, 8pt after para */
293
+ "@page { margin: 2.54cm; }",
294
+ "*, *::before, *::after { box-sizing: border-box; }",
295
+ "body {",
296
+ " font-family: 'Pretendard', 'Pretendard Variable', system-ui, -apple-system, sans-serif;",
297
+ " font-size: 11pt;",
298
+ " line-height: 1.15;",
299
+ " color: #37352f;",
300
+ " background: #fff;",
301
+ " margin: 0;",
302
+ " padding: 0;",
303
+ "}",
304
+ ".file-viewer-markdown { padding: 0; max-width: 100%; }",
305
+ /* Headings — Notion style: black/near-black, bold, size hierarchy only */
306
+ ".file-viewer-markdown h1 { font-size: 16pt; font-weight: 700; color: #1a1a1a; margin: 14pt 0 4pt; line-height: 1.25; page-break-after: avoid; break-after: avoid; }",
307
+ ".file-viewer-markdown h2 { font-size: 13pt; font-weight: 700; color: #1a1a1a; margin: 12pt 0 3pt; line-height: 1.25; page-break-after: avoid; break-after: avoid; }",
308
+ ".file-viewer-markdown h3 { font-size: 11pt; font-weight: 600; color: #1a1a1a; margin: 10pt 0 2pt; line-height: 1.25; page-break-after: avoid; break-after: avoid; }",
309
+ ".file-viewer-markdown h4, .file-viewer-markdown h5, .file-viewer-markdown h6 { font-size: 11pt; font-weight: 600; color: #37352f; margin: 8pt 0 2pt; line-height: 1.25; page-break-after: avoid; break-after: avoid; }",
310
+ /* Paragraphs & links — 8pt after (Word standard spacing) */
311
+ ".file-viewer-markdown p { margin: 0 0 8pt; orphans: 3; widows: 3; }",
312
+ ".file-viewer-markdown a { color: #37352f; text-decoration: underline; text-underline-offset: 2px; }",
313
+ /* Lists */
314
+ ".file-viewer-markdown ul, .file-viewer-markdown ol { padding-left: 36pt; margin: 0 0 8pt; }",
315
+ ".file-viewer-markdown li { margin: 0 0 2pt; }",
316
+ /* Inline code — Roboto Mono, Notion-style subtle red on light bg */
317
+ ".file-viewer-markdown code { font-family: 'Roboto Mono', 'Courier New', monospace; font-size: 9pt; background: #f0eeec; padding: 1px 5px; border-radius: 3px; color: #eb5757; }",
318
+ /* Code blocks — Roboto Mono, Notion warm gray */
319
+ ".file-viewer-markdown pre { background: #f7f6f3; border: 1px solid #e8e7e3; border-radius: 4px; padding: 10pt 12pt; margin: 0 0 8pt; page-break-inside: avoid; break-inside: avoid; font-size: 9pt; line-height: 1.5; overflow-x: auto; }",
320
+ ".file-viewer-markdown pre code { background: none; padding: 0; font-size: inherit; color: #37352f; border-radius: 0; }",
321
+ /* Blockquote — Notion gray left bar, no italic */
322
+ ".file-viewer-markdown blockquote { border-left: 3px solid #c7c5c2; padding-left: 10pt; margin: 0 0 8pt; color: #6b6860; font-style: normal; }",
323
+ /* Tables — minimal, clean */
324
+ ".file-viewer-markdown table { border-collapse: collapse; width: 100%; margin: 0 0 8pt; page-break-inside: avoid; break-inside: avoid; font-size: 10pt; }",
325
+ ".file-viewer-markdown th, .file-viewer-markdown td { border: 1px solid #e0dfdc; padding: 5pt 9pt; text-align: left; }",
326
+ ".file-viewer-markdown th { background: #f7f6f3; font-weight: 600; color: #37352f; }",
327
+ /* HR */
328
+ ".file-viewer-markdown hr { border: none; border-top: 1px solid #e0dfdc; margin: 12pt 0; }",
329
+ /* Images */
330
+ ".file-viewer-markdown img, .pdf-mermaid-img { max-width: 100%; height: auto; display: block; margin: 8pt 0; page-break-inside: avoid; break-inside: avoid; }",
331
+ /* hljs syntax colors — GitHub Light palette, no CSS vars */
332
+ ".hljs { color: #24292e; background: transparent; }",
333
+ ".hljs-comment, .hljs-quote { color: #6a737d; font-style: italic; }",
334
+ ".hljs-keyword, .hljs-selector-tag, .hljs-operator { color: #d73a49; font-weight: 600; }",
335
+ ".hljs-string, .hljs-doctag { color: #032f62; }",
336
+ ".hljs-number, .hljs-literal, .hljs-boolean { color: #005cc5; }",
337
+ ".hljs-title, .hljs-title.function_, .hljs-section { color: #6f42c1; }",
338
+ ".hljs-variable, .hljs-template-variable, .hljs-params { color: #e36209; }",
339
+ ".hljs-type, .hljs-title.class_, .hljs-title.class_.inherited__ { color: #6f42c1; }",
340
+ ".hljs-built_in { color: #005cc5; }",
341
+ ".hljs-tag, .hljs-name { color: #22863a; }",
342
+ ".hljs-attr, .hljs-attribute { color: #005cc5; }",
343
+ ".hljs-regexp, .hljs-link { color: #032f62; }",
344
+ ".hljs-meta, .hljs-meta .hljs-keyword { color: #6a737d; }",
345
+ ".hljs-symbol, .hljs-bullet { color: #e36209; }",
346
+ ".hljs-addition { color: #22863a; background: #f0fff4; }",
347
+ ".hljs-deletion { color: #b31d28; background: #ffeef0; }",
348
+ ".hljs-emphasis { font-style: italic; }",
349
+ ".hljs-strong { font-weight: 700; }",
350
+ ".hljs-punctuation { color: #555; }",
351
+ ".hljs-property { color: #005cc5; }",
352
+ "@media print {",
353
+ " body { -webkit-print-color-adjust: exact; print-color-adjust: exact; }",
354
+ "}"
355
+ ].join("\n");
356
+ }