claudeck 1.2.0 → 1.3.1

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 (42) hide show
  1. package/README.md +64 -5
  2. package/cli.js +53 -4
  3. package/package.json +3 -2
  4. package/public/css/core/responsive.css +2 -2
  5. package/public/css/ui/file-picker.css +243 -17
  6. package/public/css/ui/messages.css +72 -9
  7. package/public/css/ui/toolbox.css +43 -0
  8. package/public/index.html +80 -745
  9. package/public/js/components/add-project-modal.js +27 -0
  10. package/public/js/components/agent-modal.js +73 -0
  11. package/public/js/components/agent-monitor-modal.js +19 -0
  12. package/public/js/components/bg-confirm-modal.js +22 -0
  13. package/public/js/components/chain-modal.js +52 -0
  14. package/public/js/components/cost-dashboard-modal.js +39 -0
  15. package/public/js/components/dag-editor-modal.js +55 -0
  16. package/public/js/components/file-picker-modal.js +45 -0
  17. package/public/js/components/linear-create-modal.js +43 -0
  18. package/public/js/components/mcp-modal.js +58 -0
  19. package/public/js/components/orchestrate-modal.js +40 -0
  20. package/public/js/components/permission-modal.js +30 -0
  21. package/public/js/components/prompt-modal.js +31 -0
  22. package/public/js/components/shortcuts-modal.js +45 -0
  23. package/public/js/components/status-bar.js +97 -0
  24. package/public/js/components/system-prompt-modal.js +29 -0
  25. package/public/js/components/telegram-modal.js +84 -0
  26. package/public/js/components/welcome-overlay.js +60 -0
  27. package/public/js/components/workflow-modal.js +41 -0
  28. package/public/js/core/api.js +10 -0
  29. package/public/js/core/dom.js +3 -2
  30. package/public/js/core/ws.js +7 -1
  31. package/public/js/features/attachments.js +226 -23
  32. package/public/js/features/projects.js +7 -0
  33. package/public/js/main.js +22 -0
  34. package/public/js/ui/shortcuts.js +4 -8
  35. package/public/login.html +470 -0
  36. package/public/offline.html +300 -168
  37. package/public/sw.js +10 -2
  38. package/server/agent-loop.js +1 -0
  39. package/server/auth.js +141 -0
  40. package/server/orchestrator.js +1 -0
  41. package/server/ws-handler.js +2 -0
  42. package/server.js +14 -3
@@ -0,0 +1,470 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <meta name="theme-color" content="#020203">
7
+ <meta name="apple-mobile-web-app-capable" content="yes">
8
+ <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
9
+ <title>Claudeck :: authenticate</title>
10
+ <link rel="icon" type="image/png" href="/icons/favicon.png">
11
+ <link rel="preconnect" href="https://fonts.googleapis.com">
12
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
13
+ <link href="https://fonts.googleapis.com/css2?family=Chakra+Petch:wght@400;500;600;700&family=Outfit:wght@300;400;500&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
14
+ <style>
15
+ *,*::before,*::after{margin:0;padding:0;box-sizing:border-box}
16
+
17
+ :root{
18
+ --bg: #050508;
19
+ --bg-secondary: #0c0d10;
20
+ --bg-deep: #020203;
21
+ --bg-elevated: #181a22;
22
+ --border: #1e2028;
23
+ --border-subtle: #161820;
24
+ --border-accent: rgba(51,209,122,.12);
25
+ --text: #c4cfd9;
26
+ --text-sec: #6b7a8d;
27
+ --text-dim: #3a4250;
28
+ --accent: #33d17a;
29
+ --accent-dim: rgba(51,209,122,.08);
30
+ --accent-mid: rgba(51,209,122,.18);
31
+ --accent-glow: rgba(51,209,122,.25);
32
+ --success: #33d17a;
33
+ --error: #ed333b;
34
+ --purple: #c69ff5;
35
+ --font-display: "Chakra Petch","Rajdhani","Exo 2",sans-serif;
36
+ --font-sans: "Outfit","DM Sans","Segoe UI",sans-serif;
37
+ --font-mono: "JetBrains Mono","SF Mono","Fira Code","Cascadia Code","Consolas",monospace;
38
+ --radius: 4px;
39
+ --header-h: 40px;
40
+ }
41
+
42
+ html{height:100%}
43
+ body{
44
+ background:var(--bg);
45
+ color:var(--text);
46
+ font-family:var(--font-sans);
47
+ min-height:100vh;
48
+ display:flex;
49
+ flex-direction:column;
50
+ }
51
+
52
+ /* ═══════════════════════════════════════
53
+ HEADER — matches app top-header
54
+ ═══════════════════════════════════════ */
55
+ .top-header{
56
+ height:var(--header-h);
57
+ background:var(--bg-deep);
58
+ border-bottom:1px solid var(--border);
59
+ display:flex;
60
+ align-items:center;
61
+ padding:0 16px;
62
+ font-family:var(--font-mono);
63
+ font-size:12px;
64
+ gap:8px;
65
+ flex-shrink:0;
66
+ position:relative;
67
+ }
68
+ /* Gradient underline — same as app */
69
+ .top-header::after{
70
+ content:"";
71
+ position:absolute;
72
+ bottom:-1px;left:0;right:0;
73
+ height:1px;
74
+ background:linear-gradient(90deg, transparent, var(--accent-glow), transparent);
75
+ opacity:.3;
76
+ }
77
+ .term-prompt{ color:var(--success); font-weight:700; }
78
+ .term-cmd{ color:var(--accent); font-weight:600; font-family:var(--font-display); letter-spacing:.04em; }
79
+ .term-sep{ color:var(--border); margin:0 2px; }
80
+ .term-status{ color:var(--text-dim); font-size:11px; }
81
+ .header-lock{
82
+ margin-left:auto;
83
+ display:flex;align-items:center;gap:6px;
84
+ color:var(--text-dim);
85
+ font-size:11px;
86
+ }
87
+ .header-lock svg{ color:var(--text-dim); }
88
+
89
+ /* ═══════════════════════════════════════
90
+ MAIN — chat area mimic
91
+ ═══════════════════════════════════════ */
92
+ .main-area{
93
+ flex:1;
94
+ display:flex;
95
+ flex-direction:column;
96
+ align-items:center;
97
+ justify-content:center;
98
+ padding:40px 24px 80px;
99
+ gap:16px;
100
+ opacity:0;
101
+ animation:fadeIn .5s ease .1s forwards;
102
+ }
103
+ @keyframes fadeIn{to{opacity:1}}
104
+
105
+ /* Whaly — same style as .whaly-placeholder in the app */
106
+ .whaly-img{
107
+ width:120px;
108
+ height:auto;
109
+ image-rendering:pixelated;
110
+ filter:drop-shadow(0 8px 24px rgba(51,209,122,.08));
111
+ transition:filter .3s;
112
+ cursor:pointer;
113
+ }
114
+ .whaly-img:hover{
115
+ filter:drop-shadow(0 8px 32px rgba(51,209,122,.15));
116
+ }
117
+
118
+ .whaly-text{
119
+ color:var(--text-dim);
120
+ font-size:14px;
121
+ font-family:var(--font-display);
122
+ text-align:center;
123
+ letter-spacing:.04em;
124
+ font-weight:500;
125
+ }
126
+ .whaly-hint{
127
+ color:var(--text-dim);
128
+ font-size:11px;
129
+ font-family:var(--font-sans);
130
+ opacity:.5;
131
+ text-align:center;
132
+ }
133
+
134
+ /* ═══════════════════════════════════════
135
+ INPUT BAR — matches app chat input
136
+ ═══════════════════════════════════════ */
137
+ .input-bar{
138
+ position:fixed;
139
+ bottom:24px;
140
+ left:50%;
141
+ transform:translateX(-50%);
142
+ width:100%;
143
+ max-width:680px;
144
+ padding:0 20px;
145
+ opacity:0;
146
+ animation:slideUp .4s ease .3s forwards;
147
+ }
148
+ @keyframes slideUp{
149
+ from{opacity:0;transform:translateX(-50%) translateY(12px)}
150
+ to{opacity:1;transform:translateX(-50%) translateY(0)}
151
+ }
152
+
153
+ .input-wrap{
154
+ background:var(--bg-secondary);
155
+ border:1px solid var(--border);
156
+ border-radius:12px;
157
+ padding:4px;
158
+ transition:border-color .2s, box-shadow .2s;
159
+ }
160
+ .input-wrap:focus-within{
161
+ border-color:var(--accent);
162
+ box-shadow:0 0 0 1px var(--border-accent), 0 0 20px var(--accent-dim);
163
+ }
164
+
165
+ .input-row{
166
+ display:flex;
167
+ align-items:center;
168
+ gap:0;
169
+ }
170
+
171
+ .input-prompt{
172
+ color:var(--success);
173
+ font-family:var(--font-mono);
174
+ font-weight:700;
175
+ font-size:13px;
176
+ padding:0 6px 0 12px;
177
+ flex-shrink:0;
178
+ user-select:none;
179
+ }
180
+
181
+ .token-input{
182
+ flex:1;
183
+ background:transparent;
184
+ border:none;
185
+ color:var(--text);
186
+ font-family:var(--font-mono);
187
+ font-size:13px;
188
+ padding:12px 8px;
189
+ outline:none;
190
+ min-width:0;
191
+ }
192
+ .token-input::placeholder{
193
+ color:var(--text-dim);
194
+ }
195
+
196
+ .submit-btn{
197
+ width:36px;height:36px;
198
+ border-radius:8px;
199
+ border:none;
200
+ background:var(--accent);
201
+ color:var(--bg-deep);
202
+ cursor:pointer;
203
+ display:flex;align-items:center;justify-content:center;
204
+ flex-shrink:0;
205
+ transition:all .15s;
206
+ margin-right:2px;
207
+ }
208
+ .submit-btn:hover{
209
+ background:#2ec27e;
210
+ box-shadow:0 0 12px var(--accent-dim);
211
+ }
212
+ .submit-btn:disabled{
213
+ opacity:.3;
214
+ cursor:not-allowed;
215
+ }
216
+ .submit-btn svg{
217
+ width:18px;height:18px;
218
+ }
219
+
220
+ /* Error / success below input */
221
+ .input-feedback{
222
+ font-family:var(--font-mono);
223
+ font-size:11px;
224
+ min-height:20px;
225
+ padding:6px 16px 0;
226
+ text-align:center;
227
+ transition:color .2s;
228
+ }
229
+ .input-feedback.error{ color:var(--error); }
230
+ .input-feedback.success{ color:var(--success); }
231
+
232
+ /* ═══════════════════════════════════════
233
+ STATUS BAR — matches app exactly
234
+ ═══════════════════════════════════════ */
235
+ .status-bar{
236
+ height:24px;
237
+ background:var(--bg-deep);
238
+ border-top:1px solid var(--border);
239
+ display:flex;
240
+ align-items:center;
241
+ padding:0 12px;
242
+ font-family:var(--font-mono);
243
+ font-size:11px;
244
+ color:var(--text-dim);
245
+ flex-shrink:0;
246
+ user-select:none;
247
+ position:relative;
248
+ }
249
+ .status-bar::before{
250
+ content:"";
251
+ position:absolute;
252
+ top:-1px;left:0;right:0;
253
+ height:1px;
254
+ background:linear-gradient(90deg, transparent, var(--accent-glow), transparent);
255
+ opacity:.3;
256
+ }
257
+ .sb-left,.sb-right{
258
+ display:flex;align-items:center;gap:0;
259
+ }
260
+ .sb-left{flex:1;justify-content:flex-start;}
261
+ .sb-right{flex:1;justify-content:flex-end;}
262
+ .sb-item{
263
+ display:inline-flex;align-items:center;gap:4px;
264
+ padding:0 8px;height:24px;white-space:nowrap;
265
+ }
266
+ .sb-dot{
267
+ width:6px;height:6px;border-radius:50%;
268
+ background:var(--accent);flex-shrink:0;
269
+ animation:pulse 2s ease-in-out infinite;
270
+ }
271
+ @keyframes pulse{
272
+ 0%,100%{opacity:.6}
273
+ 50%{opacity:1}
274
+ }
275
+ .sb-sep{
276
+ width:1px;height:12px;
277
+ background:var(--border);flex-shrink:0;
278
+ }
279
+
280
+ /* ═══════════════════════════════════════
281
+ SHAKE
282
+ ═══════════════════════════════════════ */
283
+ @keyframes shake{
284
+ 0%,100%{transform:translateX(0)}
285
+ 20%{transform:translateX(-6px)}
286
+ 40%{transform:translateX(6px)}
287
+ 60%{transform:translateX(-3px)}
288
+ 80%{transform:translateX(3px)}
289
+ }
290
+ .shake{ animation:shake .35s ease-in-out; }
291
+
292
+ /* ═══════════════════════════════════════
293
+ LIGHT THEME — mirrors variables.css
294
+ ═══════════════════════════════════════ */
295
+ html[data-theme="light"]{
296
+ --bg: #f7f7f4;
297
+ --bg-secondary: #eeeee9;
298
+ --bg-deep: #eaeae4;
299
+ --bg-elevated: #ffffff;
300
+ --border: #c8c8c0;
301
+ --border-subtle: #d6d6cf;
302
+ --border-accent: rgba(26,138,74,.12);
303
+ --text: #1a1a18;
304
+ --text-sec: #4a4a44;
305
+ --text-dim: #8a8a80;
306
+ --accent: #1a8a4a;
307
+ --accent-dim: rgba(26,138,74,.06);
308
+ --accent-mid: rgba(26,138,74,.14);
309
+ --accent-glow: rgba(26,138,74,.15);
310
+ --success: #1a8a4a;
311
+ --error: #c41a22;
312
+ --purple: #6a40b0;
313
+ }
314
+ html[data-theme="light"] .top-header::after,
315
+ html[data-theme="light"] .status-bar::before{
316
+ opacity:0;
317
+ }
318
+ html[data-theme="light"] .input-wrap:focus-within{
319
+ box-shadow:0 0 0 1px var(--border-accent);
320
+ }
321
+ html[data-theme="light"] .submit-btn{
322
+ color:#fff;
323
+ }
324
+ html[data-theme="light"] .submit-btn:hover{
325
+ box-shadow:none;
326
+ }
327
+
328
+ /* ═══════════════════════════════════════
329
+ RESPONSIVE
330
+ ═══════════════════════════════════════ */
331
+ @media(max-width:640px){
332
+ .input-bar{max-width:100%;padding:0 12px;}
333
+ .whaly-img{width:80px}
334
+ .header-lock span{display:none}
335
+ }
336
+ </style>
337
+ <script>
338
+ // Apply saved theme before paint to prevent flash
339
+ const t = localStorage.getItem('claudeck-theme');
340
+ if (t) document.documentElement.setAttribute('data-theme', t);
341
+ </script>
342
+ </head>
343
+ <body>
344
+
345
+ <!-- Header — identical structure to the app -->
346
+ <header class="top-header">
347
+ <span class="term-prompt">&gt;</span>
348
+ <span class="term-cmd">claude</span>
349
+ <span class="term-sep">&middot;</span>
350
+ <span class="term-status">authenticate</span>
351
+ <div class="header-lock">
352
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
353
+ <rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>
354
+ </svg>
355
+ <span>auth required</span>
356
+ </div>
357
+ </header>
358
+
359
+ <!-- Main — mirrors the empty chat state -->
360
+ <main class="main-area">
361
+ <img src="/icons/whaly.png" alt="Whaly" class="whaly-img" id="whaly">
362
+ <div class="whaly-text">~ authenticate to continue ~</div>
363
+ <div class="whaly-hint">Enter the token from your terminal below</div>
364
+ </main>
365
+
366
+ <!-- Input bar — matches the chat input bar -->
367
+ <div class="input-bar">
368
+ <form id="loginForm" autocomplete="off">
369
+ <div class="input-wrap" id="inputWrap">
370
+ <div class="input-row">
371
+ <span class="input-prompt">&gt;</span>
372
+ <input
373
+ type="password"
374
+ class="token-input"
375
+ id="tokenInput"
376
+ placeholder="paste access token here"
377
+ autocomplete="off"
378
+ spellcheck="false"
379
+ autofocus
380
+ >
381
+ <button type="submit" class="submit-btn" id="loginBtn" aria-label="Authenticate">
382
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
383
+ <line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/>
384
+ </svg>
385
+ </button>
386
+ </div>
387
+ </div>
388
+ <div class="input-feedback" id="feedback"></div>
389
+ </form>
390
+ </div>
391
+
392
+ <!-- Status bar — matches the app's VS Code-style bar -->
393
+ <div class="status-bar">
394
+ <div class="sb-left">
395
+ <div class="sb-item">
396
+ <div class="sb-dot"></div>
397
+ waiting
398
+ </div>
399
+ <div class="sb-sep"></div>
400
+ <div class="sb-item">claudeck --auth</div>
401
+ </div>
402
+ <div class="sb-right">
403
+ <div class="sb-item">token required</div>
404
+ </div>
405
+ </div>
406
+
407
+ <script>
408
+ const form = document.getElementById('loginForm');
409
+ const input = document.getElementById('tokenInput');
410
+ const feedback = document.getElementById('feedback');
411
+ const loginBtn = document.getElementById('loginBtn');
412
+ const inputWrap = document.getElementById('inputWrap');
413
+ const whaly = document.getElementById('whaly');
414
+
415
+ // Whaly easter egg
416
+ let clicks = 0;
417
+ whaly.addEventListener('click', () => {
418
+ clicks++;
419
+ if (clicks >= 5) {
420
+ clicks = 0;
421
+ whaly.style.transition = 'transform .4s cubic-bezier(.34,1.56,.64,1)';
422
+ whaly.style.transform = 'scale(1.2) rotate(-10deg)';
423
+ setTimeout(() => { whaly.style.transform = ''; }, 500);
424
+ }
425
+ });
426
+
427
+ form.addEventListener('submit', async (e) => {
428
+ e.preventDefault();
429
+ const token = input.value.trim();
430
+ if (!token) {
431
+ feedback.textContent = 'enter a token to continue';
432
+ feedback.className = 'input-feedback error';
433
+ inputWrap.classList.add('shake');
434
+ setTimeout(() => inputWrap.classList.remove('shake'), 350);
435
+ return;
436
+ }
437
+
438
+ loginBtn.disabled = true;
439
+ feedback.textContent = 'verifying...';
440
+ feedback.className = 'input-feedback';
441
+
442
+ try {
443
+ const res = await fetch('/api/auth/login', {
444
+ method: 'POST',
445
+ headers: { 'Content-Type': 'application/json' },
446
+ body: JSON.stringify({ token }),
447
+ });
448
+
449
+ if (res.ok) {
450
+ feedback.textContent = 'access granted';
451
+ feedback.className = 'input-feedback success';
452
+ inputWrap.style.borderColor = 'var(--accent)';
453
+ setTimeout(() => { window.location.href = '/'; }, 500);
454
+ } else {
455
+ feedback.textContent = 'invalid token';
456
+ feedback.className = 'input-feedback error';
457
+ inputWrap.classList.add('shake');
458
+ setTimeout(() => inputWrap.classList.remove('shake'), 350);
459
+ input.select();
460
+ }
461
+ } catch {
462
+ feedback.textContent = 'connection error';
463
+ feedback.className = 'input-feedback error';
464
+ } finally {
465
+ loginBtn.disabled = false;
466
+ }
467
+ });
468
+ </script>
469
+ </body>
470
+ </html>