devglide 0.1.2 → 0.1.4

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 (38) hide show
  1. package/package.json +5 -1
  2. package/src/apps/kanban/src/index.ts +1 -1
  3. package/src/apps/log/.turbo/turbo-lint.log +2 -2
  4. package/src/apps/log/src/index.ts +1 -1
  5. package/src/apps/prompts/.turbo/turbo-lint.log +3 -4
  6. package/src/apps/prompts/src/index.ts +1 -1
  7. package/src/apps/shell/.turbo/turbo-lint.log +5 -5
  8. package/src/apps/shell/src/index.ts +1 -1
  9. package/src/apps/test/.turbo/turbo-lint.log +2 -2
  10. package/src/apps/test/src/index.ts +1 -1
  11. package/src/apps/vocabulary/.turbo/turbo-lint.log +3 -4
  12. package/src/apps/vocabulary/src/index.ts +1 -1
  13. package/src/apps/voice/.turbo/turbo-lint.log +2 -2
  14. package/src/apps/voice/src/index.ts +1 -1
  15. package/src/apps/workflow/.turbo/turbo-lint.log +3 -4
  16. package/src/apps/workflow/src/index.ts +1 -1
  17. package/src/project-context.ts +36 -0
  18. package/src/public/app.js +701 -0
  19. package/src/public/favicon.svg +7 -0
  20. package/src/public/index.html +78 -0
  21. package/src/public/state.js +84 -0
  22. package/src/public/style.css +1213 -0
  23. package/src/routers/coder.ts +157 -0
  24. package/src/routers/dashboard.ts +158 -0
  25. package/src/routers/kanban.ts +38 -0
  26. package/src/routers/log.ts +42 -0
  27. package/src/routers/prompts.ts +134 -0
  28. package/src/routers/shell/index.ts +47 -0
  29. package/src/routers/shell/pty-manager.ts +107 -0
  30. package/src/routers/shell/shell-config.ts +38 -0
  31. package/src/routers/shell/shell-routes.ts +108 -0
  32. package/src/routers/shell/shell-socket.ts +321 -0
  33. package/src/routers/shell/shell-state.ts +59 -0
  34. package/src/routers/test.ts +254 -0
  35. package/src/routers/vocabulary.ts +149 -0
  36. package/src/routers/voice.ts +10 -0
  37. package/src/routers/workflow.ts +243 -0
  38. package/src/server.ts +325 -0
@@ -0,0 +1,1213 @@
1
+ *,
2
+ *::before,
3
+ *::after {
4
+ margin: 0;
5
+ padding: 0;
6
+ box-sizing: border-box;
7
+ }
8
+
9
+ :root {
10
+ --sidebar-width: 200px;
11
+ --topbar-height: 48px;
12
+ }
13
+
14
+ @media (min-width: 1440px) {
15
+ :root { --sidebar-width: 240px; }
16
+ }
17
+
18
+ @media (min-width: 1920px) {
19
+ :root { --sidebar-width: 280px; }
20
+ }
21
+
22
+ html, body {
23
+ height: 100%;
24
+ overflow: hidden;
25
+ }
26
+
27
+ body {
28
+ font-family: var(--df-font-mono);
29
+ background: var(--df-color-bg-base);
30
+ color: var(--df-color-text-primary);
31
+ -webkit-font-smoothing: antialiased;
32
+ font-size: var(--df-font-size-md);
33
+ }
34
+
35
+ /* ── Mobile top bar ──────────────────────────────────────────────────────── */
36
+
37
+ .mobile-topbar {
38
+ display: none;
39
+ align-items: center;
40
+ gap: var(--df-space-3);
41
+ height: var(--topbar-height);
42
+ padding: 0 var(--df-space-4);
43
+ background: var(--df-color-bg-sunken);
44
+ border-bottom: 1px solid var(--df-color-border-default);
45
+ position: fixed;
46
+ top: 0;
47
+ left: 0;
48
+ right: 0;
49
+ z-index: 30;
50
+ }
51
+
52
+ .hamburger {
53
+ display: flex;
54
+ flex-direction: column;
55
+ justify-content: center;
56
+ gap: 4px;
57
+ width: 28px;
58
+ height: 28px;
59
+ background: none;
60
+ border: none;
61
+ cursor: pointer;
62
+ padding: 2px;
63
+ flex-shrink: 0;
64
+ }
65
+
66
+ .hamburger span {
67
+ display: block;
68
+ height: 2px;
69
+ background: var(--df-color-text-primary);
70
+ transition: transform var(--df-duration-base), opacity var(--df-duration-base);
71
+ }
72
+
73
+ .mobile-title {
74
+ font-size: var(--df-font-size-sm);
75
+ font-weight: 400;
76
+ letter-spacing: var(--df-letter-spacing-wide);
77
+ text-transform: uppercase;
78
+ color: var(--df-color-text-primary);
79
+ overflow: hidden;
80
+ text-overflow: ellipsis;
81
+ white-space: nowrap;
82
+ }
83
+
84
+ /* ── Layout ──────────────────────────────────────────────────────────────── */
85
+
86
+ .layout {
87
+ display: flex;
88
+ height: 100vh;
89
+ }
90
+
91
+ /* ── Overlay ─────────────────────────────────────────────────────────────── */
92
+
93
+ .overlay {
94
+ display: none;
95
+ position: fixed;
96
+ inset: 0;
97
+ background: color-mix(in srgb, black 70%, transparent);
98
+ z-index: 19;
99
+ }
100
+
101
+ .overlay.visible {
102
+ display: block;
103
+ }
104
+
105
+ /* ── Sidebar ─────────────────────────────────────────────────────────────── */
106
+
107
+ .sidebar {
108
+ width: var(--sidebar-width);
109
+ flex-shrink: 0;
110
+ background: var(--df-color-bg-sunken);
111
+ border-right: 1px solid var(--df-color-border-default);
112
+ display: flex;
113
+ flex-direction: column;
114
+ overflow: hidden;
115
+ z-index: 20;
116
+ position: relative;
117
+ }
118
+
119
+ /* Corner brackets on sidebar */
120
+ .sidebar::before,
121
+ .sidebar::after {
122
+ content: '';
123
+ position: absolute;
124
+ width: 10px;
125
+ height: 10px;
126
+ border-color: var(--df-color-accent-default);
127
+ border-style: solid;
128
+ pointer-events: none;
129
+ opacity: 0.5;
130
+ z-index: 1;
131
+ }
132
+
133
+ .sidebar::before { top: 0; left: 0; border-width: 2px 0 0 2px; }
134
+ .sidebar::after { bottom: 0; right: 0; border-width: 0 2px 2px 0; }
135
+
136
+ .sidebar-brand {
137
+ display: flex;
138
+ align-items: center;
139
+ gap: var(--df-space-3);
140
+ padding: var(--df-space-4) var(--df-space-3);
141
+ border-bottom: 1px solid var(--df-color-border-default);
142
+ }
143
+
144
+ .brand-name {
145
+ font-size: var(--df-font-size-sm);
146
+ font-weight: 400;
147
+ letter-spacing: var(--df-letter-spacing-wide);
148
+ text-transform: uppercase;
149
+ color: var(--df-color-text-primary);
150
+ }
151
+
152
+ /* ── Sidebar voice widget ────────────────────────────────────────────────── */
153
+
154
+ .sidebar-voice {
155
+ margin-left: auto;
156
+ position: relative;
157
+ }
158
+
159
+ /* ── Service nav ─────────────────────────────────────────────────────────── */
160
+
161
+ .service-nav {
162
+ flex: 1;
163
+ padding: var(--df-space-2) 0;
164
+ overflow-y: auto;
165
+ }
166
+
167
+ .nav-section + .nav-section {
168
+ border-top: 1px solid var(--df-color-border-default);
169
+ margin-top: var(--df-space-2);
170
+ padding-top: var(--df-space-2);
171
+ }
172
+
173
+ .nav-section-label {
174
+ padding: var(--df-space-2) var(--df-space-3) var(--df-space-1);
175
+ font-size: 10px;
176
+ font-weight: 400;
177
+ letter-spacing: 0.12em;
178
+ text-transform: uppercase;
179
+ color: var(--df-color-text-muted);
180
+ user-select: none;
181
+ }
182
+
183
+ .service-item {
184
+ width: 100%;
185
+ display: grid;
186
+ grid-template-columns: 14px 16px 1fr;
187
+ grid-template-rows: auto auto;
188
+ column-gap: var(--df-space-2);
189
+ row-gap: 2px;
190
+ align-items: center;
191
+ padding: var(--df-space-2) var(--df-space-3);
192
+ background: none;
193
+ border: none;
194
+ color: var(--df-color-text-primary);
195
+ font-family: inherit;
196
+ font-size: var(--df-font-size-sm);
197
+ cursor: pointer;
198
+ text-align: left;
199
+ border-left: 2px solid transparent;
200
+ transition: background var(--df-duration-fast), border-color var(--df-duration-fast);
201
+ }
202
+
203
+ .service-item:hover {
204
+ background: color-mix(in srgb, var(--df-color-accent-default) 4%, transparent);
205
+ }
206
+
207
+ .service-item.active {
208
+ background: color-mix(in srgb, var(--df-color-accent-default) 7%, transparent);
209
+ border-left-color: var(--df-color-accent-default);
210
+ }
211
+
212
+ .service-item.disabled {
213
+ opacity: 0.35;
214
+ cursor: not-allowed;
215
+ }
216
+
217
+ .service-item.disabled:hover {
218
+ background: none;
219
+ }
220
+
221
+ .service-item.disabled .service-icon,
222
+ .service-item.disabled .service-name,
223
+ .service-item.disabled .service-desc {
224
+ color: var(--df-color-text-muted);
225
+ }
226
+
227
+ .drag-handle {
228
+ grid-column: 1;
229
+ grid-row: 1 / 3;
230
+ display: flex;
231
+ align-items: center;
232
+ justify-content: center;
233
+ font-size: var(--df-font-size-md);
234
+ color: var(--df-color-text-muted);
235
+ cursor: grab;
236
+ user-select: none;
237
+ line-height: 1;
238
+ transition: color var(--df-duration-fast);
239
+ }
240
+
241
+ .drag-handle:active {
242
+ cursor: grabbing;
243
+ }
244
+
245
+ .service-item:hover .drag-handle {
246
+ color: var(--df-color-text-secondary);
247
+ }
248
+
249
+ .service-icon {
250
+ grid-column: 2;
251
+ grid-row: 1 / 3;
252
+ display: flex;
253
+ align-items: center;
254
+ justify-content: center;
255
+ font-size: var(--df-font-size-sm);
256
+ color: var(--df-color-text-muted);
257
+ line-height: 1;
258
+ }
259
+
260
+ .service-item.active .service-icon {
261
+ color: var(--df-color-accent-default);
262
+ }
263
+
264
+ .service-name {
265
+ font-weight: 400;
266
+ letter-spacing: var(--df-letter-spacing-wide);
267
+ text-transform: uppercase;
268
+ color: var(--df-color-text-primary);
269
+ grid-column: 3;
270
+ grid-row: 1;
271
+ }
272
+
273
+ .service-item.active .service-name {
274
+ color: var(--df-color-accent-default);
275
+ }
276
+
277
+ .service-desc {
278
+ font-size: var(--df-font-size-xs);
279
+ color: var(--df-color-text-secondary);
280
+ letter-spacing: var(--df-letter-spacing-normal);
281
+ grid-column: 3;
282
+ grid-row: 2;
283
+ }
284
+
285
+ .service-item.dragging {
286
+ opacity: 0.4;
287
+ }
288
+
289
+ .service-item.drag-over-top {
290
+ border-top: 2px solid var(--df-color-accent-default);
291
+ }
292
+
293
+ .service-item.drag-over-bottom {
294
+ border-bottom: 2px solid var(--df-color-accent-default);
295
+ }
296
+
297
+ /* ── Content ─────────────────────────────────────────────────────────────── */
298
+
299
+ .content {
300
+ flex: 1;
301
+ position: relative;
302
+ overflow: hidden;
303
+ background: var(--df-color-bg-base);
304
+ }
305
+
306
+ .app-content {
307
+ position: absolute;
308
+ inset: 0;
309
+ overflow: auto;
310
+ }
311
+
312
+ /* ── App loading state ───────────────────────────────────────────────────── */
313
+
314
+ .app-loading {
315
+ display: flex;
316
+ align-items: center;
317
+ justify-content: center;
318
+ height: 100%;
319
+ font-size: var(--df-font-size-sm);
320
+ color: var(--df-color-text-muted);
321
+ letter-spacing: var(--df-letter-spacing-wide);
322
+ text-transform: uppercase;
323
+ }
324
+
325
+ /* ── Focus rings (WCAG 2.2) ──────────────────────────────────────────────── */
326
+
327
+ button:focus-visible,
328
+ a:focus-visible,
329
+ [tabindex]:focus-visible {
330
+ outline: var(--df-focus-ring-width) solid var(--df-focus-ring-color);
331
+ outline-offset: var(--df-focus-ring-offset);
332
+ }
333
+
334
+ /* ── Utility ─────────────────────────────────────────────────────────────── */
335
+
336
+ .hidden {
337
+ display: none !important;
338
+ }
339
+
340
+ /* ── Project bar ──────────────────────────────────────────────────────────── */
341
+
342
+ .project-bar {
343
+ position: relative;
344
+ padding: 0 var(--df-space-3);
345
+ margin-bottom: var(--df-space-2);
346
+ }
347
+
348
+ .project-selector {
349
+ display: flex;
350
+ align-items: center;
351
+ justify-content: space-between;
352
+ width: 100%;
353
+ padding: var(--df-space-2) var(--df-space-3);
354
+ background: var(--df-color-bg-raised);
355
+ border: 1px solid var(--df-color-border-default);
356
+ color: var(--df-color-text-primary);
357
+ font-family: var(--df-font-mono);
358
+ font-size: var(--df-font-size-sm);
359
+ cursor: pointer;
360
+ transition: border-color var(--df-duration-fast);
361
+ }
362
+
363
+ .project-selector:hover {
364
+ border-color: var(--df-color-accent-default);
365
+ }
366
+
367
+ .project-selector.has-project {
368
+ border-color: color-mix(in srgb, var(--df-color-accent-default) 40%, transparent);
369
+ }
370
+
371
+ .project-selector.has-project .project-name {
372
+ color: var(--df-color-accent-default);
373
+ }
374
+
375
+ .project-name {
376
+ overflow: hidden;
377
+ text-overflow: ellipsis;
378
+ white-space: nowrap;
379
+ }
380
+
381
+ .project-chevron {
382
+ font-size: var(--df-font-size-xs);
383
+ color: var(--df-color-text-secondary);
384
+ flex-shrink: 0;
385
+ margin-left: var(--df-space-2);
386
+ }
387
+
388
+ .project-dropdown {
389
+ display: none;
390
+ position: absolute;
391
+ left: var(--df-space-3);
392
+ right: var(--df-space-3);
393
+ top: 100%;
394
+ background: var(--df-color-bg-overlay);
395
+ border: 1px solid var(--df-color-border-default);
396
+ box-shadow: var(--df-shadow-lg);
397
+ z-index: 100;
398
+ max-height: 240px;
399
+ overflow-y: auto;
400
+ }
401
+
402
+ .project-dropdown.open {
403
+ display: block;
404
+ }
405
+
406
+ .project-item {
407
+ display: block;
408
+ width: 100%;
409
+ padding: var(--df-space-2) var(--df-space-3);
410
+ background: none;
411
+ border: none;
412
+ border-bottom: 1px solid var(--df-color-border-default);
413
+ color: var(--df-color-text-primary);
414
+ font-family: var(--df-font-mono);
415
+ font-size: var(--df-font-size-sm);
416
+ cursor: pointer;
417
+ text-align: left;
418
+ transition: background var(--df-duration-fast);
419
+ }
420
+
421
+ .project-item:last-child {
422
+ border-bottom: none;
423
+ }
424
+
425
+ .project-item:hover {
426
+ background: color-mix(in srgb, var(--df-color-accent-default) 10%, transparent);
427
+ }
428
+
429
+ .project-item.active {
430
+ background: color-mix(in srgb, var(--df-color-accent-default) 20%, transparent);
431
+ border-left: 2px solid var(--df-color-accent-default);
432
+ }
433
+
434
+ .project-item.active .project-item-name {
435
+ color: var(--df-color-accent-default);
436
+ }
437
+
438
+ .project-item-name {
439
+ font-weight: 500;
440
+ }
441
+
442
+ .project-item-path {
443
+ font-size: var(--df-font-size-xs);
444
+ color: var(--df-color-text-secondary);
445
+ overflow: hidden;
446
+ text-overflow: ellipsis;
447
+ white-space: nowrap;
448
+ }
449
+
450
+ .project-add {
451
+ color: var(--df-color-accent-default);
452
+ font-weight: 500;
453
+ }
454
+
455
+ /* ── Project item row layout ─────────────────────────────────────────────── */
456
+
457
+ .project-item-row {
458
+ display: flex;
459
+ align-items: center;
460
+ gap: var(--df-space-2);
461
+ }
462
+
463
+ .project-item-info {
464
+ flex: 1;
465
+ min-width: 0;
466
+ display: flex;
467
+ flex-direction: column;
468
+ }
469
+
470
+ .project-item-actions {
471
+ display: flex;
472
+ gap: var(--df-space-1);
473
+ flex-shrink: 0;
474
+ opacity: 0;
475
+ transition: opacity var(--df-duration-fast);
476
+ }
477
+
478
+ .project-item:hover .project-item-actions {
479
+ opacity: 1;
480
+ }
481
+
482
+ .project-item-action {
483
+ width: 24px;
484
+ height: 24px;
485
+ display: flex;
486
+ align-items: center;
487
+ justify-content: center;
488
+ background: none;
489
+ border: 1px solid transparent;
490
+ color: var(--df-color-text-secondary);
491
+ font-size: var(--df-font-size-sm);
492
+ cursor: pointer;
493
+ transition: color var(--df-duration-fast), border-color var(--df-duration-fast);
494
+ }
495
+
496
+ .project-item-action:hover {
497
+ color: var(--df-color-accent-default);
498
+ border-color: var(--df-color-border-default);
499
+ }
500
+
501
+ .project-item-action.delete:hover {
502
+ color: var(--df-color-state-error);
503
+ border-color: color-mix(in srgb, var(--df-color-state-error) 40%, transparent);
504
+ }
505
+
506
+ /* Delete confirmation inline */
507
+ .project-delete-confirm {
508
+ display: flex;
509
+ align-items: center;
510
+ gap: var(--df-space-2);
511
+ padding: var(--df-space-2) var(--df-space-3);
512
+ font-size: var(--df-font-size-xs);
513
+ color: var(--df-color-state-error);
514
+ }
515
+
516
+ .project-delete-confirm span {
517
+ flex: 1;
518
+ overflow: hidden;
519
+ text-overflow: ellipsis;
520
+ white-space: nowrap;
521
+ }
522
+
523
+ /* ── Project modal ─────────────────────────────────────────────────────── */
524
+
525
+ .project-modal-overlay {
526
+ position: fixed;
527
+ inset: 0;
528
+ background: color-mix(in srgb, black 70%, transparent);
529
+ z-index: 1000;
530
+ display: flex;
531
+ align-items: center;
532
+ justify-content: center;
533
+ }
534
+
535
+ .project-modal {
536
+ background: var(--df-color-bg-surface);
537
+ border: 1px solid var(--df-color-border-default);
538
+ width: 400px;
539
+ max-width: 90vw;
540
+ padding: var(--df-space-5);
541
+ box-shadow: var(--df-shadow-lg);
542
+ }
543
+
544
+ .project-modal-title {
545
+ font-size: var(--df-font-size-md);
546
+ font-weight: 400;
547
+ letter-spacing: var(--df-letter-spacing-wide);
548
+ text-transform: uppercase;
549
+ color: var(--df-color-text-primary);
550
+ margin-bottom: var(--df-space-4);
551
+ }
552
+
553
+ .project-modal-field {
554
+ margin-bottom: var(--df-space-3);
555
+ }
556
+
557
+ .project-modal-label {
558
+ display: block;
559
+ font-size: var(--df-font-size-xs);
560
+ letter-spacing: var(--df-letter-spacing-wide);
561
+ text-transform: uppercase;
562
+ color: var(--df-color-text-secondary);
563
+ margin-bottom: var(--df-space-1);
564
+ }
565
+
566
+ .project-modal-input {
567
+ width: 100%;
568
+ padding: var(--df-space-2) var(--df-space-3);
569
+ background: var(--df-color-bg-base);
570
+ border: 1px solid var(--df-color-border-default);
571
+ color: var(--df-color-text-primary);
572
+ font-family: var(--df-font-mono);
573
+ font-size: var(--df-font-size-sm);
574
+ transition: border-color var(--df-duration-fast);
575
+ }
576
+
577
+ .project-modal-input:focus {
578
+ outline: none;
579
+ border-color: var(--df-color-accent-default);
580
+ }
581
+
582
+ .project-modal-input.error {
583
+ border-color: var(--df-color-state-error);
584
+ }
585
+
586
+ .project-modal-error {
587
+ font-size: var(--df-font-size-xs);
588
+ color: var(--df-color-state-error);
589
+ margin-top: var(--df-space-2);
590
+ min-height: 1.2em;
591
+ }
592
+
593
+ .project-modal-actions {
594
+ display: flex;
595
+ justify-content: flex-end;
596
+ gap: var(--df-space-2);
597
+ margin-top: var(--df-space-4);
598
+ }
599
+
600
+ .project-modal-btn {
601
+ padding: var(--df-space-2) var(--df-space-4);
602
+ background: none;
603
+ border: 1px solid var(--df-color-border-default);
604
+ color: var(--df-color-text-primary);
605
+ font-family: var(--df-font-mono);
606
+ font-size: var(--df-font-size-xs);
607
+ letter-spacing: var(--df-letter-spacing-wide);
608
+ text-transform: uppercase;
609
+ cursor: pointer;
610
+ transition: border-color var(--df-duration-fast), color var(--df-duration-fast);
611
+ }
612
+
613
+ .project-modal-btn:hover {
614
+ border-color: var(--df-color-accent-default);
615
+ color: var(--df-color-accent-default);
616
+ }
617
+
618
+ .project-modal-btn.primary {
619
+ background: color-mix(in srgb, var(--df-color-accent-default) 15%, transparent);
620
+ border-color: var(--df-color-accent-default);
621
+ color: var(--df-color-accent-default);
622
+ }
623
+
624
+ .project-modal-btn.primary:hover {
625
+ background: color-mix(in srgb, var(--df-color-accent-default) 25%, transparent);
626
+ }
627
+
628
+ .project-modal-btn.danger {
629
+ border-color: var(--df-color-state-error);
630
+ color: var(--df-color-state-error);
631
+ }
632
+
633
+ .project-modal-btn.danger:hover {
634
+ background: color-mix(in srgb, var(--df-color-state-error) 15%, transparent);
635
+ }
636
+
637
+ /* ── Folder picker ──────────────────────────────────────────────────────── */
638
+
639
+ .project-modal-path-row {
640
+ display: flex;
641
+ gap: var(--df-space-2);
642
+ }
643
+
644
+ .project-modal-path-row .project-modal-input {
645
+ flex: 1;
646
+ min-width: 0;
647
+ }
648
+
649
+ .project-modal-path-row .project-modal-btn {
650
+ flex-shrink: 0;
651
+ padding: var(--df-space-2) var(--df-space-3);
652
+ }
653
+
654
+ .folder-picker {
655
+ margin-top: var(--df-space-2);
656
+ border: 1px solid var(--df-color-border-default);
657
+ background: var(--df-color-bg-base);
658
+ }
659
+
660
+ .folder-picker-header {
661
+ display: flex;
662
+ align-items: center;
663
+ gap: var(--df-space-2);
664
+ padding: var(--df-space-2) var(--df-space-3);
665
+ border-bottom: 1px solid var(--df-color-border-default);
666
+ background: var(--df-color-bg-raised);
667
+ }
668
+
669
+ .folder-picker-up {
670
+ background: none;
671
+ border: 1px solid var(--df-color-border-default);
672
+ color: var(--df-color-text-secondary);
673
+ font-family: var(--df-font-mono);
674
+ font-size: var(--df-font-size-sm);
675
+ width: 24px;
676
+ height: 24px;
677
+ display: flex;
678
+ align-items: center;
679
+ justify-content: center;
680
+ cursor: pointer;
681
+ flex-shrink: 0;
682
+ }
683
+
684
+ .folder-picker-up:hover {
685
+ color: var(--df-color-accent-default);
686
+ border-color: var(--df-color-accent-default);
687
+ }
688
+
689
+ .folder-picker-path {
690
+ font-size: var(--df-font-size-xs);
691
+ color: var(--df-color-text-secondary);
692
+ overflow: hidden;
693
+ text-overflow: ellipsis;
694
+ white-space: nowrap;
695
+ direction: rtl;
696
+ text-align: left;
697
+ }
698
+
699
+ .folder-picker-list {
700
+ max-height: 200px;
701
+ overflow-y: auto;
702
+ scrollbar-width: thin;
703
+ scrollbar-color: var(--df-color-border-default) transparent;
704
+ }
705
+
706
+ .folder-picker-item {
707
+ display: block;
708
+ width: 100%;
709
+ padding: var(--df-space-1) var(--df-space-3);
710
+ background: none;
711
+ border: none;
712
+ border-bottom: 1px solid color-mix(in srgb, var(--df-color-border-default) 50%, transparent);
713
+ color: var(--df-color-text-primary);
714
+ font-family: var(--df-font-mono);
715
+ font-size: var(--df-font-size-xs);
716
+ text-align: left;
717
+ cursor: pointer;
718
+ }
719
+
720
+ .folder-picker-item:last-child {
721
+ border-bottom: none;
722
+ }
723
+
724
+ .folder-picker-item:hover {
725
+ background: color-mix(in srgb, var(--df-color-accent-default) 10%, transparent);
726
+ color: var(--df-color-accent-default);
727
+ }
728
+
729
+ .folder-picker-empty {
730
+ padding: var(--df-space-3);
731
+ text-align: center;
732
+ font-size: var(--df-font-size-xs);
733
+ color: var(--df-color-text-muted);
734
+ }
735
+
736
+ .folder-picker-actions {
737
+ display: flex;
738
+ justify-content: flex-end;
739
+ gap: var(--df-space-2);
740
+ padding: var(--df-space-2) var(--df-space-3);
741
+ border-top: 1px solid var(--df-color-border-default);
742
+ }
743
+
744
+ /* ── Responsive ──────────────────────────────────────────────────────────── */
745
+
746
+ @media (max-width: 639px) {
747
+ .mobile-topbar {
748
+ display: flex;
749
+ }
750
+
751
+ .layout {
752
+ height: calc(100vh - var(--topbar-height));
753
+ margin-top: var(--topbar-height);
754
+ }
755
+
756
+ /* Sidebar becomes a fixed slide-in drawer */
757
+ .sidebar {
758
+ position: fixed;
759
+ top: var(--topbar-height);
760
+ left: 0;
761
+ bottom: 0;
762
+ transform: translateX(-100%);
763
+ transition: transform var(--df-duration-base) var(--df-easing-spring);
764
+ }
765
+
766
+ .sidebar.open {
767
+ transform: translateX(0);
768
+ }
769
+
770
+ .sidebar-brand {
771
+ display: none;
772
+ }
773
+
774
+ /* Content fills full width since sidebar is overlaid */
775
+ .content {
776
+ width: 100%;
777
+ }
778
+ }
779
+
780
+ /* ── Voice error popup ───────────────────────────────────────────────────── */
781
+
782
+ .voice-error-popup {
783
+ position: absolute;
784
+ top: calc(100% + 6px);
785
+ right: 0;
786
+ width: calc(var(--sidebar-width) - var(--df-space-3) * 2);
787
+ background: var(--df-color-bg-surface);
788
+ border: 1px solid color-mix(in srgb, var(--df-color-state-error) 40%, transparent);
789
+ color: var(--df-color-state-error);
790
+ font-family: var(--df-font-mono);
791
+ font-size: var(--df-font-size-xs);
792
+ letter-spacing: var(--df-letter-spacing-normal);
793
+ line-height: 1.4;
794
+ padding: var(--df-space-2) var(--df-space-3);
795
+ z-index: 500;
796
+ opacity: 0;
797
+ transform: translateY(-4px);
798
+ transition: opacity var(--df-duration-base), transform var(--df-duration-base);
799
+ }
800
+
801
+ .voice-error-popup.visible {
802
+ opacity: 1;
803
+ transform: translateY(0);
804
+ }
805
+
806
+ /* ── Shared page header ─────────────────────────────────────────────── */
807
+ /* Common header styles used by page modules rendered inside .app-content */
808
+
809
+ .app-content header {
810
+ display: flex;
811
+ align-items: center;
812
+ justify-content: space-between;
813
+ height: 38px;
814
+ padding: 0 var(--df-space-4);
815
+ background: var(--df-color-bg-surface);
816
+ border-bottom: 1px solid var(--df-color-border-default);
817
+ gap: var(--df-space-4);
818
+ }
819
+
820
+ .app-content .brand {
821
+ display: flex;
822
+ align-items: center;
823
+ gap: var(--df-space-2);
824
+ font-size: var(--df-font-size-md);
825
+ font-weight: normal;
826
+ letter-spacing: var(--df-letter-spacing-wider);
827
+ text-transform: uppercase;
828
+ color: var(--df-color-accent-default);
829
+ }
830
+
831
+ .app-content .header-meta {
832
+ display: flex;
833
+ align-items: center;
834
+ gap: var(--df-space-4);
835
+ font-size: var(--df-font-size-xs);
836
+ letter-spacing: var(--df-letter-spacing-wide);
837
+ text-transform: uppercase;
838
+ color: var(--df-color-text-secondary);
839
+ }
840
+
841
+ /* ── Shared Component Styles ─────────────────────────────────────────────── */
842
+ /* Modal, button, toast, and focus ring styles shared across all page modules */
843
+
844
+ /* ── Modal / Dialog ──────────────────────────────────────────────────────── */
845
+
846
+ .modal-overlay {
847
+ position: fixed;
848
+ inset: 0;
849
+ background: color-mix(in srgb, var(--df-color-bg-base) 70%, transparent);
850
+ backdrop-filter: blur(2px);
851
+ display: flex;
852
+ align-items: center;
853
+ justify-content: center;
854
+ z-index: var(--df-z-index-modal);
855
+ padding: var(--df-space-4);
856
+ }
857
+
858
+ .modal-overlay.hidden { display: none; }
859
+
860
+ .modal {
861
+ background: var(--df-color-bg-surface);
862
+ border: 1px solid var(--df-color-border-default);
863
+ clip-path: var(--df-clip-md);
864
+ padding: var(--df-space-6);
865
+ max-width: 480px;
866
+ width: 100%;
867
+ position: relative;
868
+ }
869
+
870
+ .modal::before,
871
+ .modal::after {
872
+ content: '';
873
+ position: absolute;
874
+ width: 10px;
875
+ height: 10px;
876
+ border-color: var(--df-color-accent-default);
877
+ border-style: solid;
878
+ pointer-events: none;
879
+ opacity: 0.4;
880
+ }
881
+
882
+ .modal::before { top: 0; left: 0; border-width: 2px 0 0 2px; }
883
+ .modal::after { bottom: 0; right: 0; border-width: 0 2px 2px 0; }
884
+
885
+ .modal-header {}
886
+
887
+ .modal-header h2 {
888
+ font-size: var(--df-font-size-sm);
889
+ font-weight: normal;
890
+ text-transform: uppercase;
891
+ letter-spacing: var(--df-letter-spacing-wider);
892
+ color: var(--df-color-text-primary);
893
+ margin-bottom: var(--df-space-1);
894
+ }
895
+
896
+ .modal-desc {
897
+ font-size: var(--df-font-size-xs);
898
+ color: var(--df-color-text-secondary);
899
+ letter-spacing: var(--df-letter-spacing-normal);
900
+ line-height: var(--df-line-height-normal);
901
+ }
902
+
903
+ .modal-actions {
904
+ display: flex;
905
+ justify-content: flex-end;
906
+ gap: var(--df-space-2);
907
+ margin-top: var(--df-space-4);
908
+ }
909
+
910
+ /* ── Buttons ─────────────────────────────────────────────────────────────── */
911
+
912
+ .btn {
913
+ display: inline-flex;
914
+ align-items: center;
915
+ gap: var(--df-space-2);
916
+ font-family: var(--df-font-mono);
917
+ font-size: var(--df-font-size-xs);
918
+ letter-spacing: var(--df-letter-spacing-wide);
919
+ text-transform: uppercase;
920
+ padding: var(--df-space-2) var(--df-space-4);
921
+ border: 1px solid transparent;
922
+ clip-path: var(--df-clip-sm);
923
+ cursor: pointer;
924
+ transition: background var(--df-duration-fast), box-shadow var(--df-duration-fast), color var(--df-duration-fast);
925
+ white-space: nowrap;
926
+ }
927
+
928
+ .btn:disabled {
929
+ opacity: 0.35;
930
+ cursor: not-allowed;
931
+ }
932
+
933
+ .btn-primary {
934
+ background: color-mix(in srgb, var(--df-color-accent-default) 12%, var(--df-color-bg-raised));
935
+ color: var(--df-color-accent-default);
936
+ border-color: color-mix(in srgb, var(--df-color-accent-default) 40%, transparent);
937
+ }
938
+
939
+ .btn-primary:not(:disabled):hover {
940
+ background: color-mix(in srgb, var(--df-color-accent-default) 20%, var(--df-color-bg-raised));
941
+ border-color: var(--df-color-accent-default);
942
+ box-shadow: var(--df-glow-accent);
943
+ }
944
+
945
+ .btn-secondary {
946
+ background: var(--df-color-bg-raised);
947
+ color: var(--df-color-text-primary);
948
+ border-color: var(--df-color-border-default);
949
+ }
950
+
951
+ .btn-secondary:not(:disabled):hover {
952
+ border-color: var(--df-color-accent-default);
953
+ color: var(--df-color-accent-default);
954
+ }
955
+
956
+ .btn-danger {
957
+ background: color-mix(in srgb, var(--df-color-state-error) 15%, var(--df-color-bg-raised));
958
+ color: var(--df-color-state-error);
959
+ border-color: color-mix(in srgb, var(--df-color-state-error) 40%, transparent);
960
+ }
961
+
962
+ .btn-danger:not(:disabled):hover {
963
+ background: color-mix(in srgb, var(--df-color-state-error) 25%, var(--df-color-bg-raised));
964
+ box-shadow: var(--df-glow-error);
965
+ }
966
+
967
+ /* ── Button variants ──────────────────────────────────────────────────────── */
968
+
969
+ .btn-sm {
970
+ font-size: var(--df-font-size-xs);
971
+ padding: 2px var(--df-space-3);
972
+ }
973
+
974
+ .btn-ghost {
975
+ background: transparent;
976
+ color: var(--df-color-text-secondary);
977
+ border-color: transparent;
978
+ }
979
+
980
+ .btn-ghost:not(:disabled):hover {
981
+ color: var(--df-color-accent-default);
982
+ border-color: var(--df-color-accent-default);
983
+ }
984
+
985
+ .btn-icon {
986
+ background: var(--df-color-bg-raised);
987
+ border: 1px solid var(--df-color-border-default);
988
+ color: var(--df-color-text-secondary);
989
+ padding: var(--df-space-1);
990
+ display: inline-flex;
991
+ align-items: center;
992
+ justify-content: center;
993
+ min-width: 28px;
994
+ min-height: 28px;
995
+ }
996
+
997
+ .btn-icon:hover { color: var(--df-color-accent-default); }
998
+
999
+ .btn-link {
1000
+ background: none;
1001
+ border: none;
1002
+ color: var(--df-color-accent-default);
1003
+ text-decoration: underline;
1004
+ text-underline-offset: 2px;
1005
+ padding: 0;
1006
+ }
1007
+
1008
+ .btn-link:hover { color: var(--df-color-accent-bright); }
1009
+
1010
+ /* ── Forms ────────────────────────────────────────────────────────────────── */
1011
+
1012
+ .form-group {
1013
+ display: flex;
1014
+ flex-direction: column;
1015
+ gap: var(--df-space-2);
1016
+ margin-bottom: var(--df-space-3);
1017
+ }
1018
+
1019
+ .form-group:last-child { margin-bottom: 0; }
1020
+
1021
+ .form-group label {
1022
+ font-size: var(--df-font-size-xs);
1023
+ color: var(--df-color-text-secondary);
1024
+ letter-spacing: var(--df-letter-spacing-wide);
1025
+ text-transform: uppercase;
1026
+ display: flex;
1027
+ align-items: center;
1028
+ gap: var(--df-space-2);
1029
+ }
1030
+
1031
+ .form-group input[type="text"],
1032
+ .form-group input[type="date"],
1033
+ .form-group input[type="url"],
1034
+ .form-group input[type="password"],
1035
+ .form-group input[type="number"],
1036
+ .form-group textarea,
1037
+ .form-group select {
1038
+ width: 100%;
1039
+ background: var(--df-color-bg-raised);
1040
+ border: 1px solid var(--df-color-border-default);
1041
+ border-radius: 0;
1042
+ color: var(--df-color-text-primary);
1043
+ font-family: var(--df-font-mono);
1044
+ font-size: var(--df-font-size-sm);
1045
+ padding: var(--df-space-2) var(--df-space-3);
1046
+ outline: none;
1047
+ transition: border-color var(--df-duration-fast);
1048
+ }
1049
+
1050
+ .form-group textarea {
1051
+ resize: vertical;
1052
+ line-height: var(--df-line-height-normal);
1053
+ }
1054
+
1055
+ .form-group input[type="number"] {
1056
+ -moz-appearance: textfield;
1057
+ }
1058
+
1059
+ .form-group input[type="number"]::-webkit-inner-spin-button,
1060
+ .form-group input[type="number"]::-webkit-outer-spin-button {
1061
+ -webkit-appearance: none;
1062
+ margin: 0;
1063
+ }
1064
+
1065
+ .form-group input:focus-visible,
1066
+ .form-group textarea:focus-visible,
1067
+ .form-group select:focus-visible {
1068
+ border-color: var(--df-color-accent-default);
1069
+ box-shadow: inset 0 0 0 1px var(--df-color-accent-dim);
1070
+ }
1071
+
1072
+ .form-group input::placeholder,
1073
+ .form-group textarea::placeholder {
1074
+ color: var(--df-color-text-muted);
1075
+ }
1076
+
1077
+ .form-group select {
1078
+ cursor: pointer;
1079
+ appearance: none;
1080
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23636e7b'/%3E%3C/svg%3E");
1081
+ background-repeat: no-repeat;
1082
+ background-position: right 10px center;
1083
+ padding-right: var(--df-space-6);
1084
+ }
1085
+
1086
+ .form-group select option {
1087
+ background: var(--df-color-bg-raised);
1088
+ }
1089
+
1090
+ .form-row {
1091
+ display: grid;
1092
+ grid-template-columns: repeat(2, 1fr);
1093
+ gap: var(--df-space-3);
1094
+ }
1095
+
1096
+ .field-row {
1097
+ display: grid;
1098
+ grid-template-columns: repeat(3, 1fr);
1099
+ gap: var(--df-space-3);
1100
+ }
1101
+
1102
+ @media (max-width: 500px) {
1103
+ .field-row { grid-template-columns: 1fr; }
1104
+ .form-row { grid-template-columns: 1fr; }
1105
+ }
1106
+
1107
+ /* ── Modal extensions ────────────────────────────────────────────────────── */
1108
+
1109
+ .modal-lg {
1110
+ max-width: 960px;
1111
+ max-height: 90vh;
1112
+ display: flex;
1113
+ flex-direction: column;
1114
+ overflow: hidden;
1115
+ }
1116
+
1117
+ .modal-body {
1118
+ flex: 1;
1119
+ overflow-y: auto;
1120
+ min-height: 0;
1121
+ margin: 0 calc(-1 * var(--df-space-6));
1122
+ padding: var(--df-space-3) var(--df-space-6);
1123
+ border-top: 1px solid var(--df-color-border-default);
1124
+ border-bottom: 1px solid var(--df-color-border-default);
1125
+ background: color-mix(in srgb, var(--df-color-bg-base) 40%, var(--df-color-bg-surface));
1126
+ box-shadow: inset 0 2px 4px color-mix(in srgb, var(--df-color-bg-base) 50%, transparent);
1127
+ scrollbar-width: thin;
1128
+ scrollbar-color: var(--df-color-border-default) transparent;
1129
+ }
1130
+
1131
+ .modal-footer {
1132
+ display: flex;
1133
+ align-items: center;
1134
+ gap: var(--df-space-2);
1135
+ padding-top: var(--df-space-4);
1136
+ flex-shrink: 0;
1137
+ }
1138
+
1139
+ .modal-footer-right {
1140
+ margin-left: auto;
1141
+ display: flex;
1142
+ gap: var(--df-space-2);
1143
+ }
1144
+
1145
+ /* ── Badge ────────────────────────────────────────────────────────────────── */
1146
+
1147
+ .badge {
1148
+ display: inline-flex;
1149
+ align-items: center;
1150
+ gap: 4px;
1151
+ padding: 2px 8px;
1152
+ border-radius: 999px;
1153
+ font-family: var(--df-font-mono);
1154
+ font-size: 10px;
1155
+ letter-spacing: var(--df-letter-spacing-wide);
1156
+ text-transform: uppercase;
1157
+ white-space: nowrap;
1158
+ background: var(--df-color-bg-overlay);
1159
+ color: var(--df-color-text-secondary);
1160
+ border: 1px solid var(--df-color-border-default);
1161
+ }
1162
+
1163
+ /* ── Empty state ─────────────────────────────────────────────────────────── */
1164
+
1165
+ .empty-state {
1166
+ display: flex;
1167
+ flex-direction: column;
1168
+ align-items: center;
1169
+ justify-content: center;
1170
+ gap: var(--df-space-3);
1171
+ padding: var(--df-space-8);
1172
+ text-align: center;
1173
+ color: var(--df-color-text-muted);
1174
+ font-size: var(--df-font-size-xs);
1175
+ letter-spacing: var(--df-letter-spacing-wide);
1176
+ text-transform: uppercase;
1177
+ }
1178
+
1179
+ /* ── Toast ────────────────────────────────────────────────────────────────── */
1180
+
1181
+ .toast-container {
1182
+ position: fixed;
1183
+ bottom: 24px;
1184
+ left: 50%;
1185
+ transform: translateX(-50%);
1186
+ z-index: var(--df-z-index-toast, 9999);
1187
+ pointer-events: none;
1188
+ }
1189
+
1190
+ .toast {
1191
+ padding: 10px 20px;
1192
+ background: var(--df-color-bg-overlay);
1193
+ color: var(--df-color-text-primary);
1194
+ border: 1px solid var(--df-color-border-default);
1195
+ font-family: var(--df-font-mono);
1196
+ font-size: var(--df-font-size-sm);
1197
+ letter-spacing: var(--df-letter-spacing-wide);
1198
+ opacity: 0;
1199
+ pointer-events: none;
1200
+ transform: translateY(12px);
1201
+ transition: opacity var(--df-duration-base), transform var(--df-duration-base);
1202
+ }
1203
+
1204
+ .toast.visible {
1205
+ opacity: 1;
1206
+ transform: translateY(0);
1207
+ pointer-events: auto;
1208
+ }
1209
+
1210
+ .toast[data-type="error"] {
1211
+ border-color: var(--df-color-state-error);
1212
+ color: var(--df-color-state-error);
1213
+ }