@aion0/forge 0.2.26 → 0.2.27

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/app/globals.css CHANGED
@@ -11,6 +11,33 @@
11
11
  --green: #22c55e;
12
12
  --yellow: #eab308;
13
13
  --red: #ef4444;
14
+ /* Terminal */
15
+ --term-bg: #1a1a2e;
16
+ --term-bar: #12122a;
17
+ --term-border: #2a2a4a;
18
+ --term-fg: #e0e0e0;
19
+ --term-cursor: #7c5bf0;
20
+ --term-dim: #555;
21
+ }
22
+
23
+ [data-theme="light"] {
24
+ --bg-primary: #ffffff;
25
+ --bg-secondary: #f5f5f5;
26
+ --bg-tertiary: #e8e8e8;
27
+ --border: #d4d4d4;
28
+ --text-primary: #1a1a1a;
29
+ --text-secondary: #666;
30
+ --accent: #2563eb;
31
+ --green: #16a34a;
32
+ --yellow: #ca8a04;
33
+ --red: #dc2626;
34
+ /* Terminal */
35
+ --term-bg: #fafafa;
36
+ --term-bar: #f0f0f0;
37
+ --term-border: #d0d0d0;
38
+ --term-fg: #1a1a1a;
39
+ --term-cursor: #2563eb;
40
+ --term-dim: #999;
14
41
  }
15
42
 
16
43
  body {
@@ -23,4 +50,3 @@ body {
23
50
  ::-webkit-scrollbar { width: 6px; }
24
51
  ::-webkit-scrollbar-track { background: var(--bg-primary); }
25
52
  ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
26
-
@@ -12,6 +12,9 @@ export default function LoginPage() {
12
12
  useEffect(() => {
13
13
  const host = window.location.hostname;
14
14
  setIsRemote(!['localhost', '127.0.0.1'].includes(host));
15
+ // Restore theme
16
+ const saved = localStorage.getItem('forge-theme');
17
+ if (saved === 'light') document.documentElement.setAttribute('data-theme', 'light');
15
18
  }, []);
16
19
 
17
20
  const handleLocal = async (e: React.FormEvent) => {
@@ -54,8 +54,26 @@ export default function Dashboard({ user }: { user: any }) {
54
54
  const [notifications, setNotifications] = useState<any[]>([]);
55
55
  const [unreadCount, setUnreadCount] = useState(0);
56
56
  const [showNotifications, setShowNotifications] = useState(false);
57
+ const [showUserMenu, setShowUserMenu] = useState(false);
58
+ const [theme, setTheme] = useState<'dark' | 'light'>('dark');
57
59
  const terminalRef = useRef<WebTerminalHandle>(null);
58
60
 
61
+ // Theme: load from localStorage + apply
62
+ useEffect(() => {
63
+ const saved = localStorage.getItem('forge-theme') as 'dark' | 'light' | null;
64
+ if (saved) {
65
+ setTheme(saved);
66
+ document.documentElement.setAttribute('data-theme', saved === 'light' ? 'light' : '');
67
+ }
68
+ }, []);
69
+
70
+ const toggleTheme = () => {
71
+ const next = theme === 'dark' ? 'light' : 'dark';
72
+ setTheme(next);
73
+ document.documentElement.setAttribute('data-theme', next === 'light' ? 'light' : '');
74
+ localStorage.setItem('forge-theme', next);
75
+ };
76
+
59
77
  // Version check (on mount + every 10 min)
60
78
  useEffect(() => {
61
79
  const check = () => fetch('/api/version').then(r => r.json()).then(setVersionInfo).catch(() => {});
@@ -228,11 +246,11 @@ export default function Dashboard({ user }: { user: any }) {
228
246
  </span>
229
247
  )}
230
248
  </div>
231
- <div className="flex items-center gap-3">
249
+ <div className="flex items-center gap-2.5">
232
250
  {viewMode === 'tasks' && (
233
251
  <button
234
252
  onClick={() => setShowNewTask(true)}
235
- className="text-xs px-3 py-1 bg-[var(--accent)] text-white rounded hover:opacity-90"
253
+ className="text-[10px] px-2.5 py-1 bg-[var(--accent)] text-white rounded hover:opacity-90"
236
254
  >
237
255
  + New Task
238
256
  </button>
@@ -242,24 +260,24 @@ export default function Dashboard({ user }: { user: any }) {
242
260
  <span className="text-[10px] text-[var(--text-secondary)] flex items-center gap-1" title={`${onlineCount.total} online${onlineCount.remote > 0 ? `, ${onlineCount.remote} remote` : ''}`}>
243
261
  <span className="text-green-500">●</span>
244
262
  {onlineCount.total}
245
- {onlineCount.remote > 0 && (
246
- <span className="text-[var(--accent)]">({onlineCount.remote} remote)</span>
247
- )}
248
263
  </span>
249
264
  )}
265
+ {/* Alerts */}
250
266
  <div className="relative">
251
267
  <button
252
- onClick={() => { setShowNotifications(v => !v); }}
253
- className="text-xs text-[var(--text-secondary)] hover:text-[var(--text-primary)] relative"
268
+ onClick={() => { setShowNotifications(v => !v); setShowUserMenu(false); }}
269
+ className="text-[10px] text-[var(--text-secondary)] hover:text-[var(--text-primary)] relative px-1"
254
270
  >
255
271
  Alerts
256
272
  {unreadCount > 0 && (
257
- <span className="absolute -top-1.5 -right-2.5 min-w-[14px] h-[14px] rounded-full bg-[var(--red)] text-[8px] text-white flex items-center justify-center px-1 font-bold">
273
+ <span className="absolute -top-1.5 -right-1.5 min-w-[14px] h-[14px] rounded-full bg-[var(--red)] text-[8px] text-white flex items-center justify-center px-1 font-bold">
258
274
  {unreadCount > 99 ? '99+' : unreadCount}
259
275
  </span>
260
276
  )}
261
277
  </button>
262
278
  {showNotifications && (
279
+ <>
280
+ <div className="fixed inset-0 z-40" onClick={() => setShowNotifications(false)} />
263
281
  <div className="absolute right-0 top-8 w-[360px] max-h-[480px] bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg shadow-xl z-50 flex flex-col">
264
282
  <div className="flex items-center justify-between px-3 py-2 border-b border-[var(--border)]">
265
283
  <span className="text-xs font-bold text-[var(--text-primary)]">Notifications</span>
@@ -352,27 +370,44 @@ export default function Dashboard({ user }: { user: any }) {
352
370
  )}
353
371
  </div>
354
372
  </div>
373
+ </>
374
+ )}
375
+ </div>
376
+ {/* User menu */}
377
+ <div className="relative">
378
+ <button
379
+ onClick={() => { setShowUserMenu(v => !v); setShowNotifications(false); }}
380
+ className="text-[10px] text-[var(--text-secondary)] hover:text-[var(--text-primary)] flex items-center gap-1 px-1"
381
+ >
382
+ {user?.name || 'local'} <span className="text-[8px]">▾</span>
383
+ </button>
384
+ {showUserMenu && (
385
+ <>
386
+ <div className="fixed inset-0 z-40" onClick={() => setShowUserMenu(false)} />
387
+ <div className="absolute right-0 top-8 w-[140px] bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg shadow-xl z-50 py-1">
388
+ <button
389
+ onClick={() => { setShowMonitor(true); setShowUserMenu(false); }}
390
+ className="w-full text-left text-[11px] px-3 py-1.5 text-[var(--text-secondary)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-tertiary)]"
391
+ >
392
+ Monitor
393
+ </button>
394
+ <button
395
+ onClick={() => { setShowSettings(true); setShowUserMenu(false); }}
396
+ className="w-full text-left text-[11px] px-3 py-1.5 text-[var(--text-secondary)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-tertiary)]"
397
+ >
398
+ Settings
399
+ </button>
400
+ <div className="border-t border-[var(--border)] my-1" />
401
+ <button
402
+ onClick={() => signOut({ callbackUrl: '/login' })}
403
+ className="w-full text-left text-[11px] px-3 py-1.5 text-[var(--text-secondary)] hover:text-[var(--red)] hover:bg-[var(--bg-tertiary)]"
404
+ >
405
+ Logout
406
+ </button>
407
+ </div>
408
+ </>
355
409
  )}
356
410
  </div>
357
- <button
358
- onClick={() => setShowMonitor(true)}
359
- className="text-xs text-[var(--text-secondary)] hover:text-[var(--text-primary)]"
360
- >
361
- Monitor
362
- </button>
363
- <button
364
- onClick={() => setShowSettings(true)}
365
- className="text-xs text-[var(--text-secondary)] hover:text-[var(--text-primary)]"
366
- >
367
- Settings
368
- </button>
369
- <span className="text-xs text-[var(--text-secondary)]">{user?.name || 'local'}</span>
370
- <button
371
- onClick={() => signOut({ callbackUrl: '/login' })}
372
- className="text-xs text-[var(--text-secondary)] hover:text-[var(--red)]"
373
- >
374
- Logout
375
- </button>
376
411
  </div>
377
412
  </header>
378
413
 
@@ -35,6 +35,8 @@ export default function DocTerminal({ docRoot }: { docRoot: string }) {
35
35
  if (!containerRef.current) return;
36
36
 
37
37
  let disposed = false;
38
+ const cs = getComputedStyle(document.documentElement);
39
+ const tv = (name: string) => cs.getPropertyValue(name).trim();
38
40
  const term = new Terminal({
39
41
  cursorBlink: true,
40
42
  fontSize: 13,
@@ -42,10 +44,10 @@ export default function DocTerminal({ docRoot }: { docRoot: string }) {
42
44
  scrollback: 5000,
43
45
  logger: { trace: () => {}, debug: () => {}, info: () => {}, warn: () => {}, error: () => {} },
44
46
  theme: {
45
- background: '#1a1a2e',
46
- foreground: '#e0e0e0',
47
- cursor: '#7c5bf0',
48
- selectionBackground: '#7c5bf066',
47
+ background: tv('--term-bg') || '#1a1a2e',
48
+ foreground: tv('--term-fg') || '#e0e0e0',
49
+ cursor: tv('--term-cursor') || '#7c5bf0',
50
+ selectionBackground: (tv('--term-cursor') || '#7c5bf0') + '44',
49
51
  },
50
52
  });
51
53
  const fit = new FitAddon();
@@ -434,9 +434,9 @@ const WebTerminal = forwardRef<WebTerminalHandle, WebTerminalProps>(function Web
434
434
  const detachedCount = tmuxSessions.filter(s => !usedSessions.includes(s.name)).length;
435
435
 
436
436
  return (
437
- <div className="h-full w-full flex-1 flex flex-col bg-[#1a1a2e] overflow-hidden">
437
+ <div className="h-full w-full flex-1 flex flex-col bg-[var(--term-bg)] overflow-hidden">
438
438
  {/* Tab bar + toolbar */}
439
- <div className="flex items-center bg-[#12122a] border-b border-[#2a2a4a] shrink-0">
439
+ <div className="flex items-center bg-[var(--term-bar)] border-b border-[var(--term-border)] shrink-0">
440
440
  {/* Tabs */}
441
441
  <div className="flex items-center overflow-x-auto">
442
442
  {tabs.map(tab => (
@@ -463,10 +463,10 @@ const WebTerminal = forwardRef<WebTerminalHandle, WebTerminalProps>(function Web
463
463
  }
464
464
  }}
465
465
  onDragEnd={() => { dragTabRef.current = null; }}
466
- className={`flex items-center gap-1 px-3 py-1 text-[11px] cursor-pointer border-r border-[#2a2a4a] select-none ${
466
+ className={`flex items-center gap-1 px-3 py-1 text-[11px] cursor-pointer border-r border-[var(--term-border)] select-none ${
467
467
  tab.id === activeTabId
468
- ? 'bg-[#1a1a2e] text-white'
469
- : 'text-gray-500 hover:text-gray-300 hover:bg-[#1a1a2e]/50'
468
+ ? 'bg-[var(--term-bg)] text-white'
469
+ : 'text-gray-500 hover:text-gray-300 hover:bg-[var(--term-bg)]/50'
470
470
  }`}
471
471
  onClick={() => setActiveTabId(tab.id)}
472
472
  >
@@ -481,7 +481,7 @@ const WebTerminal = forwardRef<WebTerminalHandle, WebTerminalProps>(function Web
481
481
  if (e.key === 'Escape') setEditingTabId(null);
482
482
  }}
483
483
  onClick={(e) => e.stopPropagation()}
484
- className="bg-transparent border border-[#4a4a6a] rounded px-1 text-[11px] text-white outline-none w-20"
484
+ className="bg-transparent border border-[var(--term-border)] rounded px-1 text-[11px] text-white outline-none w-20"
485
485
  />
486
486
  ) : (
487
487
  <span
@@ -507,7 +507,7 @@ const WebTerminal = forwardRef<WebTerminalHandle, WebTerminalProps>(function Web
507
507
  ))}
508
508
  <button
509
509
  onClick={() => setShowNewTabModal(true)}
510
- className="px-2 py-1 text-[11px] text-gray-500 hover:text-white hover:bg-[#2a2a4a]"
510
+ className="px-2 py-1 text-[11px] text-gray-500 hover:text-white hover:bg-[var(--term-border)]"
511
511
  title="New tab"
512
512
  >
513
513
  +
@@ -517,15 +517,15 @@ const WebTerminal = forwardRef<WebTerminalHandle, WebTerminalProps>(function Web
517
517
  {/* Toolbar */}
518
518
  <div className="flex items-center gap-1 px-2 ml-auto">
519
519
  <span className="text-[9px] text-gray-600 mr-2">Shift+drag to copy</span>
520
- <button onClick={() => onSplit('vertical')} className="text-[10px] px-2 py-0.5 text-gray-400 hover:text-white hover:bg-[#2a2a4a] rounded">
520
+ <button onClick={() => onSplit('vertical')} className="text-[10px] px-2 py-0.5 text-gray-400 hover:text-white hover:bg-[var(--term-border)] rounded">
521
521
  Split Right
522
522
  </button>
523
- <button onClick={() => onSplit('horizontal')} className="text-[10px] px-2 py-0.5 text-gray-400 hover:text-white hover:bg-[#2a2a4a] rounded">
523
+ <button onClick={() => onSplit('horizontal')} className="text-[10px] px-2 py-0.5 text-gray-400 hover:text-white hover:bg-[var(--term-border)] rounded">
524
524
  Split Down
525
525
  </button>
526
526
  <button
527
527
  onClick={() => { refreshSessions(); setShowSessionPicker(v => !v); }}
528
- className={`text-[10px] px-2 py-0.5 rounded relative ${showSessionPicker ? 'text-white bg-[#7c5bf0]/30' : 'text-gray-400 hover:text-white hover:bg-[#2a2a4a]'}`}
528
+ className={`text-[10px] px-2 py-0.5 rounded relative ${showSessionPicker ? 'text-white bg-[#7c5bf0]/30' : 'text-gray-400 hover:text-white hover:bg-[var(--term-border)]'}`}
529
529
  >
530
530
  Sessions
531
531
  {detachedCount > 0 && (
@@ -559,7 +559,7 @@ const WebTerminal = forwardRef<WebTerminalHandle, WebTerminalProps>(function Web
559
559
  </button>
560
560
  )}
561
561
  {activeTab && countTerminals(activeTab.tree) > 1 && (
562
- <button onClick={onClosePane} className="text-[10px] px-2 py-0.5 text-gray-400 hover:text-red-400 hover:bg-[#2a2a4a] rounded">
562
+ <button onClick={onClosePane} className="text-[10px] px-2 py-0.5 text-gray-400 hover:text-red-400 hover:bg-[var(--term-border)] rounded">
563
563
  Close Pane
564
564
  </button>
565
565
  )}
@@ -568,7 +568,7 @@ const WebTerminal = forwardRef<WebTerminalHandle, WebTerminalProps>(function Web
568
568
 
569
569
  {/* Session management panel */}
570
570
  {showSessionPicker && (
571
- <div className="bg-[#0e0e20] border-b border-[#2a2a4a] px-3 py-2 shrink-0 max-h-48 overflow-y-auto">
571
+ <div className="bg-[var(--term-bar)] border-b border-[var(--term-border)] px-3 py-2 shrink-0 max-h-48 overflow-y-auto">
572
572
  <div className="flex items-center justify-between mb-2">
573
573
  <span className="text-[10px] text-gray-400 font-semibold uppercase">Tmux Sessions</span>
574
574
  <button
@@ -583,7 +583,7 @@ const WebTerminal = forwardRef<WebTerminalHandle, WebTerminalProps>(function Web
583
583
  ) : (
584
584
  <table className="w-full text-[10px]">
585
585
  <thead>
586
- <tr className="text-gray-500 text-left border-b border-[#2a2a4a]">
586
+ <tr className="text-gray-500 text-left border-b border-[var(--term-border)]">
587
587
  <th className="py-1 pr-3 font-medium">Session</th>
588
588
  <th className="py-1 pr-3 font-medium">Created</th>
589
589
  <th className="py-1 pr-3 font-medium">Status</th>
@@ -595,7 +595,7 @@ const WebTerminal = forwardRef<WebTerminalHandle, WebTerminalProps>(function Web
595
595
  const inUse = usedSessions.includes(s.name);
596
596
  const savedLabel = sessionLabelsRef.current[s.name];
597
597
  return (
598
- <tr key={s.name} className="border-b border-[#2a2a4a]/50 hover:bg-[#1a1a2e]">
598
+ <tr key={s.name} className="border-b border-[var(--term-border)]/50 hover:bg-[var(--term-bg)]">
599
599
  <td className="py-1.5 pr-3 text-gray-300">
600
600
  {savedLabel ? (
601
601
  <><span>{savedLabel}</span> <span className="font-mono text-gray-600 text-[9px]">{s.name.replace('mw-', '')}</span></>
@@ -654,22 +654,22 @@ const WebTerminal = forwardRef<WebTerminalHandle, WebTerminalProps>(function Web
654
654
  {/* New tab modal */}
655
655
  {showNewTabModal && (
656
656
  <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50" onClick={() => { setShowNewTabModal(false); setExpandedRoot(null); }}>
657
- <div className="bg-[#1a1a2e] border border-[#2a2a4a] rounded-lg shadow-xl w-[350px] max-h-[70vh] flex flex-col" onClick={e => e.stopPropagation()}>
658
- <div className="px-4 py-3 border-b border-[#2a2a4a]">
657
+ <div className="bg-[var(--term-bg)] border border-[var(--term-border)] rounded-lg shadow-xl w-[350px] max-h-[70vh] flex flex-col" onClick={e => e.stopPropagation()}>
658
+ <div className="px-4 py-3 border-b border-[var(--term-border)]">
659
659
  <h3 className="text-sm font-semibold text-white">New Tab</h3>
660
660
  </div>
661
661
  <div className="flex-1 overflow-y-auto p-2">
662
662
  {/* Plain terminal */}
663
663
  <button
664
664
  onClick={() => { addTab(); setShowNewTabModal(false); setExpandedRoot(null); }}
665
- className="w-full text-left px-3 py-2 rounded hover:bg-[#2a2a4a] text-[12px] text-gray-300 flex items-center gap-2"
665
+ className="w-full text-left px-3 py-2 rounded hover:bg-[var(--term-border)] text-[12px] text-gray-300 flex items-center gap-2"
666
666
  >
667
667
  <span className="text-gray-500">▸</span> Terminal
668
668
  </button>
669
669
 
670
670
  {/* Project roots */}
671
671
  {projectRoots.length > 0 && (
672
- <div className="mt-2 pt-2 border-t border-[#2a2a4a]">
672
+ <div className="mt-2 pt-2 border-t border-[var(--term-border)]">
673
673
  <div className="px-3 py-1 text-[9px] text-gray-500 uppercase">Claude in Project</div>
674
674
  {projectRoots.map(root => {
675
675
  const rootName = root.split('/').pop() || root;
@@ -679,7 +679,7 @@ const WebTerminal = forwardRef<WebTerminalHandle, WebTerminalProps>(function Web
679
679
  <div key={root}>
680
680
  <button
681
681
  onClick={() => setExpandedRoot(isExpanded ? null : root)}
682
- className="w-full text-left px-3 py-2 rounded hover:bg-[#2a2a4a] text-[12px] text-gray-300 flex items-center gap-2"
682
+ className="w-full text-left px-3 py-2 rounded hover:bg-[var(--term-border)] text-[12px] text-gray-300 flex items-center gap-2"
683
683
  >
684
684
  <span className="text-gray-500 text-[10px] w-3">{isExpanded ? '▾' : '▸'}</span>
685
685
  <span>{rootName}</span>
@@ -691,7 +691,7 @@ const WebTerminal = forwardRef<WebTerminalHandle, WebTerminalProps>(function Web
691
691
  <button
692
692
  key={p.path}
693
693
  onClick={() => { addTab(p.path); setShowNewTabModal(false); setExpandedRoot(null); }}
694
- className="w-full text-left px-3 py-1.5 rounded hover:bg-[#2a2a4a] text-[11px] text-gray-300 flex items-center gap-2 truncate"
694
+ className="w-full text-left px-3 py-1.5 rounded hover:bg-[var(--term-border)] text-[11px] text-gray-300 flex items-center gap-2 truncate"
695
695
  title={p.path}
696
696
  >
697
697
  <span className="text-gray-600 text-[10px]">↳</span> {p.name}
@@ -708,7 +708,7 @@ const WebTerminal = forwardRef<WebTerminalHandle, WebTerminalProps>(function Web
708
708
  </div>
709
709
  )}
710
710
  </div>
711
- <div className="px-4 py-2 border-t border-[#2a2a4a]">
711
+ <div className="px-4 py-2 border-t border-[var(--term-border)]">
712
712
  <button
713
713
  onClick={() => { setShowNewTabModal(false); setExpandedRoot(null); }}
714
714
  className="w-full text-center text-[11px] text-gray-500 hover:text-gray-300 py-1"
@@ -723,7 +723,7 @@ const WebTerminal = forwardRef<WebTerminalHandle, WebTerminalProps>(function Web
723
723
  {/* Close confirmation dialog */}
724
724
  {closeConfirm && (
725
725
  <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50" onClick={() => setCloseConfirm(null)}>
726
- <div className="bg-[#1a1a2e] border border-[#2a2a4a] rounded-lg p-4 shadow-xl max-w-sm" onClick={(e) => e.stopPropagation()}>
726
+ <div className="bg-[var(--term-bg)] border border-[var(--term-border)] rounded-lg p-4 shadow-xl max-w-sm" onClick={(e) => e.stopPropagation()}>
727
727
  <h3 className="text-sm font-semibold text-white mb-2">Close Tab</h3>
728
728
  <p className="text-xs text-gray-400 mb-1">
729
729
  This tab has {closeConfirm.sessions.length} active session{closeConfirm.sessions.length > 1 ? 's' : ''}:
@@ -950,17 +950,46 @@ const MemoTerminalPane = memo(function TerminalPane({
950
950
 
951
951
  let disposed = false; // guard against post-cleanup writes (React Strict Mode)
952
952
 
953
+ // Read terminal theme from CSS variables
954
+ const cs = getComputedStyle(document.documentElement);
955
+ const tv = (name: string) => cs.getPropertyValue(name).trim();
956
+ const termBg = tv('--term-bg') || '#1a1a2e';
957
+ const termFg = tv('--term-fg') || '#e0e0e0';
958
+ const termCursor = tv('--term-cursor') || '#7c5bf0';
959
+ const isLight = document.documentElement.getAttribute('data-theme') === 'light';
960
+
953
961
  const term = new Terminal({
954
962
  cursorBlink: true,
955
963
  fontSize: 13,
956
964
  fontFamily: 'Menlo, Monaco, "Courier New", monospace',
957
965
  scrollback: 10000,
958
966
  logger: { trace: () => {}, debug: () => {}, info: () => {}, warn: () => {}, error: () => {} },
959
- theme: {
960
- background: '#1a1a2e',
961
- foreground: '#e0e0e0',
962
- cursor: '#7c5bf0',
963
- selectionBackground: '#7c5bf066',
967
+ theme: isLight ? {
968
+ background: termBg,
969
+ foreground: termFg,
970
+ cursor: termCursor,
971
+ selectionBackground: termCursor + '44',
972
+ black: '#1a1a1a',
973
+ red: '#d32f2f',
974
+ green: '#388e3c',
975
+ yellow: '#f57f17',
976
+ blue: '#1976d2',
977
+ magenta: '#7b1fa2',
978
+ cyan: '#0097a7',
979
+ white: '#424242',
980
+ brightBlack: '#757575',
981
+ brightRed: '#e53935',
982
+ brightGreen: '#43a047',
983
+ brightYellow: '#f9a825',
984
+ brightBlue: '#1e88e5',
985
+ brightMagenta: '#8e24aa',
986
+ brightCyan: '#00acc1',
987
+ brightWhite: '#1a1a1a',
988
+ } : {
989
+ background: termBg,
990
+ foreground: termFg,
991
+ cursor: termCursor,
992
+ selectionBackground: termCursor + '66',
964
993
  black: '#1a1a2e',
965
994
  red: '#ff6b6b',
966
995
  green: '#69db7c',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aion0/forge",
3
- "version": "0.2.26",
3
+ "version": "0.2.27",
4
4
  "description": "Unified AI workflow platform — multi-model task orchestration, persistent sessions, web terminal, remote access",
5
5
  "type": "module",
6
6
  "scripts": {