claudeck 1.4.0 → 1.4.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 (61) hide show
  1. package/README.md +6 -8
  2. package/package.json +1 -1
  3. package/plugins/claude-editor/manifest.json +10 -0
  4. package/plugins/repos/manifest.json +10 -0
  5. package/public/css/core/theme.css +6 -21
  6. package/public/css/core/variables.css +2 -0
  7. package/public/css/features/message-queue.css +348 -0
  8. package/public/css/ui/commands.css +4 -4
  9. package/public/css/ui/messages.css +310 -78
  10. package/public/css/ui/right-panel.css +207 -0
  11. package/public/css/ui/sessions.css +173 -0
  12. package/public/css/ui/settings.css +75 -0
  13. package/public/index.html +10 -2
  14. package/public/js/components/add-project-modal.js +14 -0
  15. package/public/js/components/jump-to-latest.js +42 -0
  16. package/public/js/components/queue-stop-modal.js +23 -0
  17. package/public/js/components/settings-modal.js +65 -0
  18. package/public/js/core/api.js +15 -43
  19. package/public/js/core/dom.js +17 -0
  20. package/public/js/core/events.js +11 -0
  21. package/public/js/core/plugin-loader.js +96 -11
  22. package/public/js/core/store.js +11 -0
  23. package/public/js/core/utils.js +38 -2
  24. package/public/js/features/chat.js +49 -1
  25. package/public/js/features/message-queue.js +423 -0
  26. package/public/js/features/projects.js +185 -3
  27. package/public/js/main.js +4 -1
  28. package/public/js/panels/assistant-bot.js +16 -0
  29. package/public/js/panels/dev-docs.js +2 -2
  30. package/public/js/panels/memory.js +1 -0
  31. package/public/js/ui/context-gauge.js +10 -1
  32. package/public/js/ui/formatting.js +65 -11
  33. package/public/js/ui/header-dropdowns.js +30 -0
  34. package/public/js/ui/input-meta.js +13 -6
  35. package/public/js/ui/max-turns.js +6 -3
  36. package/public/js/ui/messages.js +97 -1
  37. package/public/js/ui/model-selector.js +1 -0
  38. package/public/js/ui/parallel.js +32 -2
  39. package/public/js/ui/permissions.js +1 -0
  40. package/public/js/ui/right-panel.js +0 -8
  41. package/public/js/ui/tab-sdk.js +395 -176
  42. package/public/style.css +2 -0
  43. package/server/memory-optimizer.js +17 -13
  44. package/server/routes/marketplace.js +316 -0
  45. package/server/routes/projects.js +0 -0
  46. package/server/ws-handler.js +22 -15
  47. package/server.js +18 -0
  48. package/plugins/event-stream/client.css +0 -207
  49. package/plugins/event-stream/client.js +0 -271
  50. package/plugins/linear/client.css +0 -345
  51. package/plugins/linear/client.js +0 -380
  52. package/plugins/linear/config.json +0 -5
  53. package/plugins/linear/server.js +0 -312
  54. package/plugins/sudoku/client.css +0 -196
  55. package/plugins/sudoku/client.js +0 -329
  56. package/plugins/tasks/client.css +0 -414
  57. package/plugins/tasks/client.js +0 -394
  58. package/plugins/tasks/server.js +0 -116
  59. package/plugins/tic-tac-toe/client.css +0 -167
  60. package/plugins/tic-tac-toe/client.js +0 -241
  61. package/public/js/components/linear-create-modal.js +0 -43
@@ -1,207 +0,0 @@
1
- /* Event Stream Panel */
2
- .event-stream-toolbar {
3
- display: flex;
4
- align-items: center;
5
- gap: 6px;
6
- padding: 8px;
7
- border-bottom: 1px solid var(--border);
8
- }
9
-
10
- .event-stream-filters {
11
- display: flex;
12
- gap: 4px;
13
- flex-shrink: 0;
14
- }
15
-
16
- .event-filter-btn {
17
- font-family: var(--font-mono);
18
- font-size: 10px;
19
- padding: 2px 8px;
20
- border-radius: 10px;
21
- border: 1px solid var(--border);
22
- background: var(--bg-tertiary);
23
- color: var(--text-secondary);
24
- cursor: pointer;
25
- transition: background 0.15s, color 0.15s;
26
- }
27
-
28
- .event-filter-btn:hover {
29
- background: var(--bg-secondary);
30
- color: var(--text-primary);
31
- }
32
-
33
- .event-filter-btn.active {
34
- background: var(--accent);
35
- color: #fff;
36
- border-color: var(--accent);
37
- }
38
-
39
- .event-stream-search {
40
- flex-grow: 1;
41
- min-width: 0;
42
- font-family: var(--font-mono);
43
- font-size: 11px;
44
- padding: 3px 8px;
45
- border-radius: 4px;
46
- border: 1px solid var(--border);
47
- background: var(--bg-secondary);
48
- color: var(--text-primary);
49
- outline: none;
50
- }
51
-
52
- .event-stream-search:focus {
53
- border-color: var(--accent);
54
- }
55
-
56
- .event-stream-clear-btn {
57
- font-family: var(--font-mono);
58
- font-size: 10px;
59
- padding: 2px 8px;
60
- border-radius: 4px;
61
- border: 1px solid var(--border);
62
- background: var(--bg-tertiary);
63
- color: var(--error, #f85149);
64
- cursor: pointer;
65
- flex-shrink: 0;
66
- }
67
-
68
- .event-stream-clear-btn:hover {
69
- background: var(--error, #f85149);
70
- color: #fff;
71
- }
72
-
73
- .event-stream-list {
74
- display: flex;
75
- flex-direction: column;
76
- overflow-y: auto;
77
- flex-grow: 1;
78
- }
79
-
80
- .event-row {
81
- display: flex;
82
- align-items: flex-start;
83
- padding: 4px 8px;
84
- gap: 8px;
85
- border-bottom: 1px solid var(--border);
86
- cursor: pointer;
87
- font-size: 11px;
88
- font-family: var(--font-mono);
89
- transition: background 0.1s;
90
- }
91
-
92
- .event-row:hover {
93
- background: var(--bg-tertiary);
94
- }
95
-
96
- .event-row.expanded {
97
- background: var(--bg-tertiary);
98
- }
99
-
100
- .event-time {
101
- color: var(--text-secondary);
102
- flex-shrink: 0;
103
- font-size: 10px;
104
- line-height: 1.6;
105
- }
106
-
107
- .event-badge {
108
- text-transform: uppercase;
109
- font-size: 9px;
110
- font-weight: 600;
111
- padding: 1px 5px;
112
- border-radius: 3px;
113
- flex-shrink: 0;
114
- line-height: 1.6;
115
- letter-spacing: 0.3px;
116
- }
117
-
118
- .badge-tool {
119
- background: rgba(56, 189, 248, 0.15);
120
- color: #38bdf8;
121
- }
122
-
123
- .badge-result {
124
- background: rgba(63, 185, 80, 0.15);
125
- color: #3fb950;
126
- }
127
-
128
- .badge-error {
129
- background: rgba(248, 81, 73, 0.15);
130
- color: #f85149;
131
- }
132
-
133
- .badge-done {
134
- background: rgba(var(--accent-rgb, 130, 80, 223), 0.15);
135
- color: var(--accent);
136
- }
137
-
138
- .event-summary {
139
- flex-grow: 1;
140
- min-width: 0;
141
- overflow: hidden;
142
- text-overflow: ellipsis;
143
- white-space: nowrap;
144
- line-height: 1.6;
145
- }
146
-
147
- .event-detail {
148
- display: none;
149
- padding: 4px 0 4px 70px;
150
- font-size: 10px;
151
- color: var(--text-secondary);
152
- white-space: pre-wrap;
153
- max-height: 200px;
154
- overflow: auto;
155
- word-break: break-all;
156
- }
157
-
158
- .event-row.expanded .event-detail {
159
- display: block;
160
- }
161
-
162
- .event-stream-footer {
163
- display: flex;
164
- justify-content: space-between;
165
- align-items: center;
166
- padding: 6px 8px;
167
- font-size: 10px;
168
- color: var(--text-secondary);
169
- border-top: 1px solid var(--border);
170
- font-family: var(--font-mono);
171
- flex-shrink: 0;
172
- }
173
-
174
- .event-autoscroll-toggle {
175
- display: flex;
176
- align-items: center;
177
- gap: 4px;
178
- cursor: pointer;
179
- user-select: none;
180
- }
181
-
182
- .event-autoscroll-toggle input[type="checkbox"] {
183
- accent-color: var(--accent);
184
- }
185
-
186
- /* Empty state */
187
- .event-stream-empty {
188
- display: flex;
189
- flex-direction: column;
190
- align-items: center;
191
- justify-content: center;
192
- padding: 32px 16px;
193
- color: var(--text-dim);
194
- font-size: 12px;
195
- text-align: center;
196
- gap: 8px;
197
- flex: 1;
198
- }
199
-
200
- .event-stream-empty svg {
201
- opacity: 0.4;
202
- }
203
-
204
- .event-stream-empty-hint {
205
- font-size: 11px;
206
- opacity: 0.6;
207
- }
@@ -1,271 +0,0 @@
1
- // Event Stream — migrated to Tab SDK plugin
2
- // This file replaces the old event-stream.js + HTML template
3
- import { registerTab } from '/js/ui/tab-sdk.js';
4
- import { escapeHtml, getToolDetail } from '/js/core/utils.js';
5
-
6
- registerTab({
7
- id: 'events',
8
- title: 'Events',
9
- icon: '<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>',
10
- lazy: true,
11
-
12
- init(ctx) {
13
- let events = [];
14
- let activeFilter = 'all';
15
- let searchQuery = '';
16
- let listEl, countEl, autoscrollEl, searchEl;
17
-
18
- // ── Build DOM ─────────────────────────────────────
19
- const root = document.createElement('div');
20
- root.className = 'event-stream-tab';
21
- root.style.cssText = 'display:flex;flex-direction:column;flex:1;overflow:hidden;';
22
-
23
- root.innerHTML = `
24
- <div class="event-stream-toolbar">
25
- <div class="event-stream-filters">
26
- <button class="event-filter-btn active" data-filter="all">All</button>
27
- <button class="event-filter-btn" data-filter="tool">Tools</button>
28
- <button class="event-filter-btn" data-filter="error">Errors</button>
29
- <button class="event-filter-btn" data-filter="result">Results</button>
30
- </div>
31
- <input type="text" placeholder="Search events..." autocomplete="off" class="event-stream-search">
32
- <button class="event-stream-clear-btn" title="Clear">Clear</button>
33
- </div>
34
- <div class="event-stream-list">
35
- <div class="event-stream-empty">
36
- <svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
37
- <polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/>
38
- </svg>
39
- <span>No events yet</span>
40
- <span class="event-stream-empty-hint">Events will appear here as the AI works</span>
41
- </div>
42
- </div>
43
- <div class="event-stream-footer">
44
- <span class="event-stream-count">0 events</span>
45
- <label class="event-autoscroll-toggle" title="Auto-scroll to latest">
46
- <input type="checkbox" checked> Auto
47
- </label>
48
- </div>
49
- `;
50
-
51
- listEl = root.querySelector('.event-stream-list');
52
- countEl = root.querySelector('.event-stream-count');
53
- autoscrollEl = root.querySelector('.event-autoscroll-toggle input');
54
- searchEl = root.querySelector('.event-stream-search');
55
-
56
- // ── Helpers ───────────────────────────────────────
57
- function formatTime(ts) {
58
- const d = ts instanceof Date ? ts : new Date(ts);
59
- return d.toTimeString().slice(0, 8);
60
- }
61
-
62
- function badgeClass(type) {
63
- switch (type) {
64
- case 'tool': return 'badge-tool';
65
- case 'result': return 'badge-result';
66
- case 'error': return 'badge-error';
67
- case 'done': return 'badge-done';
68
- default: return 'badge-tool';
69
- }
70
- }
71
-
72
- function badgeLabel(type) {
73
- switch (type) {
74
- case 'tool': return 'TOOL';
75
- case 'result': return 'OK';
76
- case 'error': return 'ERR';
77
- case 'done': return 'DONE';
78
- default: return type.toUpperCase();
79
- }
80
- }
81
-
82
- function renderEvent(evt) {
83
- const row = document.createElement('div');
84
- row.className = 'event-row';
85
- row.dataset.type = evt.type;
86
-
87
- const content = document.createElement('div');
88
- content.style.cssText = 'display:flex;align-items:flex-start;gap:8px;flex-grow:1;min-width:0;';
89
- content.innerHTML = `
90
- <span class="event-time">${formatTime(evt.timestamp)}</span>
91
- <span class="event-badge ${badgeClass(evt.type)}">${badgeLabel(evt.type)}</span>
92
- <span class="event-summary">${escapeHtml(evt.summary)}</span>
93
- `;
94
- row.appendChild(content);
95
-
96
- if (evt.detail) {
97
- const detail = document.createElement('div');
98
- detail.className = 'event-detail';
99
- detail.textContent = evt.detail;
100
- row.appendChild(detail);
101
- }
102
-
103
- row.addEventListener('click', () => row.classList.toggle('expanded'));
104
- return row;
105
- }
106
-
107
- function matchesFilter(evt) {
108
- if (activeFilter === 'all') return true;
109
- if (activeFilter === 'tool') return evt.type === 'tool';
110
- if (activeFilter === 'error') return evt.type === 'error';
111
- if (activeFilter === 'result') return evt.type === 'result' || evt.type === 'done';
112
- return true;
113
- }
114
-
115
- function matchesSearch(row) {
116
- if (!searchQuery) return true;
117
- return row.textContent.toLowerCase().includes(searchQuery.toLowerCase());
118
- }
119
-
120
- function applyFilters() {
121
- let visible = 0;
122
- for (const row of listEl.children) {
123
- const show = matchesFilter({ type: row.dataset.type }) && matchesSearch(row);
124
- row.style.display = show ? '' : 'none';
125
- if (show) visible++;
126
- }
127
- updateCount(visible);
128
- }
129
-
130
- function updateCount(count) {
131
- const n = count != null ? count : events.length;
132
- countEl.textContent = `${n} event${n !== 1 ? 's' : ''}`;
133
- ctx.showBadge(events.length);
134
- }
135
-
136
- function autoScroll() {
137
- if (autoscrollEl.checked) {
138
- listEl.scrollTop = listEl.scrollHeight;
139
- }
140
- }
141
-
142
- function addEvent(evt) {
143
- evt.timestamp = evt.timestamp || new Date();
144
- events.push(evt);
145
- // Remove empty state placeholder if present
146
- const emptyEl = listEl.querySelector('.event-stream-empty');
147
- if (emptyEl) emptyEl.remove();
148
- const row = renderEvent(evt);
149
- listEl.appendChild(row);
150
- if (!matchesFilter(evt) || !matchesSearch(row)) {
151
- row.style.display = 'none';
152
- }
153
- updateCount();
154
- autoScroll();
155
- }
156
-
157
- function clearEvents() {
158
- events = [];
159
- listEl.innerHTML = `
160
- <div class="event-stream-empty">
161
- <svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
162
- <polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/>
163
- </svg>
164
- <span>No events yet</span>
165
- <span class="event-stream-empty-hint">Events will appear here as the AI works</span>
166
- </div>`;
167
- updateCount(0);
168
- }
169
-
170
- async function loadSessionEvents(sessionId) {
171
- if (!sessionId) return;
172
- try {
173
- const messages = await (await fetch(`/api/sessions/${encodeURIComponent(sessionId)}/messages-single`)).json();
174
- for (const msg of messages) {
175
- const data = JSON.parse(msg.content);
176
- const ts = msg.created_at ? new Date(msg.created_at * 1000) : new Date();
177
- switch (msg.role) {
178
- case 'tool':
179
- addEvent({
180
- type: 'tool', timestamp: ts,
181
- summary: `${data.name}: ${getToolDetail(data.name, data.input) || '(no detail)'}`,
182
- detail: JSON.stringify(data.input, null, 2),
183
- toolName: data.name,
184
- });
185
- break;
186
- case 'tool_result':
187
- addEvent({
188
- type: data.isError ? 'error' : 'result', timestamp: ts,
189
- summary: (data.content || '').slice(0, 100),
190
- detail: data.content,
191
- });
192
- break;
193
- case 'result':
194
- addEvent({
195
- type: 'done', timestamp: ts,
196
- summary: `${data.model || ''} · ${data.num_turns || 0} turns · $${(data.cost_usd || 0).toFixed(4)}`,
197
- });
198
- break;
199
- case 'error':
200
- addEvent({ type: 'error', timestamp: ts, summary: data.error || 'Unknown error' });
201
- break;
202
- }
203
- }
204
- } catch (err) {
205
- console.error('Failed to load session events:', err);
206
- }
207
- }
208
-
209
- // ── UI bindings ──────────────────────────────────
210
- // Filter buttons
211
- const filterBtns = root.querySelectorAll('.event-filter-btn');
212
- for (const btn of filterBtns) {
213
- btn.addEventListener('click', () => {
214
- activeFilter = btn.dataset.filter;
215
- filterBtns.forEach(b => b.classList.toggle('active', b === btn));
216
- applyFilters();
217
- });
218
- }
219
-
220
- // Search
221
- let searchTimer = null;
222
- searchEl.addEventListener('input', () => {
223
- clearTimeout(searchTimer);
224
- searchTimer = setTimeout(() => {
225
- searchQuery = searchEl.value;
226
- applyFilters();
227
- }, 200);
228
- });
229
-
230
- // Clear
231
- root.querySelector('.event-stream-clear-btn').addEventListener('click', () => clearEvents());
232
-
233
- // ── Event listeners ──────────────────────────────
234
- ctx.on('ws:message', (msg) => {
235
- switch (msg.type) {
236
- case 'tool':
237
- addEvent({
238
- type: 'tool',
239
- summary: `${msg.name}: ${getToolDetail(msg.name, msg.input) || '(no detail)'}`,
240
- detail: JSON.stringify(msg.input, null, 2),
241
- toolName: msg.name,
242
- });
243
- break;
244
- case 'tool_result':
245
- addEvent({
246
- type: msg.isError ? 'error' : 'result',
247
- summary: (msg.content || '').slice(0, 100),
248
- detail: msg.content,
249
- });
250
- break;
251
- case 'result':
252
- addEvent({
253
- type: 'done',
254
- summary: `${msg.model || ''} · ${msg.num_turns || 0} turns · $${(msg.cost_usd || 0).toFixed(4)}`,
255
- });
256
- break;
257
- case 'error':
258
- addEvent({ type: 'error', summary: msg.error || 'Unknown error' });
259
- break;
260
- }
261
- });
262
-
263
- // Session switch
264
- ctx.onState('sessionId', (newId) => {
265
- clearEvents();
266
- if (newId) loadSessionEvents(newId);
267
- });
268
-
269
- return root;
270
- },
271
- });