claudeck 1.0.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/LICENSE +21 -0
- package/README.md +233 -0
- package/cli.js +2 -0
- package/config/agent-chains.json +16 -0
- package/config/agent-dags.json +16 -0
- package/config/agents.json +46 -0
- package/config/bot-prompt.json +3 -0
- package/config/folders.json +66 -0
- package/config/prompts.json +92 -0
- package/config/repos.json +86 -0
- package/config/telegram-config.json +17 -0
- package/config/workflows.json +90 -0
- package/db.js +1198 -0
- package/package.json +55 -0
- package/plugins/claude-editor/client.css +171 -0
- package/plugins/claude-editor/client.js +183 -0
- package/plugins/event-stream/client.css +207 -0
- package/plugins/event-stream/client.js +271 -0
- package/plugins/linear/client.css +345 -0
- package/plugins/linear/client.js +380 -0
- package/plugins/linear/config.json +5 -0
- package/plugins/linear/server.js +312 -0
- package/plugins/repos/client.css +549 -0
- package/plugins/repos/client.js +663 -0
- package/plugins/repos/server.js +232 -0
- package/plugins/sudoku/client.css +196 -0
- package/plugins/sudoku/client.js +329 -0
- package/plugins/tasks/client.css +414 -0
- package/plugins/tasks/client.js +394 -0
- package/plugins/tasks/server.js +116 -0
- package/plugins/tic-tac-toe/client.css +167 -0
- package/plugins/tic-tac-toe/client.js +241 -0
- package/public/css/core/components.css +232 -0
- package/public/css/core/layout.css +330 -0
- package/public/css/core/print.css +18 -0
- package/public/css/core/reset.css +36 -0
- package/public/css/core/responsive.css +378 -0
- package/public/css/core/theme.css +116 -0
- package/public/css/core/variables.css +93 -0
- package/public/css/features/agent-monitor.css +297 -0
- package/public/css/features/agent-sidebar.css +525 -0
- package/public/css/features/agents.css +996 -0
- package/public/css/features/analytics.css +181 -0
- package/public/css/features/background-sessions.css +321 -0
- package/public/css/features/cost-dashboard.css +168 -0
- package/public/css/features/home.css +313 -0
- package/public/css/features/retro-terminal.css +88 -0
- package/public/css/features/telegram.css +127 -0
- package/public/css/features/tour.css +148 -0
- package/public/css/features/voice-input.css +60 -0
- package/public/css/features/welcome.css +241 -0
- package/public/css/panels/assistant-bot.css +442 -0
- package/public/css/panels/dev-docs.css +292 -0
- package/public/css/panels/file-explorer.css +322 -0
- package/public/css/panels/git-panel.css +221 -0
- package/public/css/panels/mcp-manager.css +199 -0
- package/public/css/panels/tips-feed.css +353 -0
- package/public/css/ui/commands.css +273 -0
- package/public/css/ui/context-gauge.css +76 -0
- package/public/css/ui/file-picker.css +69 -0
- package/public/css/ui/image-attachments.css +106 -0
- package/public/css/ui/messages.css +884 -0
- package/public/css/ui/modals.css +122 -0
- package/public/css/ui/parallel.css +217 -0
- package/public/css/ui/permissions.css +110 -0
- package/public/css/ui/right-panel.css +481 -0
- package/public/css/ui/sessions.css +689 -0
- package/public/css/ui/status-bar.css +425 -0
- package/public/css/ui/toolbox.css +206 -0
- package/public/data/tips.json +218 -0
- package/public/icons/favicon.png +0 -0
- package/public/icons/icon-192.png +0 -0
- package/public/icons/icon-512.png +0 -0
- package/public/icons/whaly.png +0 -0
- package/public/index.html +1140 -0
- package/public/js/core/api.js +591 -0
- package/public/js/core/constants.js +3 -0
- package/public/js/core/dom.js +270 -0
- package/public/js/core/events.js +10 -0
- package/public/js/core/plugin-loader.js +153 -0
- package/public/js/core/store.js +39 -0
- package/public/js/core/utils.js +25 -0
- package/public/js/core/ws.js +64 -0
- package/public/js/features/agent-monitor.js +222 -0
- package/public/js/features/agents.js +1209 -0
- package/public/js/features/analytics.js +397 -0
- package/public/js/features/attachments.js +251 -0
- package/public/js/features/background-sessions.js +475 -0
- package/public/js/features/chat.js +589 -0
- package/public/js/features/cost-dashboard.js +152 -0
- package/public/js/features/dag-editor.js +399 -0
- package/public/js/features/easter-egg.js +46 -0
- package/public/js/features/home.js +270 -0
- package/public/js/features/projects.js +372 -0
- package/public/js/features/prompts.js +228 -0
- package/public/js/features/sessions.js +332 -0
- package/public/js/features/telegram.js +131 -0
- package/public/js/features/tour.js +210 -0
- package/public/js/features/voice-input.js +185 -0
- package/public/js/features/welcome.js +43 -0
- package/public/js/features/workflows.js +277 -0
- package/public/js/main.js +51 -0
- package/public/js/panels/assistant-bot.js +445 -0
- package/public/js/panels/dev-docs.js +380 -0
- package/public/js/panels/file-explorer.js +486 -0
- package/public/js/panels/git-panel.js +285 -0
- package/public/js/panels/mcp-manager.js +311 -0
- package/public/js/panels/tips-feed.js +303 -0
- package/public/js/ui/commands.js +114 -0
- package/public/js/ui/context-gauge.js +100 -0
- package/public/js/ui/diff.js +124 -0
- package/public/js/ui/disabled-tools.js +36 -0
- package/public/js/ui/export.js +74 -0
- package/public/js/ui/formatting.js +206 -0
- package/public/js/ui/header-dropdowns.js +72 -0
- package/public/js/ui/input-meta.js +71 -0
- package/public/js/ui/max-turns.js +21 -0
- package/public/js/ui/messages.js +387 -0
- package/public/js/ui/model-selector.js +20 -0
- package/public/js/ui/notifications.js +232 -0
- package/public/js/ui/parallel.js +176 -0
- package/public/js/ui/permissions.js +168 -0
- package/public/js/ui/right-panel.js +173 -0
- package/public/js/ui/shortcuts.js +143 -0
- package/public/js/ui/sidebar-toggle.js +29 -0
- package/public/js/ui/status-bar.js +172 -0
- package/public/js/ui/tab-sdk.js +623 -0
- package/public/js/ui/theme.js +38 -0
- package/public/manifest.json +13 -0
- package/public/offline.html +190 -0
- package/public/style.css +42 -0
- package/public/sw.js +91 -0
- package/server/agent-loop.js +385 -0
- package/server/dag-executor.js +265 -0
- package/server/orchestrator.js +514 -0
- package/server/paths.js +61 -0
- package/server/plugin-mount.js +56 -0
- package/server/push-sender.js +31 -0
- package/server/routes/agents.js +294 -0
- package/server/routes/bot.js +45 -0
- package/server/routes/exec.js +35 -0
- package/server/routes/files.js +218 -0
- package/server/routes/mcp.js +82 -0
- package/server/routes/messages.js +36 -0
- package/server/routes/notifications.js +37 -0
- package/server/routes/projects.js +207 -0
- package/server/routes/prompts.js +53 -0
- package/server/routes/sessions.js +103 -0
- package/server/routes/stats.js +143 -0
- package/server/routes/telegram.js +71 -0
- package/server/routes/tips.js +135 -0
- package/server/routes/workflows.js +81 -0
- package/server/summarizer.js +55 -0
- package/server/telegram-poller.js +205 -0
- package/server/telegram-sender.js +304 -0
- package/server/ws-handler.js +926 -0
- package/server.js +179 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// Export functionality
|
|
2
|
+
import { escapeHtml } from '../core/utils.js';
|
|
3
|
+
import { renderMarkdown } from './formatting.js';
|
|
4
|
+
|
|
5
|
+
export function exportAsMarkdown(msgs) {
|
|
6
|
+
let md = "# Chat Export\n\n";
|
|
7
|
+
msgs.forEach((m) => {
|
|
8
|
+
if (m.querySelector(".msg-user")) {
|
|
9
|
+
md += "## User\n" + m.textContent.trim() + "\n\n";
|
|
10
|
+
} else if (m.querySelector(".text-content")) {
|
|
11
|
+
md += "## Assistant\n" + (m.querySelector(".text-content").dataset.raw || m.textContent.trim()) + "\n\n";
|
|
12
|
+
} else if (m.querySelector(".tool-indicator")) {
|
|
13
|
+
const name = m.querySelector(".tool-name")?.textContent || "";
|
|
14
|
+
md += "> Tool: " + name + "\n\n";
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
const blob = new Blob([md], { type: "text/markdown" });
|
|
18
|
+
const a = document.createElement("a");
|
|
19
|
+
a.href = URL.createObjectURL(blob);
|
|
20
|
+
a.download = `chat-export-${Date.now()}.md`;
|
|
21
|
+
a.click();
|
|
22
|
+
URL.revokeObjectURL(a.href);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function exportAsHtml(msgs) {
|
|
26
|
+
let body = "";
|
|
27
|
+
msgs.forEach((m) => {
|
|
28
|
+
if (m.querySelector(".msg-user")) {
|
|
29
|
+
body += `<div class="msg msg-user">${escapeHtml(m.textContent.trim())}</div>\n`;
|
|
30
|
+
} else if (m.querySelector(".text-content")) {
|
|
31
|
+
const raw = m.querySelector(".text-content").dataset.raw || m.textContent.trim();
|
|
32
|
+
body += `<div class="msg msg-assistant"><div class="text-content">${renderMarkdown(raw)}</div></div>\n`;
|
|
33
|
+
} else if (m.querySelector(".tool-indicator")) {
|
|
34
|
+
const name = m.querySelector(".tool-name")?.textContent || "";
|
|
35
|
+
body += `<div class="msg tool-use">Tool: ${escapeHtml(name)}</div>\n`;
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const html = `<!DOCTYPE html>
|
|
40
|
+
<html lang="en">
|
|
41
|
+
<head>
|
|
42
|
+
<meta charset="UTF-8">
|
|
43
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
44
|
+
<title>Chat Export — Claudeck</title>
|
|
45
|
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css">
|
|
46
|
+
<style>
|
|
47
|
+
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; background: #0d1117; color: #e6edf3; max-width: 820px; margin: 0 auto; padding: 24px; }
|
|
48
|
+
.msg { margin-bottom: 14px; }
|
|
49
|
+
.msg-user { background: rgba(31, 111, 235, 0.13); border: 1px solid rgba(31, 111, 235, 0.27); border-radius: 8px; padding: 12px 16px; font-size: 14px; line-height: 1.6; white-space: pre-wrap; }
|
|
50
|
+
.msg-assistant { font-size: 14px; line-height: 1.7; }
|
|
51
|
+
.text-content { white-space: pre-wrap; word-wrap: break-word; }
|
|
52
|
+
.text-content code { font-family: "SF Mono", "Fira Code", monospace; font-size: 13px; background: #1c2128; padding: 2px 6px; border-radius: 4px; }
|
|
53
|
+
.text-content pre { background: #161b22; border: 1px solid #30363d; border-radius: 8px; padding: 12px 16px; overflow-x: auto; margin: 8px 0; }
|
|
54
|
+
.text-content pre code { background: none; padding: 0; }
|
|
55
|
+
.tool-use { font-family: "SF Mono", monospace; font-size: 12px; color: #8b949e; padding: 4px 0; }
|
|
56
|
+
h1, h2, h3 { color: #e6edf3; }
|
|
57
|
+
strong { font-weight: 600; }
|
|
58
|
+
</style>
|
|
59
|
+
</head>
|
|
60
|
+
<body>
|
|
61
|
+
<h1>Chat Export</h1>
|
|
62
|
+
${body}
|
|
63
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"><\/script>
|
|
64
|
+
<script>hljs.highlightAll();<\/script>
|
|
65
|
+
</body>
|
|
66
|
+
</html>`;
|
|
67
|
+
|
|
68
|
+
const blob = new Blob([html], { type: "text/html" });
|
|
69
|
+
const a = document.createElement("a");
|
|
70
|
+
a.href = URL.createObjectURL(blob);
|
|
71
|
+
a.download = `chat-export-${Date.now()}.html`;
|
|
72
|
+
a.click();
|
|
73
|
+
URL.revokeObjectURL(a.href);
|
|
74
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
// Markdown rendering + code highlighting + mermaid
|
|
2
|
+
import { escapeHtml } from '../core/utils.js';
|
|
3
|
+
import { getState, setState } from '../core/store.js';
|
|
4
|
+
|
|
5
|
+
// Language display names for the code block header
|
|
6
|
+
const LANG_LABELS = {
|
|
7
|
+
js: "JavaScript", javascript: "JavaScript", ts: "TypeScript", typescript: "TypeScript",
|
|
8
|
+
py: "Python", python: "Python", rb: "Ruby", ruby: "Ruby",
|
|
9
|
+
go: "Go", rust: "Rust", rs: "Rust", java: "Java", c: "C", cpp: "C++",
|
|
10
|
+
cs: "C#", csharp: "C#", swift: "Swift", kt: "Kotlin", kotlin: "Kotlin",
|
|
11
|
+
php: "PHP", sh: "Shell", bash: "Bash", zsh: "Zsh", fish: "Fish",
|
|
12
|
+
sql: "SQL", html: "HTML", css: "CSS", scss: "SCSS", less: "LESS",
|
|
13
|
+
json: "JSON", yaml: "YAML", yml: "YAML", toml: "TOML", xml: "XML",
|
|
14
|
+
md: "Markdown", markdown: "Markdown", txt: "Text", plaintext: "Text",
|
|
15
|
+
jsx: "JSX", tsx: "TSX", vue: "Vue", svelte: "Svelte",
|
|
16
|
+
dockerfile: "Dockerfile", docker: "Dockerfile", makefile: "Makefile",
|
|
17
|
+
graphql: "GraphQL", gql: "GraphQL", lua: "Lua", r: "R",
|
|
18
|
+
perl: "Perl", scala: "Scala", elixir: "Elixir", ex: "Elixir",
|
|
19
|
+
clojure: "Clojure", clj: "Clojure", haskell: "Haskell", hs: "Haskell",
|
|
20
|
+
ocaml: "OCaml", ml: "OCaml", erlang: "Erlang", dart: "Dart",
|
|
21
|
+
powershell: "PowerShell", ps1: "PowerShell", ini: "INI", conf: "Config",
|
|
22
|
+
diff: "Diff", patch: "Diff", mermaid: "Mermaid", proto: "Protobuf",
|
|
23
|
+
terraform: "Terraform", tf: "Terraform", hcl: "HCL", nginx: "Nginx",
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
function getLangLabel(lang) {
|
|
27
|
+
if (!lang) return "";
|
|
28
|
+
return LANG_LABELS[lang.toLowerCase()] || lang.toUpperCase();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function renderMarkdown(text) {
|
|
32
|
+
let html = escapeHtml(text);
|
|
33
|
+
|
|
34
|
+
// ── Code blocks — with language header ──
|
|
35
|
+
html = html.replace(
|
|
36
|
+
/```(\w*)\n([\s\S]*?)```/g,
|
|
37
|
+
(_, lang, code) => {
|
|
38
|
+
const langClass = lang ? `language-${lang}` : "";
|
|
39
|
+
const label = getLangLabel(lang);
|
|
40
|
+
const headerHtml = label
|
|
41
|
+
? `<div class="code-block-header"><span class="code-lang-label">${escapeHtml(label)}</span></div>`
|
|
42
|
+
: "";
|
|
43
|
+
return `<div class="code-block-wrapper">${headerHtml}<pre><code class="${langClass}" data-lang="${lang}">${code}</code></pre></div>`;
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
// ── Inline code ──
|
|
48
|
+
html = html.replace(/`([^`]+)`/g, '<code class="inline-code">$1</code>');
|
|
49
|
+
|
|
50
|
+
// ── Bold + Italic combined ──
|
|
51
|
+
html = html.replace(/\*\*\*(.+?)\*\*\*/g, "<strong><em>$1</em></strong>");
|
|
52
|
+
|
|
53
|
+
// ── Bold ──
|
|
54
|
+
html = html.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>");
|
|
55
|
+
|
|
56
|
+
// ── Italic ──
|
|
57
|
+
html = html.replace(/(?<!\*)\*([^*\n]+?)\*(?!\*)/g, "<em>$1</em>");
|
|
58
|
+
|
|
59
|
+
// ── Strikethrough ──
|
|
60
|
+
html = html.replace(/~~(.+?)~~/g, "<del>$1</del>");
|
|
61
|
+
|
|
62
|
+
// ── Headers ──
|
|
63
|
+
html = html.replace(/^#### (.+)$/gm, '<h4 class="md-h4">$1</h4>');
|
|
64
|
+
html = html.replace(/^### (.+)$/gm, '<h3 class="md-h3">$1</h3>');
|
|
65
|
+
html = html.replace(/^## (.+)$/gm, '<h2 class="md-h2">$1</h2>');
|
|
66
|
+
html = html.replace(/^# (.+)$/gm, '<h1 class="md-h1">$1</h1>');
|
|
67
|
+
|
|
68
|
+
// ── Horizontal rules ──
|
|
69
|
+
html = html.replace(/^---+$/gm, '<hr class="md-hr">');
|
|
70
|
+
|
|
71
|
+
// ── Blockquotes ──
|
|
72
|
+
// Match consecutive lines starting with >
|
|
73
|
+
html = html.replace(/(?:^> (.*)$\n?)+/gm, (match) => {
|
|
74
|
+
const lines = match.trim().split("\n").map(l => l.replace(/^> ?/, "")).join("<br>");
|
|
75
|
+
return `<blockquote class="md-blockquote">${lines}</blockquote>\n`;
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// ── Links ──
|
|
79
|
+
html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" class="md-link" target="_blank" rel="noopener">$1</a>');
|
|
80
|
+
|
|
81
|
+
// ── Tables ──
|
|
82
|
+
// Match table blocks: header row, separator row, then data rows
|
|
83
|
+
html = html.replace(
|
|
84
|
+
/(?:^\|(.+)\|$\n^\|[-| :]+\|$\n(?:^\|(.+)\|$\n?)*)/gm,
|
|
85
|
+
(match) => {
|
|
86
|
+
const rows = match.trim().split("\n");
|
|
87
|
+
if (rows.length < 2) return match;
|
|
88
|
+
|
|
89
|
+
const parseRow = (row) =>
|
|
90
|
+
row.split("|").filter((_, i, arr) => i > 0 && i < arr.length - 1).map(c => c.trim());
|
|
91
|
+
|
|
92
|
+
// Parse alignment from separator row
|
|
93
|
+
const sepCells = parseRow(rows[1]);
|
|
94
|
+
const aligns = sepCells.map(c => {
|
|
95
|
+
if (c.startsWith(":") && c.endsWith(":")) return "center";
|
|
96
|
+
if (c.endsWith(":")) return "right";
|
|
97
|
+
return "left";
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const headerCells = parseRow(rows[0]);
|
|
101
|
+
let tableHtml = '<div class="md-table-wrap"><table class="md-table"><thead><tr>';
|
|
102
|
+
headerCells.forEach((cell, i) => {
|
|
103
|
+
tableHtml += `<th style="text-align:${aligns[i] || "left"}">${cell}</th>`;
|
|
104
|
+
});
|
|
105
|
+
tableHtml += "</tr></thead><tbody>";
|
|
106
|
+
|
|
107
|
+
for (let r = 2; r < rows.length; r++) {
|
|
108
|
+
const cells = parseRow(rows[r]);
|
|
109
|
+
tableHtml += "<tr>";
|
|
110
|
+
cells.forEach((cell, i) => {
|
|
111
|
+
tableHtml += `<td style="text-align:${aligns[i] || "left"}">${cell}</td>`;
|
|
112
|
+
});
|
|
113
|
+
tableHtml += "</tr>";
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
tableHtml += "</tbody></table></div>";
|
|
117
|
+
return tableHtml;
|
|
118
|
+
}
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
// ── Ordered lists ──
|
|
122
|
+
// Match consecutive lines starting with digits followed by . or )
|
|
123
|
+
html = html.replace(/(?:^\d+[.)]\s+.+$\n?)+/gm, (match) => {
|
|
124
|
+
const items = match.trim().split("\n").map(l => l.replace(/^\d+[.)]\s+/, ""));
|
|
125
|
+
return '<ol class="md-list md-ol">' + items.map(i => `<li>${i}</li>`).join("") + "</ol>\n";
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// ── Unordered lists ──
|
|
129
|
+
// Match consecutive lines starting with -, *, or +
|
|
130
|
+
html = html.replace(/(?:^[-*+]\s+.+$\n?)+/gm, (match) => {
|
|
131
|
+
const items = match.trim().split("\n").map(l => l.replace(/^[-*+]\s+/, ""));
|
|
132
|
+
return '<ul class="md-list md-ul">' + items.map(i => `<li>${i}</li>`).join("") + "</ul>\n";
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// ── Line breaks ──
|
|
136
|
+
html = html.replace(/\n/g, "<br>");
|
|
137
|
+
|
|
138
|
+
return html;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function highlightCodeBlocks(container) {
|
|
142
|
+
if (typeof hljs === "undefined") return;
|
|
143
|
+
container.querySelectorAll("pre code").forEach((block) => {
|
|
144
|
+
if (block.dataset.highlighted === "yes") return;
|
|
145
|
+
try {
|
|
146
|
+
// Highlight both language-tagged and untagged blocks (auto-detect)
|
|
147
|
+
hljs.highlightElement(block);
|
|
148
|
+
} catch { /* ignore unsupported languages */ }
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function addCopyButtons(container) {
|
|
153
|
+
container.querySelectorAll(".code-block-wrapper").forEach((wrapper) => {
|
|
154
|
+
if (wrapper.querySelector(".code-copy-btn")) return;
|
|
155
|
+
const btn = document.createElement("button");
|
|
156
|
+
btn.className = "code-copy-btn";
|
|
157
|
+
btn.textContent = "Copy";
|
|
158
|
+
btn.addEventListener("click", (e) => {
|
|
159
|
+
e.stopPropagation();
|
|
160
|
+
const code = wrapper.querySelector("code");
|
|
161
|
+
if (code) {
|
|
162
|
+
navigator.clipboard.writeText(code.textContent).then(() => {
|
|
163
|
+
btn.textContent = "Copied!";
|
|
164
|
+
btn.classList.add("copied");
|
|
165
|
+
setTimeout(() => {
|
|
166
|
+
btn.textContent = "Copy";
|
|
167
|
+
btn.classList.remove("copied");
|
|
168
|
+
}, 2000);
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
// Place copy button inside the header if it exists, otherwise in wrapper
|
|
173
|
+
const header = wrapper.querySelector(".code-block-header");
|
|
174
|
+
if (header) {
|
|
175
|
+
header.appendChild(btn);
|
|
176
|
+
} else {
|
|
177
|
+
wrapper.appendChild(btn);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export function renderMermaidBlocks(container) {
|
|
183
|
+
if (typeof mermaid === "undefined") return;
|
|
184
|
+
container.querySelectorAll('.code-block-wrapper code[data-lang="mermaid"]').forEach((block) => {
|
|
185
|
+
const wrapper = block.closest(".code-block-wrapper");
|
|
186
|
+
if (!wrapper || wrapper.dataset.mermaidRendered) return;
|
|
187
|
+
wrapper.dataset.mermaidRendered = "true";
|
|
188
|
+
|
|
189
|
+
const source = block.textContent;
|
|
190
|
+
let counter = getState("mermaidCounter") + 1;
|
|
191
|
+
setState("mermaidCounter", counter);
|
|
192
|
+
const id = `mermaid-${counter}`;
|
|
193
|
+
try {
|
|
194
|
+
mermaid.render(id, source).then(({ svg }) => {
|
|
195
|
+
const div = document.createElement("div");
|
|
196
|
+
div.className = "mermaid-container";
|
|
197
|
+
div.innerHTML = svg;
|
|
198
|
+
wrapper.replaceWith(div);
|
|
199
|
+
}).catch(() => {
|
|
200
|
+
// Leave original code block on error
|
|
201
|
+
});
|
|
202
|
+
} catch {
|
|
203
|
+
// Sync error — leave code block
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// Header dropdown menus with multi-level submenus
|
|
2
|
+
|
|
3
|
+
// Toggle dropdown open/close
|
|
4
|
+
document.querySelectorAll(".header-dropdown-trigger").forEach((trigger) => {
|
|
5
|
+
trigger.addEventListener("click", (e) => {
|
|
6
|
+
e.stopPropagation();
|
|
7
|
+
const dropdown = trigger.closest(".header-dropdown");
|
|
8
|
+
const wasOpen = dropdown.classList.contains("open");
|
|
9
|
+
|
|
10
|
+
// Close all dropdowns
|
|
11
|
+
document.querySelectorAll(".header-dropdown.open").forEach((d) => d.classList.remove("open"));
|
|
12
|
+
|
|
13
|
+
if (!wasOpen) dropdown.classList.add("open");
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// Close dropdowns on outside click
|
|
18
|
+
document.addEventListener("click", () => {
|
|
19
|
+
document.querySelectorAll(".header-dropdown.open").forEach((d) => d.classList.remove("open"));
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// Prevent menu clicks from closing the dropdown (except submenu item clicks)
|
|
23
|
+
document.querySelectorAll(".header-dropdown-menu").forEach((menu) => {
|
|
24
|
+
menu.addEventListener("click", (e) => {
|
|
25
|
+
if (!e.target.closest(".header-submenu-item") && !e.target.closest(".header-dropdown-item:not(.has-submenu)")) {
|
|
26
|
+
e.stopPropagation();
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// Submenu item selection — sync with hidden <select> elements
|
|
32
|
+
document.querySelectorAll(".header-submenu-item").forEach((item) => {
|
|
33
|
+
item.addEventListener("click", () => {
|
|
34
|
+
const targetId = item.dataset.target;
|
|
35
|
+
const value = item.dataset.value;
|
|
36
|
+
const select = document.getElementById(targetId);
|
|
37
|
+
if (!select) return;
|
|
38
|
+
|
|
39
|
+
// Update hidden select and fire change event
|
|
40
|
+
select.value = value;
|
|
41
|
+
select.dispatchEvent(new Event("change", { bubbles: true }));
|
|
42
|
+
|
|
43
|
+
// Update active state in submenu
|
|
44
|
+
const submenu = item.closest(".header-submenu");
|
|
45
|
+
submenu.querySelectorAll(".header-submenu-item").forEach((s) => s.classList.remove("active"));
|
|
46
|
+
item.classList.add("active");
|
|
47
|
+
|
|
48
|
+
// Update display value
|
|
49
|
+
const parent = item.closest(".header-dropdown-item");
|
|
50
|
+
const display = parent.querySelector(".header-dropdown-item-value");
|
|
51
|
+
if (display) display.textContent = item.textContent.trim();
|
|
52
|
+
|
|
53
|
+
// Close dropdown
|
|
54
|
+
document.querySelectorAll(".header-dropdown.open").forEach((d) => d.classList.remove("open"));
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// Tools dropdown items — close menu after click
|
|
59
|
+
document.querySelectorAll(".header-dropdown-item:not(.has-submenu)").forEach((item) => {
|
|
60
|
+
if (item.id) {
|
|
61
|
+
item.addEventListener("click", () => {
|
|
62
|
+
document.querySelectorAll(".header-dropdown.open").forEach((d) => d.classList.remove("open"));
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Close on Escape
|
|
68
|
+
document.addEventListener("keydown", (e) => {
|
|
69
|
+
if (e.key === "Escape") {
|
|
70
|
+
document.querySelectorAll(".header-dropdown.open").forEach((d) => d.classList.remove("open"));
|
|
71
|
+
}
|
|
72
|
+
});
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// Input meta labels — show model, permissions, max turns below chat input
|
|
2
|
+
import { $ } from '../core/dom.js';
|
|
3
|
+
|
|
4
|
+
const elModel = document.getElementById("input-meta-model");
|
|
5
|
+
const elPerm = document.getElementById("input-meta-perm");
|
|
6
|
+
const elTurns = document.getElementById("input-meta-turns");
|
|
7
|
+
|
|
8
|
+
const permLabels = {
|
|
9
|
+
bypass: "bypass",
|
|
10
|
+
confirmDangerous: "confirm dangerous",
|
|
11
|
+
confirmAll: "confirm all",
|
|
12
|
+
plan: "plan only",
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
function updateModel() {
|
|
16
|
+
if (!elModel) return;
|
|
17
|
+
const val = $.modelSelect?.value || "";
|
|
18
|
+
elModel.textContent = val || "default model";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function updatePerm() {
|
|
22
|
+
if (!elPerm) return;
|
|
23
|
+
const val = $.permModeSelect?.value || "confirmDangerous";
|
|
24
|
+
elPerm.textContent = permLabels[val] || val;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function updateTurns() {
|
|
28
|
+
if (!elTurns) return;
|
|
29
|
+
const val = $.maxTurnsSelect?.value || "30";
|
|
30
|
+
elTurns.textContent = val === "0" ? "unlimited turns" : `${val} turns`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Also watch header dropdown display elements (used when dropdowns replace <select>)
|
|
34
|
+
function observeDisplay(id, fn) {
|
|
35
|
+
const el = document.getElementById(id);
|
|
36
|
+
if (el) {
|
|
37
|
+
new MutationObserver(fn).observe(el, { childList: true, characterData: true, subtree: true });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
$.modelSelect?.addEventListener("change", updateModel);
|
|
42
|
+
$.permModeSelect?.addEventListener("change", updatePerm);
|
|
43
|
+
$.maxTurnsSelect?.addEventListener("change", updateTurns);
|
|
44
|
+
|
|
45
|
+
observeDisplay("model-display", updateModel);
|
|
46
|
+
observeDisplay("perm-mode-display", updatePerm);
|
|
47
|
+
observeDisplay("max-turns-display", updateTurns);
|
|
48
|
+
|
|
49
|
+
updateModel();
|
|
50
|
+
updatePerm();
|
|
51
|
+
updateTurns();
|
|
52
|
+
|
|
53
|
+
// Make shortcut kbd hints clickable — dispatch the matching keyboard shortcut
|
|
54
|
+
document.querySelectorAll(".input-meta-kbd").forEach((kbd) => {
|
|
55
|
+
kbd.style.cursor = "pointer";
|
|
56
|
+
kbd.addEventListener("click", () => {
|
|
57
|
+
const text = kbd.textContent.trim();
|
|
58
|
+
let key = "";
|
|
59
|
+
if (text === "\u2318B") key = "b";
|
|
60
|
+
else if (text === "\u2318N") key = "n";
|
|
61
|
+
else if (text === "\u2318K") key = "k";
|
|
62
|
+
else if (text === "\u2318/") key = "/";
|
|
63
|
+
if (key) {
|
|
64
|
+
document.dispatchEvent(new KeyboardEvent("keydown", {
|
|
65
|
+
key,
|
|
66
|
+
metaKey: true,
|
|
67
|
+
bubbles: true,
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Max turns selector — localStorage persistence + getter
|
|
2
|
+
import { $ } from '../core/dom.js';
|
|
3
|
+
|
|
4
|
+
const STORAGE_KEY = 'claudeck-max-turns';
|
|
5
|
+
|
|
6
|
+
export function getMaxTurns() {
|
|
7
|
+
const val = parseInt($.maxTurnsSelect?.value, 10);
|
|
8
|
+
return val || 0; // 0 = unlimited
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function init() {
|
|
12
|
+
const saved = localStorage.getItem(STORAGE_KEY);
|
|
13
|
+
if (saved && $.maxTurnsSelect) {
|
|
14
|
+
$.maxTurnsSelect.value = saved;
|
|
15
|
+
}
|
|
16
|
+
$.maxTurnsSelect?.addEventListener('change', () => {
|
|
17
|
+
localStorage.setItem(STORAGE_KEY, $.maxTurnsSelect.value);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
init();
|