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,380 @@
|
|
|
1
|
+
// Developer Documentation — extensible docs modal
|
|
2
|
+
// To add a new section, push to the `sections` array below.
|
|
3
|
+
|
|
4
|
+
// ── Section registry ────────────────────────────────────
|
|
5
|
+
// Each section: { id, title, icon (SVG string), render() → HTML string }
|
|
6
|
+
// render() is called once when the section is first viewed.
|
|
7
|
+
|
|
8
|
+
const sections = [];
|
|
9
|
+
|
|
10
|
+
/** Register a documentation section. Call before init or at module load time. */
|
|
11
|
+
export function registerDocSection(section) {
|
|
12
|
+
if (!section.id || !section.title || !section.render) {
|
|
13
|
+
throw new Error('registerDocSection requires id, title, and render');
|
|
14
|
+
}
|
|
15
|
+
sections.push(section);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// ── Built-in: Tab SDK Guide ─────────────────────────────
|
|
19
|
+
|
|
20
|
+
registerDocSection({
|
|
21
|
+
id: 'tab-sdk',
|
|
22
|
+
title: 'Tab SDK',
|
|
23
|
+
icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><line x1="9" y1="3" x2="9" y2="21"/></svg>',
|
|
24
|
+
render: () => `
|
|
25
|
+
<h2>Tab SDK — Plugin Guide</h2>
|
|
26
|
+
<p>Register custom tabs in the right panel with a single function call.
|
|
27
|
+
No HTML or <code>dom.js</code> changes needed — the SDK handles DOM creation,
|
|
28
|
+
lifecycle hooks, badges, and state management.</p>
|
|
29
|
+
|
|
30
|
+
<h3>Quick Start</h3>
|
|
31
|
+
<pre><code>// plugins/my-tab/client.js
|
|
32
|
+
import { registerTab } from '/js/ui/tab-sdk.js';
|
|
33
|
+
|
|
34
|
+
registerTab({
|
|
35
|
+
id: 'my-tab',
|
|
36
|
+
title: 'My Tab',
|
|
37
|
+
icon: '<svg>...</svg>', // optional, 12×12 recommended
|
|
38
|
+
lazy: true, // defer init until first open
|
|
39
|
+
|
|
40
|
+
init(ctx) {
|
|
41
|
+
const root = document.createElement('div');
|
|
42
|
+
root.textContent = 'Hello from my tab!';
|
|
43
|
+
|
|
44
|
+
// Listen for live WebSocket messages
|
|
45
|
+
ctx.on('ws:message', (msg) => { /* ... */ });
|
|
46
|
+
|
|
47
|
+
// React to session switch
|
|
48
|
+
ctx.onState('sessionId', (id) => { /* reload */ });
|
|
49
|
+
|
|
50
|
+
// Show a badge count on the tab button
|
|
51
|
+
ctx.showBadge(5);
|
|
52
|
+
|
|
53
|
+
return root; // must return an HTMLElement
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
onActivate() { /* tab became visible */ },
|
|
57
|
+
onDeactivate() { /* tab was hidden */ },
|
|
58
|
+
onDestroy() { /* cleanup */ },
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Auto-discovered — no main.js changes needed!</code></pre>
|
|
62
|
+
|
|
63
|
+
<h3>registerTab(config)</h3>
|
|
64
|
+
<table class="param-table">
|
|
65
|
+
<thead><tr><th>Param</th><th>Type</th><th>Description</th></tr></thead>
|
|
66
|
+
<tbody>
|
|
67
|
+
<tr><td>id</td><td>string</td><td><span class="tag tag-required">required</span> Unique tab identifier (used as <code>data-tab</code>)</td></tr>
|
|
68
|
+
<tr><td>title</td><td>string</td><td><span class="tag tag-required">required</span> Button label text</td></tr>
|
|
69
|
+
<tr><td>icon</td><td>string</td><td><span class="tag tag-optional">optional</span> SVG/HTML icon shown before title</td></tr>
|
|
70
|
+
<tr><td>position</td><td>number</td><td><span class="tag tag-optional">optional</span> 0-based insert index. Omit to append at end</td></tr>
|
|
71
|
+
<tr><td>shortcut</td><td>string</td><td><span class="tag tag-optional">optional</span> Informational shortcut label</td></tr>
|
|
72
|
+
<tr><td>lazy</td><td>boolean</td><td><span class="tag tag-optional">optional</span> Default <code>false</code>. If true, <code>init()</code> is deferred until first open</td></tr>
|
|
73
|
+
<tr><td>init(ctx)</td><td>function</td><td><span class="tag tag-required">required</span> Receives context object, must return an <code>HTMLElement</code></td></tr>
|
|
74
|
+
<tr><td>onActivate</td><td>function</td><td><span class="tag tag-optional">optional</span> Called each time the tab becomes visible</td></tr>
|
|
75
|
+
<tr><td>onDeactivate</td><td>function</td><td><span class="tag tag-optional">optional</span> Called each time the tab is hidden</td></tr>
|
|
76
|
+
<tr><td>onDestroy</td><td>function</td><td><span class="tag tag-optional">optional</span> Called when the tab is unregistered</td></tr>
|
|
77
|
+
</tbody>
|
|
78
|
+
</table>
|
|
79
|
+
|
|
80
|
+
<h3>Context Object (ctx)</h3>
|
|
81
|
+
<p>Passed to <code>init(ctx)</code>. Provides scoped access to the app's event bus, state, and API.</p>
|
|
82
|
+
<table class="param-table">
|
|
83
|
+
<thead><tr><th>Method</th><th>Description</th></tr></thead>
|
|
84
|
+
<tbody>
|
|
85
|
+
<tr><td>ctx.on(event, fn)</td><td>Subscribe to the app event bus</td></tr>
|
|
86
|
+
<tr><td>ctx.emit(event, data)</td><td>Publish to the app event bus</td></tr>
|
|
87
|
+
<tr><td>ctx.getState(key)</td><td>Read from the reactive store</td></tr>
|
|
88
|
+
<tr><td>ctx.onState(key, fn)</td><td>Subscribe to store key changes</td></tr>
|
|
89
|
+
<tr><td>ctx.api</td><td>The full API module (all fetch helpers)</td></tr>
|
|
90
|
+
<tr><td>ctx.getProjectPath()</td><td>Current project path</td></tr>
|
|
91
|
+
<tr><td>ctx.getSessionId()</td><td>Current session ID</td></tr>
|
|
92
|
+
<tr><td>ctx.showBadge(count)</td><td>Show a number badge on the tab button</td></tr>
|
|
93
|
+
<tr><td>ctx.clearBadge()</td><td>Hide the badge</td></tr>
|
|
94
|
+
<tr><td>ctx.setTitle(text)</td><td>Update the tab button label at runtime</td></tr>
|
|
95
|
+
</tbody>
|
|
96
|
+
</table>
|
|
97
|
+
|
|
98
|
+
<h3>Other Exports</h3>
|
|
99
|
+
<table class="param-table">
|
|
100
|
+
<thead><tr><th>Function</th><th>Description</th></tr></thead>
|
|
101
|
+
<tbody>
|
|
102
|
+
<tr><td>unregisterTab(id)</td><td>Remove a tab and call its <code>onDestroy</code></td></tr>
|
|
103
|
+
<tr><td>getRegisteredTabs()</td><td>Returns array of registered tab IDs</td></tr>
|
|
104
|
+
</tbody>
|
|
105
|
+
</table>
|
|
106
|
+
|
|
107
|
+
<h3>Lifecycle</h3>
|
|
108
|
+
<pre><code>registerTab() → mountTab() → [lazy? wait for click] → init(ctx)
|
|
109
|
+
↓
|
|
110
|
+
onActivate / onDeactivate
|
|
111
|
+
↓
|
|
112
|
+
unregisterTab() → onDestroy</code></pre>
|
|
113
|
+
|
|
114
|
+
<h3>Important: Tab Content is NOT Destroyed on Switch</h3>
|
|
115
|
+
<div class="callout">Switching tabs only toggles <code>display:none</code> via CSS. The DOM stays alive, timers keep running, and animations continue in the background.</div>
|
|
116
|
+
<p>If your tab runs expensive work (canvas rendering, intervals, WebSocket listeners), you <strong>must</strong> pause it when the tab is hidden. Use the event bus to listen for tab switches:</p>
|
|
117
|
+
<pre><code>init(ctx) {
|
|
118
|
+
let running = false;
|
|
119
|
+
|
|
120
|
+
function start() { if (!running) { running = true; loop(); } }
|
|
121
|
+
function stop() { running = false; }
|
|
122
|
+
|
|
123
|
+
function loop() {
|
|
124
|
+
// ... your render logic ...
|
|
125
|
+
if (running) requestAnimationFrame(loop);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Pause/resume on tab switch
|
|
129
|
+
ctx.on('rightPanel:tabChanged', (tabId) => {
|
|
130
|
+
if (tabId === 'my-tab') start();
|
|
131
|
+
else stop();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
start();
|
|
135
|
+
return root;
|
|
136
|
+
}</code></pre>
|
|
137
|
+
<p>This pattern ensures zero CPU/GPU usage when your tab isn't visible.</p>
|
|
138
|
+
|
|
139
|
+
<h3>Tips</h3>
|
|
140
|
+
<ul>
|
|
141
|
+
<li>Use <code>lazy: true</code> for heavy tabs — init runs only on first open</li>
|
|
142
|
+
<li>Build all DOM in <code>init()</code>; no <code>index.html</code> edits needed</li>
|
|
143
|
+
<li>Use <code>ctx.on('ws:message', fn)</code> for real-time streaming events</li>
|
|
144
|
+
<li>Use <code>ctx.onState('sessionId', fn)</code> to reload on session switch</li>
|
|
145
|
+
<li>Pause expensive work (canvas, timers) on <code>rightPanel:tabChanged</code> — tabs are hidden, not destroyed</li>
|
|
146
|
+
<li>Existing shortcuts (e.g. <code>openRightPanel('my-tab')</code>) work automatically</li>
|
|
147
|
+
</ul>
|
|
148
|
+
|
|
149
|
+
<div class="callout">See <code>plugins/event-stream/client.js</code> for a complete working example of a plugin tab.</div>
|
|
150
|
+
`,
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// ── Built-in: Architecture Overview ─────────────────────
|
|
154
|
+
|
|
155
|
+
registerDocSection({
|
|
156
|
+
id: 'architecture',
|
|
157
|
+
title: 'Architecture',
|
|
158
|
+
icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>',
|
|
159
|
+
render: () => `
|
|
160
|
+
<h2>Architecture Overview</h2>
|
|
161
|
+
<p>Claudeck is a vanilla ES module frontend with no bundler. All modules are loaded via <code><script type="module"></code> from <code>main.js</code>.</p>
|
|
162
|
+
|
|
163
|
+
<h3>Module Loading Order</h3>
|
|
164
|
+
<pre><code>main.js
|
|
165
|
+
├── store.js → Reactive state store
|
|
166
|
+
├── dom.js → Centralized DOM references
|
|
167
|
+
├── constants.js → Shared constants
|
|
168
|
+
├── events.js → Event bus (on/emit/off)
|
|
169
|
+
├── utils.js → Shared utilities
|
|
170
|
+
├── ... → Feature modules
|
|
171
|
+
├── right-panel.js → Right panel + Tab SDK init
|
|
172
|
+
├── plugin-loader.js → Loads enabled plugins from plugins/
|
|
173
|
+
└── ...</code></pre>
|
|
174
|
+
|
|
175
|
+
<h3>Key Patterns</h3>
|
|
176
|
+
<ul>
|
|
177
|
+
<li><strong>Event Bus</strong> — <code>events.js</code> provides <code>on(event, fn)</code>, <code>emit(event, data)</code>, <code>off(event, fn)</code>. All cross-module communication uses this.</li>
|
|
178
|
+
<li><strong>Reactive Store</strong> — <code>store.js</code> provides <code>getState(key)</code>, <code>setState(key, val)</code>, and <code>on(key, fn)</code> for reactive subscriptions.</li>
|
|
179
|
+
<li><strong>DOM Registry</strong> — <code>dom.js</code> exports <code>$</code> object with cached element references. Only used for built-in (HTML-defined) elements.</li>
|
|
180
|
+
<li><strong>API Layer</strong> — <code>api.js</code> contains all <code>fetch()</code> calls. Server runs on port 9009.</li>
|
|
181
|
+
</ul>
|
|
182
|
+
|
|
183
|
+
<h3>Common Events</h3>
|
|
184
|
+
<table class="param-table">
|
|
185
|
+
<thead><tr><th>Event</th><th>Payload</th></tr></thead>
|
|
186
|
+
<tbody>
|
|
187
|
+
<tr><td>ws:message</td><td>Parsed WebSocket message object</td></tr>
|
|
188
|
+
<tr><td>session:changed</td><td>New session ID</td></tr>
|
|
189
|
+
<tr><td>rightPanel:opened</td><td>Active tab name</td></tr>
|
|
190
|
+
<tr><td>rightPanel:tabChanged</td><td>New tab name</td></tr>
|
|
191
|
+
</tbody>
|
|
192
|
+
</table>
|
|
193
|
+
|
|
194
|
+
<h3>Store Keys</h3>
|
|
195
|
+
<table class="param-table">
|
|
196
|
+
<thead><tr><th>Key</th><th>Description</th></tr></thead>
|
|
197
|
+
<tbody>
|
|
198
|
+
<tr><td>sessionId</td><td>Current active session ID</td></tr>
|
|
199
|
+
<tr><td>projectPath</td><td>Current project path</td></tr>
|
|
200
|
+
<tr><td>isStreaming</td><td>Whether AI is currently responding</td></tr>
|
|
201
|
+
</tbody>
|
|
202
|
+
</table>
|
|
203
|
+
|
|
204
|
+
<div class="callout">All modules are independent — import only what you need. No global state beyond the event bus and store.</div>
|
|
205
|
+
`,
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// ── Built-in: Adding Features ───────────────────────────
|
|
209
|
+
|
|
210
|
+
registerDocSection({
|
|
211
|
+
id: 'adding-features',
|
|
212
|
+
title: 'Adding Features',
|
|
213
|
+
icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>',
|
|
214
|
+
render: () => `
|
|
215
|
+
<h2>Adding New Features</h2>
|
|
216
|
+
<p>Follow these patterns when extending the application.</p>
|
|
217
|
+
|
|
218
|
+
<h3>Adding a New Plugin Tab</h3>
|
|
219
|
+
<ol>
|
|
220
|
+
<li>Create a directory: <code>plugins/my-feature/</code></li>
|
|
221
|
+
<li>Create <code>plugins/my-feature/client.js</code> — import <code>registerTab()</code> from <code>/js/ui/tab-sdk.js</code></li>
|
|
222
|
+
<li>Build all DOM inside <code>init(ctx)</code></li>
|
|
223
|
+
<li>Optionally add <code>client.css</code> in the same directory (auto-injected)</li>
|
|
224
|
+
<li>Optionally add <code>server.js</code> for API routes (auto-mounted at <code>/api/plugins/my-feature/</code>)</li>
|
|
225
|
+
</ol>
|
|
226
|
+
|
|
227
|
+
<h3>Adding a New API Endpoint</h3>
|
|
228
|
+
<ol>
|
|
229
|
+
<li>Add the route in <code>server/routes/</code> (create a new file or extend existing)</li>
|
|
230
|
+
<li>Register it in <code>server.js</code></li>
|
|
231
|
+
<li>Add the fetch helper in <code>public/js/core/api.js</code></li>
|
|
232
|
+
<li>Use it from your module</li>
|
|
233
|
+
</ol>
|
|
234
|
+
|
|
235
|
+
<h3>Adding a Database Table</h3>
|
|
236
|
+
<ol>
|
|
237
|
+
<li>Add <code>CREATE TABLE IF NOT EXISTS</code> in <code>db.js</code></li>
|
|
238
|
+
<li>For migrations on existing tables, use <code>try/catch</code> with <code>ALTER TABLE</code></li>
|
|
239
|
+
<li>Add prepared statements and export helper functions</li>
|
|
240
|
+
</ol>
|
|
241
|
+
|
|
242
|
+
<h3>Adding a CSS Module</h3>
|
|
243
|
+
<ol>
|
|
244
|
+
<li>Create <code>public/css/my-feature.css</code></li>
|
|
245
|
+
<li>Add <code>@import url("css/my-feature.css");</code> in <code>style.css</code></li>
|
|
246
|
+
<li>Use CSS variables from <code>variables.css</code> for consistency</li>
|
|
247
|
+
</ol>
|
|
248
|
+
|
|
249
|
+
<h3>Plugin Structure</h3>
|
|
250
|
+
<ul>
|
|
251
|
+
<li>Plugin directories: <code>plugins/kebab-case/</code></li>
|
|
252
|
+
<li>Client module: <code>client.js</code> (required)</li>
|
|
253
|
+
<li>Client styles: <code>client.css</code> (optional)</li>
|
|
254
|
+
<li>Server routes: <code>server.js</code> (optional, auto-mounted)</li>
|
|
255
|
+
<li>Default config: <code>config.json</code> (optional, copied to <code>~/.claudeck/config/</code>)</li>
|
|
256
|
+
<li>Import paths: use absolute paths (e.g. <code>/js/ui/tab-sdk.js</code>)</li>
|
|
257
|
+
</ul>
|
|
258
|
+
|
|
259
|
+
<div class="callout">When in doubt, look at <code>plugins/event-stream/</code>, <code>plugins/repos/</code>, or <code>plugins/tasks/</code> as reference implementations. For full-stack with server routes, see <code>plugins/linear/</code>.</div>
|
|
260
|
+
`,
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
// ── Modal renderer ──────────────────────────────────────
|
|
264
|
+
|
|
265
|
+
let overlayEl = null;
|
|
266
|
+
let activeSection = null;
|
|
267
|
+
const renderedCache = {};
|
|
268
|
+
|
|
269
|
+
function buildNav() {
|
|
270
|
+
return sections.map(s => `
|
|
271
|
+
<button class="dev-docs-nav-item${s.id === activeSection ? ' active' : ''}" data-section="${s.id}">
|
|
272
|
+
${s.icon || ''}
|
|
273
|
+
<span>${s.title}</span>
|
|
274
|
+
</button>
|
|
275
|
+
`).join('');
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function showSection(id) {
|
|
279
|
+
activeSection = id;
|
|
280
|
+
if (!overlayEl) return;
|
|
281
|
+
|
|
282
|
+
// Update nav
|
|
283
|
+
overlayEl.querySelectorAll('.dev-docs-nav-item').forEach(btn => {
|
|
284
|
+
btn.classList.toggle('active', btn.dataset.section === id);
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
// Update content
|
|
288
|
+
const contentEl = overlayEl.querySelector('.dev-docs-content');
|
|
289
|
+
const section = sections.find(s => s.id === id);
|
|
290
|
+
if (!section) return;
|
|
291
|
+
|
|
292
|
+
// Cache rendered HTML
|
|
293
|
+
if (!renderedCache[id]) {
|
|
294
|
+
renderedCache[id] = section.render();
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Hide all, show target
|
|
298
|
+
contentEl.querySelectorAll('.dev-docs-section').forEach(el => {
|
|
299
|
+
el.classList.toggle('active', el.dataset.section === id);
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
// Update header title
|
|
303
|
+
const titleEl = overlayEl.querySelector('.dev-docs-title');
|
|
304
|
+
if (titleEl) titleEl.textContent = section.title;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
export function openDevDocs(sectionId) {
|
|
308
|
+
if (overlayEl) {
|
|
309
|
+
if (sectionId) showSection(sectionId);
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
activeSection = sectionId || sections[0]?.id || 'tab-sdk';
|
|
314
|
+
|
|
315
|
+
overlayEl = document.createElement('div');
|
|
316
|
+
overlayEl.className = 'dev-docs-overlay';
|
|
317
|
+
|
|
318
|
+
const currentSection = sections.find(s => s.id === activeSection);
|
|
319
|
+
|
|
320
|
+
overlayEl.innerHTML = `
|
|
321
|
+
<div class="dev-docs-modal">
|
|
322
|
+
<nav class="dev-docs-nav">
|
|
323
|
+
<div class="dev-docs-nav-header">
|
|
324
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/></svg>
|
|
325
|
+
<span>Dev Docs</span>
|
|
326
|
+
</div>
|
|
327
|
+
${buildNav()}
|
|
328
|
+
</nav>
|
|
329
|
+
<div class="dev-docs-body">
|
|
330
|
+
<div class="dev-docs-header">
|
|
331
|
+
<span class="dev-docs-title">${currentSection?.title || 'Documentation'}</span>
|
|
332
|
+
<button class="dev-docs-close" title="Close (Esc)">×</button>
|
|
333
|
+
</div>
|
|
334
|
+
<div class="dev-docs-content">
|
|
335
|
+
${sections.map(s => {
|
|
336
|
+
if (!renderedCache[s.id]) renderedCache[s.id] = s.render();
|
|
337
|
+
return `<div class="dev-docs-section${s.id === activeSection ? ' active' : ''}" data-section="${s.id}">${renderedCache[s.id]}</div>`;
|
|
338
|
+
}).join('')}
|
|
339
|
+
</div>
|
|
340
|
+
</div>
|
|
341
|
+
</div>
|
|
342
|
+
`;
|
|
343
|
+
|
|
344
|
+
// ── Event bindings ──
|
|
345
|
+
// Close
|
|
346
|
+
overlayEl.querySelector('.dev-docs-close').addEventListener('click', closeDevDocs);
|
|
347
|
+
overlayEl.addEventListener('click', (e) => {
|
|
348
|
+
if (e.target === overlayEl) closeDevDocs();
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
// Nav clicks
|
|
352
|
+
overlayEl.querySelectorAll('.dev-docs-nav-item').forEach(btn => {
|
|
353
|
+
btn.addEventListener('click', () => showSection(btn.dataset.section));
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
// Esc key
|
|
357
|
+
overlayEl._onKey = (e) => {
|
|
358
|
+
if (e.key === 'Escape') closeDevDocs();
|
|
359
|
+
};
|
|
360
|
+
document.addEventListener('keydown', overlayEl._onKey);
|
|
361
|
+
|
|
362
|
+
document.body.appendChild(overlayEl);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
export function closeDevDocs() {
|
|
366
|
+
if (!overlayEl) return;
|
|
367
|
+
document.removeEventListener('keydown', overlayEl._onKey);
|
|
368
|
+
overlayEl.remove();
|
|
369
|
+
overlayEl = null;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// ── Init: wire up the header button ─────────────────────
|
|
373
|
+
function init() {
|
|
374
|
+
const btn = document.getElementById('dev-docs-btn');
|
|
375
|
+
if (btn) {
|
|
376
|
+
btn.addEventListener('click', () => openDevDocs());
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
init();
|