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,196 +0,0 @@
1
- /* Sudoku — Tab SDK plugin styles */
2
-
3
- .sudoku-tab {
4
- font-family: var(--font-mono);
5
- color: var(--text);
6
- padding: 12px;
7
- gap: 12px;
8
- align-items: center;
9
- outline: none;
10
- }
11
-
12
- /* ── Header ── */
13
- .sudoku-header {
14
- display: flex;
15
- justify-content: space-between;
16
- align-items: center;
17
- width: 100%;
18
- max-width: 320px;
19
- gap: 8px;
20
- }
21
-
22
- .sudoku-controls {
23
- display: flex;
24
- gap: 4px;
25
- }
26
-
27
- .sudoku-diff-btn {
28
- background: var(--bg-tertiary);
29
- color: var(--text-secondary);
30
- border: 1px solid var(--border);
31
- border-radius: var(--radius);
32
- padding: 3px 8px;
33
- font-size: 11px;
34
- font-family: var(--font-mono);
35
- cursor: pointer;
36
- transition: all 0.15s;
37
- }
38
-
39
- .sudoku-diff-btn:hover {
40
- background: var(--bg-secondary);
41
- color: var(--text);
42
- }
43
-
44
- .sudoku-diff-btn.active {
45
- background: var(--accent);
46
- color: var(--bg);
47
- border-color: var(--accent);
48
- }
49
-
50
- .sudoku-info {
51
- display: flex;
52
- gap: 10px;
53
- font-size: 11px;
54
- color: var(--text-dim);
55
- }
56
-
57
- .sudoku-timer {
58
- color: var(--accent);
59
- font-weight: 600;
60
- }
61
-
62
- /* ── Board ── */
63
- .sudoku-board-wrap {
64
- display: flex;
65
- justify-content: center;
66
- }
67
-
68
- .sudoku-grid {
69
- display: grid;
70
- grid-template-columns: repeat(9, 32px);
71
- grid-template-rows: repeat(9, 32px);
72
- border: 2px solid var(--text-secondary);
73
- border-radius: var(--radius);
74
- overflow: hidden;
75
- }
76
-
77
- .sudoku-cell {
78
- display: flex;
79
- align-items: center;
80
- justify-content: center;
81
- font-size: 14px;
82
- font-weight: 500;
83
- border: 1px solid var(--border);
84
- cursor: pointer;
85
- transition: background 0.1s;
86
- user-select: none;
87
- background: var(--bg-secondary);
88
- color: var(--accent);
89
- }
90
-
91
- .sudoku-cell.fixed {
92
- color: var(--text);
93
- font-weight: 700;
94
- }
95
-
96
- .sudoku-cell.in-scope {
97
- background: var(--bg-tertiary);
98
- }
99
-
100
- .sudoku-cell.highlight {
101
- background: rgba(99, 102, 241, 0.15);
102
- }
103
-
104
- .sudoku-cell.selected {
105
- background: rgba(99, 102, 241, 0.25);
106
- outline: 2px solid var(--accent);
107
- outline-offset: -2px;
108
- z-index: 1;
109
- }
110
-
111
- .sudoku-cell.error {
112
- color: var(--error);
113
- background: rgba(239, 68, 68, 0.1);
114
- }
115
-
116
- .sudoku-cell:hover {
117
- background: var(--bg-tertiary);
118
- }
119
-
120
- .sudoku-cell.selected:hover {
121
- background: rgba(99, 102, 241, 0.25);
122
- }
123
-
124
- /* Box borders — thicker lines at 3x3 boundaries */
125
- .sudoku-cell.box-left {
126
- border-left: 2px solid var(--text-secondary);
127
- }
128
-
129
- .sudoku-cell.box-top {
130
- border-top: 2px solid var(--text-secondary);
131
- }
132
-
133
- /* ── Number Pad ── */
134
- .sudoku-numpad {
135
- display: flex;
136
- gap: 4px;
137
- justify-content: center;
138
- max-width: 320px;
139
- }
140
-
141
- .sudoku-num-btn {
142
- width: 30px;
143
- height: 30px;
144
- display: flex;
145
- align-items: center;
146
- justify-content: center;
147
- background: var(--bg-tertiary);
148
- color: var(--text);
149
- border: 1px solid var(--border);
150
- border-radius: var(--radius);
151
- font-size: 13px;
152
- font-family: var(--font-mono);
153
- font-weight: 600;
154
- cursor: pointer;
155
- transition: all 0.15s;
156
- }
157
-
158
- .sudoku-num-btn:hover {
159
- background: var(--accent);
160
- color: var(--bg);
161
- border-color: var(--accent);
162
- }
163
-
164
- /* ── Actions ── */
165
- .sudoku-actions {
166
- display: flex;
167
- gap: 6px;
168
- justify-content: center;
169
- }
170
-
171
- .sudoku-action-btn {
172
- background: var(--bg-tertiary);
173
- color: var(--text-secondary);
174
- border: 1px solid var(--border);
175
- border-radius: var(--radius);
176
- padding: 4px 10px;
177
- font-size: 11px;
178
- font-family: var(--font-mono);
179
- cursor: pointer;
180
- transition: all 0.15s;
181
- }
182
-
183
- .sudoku-action-btn:hover {
184
- background: var(--bg-secondary);
185
- color: var(--text);
186
- border-color: var(--text-dim);
187
- }
188
-
189
- /* ── Message ── */
190
- .sudoku-message {
191
- text-align: center;
192
- font-size: 13px;
193
- font-weight: 600;
194
- color: var(--success);
195
- min-height: 20px;
196
- }
@@ -1,329 +0,0 @@
1
- // Sudoku — Tab SDK plugin
2
- // A mini sudoku game to play while waiting for tasks to finish
3
- import { registerTab } from '/js/ui/tab-sdk.js';
4
-
5
- registerTab({
6
- id: 'sudoku',
7
- title: 'Sudoku',
8
- icon: '<svg width="12" height="12" 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"/><line x1="9" y1="3" x2="9" y2="21"/><line x1="15" y1="3" x2="15" y2="21"/><line x1="3" y1="9" x2="21" y2="9"/><line x1="3" y1="15" x2="21" y2="15"/></svg>',
9
- lazy: true,
10
-
11
- init(ctx) {
12
- const root = document.createElement('div');
13
- root.className = 'sudoku-tab';
14
- root.style.cssText = 'display:flex;flex-direction:column;flex:1;overflow:hidden;';
15
-
16
- // ── State ──
17
- let board = []; // current board (0 = empty)
18
- let solution = []; // full solution
19
- let fixed = []; // true if cell was part of the puzzle
20
- let selectedCell = null;
21
- let errors = new Set();
22
- let timerInterval = null;
23
- let seconds = 0;
24
- let gameWon = false;
25
-
26
- // ── Sudoku Generator ──
27
- function shuffle(arr) {
28
- for (let i = arr.length - 1; i > 0; i--) {
29
- const j = Math.floor(Math.random() * (i + 1));
30
- [arr[i], arr[j]] = [arr[j], arr[i]];
31
- }
32
- return arr;
33
- }
34
-
35
- function isValid(grid, row, col, num) {
36
- for (let i = 0; i < 9; i++) {
37
- if (grid[row][i] === num || grid[i][col] === num) return false;
38
- }
39
- const br = Math.floor(row / 3) * 3, bc = Math.floor(col / 3) * 3;
40
- for (let r = br; r < br + 3; r++)
41
- for (let c = bc; c < bc + 3; c++)
42
- if (grid[r][c] === num) return false;
43
- return true;
44
- }
45
-
46
- function solve(grid) {
47
- for (let r = 0; r < 9; r++) {
48
- for (let c = 0; c < 9; c++) {
49
- if (grid[r][c] === 0) {
50
- const nums = shuffle([1,2,3,4,5,6,7,8,9]);
51
- for (const n of nums) {
52
- if (isValid(grid, r, c, n)) {
53
- grid[r][c] = n;
54
- if (solve(grid)) return true;
55
- grid[r][c] = 0;
56
- }
57
- }
58
- return false;
59
- }
60
- }
61
- }
62
- return true;
63
- }
64
-
65
- function generatePuzzle(clues = 36) {
66
- const grid = Array.from({length: 9}, () => Array(9).fill(0));
67
- solve(grid);
68
- solution = grid.map(r => [...r]);
69
-
70
- board = grid.map(r => [...r]);
71
- fixed = Array.from({length: 9}, () => Array(9).fill(true));
72
-
73
- const cells = shuffle([...Array(81).keys()]);
74
- const toRemove = 81 - clues;
75
- for (let i = 0; i < toRemove; i++) {
76
- const r = Math.floor(cells[i] / 9), c = cells[i] % 9;
77
- board[r][c] = 0;
78
- fixed[r][c] = false;
79
- }
80
- }
81
-
82
- // ── Timer ──
83
- function startTimer() {
84
- stopTimer();
85
- seconds = 0;
86
- gameWon = false;
87
- timerInterval = setInterval(() => {
88
- if (!gameWon) {
89
- seconds++;
90
- const el = root.querySelector('.sudoku-timer');
91
- if (el) el.textContent = formatTime(seconds);
92
- }
93
- }, 1000);
94
- }
95
-
96
- function stopTimer() {
97
- if (timerInterval) { clearInterval(timerInterval); timerInterval = null; }
98
- }
99
-
100
- function formatTime(s) {
101
- const m = Math.floor(s / 60);
102
- return `${m}:${String(s % 60).padStart(2, '0')}`;
103
- }
104
-
105
- // ── Render ──
106
- function render() {
107
- const gridEl = root.querySelector('.sudoku-grid');
108
- if (!gridEl) return;
109
- gridEl.innerHTML = '';
110
-
111
- for (let r = 0; r < 9; r++) {
112
- for (let c = 0; c < 9; c++) {
113
- const cell = document.createElement('div');
114
- cell.className = 'sudoku-cell';
115
- cell.dataset.row = r;
116
- cell.dataset.col = c;
117
-
118
- if (fixed[r][c]) cell.classList.add('fixed');
119
- if (selectedCell && selectedCell[0] === r && selectedCell[1] === c) cell.classList.add('selected');
120
- if (errors.has(`${r},${c}`)) cell.classList.add('error');
121
-
122
- // Highlight same number
123
- if (selectedCell && board[r][c] !== 0 && board[selectedCell[0]][selectedCell[1]] === board[r][c]) {
124
- cell.classList.add('highlight');
125
- }
126
- // Highlight same row/col/box
127
- if (selectedCell) {
128
- const [sr, sc] = selectedCell;
129
- const sameRow = r === sr;
130
- const sameCol = c === sc;
131
- const sameBox = Math.floor(r/3) === Math.floor(sr/3) && Math.floor(c/3) === Math.floor(sc/3);
132
- if (sameRow || sameCol || sameBox) cell.classList.add('in-scope');
133
- }
134
-
135
- // Box borders
136
- if (c % 3 === 0 && c !== 0) cell.classList.add('box-left');
137
- if (r % 3 === 0 && r !== 0) cell.classList.add('box-top');
138
-
139
- cell.textContent = board[r][c] || '';
140
- gridEl.appendChild(cell);
141
- }
142
- }
143
-
144
- // Update remaining count
145
- const remaining = board.flat().filter(v => v === 0).length;
146
- const remEl = root.querySelector('.sudoku-remaining');
147
- if (remEl) remEl.textContent = `${remaining} empty`;
148
- }
149
-
150
- function checkErrors() {
151
- errors.clear();
152
- for (let r = 0; r < 9; r++) {
153
- for (let c = 0; c < 9; c++) {
154
- if (board[r][c] === 0) continue;
155
- const val = board[r][c];
156
- // Check row
157
- for (let i = 0; i < 9; i++) {
158
- if (i !== c && board[r][i] === val) { errors.add(`${r},${c}`); errors.add(`${r},${i}`); }
159
- }
160
- // Check col
161
- for (let i = 0; i < 9; i++) {
162
- if (i !== r && board[i][c] === val) { errors.add(`${r},${c}`); errors.add(`${i},${c}`); }
163
- }
164
- // Check box
165
- const br = Math.floor(r/3)*3, bc = Math.floor(c/3)*3;
166
- for (let rr = br; rr < br+3; rr++)
167
- for (let cc = bc; cc < bc+3; cc++)
168
- if ((rr !== r || cc !== c) && board[rr][cc] === val) { errors.add(`${r},${c}`); errors.add(`${rr},${cc}`); }
169
- }
170
- }
171
- }
172
-
173
- function checkWin() {
174
- if (board.flat().some(v => v === 0)) return false;
175
- if (errors.size > 0) return false;
176
- return true;
177
- }
178
-
179
- function newGame(difficulty) {
180
- const clues = difficulty === 'easy' ? 42 : difficulty === 'medium' ? 33 : 26;
181
- generatePuzzle(clues);
182
- selectedCell = null;
183
- errors.clear();
184
- gameWon = false;
185
- startTimer();
186
- render();
187
- const msg = root.querySelector('.sudoku-message');
188
- if (msg) msg.textContent = '';
189
- // Update active difficulty button
190
- root.querySelectorAll('.sudoku-diff-btn').forEach(b => {
191
- b.classList.toggle('active', b.dataset.diff === difficulty);
192
- });
193
- }
194
-
195
- // ── Build DOM ──
196
- root.innerHTML = `
197
- <div class="sudoku-header">
198
- <div class="sudoku-controls">
199
- <button class="sudoku-diff-btn active" data-diff="easy">Easy</button>
200
- <button class="sudoku-diff-btn" data-diff="medium">Medium</button>
201
- <button class="sudoku-diff-btn" data-diff="hard">Hard</button>
202
- </div>
203
- <div class="sudoku-info">
204
- <span class="sudoku-timer">0:00</span>
205
- <span class="sudoku-remaining">0 empty</span>
206
- </div>
207
- </div>
208
- <div class="sudoku-board-wrap">
209
- <div class="sudoku-grid"></div>
210
- </div>
211
- <div class="sudoku-numpad"></div>
212
- <div class="sudoku-actions">
213
- <button class="sudoku-action-btn" data-action="hint">Hint</button>
214
- <button class="sudoku-action-btn" data-action="erase">Erase</button>
215
- <button class="sudoku-action-btn" data-action="check">Check</button>
216
- <button class="sudoku-action-btn" data-action="new">New Game</button>
217
- </div>
218
- <div class="sudoku-message"></div>
219
- `;
220
-
221
- // Number pad
222
- const numpad = root.querySelector('.sudoku-numpad');
223
- for (let n = 1; n <= 9; n++) {
224
- const btn = document.createElement('button');
225
- btn.className = 'sudoku-num-btn';
226
- btn.textContent = n;
227
- btn.addEventListener('click', () => placeNumber(n));
228
- numpad.appendChild(btn);
229
- }
230
-
231
- function placeNumber(n) {
232
- if (!selectedCell || gameWon) return;
233
- const [r, c] = selectedCell;
234
- if (fixed[r][c]) return;
235
- board[r][c] = n;
236
- checkErrors();
237
- render();
238
- if (checkWin()) {
239
- gameWon = true;
240
- stopTimer();
241
- const msg = root.querySelector('.sudoku-message');
242
- if (msg) msg.textContent = `Solved in ${formatTime(seconds)}!`;
243
- }
244
- }
245
-
246
- // ── Grid click ──
247
- root.querySelector('.sudoku-grid').addEventListener('click', (e) => {
248
- const cell = e.target.closest('.sudoku-cell');
249
- if (!cell || gameWon) return;
250
- selectedCell = [+cell.dataset.row, +cell.dataset.col];
251
- render();
252
- });
253
-
254
- // ── Action buttons ──
255
- root.querySelector('.sudoku-actions').addEventListener('click', (e) => {
256
- const btn = e.target.closest('.sudoku-action-btn');
257
- if (!btn) return;
258
- const action = btn.dataset.action;
259
-
260
- if (action === 'new') {
261
- const activeDiff = root.querySelector('.sudoku-diff-btn.active');
262
- newGame(activeDiff ? activeDiff.dataset.diff : 'easy');
263
- } else if (action === 'erase') {
264
- if (!selectedCell) return;
265
- const [r, c] = selectedCell;
266
- if (!fixed[r][c]) { board[r][c] = 0; checkErrors(); render(); }
267
- } else if (action === 'hint') {
268
- if (!selectedCell || gameWon) return;
269
- const [r, c] = selectedCell;
270
- if (fixed[r][c] || board[r][c] === solution[r][c]) return;
271
- board[r][c] = solution[r][c];
272
- fixed[r][c] = true;
273
- checkErrors();
274
- render();
275
- if (checkWin()) {
276
- gameWon = true;
277
- stopTimer();
278
- const msg = root.querySelector('.sudoku-message');
279
- if (msg) msg.textContent = `Solved in ${formatTime(seconds)}!`;
280
- }
281
- } else if (action === 'check') {
282
- checkErrors();
283
- render();
284
- const msg = root.querySelector('.sudoku-message');
285
- if (msg) msg.textContent = errors.size === 0 ? 'No errors found!' : `${errors.size} conflicting cells`;
286
- setTimeout(() => { if (msg) msg.textContent = ''; }, 2000);
287
- }
288
- });
289
-
290
- // ── Difficulty buttons ──
291
- root.querySelector('.sudoku-controls').addEventListener('click', (e) => {
292
- const btn = e.target.closest('.sudoku-diff-btn');
293
- if (!btn) return;
294
- newGame(btn.dataset.diff);
295
- });
296
-
297
- // ── Keyboard input ──
298
- root.addEventListener('keydown', (e) => {
299
- if (!selectedCell || gameWon) return;
300
- const [r, c] = selectedCell;
301
-
302
- if (e.key >= '1' && e.key <= '9') {
303
- placeNumber(+e.key);
304
- } else if (e.key === 'Backspace' || e.key === 'Delete' || e.key === '0') {
305
- if (!fixed[r][c]) { board[r][c] = 0; checkErrors(); render(); }
306
- } else if (e.key === 'ArrowUp' && r > 0) { selectedCell = [r-1, c]; render(); }
307
- else if (e.key === 'ArrowDown' && r < 8) { selectedCell = [r+1, c]; render(); }
308
- else if (e.key === 'ArrowLeft' && c > 0) { selectedCell = [r, c-1]; render(); }
309
- else if (e.key === 'ArrowRight' && c < 8) { selectedCell = [r, c+1]; render(); }
310
- });
311
-
312
- // Make root focusable for keyboard events
313
- root.tabIndex = 0;
314
-
315
- // Start first game
316
- newGame('easy');
317
-
318
- return root;
319
- },
320
-
321
- onActivate() {
322
- const root = document.querySelector('.sudoku-tab');
323
- if (root) root.focus();
324
- },
325
-
326
- onDestroy() {
327
- // Timer cleanup handled by closure
328
- },
329
- });