glad-web 1.0.45 → 2.0.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 (68) hide show
  1. package/README.md +4 -192
  2. package/THIRD_PARTY_NOTICES.md +27 -0
  3. package/bin/glad.cjs +56 -0
  4. package/package.json +19 -58
  5. package/README.zh-CN.md +0 -198
  6. package/assets/logo.svg +0 -43
  7. package/bin/cli.js +0 -65
  8. package/lib/ai-tools/demo/enhanced-demo.js +0 -625
  9. package/lib/ai-tools/demo/index.js +0 -24
  10. package/lib/ai-tools/demo/responses.js +0 -88
  11. package/lib/ai-tools/detector.js +0 -76
  12. package/lib/ai-tools/registry.js +0 -300
  13. package/lib/claude/cli-usage.js +0 -95
  14. package/lib/claude/config.js +0 -82
  15. package/lib/claude/structured-session.js +0 -884
  16. package/lib/claude/transcript-repository.js +0 -216
  17. package/lib/codex/image-store.js +0 -174
  18. package/lib/codex/structured-session.js +0 -1578
  19. package/lib/commands/config.js +0 -78
  20. package/lib/commands/tools.js +0 -128
  21. package/lib/commands/web.js +0 -586
  22. package/lib/config/constants.js +0 -17
  23. package/lib/config/manager.js +0 -89
  24. package/lib/git/service.js +0 -83
  25. package/lib/notifications/message-formatter.js +0 -94
  26. package/lib/notifications/notification-service.js +0 -143
  27. package/lib/notifications/serverchan-client.js +0 -58
  28. package/lib/notifications/serverchan-settings-store.js +0 -115
  29. package/lib/schedule/job-runner.js +0 -162
  30. package/lib/schedule/job-store.js +0 -167
  31. package/lib/schedule/key-sequences.js +0 -49
  32. package/lib/schedule/scheduler-service.js +0 -39
  33. package/lib/server/routes/notifications.js +0 -52
  34. package/lib/server/routes/providers.js +0 -114
  35. package/lib/server/routes/schedules.js +0 -54
  36. package/lib/server/routes/usage.js +0 -23
  37. package/lib/server/routes/workspace.js +0 -77
  38. package/lib/session/buffer.js +0 -102
  39. package/lib/session/file-attachment-store.js +0 -168
  40. package/lib/session/pty-manager.js +0 -255
  41. package/lib/session/rendered-history.js +0 -225
  42. package/lib/session/session-manager.js +0 -1001
  43. package/lib/session/text-history.js +0 -274
  44. package/lib/usage/ccusage-runner.js +0 -128
  45. package/lib/usage/source-catalog.js +0 -26
  46. package/lib/usage/usage-service.js +0 -226
  47. package/lib/utils/logger.js +0 -74
  48. package/lib/utils/pid.js +0 -67
  49. package/lib/utils/validation.js +0 -53
  50. package/lib/web/claude.js +0 -1129
  51. package/lib/web/codex.js +0 -1042
  52. package/lib/web/composer.js +0 -463
  53. package/lib/web/core.js +0 -373
  54. package/lib/web/git.js +0 -535
  55. package/lib/web/gitgraph.js +0 -293
  56. package/lib/web/index.html +0 -516
  57. package/lib/web/layout.js +0 -72
  58. package/lib/web/notifications.js +0 -163
  59. package/lib/web/schedules.js +0 -245
  60. package/lib/web/session.js +0 -360
  61. package/lib/web/shell.js +0 -59
  62. package/lib/web/styles.css +0 -905
  63. package/lib/web/terminal-scroll.js +0 -81
  64. package/lib/web/theme.js +0 -60
  65. package/lib/web/timed-inputs.js +0 -216
  66. package/lib/web/usage.js +0 -323
  67. package/lib/workspace/service.js +0 -77
  68. package/scripts/check-syntax.js +0 -26
@@ -1,516 +0,0 @@
1
- <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <meta charset="UTF-8">
5
- <title>Glad - AI Sessions</title>
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
7
- <meta name="apple-mobile-web-app-capable" content="yes">
8
- <meta name="theme-color" content="#000000">
9
- <script>
10
- (() => {
11
- const storedTheme = localStorage.getItem('glad-theme');
12
- const theme = storedTheme === 'light' || storedTheme === 'dark'
13
- ? storedTheme
14
- : (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
15
- document.documentElement.dataset.theme = theme;
16
- document.documentElement.style.backgroundColor = theme === 'dark' ? '#000000' : '#f5f6f8';
17
- })();
18
- </script>
19
- <link rel="manifest" href="manifest.json">
20
- <link rel="icon" type="image/svg+xml" href="logo.svg">
21
- <link rel="apple-touch-icon" href="logo.svg">
22
- <link rel="stylesheet" href="vendor/xterm.css" />
23
- <link rel="stylesheet" href="styles.css">
24
- </head>
25
- <body>
26
- <div id="debug-log"></div>
27
- <svg class="app-icon-sprite" aria-hidden="true">
28
- <symbol id="icon-model" viewBox="0 0 24 24"><path d="M4 7h10M18 7h2M4 17h2M10 17h10M14 4v6M6 14v6"/></symbol>
29
- <symbol id="icon-usage" viewBox="0 0 24 24"><path d="M5 20V10M12 20V4M19 20v-7"/></symbol>
30
- <symbol id="icon-context" viewBox="0 0 24 24"><path d="M12 3a9 9 0 1 0 9 9h-9V3Z"/><path d="M15 3.5A9 9 0 0 1 20.5 9H15V3.5Z"/></symbol>
31
- <symbol id="icon-stop" viewBox="0 0 24 24"><rect x="6" y="6" width="12" height="12" rx="2"/></symbol>
32
- <symbol id="icon-resume" viewBox="0 0 24 24"><path d="M4 12a8 8 0 1 0 2.3-5.7L4 8.6"/><path d="M4 4v4.6h4.6"/><path d="M12 8v4l3 2"/></symbol>
33
- <symbol id="icon-fork" viewBox="0 0 24 24"><circle cx="6" cy="5" r="2"/><circle cx="18" cy="5" r="2"/><circle cx="12" cy="19" r="2"/><path d="M6 7v2c0 4 6 3 6 8M18 7v2c0 4-6 3-6 8"/></symbol>
34
- <symbol id="icon-shield" viewBox="0 0 24 24"><path d="M12 3 20 6v5c0 5-3.4 8.2-8 10-4.6-1.8-8-5-8-10V6l8-3Z"/><path d="m9 12 2 2 4-4"/></symbol>
35
- <symbol id="icon-status" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M12 11v6M12 7h.01"/></symbol>
36
- <symbol id="icon-prompts" viewBox="0 0 24 24"><path d="M4 5h16v11H8l-4 4V5Z"/><path d="M8 9h8M8 12h5"/></symbol>
37
- <symbol id="icon-compact" viewBox="0 0 24 24"><path d="m4 9 5 0 0-5M9 9 4 4M20 9h-5V4M15 9l5-5M4 15h5v5M9 15l-5 5M20 15h-5v5M15 15l5 5"/></symbol>
38
- <symbol id="icon-sandbox" viewBox="0 0 24 24"><path d="m12 3 8 4.5v9L12 21l-8-4.5v-9L12 3Z"/><path d="m4.5 7.8 7.5 4.3 7.5-4.3M12 12v9"/></symbol>
39
- <symbol id="icon-lock" viewBox="0 0 24 24"><rect x="5" y="10" width="14" height="11" rx="2"/><path d="M8 10V7a4 4 0 0 1 8 0v3"/></symbol>
40
- <symbol id="icon-skills" viewBox="0 0 24 24"><path d="M9 3H4a1 1 0 0 0-1 1v5h2.5a2.5 2.5 0 1 1 0 5H3v5a1 1 0 0 0 1 1h5v-2.5a2.5 2.5 0 1 1 5 0V20h5a1 1 0 0 0 1-1v-5h-2.5a2.5 2.5 0 1 1 0-5H20V4a1 1 0 0 0-1-1h-5v2.5a2.5 2.5 0 1 1-5 0V3Z"/></symbol>
41
- <symbol id="icon-image" viewBox="0 0 24 24"><rect x="3" y="5" width="18" height="14" rx="2"/><circle cx="9" cy="10" r="2"/><path d="m4 17 4.5-4 3.5 3 2.5-2 5.5 4"/></symbol>
42
- <symbol id="icon-file" viewBox="0 0 24 24"><path d="M9 8.5 14.5 3a4 4 0 0 1 5.7 5.7L10.4 18.5a5.5 5.5 0 0 1-7.8-7.8l9.2-9.2"/><path d="m7 11 7-7a2 2 0 1 1 2.8 2.8l-9.1 9.1a2.5 2.5 0 0 1-3.5-3.5L12 4.6"/></symbol>
43
- <symbol id="icon-schedule" viewBox="0 0 24 24"><circle cx="10.5" cy="13" r="7.5"/><path d="M10.5 9v4l2.5 1.5M18 3v6M15 6h6"/></symbol>
44
- <symbol id="icon-calendar" viewBox="0 0 24 24"><rect x="3" y="5" width="18" height="16" rx="2"/><path d="M8 3v4M16 3v4M3 10h18"/></symbol>
45
- </svg>
46
-
47
- <div id="app-shell">
48
-
49
- <!-- Lobby View -->
50
- <div id="lobby-view" class="view active">
51
- <div id="lobby">
52
- <div class="header">
53
- <h1><img class="header-logo" src="logo.svg" alt="">Glad</h1>
54
- <div class="header-actions">
55
- <button class="header-action-btn" onclick="showToolModal()" title="New AI session"><span>+</span><span>Session</span></button>
56
- <button id="usage-dashboard-button" class="header-action-btn icon-only" type="button"
57
- onclick="showUsageSourceModal()" title="Usage dashboard" aria-label="Usage dashboard">
58
- <svg width="19" height="19" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
59
- <path d="M4 19V10"></path><path d="M10 19V5"></path><path d="M16 19v-7"></path><path d="M22 19V2"></path>
60
- </svg>
61
- </button>
62
- <button id="schedule-create-button" class="header-action-btn icon-only" type="button"
63
- onclick="showScheduleModal()" title="New scheduled task" aria-label="New scheduled task">
64
- <svg width="19" height="19" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><use href="#icon-calendar"></use></svg>
65
- </button>
66
- <button id="app-settings-button" class="header-action-btn icon-only" type="button"
67
- onclick="openSettings()" title="Settings" aria-label="Settings">
68
- <svg width="19" height="19" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
69
- <circle cx="12" cy="12" r="3"></circle>
70
- <path d="M19.4 15a1.7 1.7 0 0 0 .34 1.88l.06.06-2.83 2.83-.06-.06A1.7 1.7 0 0 0 15 19.4a1.7 1.7 0 0 0-1 .6 1.7 1.7 0 0 0-.4 1.1V21h-4v-.1A1.7 1.7 0 0 0 8.6 19.4a1.7 1.7 0 0 0-1.88.34l-.06.06-2.83-2.83.06-.06A1.7 1.7 0 0 0 4.6 15a1.7 1.7 0 0 0-.6-1 1.7 1.7 0 0 0-1.1-.4H3v-4h.1A1.7 1.7 0 0 0 4.6 8.6a1.7 1.7 0 0 0-.34-1.88l-.06-.06 2.83-2.83.06.06A1.7 1.7 0 0 0 9 4.6a1.7 1.7 0 0 0 1-.6 1.7 1.7 0 0 0 .4-1.1V3h4v.1A1.7 1.7 0 0 0 15.4 4.6a1.7 1.7 0 0 0 1.88-.34l.06-.06 2.83 2.83-.06.06A1.7 1.7 0 0 0 19.4 9c.1.37.31.7.6 1 .3.25.67.39 1.1.4h.1v4h-.1a1.7 1.7 0 0 0-1.7.6z"></path>
71
- </svg>
72
- </button>
73
- </div>
74
- </div>
75
- <div class="lobby-tabs">
76
- <button id="lobby-tab-sessions" class="lobby-tab active" onclick="switchLobbyTab('sessions')" title="AI sessions">Sessions</button>
77
- <button id="lobby-tab-schedules" class="lobby-tab" onclick="switchLobbyTab('schedules')" title="Scheduled tasks">Tasks</button>
78
- </div>
79
- <div id="sessions-list">
80
- <p style="color:#888; text-align:center; margin-top:50px;">Loading sessions...</p>
81
- </div>
82
- <div id="schedules-list" style="display:none;">
83
- <p style="color:#888; text-align:center; margin-top:50px;">Loading schedules...</p>
84
- </div>
85
- </div>
86
- </div>
87
-
88
- <div id="sidebar-resizer" role="separator" aria-label="Resize session sidebar" aria-orientation="vertical"></div>
89
- <main id="detail-pane">
90
- <div id="detail-empty">
91
- <img src="logo.svg" alt="" aria-hidden="true">
92
- <h2>Select a session</h2>
93
- <p>Choose an active session from the sidebar or create a new one.</p>
94
- </div>
95
-
96
- <!-- Usage Dashboard View -->
97
- <div id="usage-view" class="view">
98
- <div class="usage-nav">
99
- <button class="usage-nav-button" type="button" onclick="showLobby()" aria-label="Back to lobby">
100
- <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg>
101
- Lobby
102
- </button>
103
- <div class="usage-nav-title">
104
- <strong id="usage-source-title">Usage</strong>
105
- <span id="usage-updated-at"></span>
106
- </div>
107
- <div class="usage-nav-actions">
108
- <button class="icon-btn usage-nav-icon" type="button" onclick="showUsageSourceModal()" title="Change CLI" aria-label="Change CLI">
109
- <svg width="19" height="19" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 7h16"></path><path d="M4 12h16"></path><path d="M4 17h16"></path></svg>
110
- </button>
111
- <button id="usage-refresh-button" class="icon-btn usage-nav-icon" type="button" onclick="refreshUsageDashboard()" title="Refresh usage" aria-label="Refresh usage">
112
- <svg width="19" height="19" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-3-6.7"></path><polyline points="21 3 21 9 15 9"></polyline></svg>
113
- </button>
114
- </div>
115
- </div>
116
- <main class="usage-content">
117
- <div class="usage-filter-bar">
118
- <div class="usage-period-toggle" role="tablist" aria-label="Usage scope">
119
- <button id="usage-scope-weekly" class="active" type="button" onclick="setUsageScope('weekly')">Week</button>
120
- <button id="usage-scope-monthly" type="button" onclick="setUsageScope('monthly')">Month</button>
121
- </div>
122
- <label class="usage-period-picker">
123
- <span>Period</span>
124
- <select id="usage-period-select" onchange="selectUsagePeriod(this.value)" aria-label="Select period"></select>
125
- </label>
126
- </div>
127
- <div id="usage-loading" class="usage-state">Loading usage data...</div>
128
- <div id="usage-dashboard" hidden>
129
- <section id="usage-summary" class="usage-summary usage-summary-totals" aria-label="All-model totals"></section>
130
- <section class="usage-panel">
131
- <div class="usage-panel-heading"><div><h2>Model summary</h2><p>All models in the selected period.</p></div></div>
132
- <div id="usage-model-summary" class="usage-table-wrap"></div>
133
- </section>
134
- <section class="usage-panel">
135
- <div class="usage-panel-heading">
136
- <div><h2>Tokens by day</h2><p>Daily totals stacked by model.</p></div>
137
- <div id="usage-token-legend" class="usage-legend" aria-label="Token chart model legend"></div>
138
- </div>
139
- <div id="usage-token-chart" class="usage-chart"></div>
140
- </section>
141
- <section id="usage-cost-panel" class="usage-panel">
142
- <div class="usage-panel-heading">
143
- <div><h2>Cost by day</h2><p>ccusage estimates stacked by GPT model.</p></div>
144
- <div id="usage-cost-legend" class="usage-legend" aria-label="Cost chart model legend"></div>
145
- </div>
146
- <div id="usage-cost-chart" class="usage-chart"></div>
147
- </section>
148
- <section class="usage-panel">
149
- <div class="usage-panel-heading"><div><h2>Daily details</h2><p>Exact daily totals reported by ccusage.</p></div></div>
150
- <div id="usage-daily-table" class="usage-table-wrap"></div>
151
- </section>
152
- <div id="usage-engine-note" class="usage-pricing-note"></div>
153
- </div>
154
- </main>
155
- </div>
156
-
157
- <!-- Terminal View -->
158
- <div id="terminal-view" class="view">
159
- <div id="top-ui">
160
- <div id="nav-bar">
161
- <button id="back-btn" onclick="showLobby()">
162
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg>
163
- Lobby
164
- </button>
165
- <div id="session-title">Terminal</div>
166
- <div style="display: flex; align-items: center; gap: 8px;">
167
- <button class="icon-btn" onclick="showHistory()" title="History" style="color: var(--primary); font-size: 12px; font-weight: 700; background: rgba(0, 122, 255, 0.1); padding: 5px 9px; border-radius: 15px; gap: 4px;">
168
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 1 0 3-6.7"></path><polyline points="3 3 3 9 9 9"></polyline><path d="M12 7v5l3 2"></path></svg>
169
- HIS
170
- </button>
171
- <button class="icon-btn" onclick="showGitPreview()" title="Git" style="color: var(--primary); font-size: 12px; font-weight: 700; background: rgba(0, 122, 255, 0.1); padding: 5px 9px; border-radius: 15px; gap: 4px;">
172
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"><circle cx="6" cy="6" r="2.5"></circle><circle cx="18" cy="6" r="2.5"></circle><circle cx="12" cy="18" r="2.5"></circle><path d="M8.2 7.6 11 15.7"></path><path d="M15.8 7.6 13 15.7"></path></svg>
173
- GIT
174
- </button>
175
- </div>
176
- </div>
177
- <div id="session-status-strip" aria-live="polite">
178
- <div id="session-attention-rail"></div>
179
- </div>
180
- <div id="terminal-controls">
181
- <div id="timed-tag-rail"></div>
182
- <div id="composer-skill-prefix"></div>
183
- <div id="input-row">
184
- <textarea id="cmd-input" rows="1" placeholder="Type a message..."></textarea>
185
- <button id="attachment-btn" class="composer-action-btn" type="button" title="Attachment" aria-label="Attachment"><svg class="action-icon" aria-hidden="true"><use href="#icon-file"></use></svg></button>
186
- <button id="schedule-send-btn" class="composer-action-btn" type="button" title="Schedule" aria-label="Schedule"><svg class="action-icon" aria-hidden="true"><use href="#icon-schedule"></use></svg></button>
187
- <button id="send-btn" type="button" title="Send" aria-label="Send">
188
- <svg class="action-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="22" y1="2" x2="11" y2="13"></line><polygon points="22 2 15 22 11 13 2 9 22 2"></polygon></svg>
189
- </button>
190
- </div>
191
- <div id="attachment-strip" aria-live="polite"></div>
192
- <input id="attachment-file-input" type="file" multiple hidden>
193
- <div id="composer-action-tooltip" role="tooltip"></div>
194
- <div id="claude-control-panel">
195
- <div class="claude-control-rail">
196
- <div class="claude-control-page primary">
197
- <div class="claude-picker-wrap">
198
- <select id="claude-model-select" class="claude-select" title="Model" onchange="updateClaudeSettingsFromControls()">
199
- <option value="default">M:Def</option>
200
- </select>
201
- <select id="claude-effort-select" class="claude-select" title="Effort" onchange="updateClaudeSettingsFromControls()">
202
- <option value="low">Low</option>
203
- <option value="medium">Medium</option>
204
- <option value="high">High</option>
205
- <option value="xhigh">Extra high</option>
206
- <option value="max">Max</option>
207
- </select>
208
- <button id="claude-model-picker-btn" class="claude-select claude-picker-btn" onclick="toggleClaudePicker('model')" title="Model and effort" aria-label="Model and effort"><svg class="action-icon" aria-hidden="true"><use href="#icon-model"></use></svg><span class="action-label">Model</span></button>
209
- </div>
210
- <button id="claude-usage-btn" class="claude-ctrl-btn" onclick="requestClaudeUsage()" title="Show Claude usage" aria-label="Show Claude usage"><svg class="action-icon" aria-hidden="true"><use href="#icon-usage"></use></svg><span class="action-label">Usage</span></button>
211
- <button id="claude-context-btn" class="claude-ctrl-btn" onclick="requestClaudeContext()" title="Show Claude context" aria-label="Show Claude context"><svg class="action-icon" aria-hidden="true"><use href="#icon-context"></use></svg><span class="action-label">Context</span></button>
212
- <button id="claude-abort-btn" class="claude-ctrl-btn danger" onclick="abortClaudeSession()" title="Abort current Claude turn" aria-label="Abort current Claude turn"><svg class="action-icon" aria-hidden="true"><use href="#icon-stop"></use></svg><span class="action-label">Abort</span></button>
213
- <button id="claude-resume-btn" class="claude-ctrl-btn primary" onclick="toggleClaudeResumePanel()" title="Choose a Claude session to resume" aria-label="Choose a Claude session to resume"><svg class="action-icon" aria-hidden="true"><use href="#icon-resume"></use></svg><span class="action-label">Resume</span></button>
214
- <button id="claude-fork-btn" class="claude-ctrl-btn primary" onclick="toggleClaudeForkPanel()" title="Fork a Claude session" aria-label="Fork a Claude session"><svg class="action-icon" aria-hidden="true"><use href="#icon-fork"></use></svg><span class="action-label">Fork</span></button>
215
- </div>
216
- <div class="claude-control-page secondary">
217
- <div class="claude-picker-wrap">
218
- <select id="claude-permission-select" class="claude-select" title="Permission mode" onchange="updateClaudeSettingsFromControls()">
219
- <option value="default">Default</option>
220
- <option value="acceptEdits">Accept edits</option>
221
- <option value="plan">Plan mode</option>
222
- <option value="bypassPermissions">Bypass</option>
223
- </select>
224
- <button id="claude-permission-picker-btn" class="claude-select claude-picker-btn" onclick="toggleClaudePicker('permission')" title="Permission mode" aria-label="Permission mode"><svg class="action-icon" aria-hidden="true"><use href="#icon-shield"></use></svg><span class="action-label">Permission</span></button>
225
- </div>
226
- </div>
227
- </div>
228
- <div id="claude-state-bar"></div>
229
- <div id="claude-picker-panel"></div>
230
- <div id="claude-resume-panel"></div>
231
- <div id="claude-fork-panel"></div>
232
- </div>
233
- <div id="codex-control-panel">
234
- <div id="codex-control-rail" class="codex-control-rail">
235
- <div class="codex-control-page">
236
- <button id="codex-model-btn" class="claude-ctrl-btn" onclick="toggleCodexModelPanel()" title="Model and effort" aria-label="Model and effort"><svg class="action-icon" aria-hidden="true"><use href="#icon-model"></use></svg><span class="action-label">Model</span></button>
237
- <button id="codex-status-btn" class="claude-ctrl-btn" onclick="requestCodexStatus()" title="Show Codex account, limits, and context status" aria-label="Show Codex status"><svg class="action-icon" aria-hidden="true"><use href="#icon-status"></use></svg><span class="action-label">Status</span></button>
238
- <button id="codex-abort-btn" class="claude-ctrl-btn danger" onclick="abortCodexSession()" title="Abort current Codex turn" aria-label="Abort current Codex turn"><svg class="action-icon" aria-hidden="true"><use href="#icon-stop"></use></svg><span class="action-label">Abort</span></button>
239
- <button id="codex-resume-btn" class="claude-ctrl-btn primary" onclick="toggleCodexResumePanel()" title="Resume a Codex thread" aria-label="Resume a Codex thread"><svg class="action-icon" aria-hidden="true"><use href="#icon-resume"></use></svg><span class="action-label">Resume</span></button>
240
- <button id="codex-fork-btn" class="claude-ctrl-btn primary" onclick="toggleCodexForkPanel()" title="Fork a Codex thread in this conversation" aria-label="Fork this Codex thread"><svg class="action-icon" aria-hidden="true"><use href="#icon-fork"></use></svg><span class="action-label">Fork</span></button>
241
- </div>
242
- <div class="codex-control-page">
243
- <button id="codex-prompts-btn" class="claude-ctrl-btn" onclick="toggleCodexPromptPanel()" title="Browse and copy recent prompts" aria-label="Browse recent prompts"><svg class="action-icon" aria-hidden="true"><use href="#icon-prompts"></use></svg><span class="action-label">Prompts</span></button>
244
- <button id="codex-compact-btn" class="claude-ctrl-btn" onclick="compactCodexContext()" title="Compact the current Codex context" aria-label="Compact Codex context"><svg class="action-icon" aria-hidden="true"><use href="#icon-compact"></use></svg><span class="action-label">Compact</span></button>
245
- <label class="codex-select-control" title="Sandbox mode">
246
- <span class="codex-select-label" aria-hidden="true"><svg class="action-icon"><use href="#icon-sandbox"></use></svg><span class="action-label">Sandbox</span></span>
247
- <select id="codex-sandbox-select" class="codex-select" aria-label="Sandbox mode" onchange="updateCodexSettingsFromControls()">
248
- <option value="default">Default</option><option value="read-only">Read only</option><option value="workspace-write">Workspace write</option><option value="danger-full-access">Full access</option>
249
- </select>
250
- </label>
251
- <label class="codex-select-control" title="Approval policy">
252
- <span class="codex-select-label" aria-hidden="true"><svg class="action-icon"><use href="#icon-lock"></use></svg><span class="action-label">Ask</span></span>
253
- <select id="codex-permission-select" class="codex-select" aria-label="Approval policy" onchange="updateCodexSettingsFromControls()">
254
- <option value="default">Default</option><option value="untrusted">Untrusted</option><option value="on-request">On request</option><option value="never">Never ask</option>
255
- </select>
256
- </label>
257
- <button id="codex-skills-btn" class="claude-ctrl-btn" onclick="toggleCodexSkillPanel()" title="Choose a skill for the next message" aria-label="Skills"><svg class="action-icon" aria-hidden="true"><use href="#icon-skills"></use></svg><span class="action-label">Skills</span></button>
258
- </div>
259
- </div>
260
- <div id="codex-state-bar"></div>
261
- <div id="codex-model-panel"></div>
262
- <div id="codex-resume-panel"></div>
263
- <div id="codex-fork-panel"></div>
264
- <div id="codex-prompt-panel"></div>
265
- <div id="codex-skill-panel"></div>
266
- </div>
267
- <div id="timed-send-panel">
268
- <div class="timed-row">
269
- <select id="timed-hours" onchange="updateTimedSendPreview()" title="Hours"></select>
270
- <select id="timed-minutes" onchange="updateTimedSendPreview()" title="Minutes"></select>
271
- <button id="timed-save-btn" class="small-btn primary" onclick="saveTimedSend()">Add Timer</button>
272
- </div>
273
- <div id="timed-preview" class="timed-preview"></div>
274
- <div class="timed-editor-actions">
275
- <button id="timed-cancel-edit-btn" class="small-btn" onclick="resetTimedEditor()" style="display:none;">Cancel Edit</button>
276
- <button id="timed-delete-btn" class="small-btn danger" onclick="deleteEditingTimedInput()" style="display:none;">Delete</button>
277
- </div>
278
- </div>
279
- <div id="shortcut-rail">
280
- <div class="shortcut-group simple-shortcuts">
281
- <div class="key-btn special-key" data-key="up">↑</div>
282
- <div class="key-btn special-key" data-key="down">↓</div>
283
- <div class="key-btn" data-key="ctrl">Ctrl</div>
284
- <div class="key-btn special-key" data-key="enter">⏎</div>
285
- <div class="key-btn" data-key="esc">Esc</div>
286
- <div class="key-btn" data-key="tab">Tab</div>
287
- </div>
288
- <div class="shortcut-group complex-shortcuts" data-role="tool-shortcuts">
289
- <div class="key-btn special-key" data-key="left">←</div>
290
- <div class="key-btn special-key" data-key="right">→</div>
291
- <div class="key-btn" data-key="ctrl-c">Ctrl+C</div>
292
- <button class="command-key" data-command="/model" title="Send /model">/model</button>
293
- <button class="command-key" data-command="/resume" title="Send /resume">/resume</button>
294
- <button class="command-key" data-command="/stat" data-role="usage-shortcut" title="Send /stat">/stat</button>
295
- </div>
296
- </div>
297
- </div>
298
- </div>
299
- <div id="scroll-controls">
300
- <div class="scroll-btn" id="scroll-up"><span>▲</span></div>
301
- <div class="scroll-btn" id="scroll-down"><span>▼</span></div>
302
- </div>
303
- <div id="terminal-controls-spacer"></div>
304
- <div id="terminal-container"><div id="terminal"></div></div>
305
- <div id="claude-chat-container"></div>
306
- <div id="codex-chat-container"></div>
307
- </div>
308
-
309
- <!-- Plain Text History View -->
310
- <div id="history-view" class="view">
311
- <div class="subview-nav">
312
- <div class="subview-nav-row">
313
- <button class="icon-btn" onclick="showTerminal()" style="color: var(--primary); font-size: 16px; font-weight: 500;">
314
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg>
315
- Terminal
316
- </button>
317
- <div class="subview-nav-title">History</div>
318
- <button class="icon-btn" onclick="loadHistory()" title="Refresh" style="color: var(--primary); margin-right: 8px;">
319
- <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-3-6.7"></path><polyline points="21 3 21 9 15 9"></polyline></svg>
320
- </button>
321
- <button class="icon-btn" onclick="copyHistoryContent()" title="Copy all" style="color: var(--text-dim); margin-right: 8px;">
322
- <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>
323
- </button>
324
- <button id="history-wrap-btn" class="icon-btn" onclick="toggleHistoryWrap()" title="Toggle wrap" style="color: var(--text-dim);">
325
- <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" y1="4" x2="4" y2="20"></line><line x1="20" y1="4" x2="20" y2="20"></line><path d="M8 8h7a3 3 0 0 1 0 6H9"></path><polyline points="11 11 8 14 11 17"></polyline></svg>
326
- </button>
327
- </div>
328
- </div>
329
- <div id="history-meta" class="history-meta">Loading...</div>
330
- <pre id="history-content">Loading...</pre>
331
- </div>
332
-
333
- <!-- Git View -->
334
- <div id="git-view" class="view">
335
- <div class="subview-nav">
336
- <div class="subview-nav-row">
337
- <button class="icon-btn" onclick="showTerminal()" style="color: var(--primary); font-size: 16px; font-weight: 500;">
338
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg>
339
- Terminal
340
- </button>
341
- <div class="subview-nav-title">Git Status</div>
342
- <button class="icon-btn" onclick="loadGitStatus()" style="color: var(--primary);">
343
- <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-3-6.7"></path><polyline points="21 3 21 9 15 9"></polyline></svg>
344
- </button>
345
- </div>
346
- </div>
347
- <div class="tab-bar">
348
- <button id="tab-changes" class="tab-btn active" onclick="switchGitTab('changes')">Changes</button>
349
- <button id="tab-directories" class="tab-btn" onclick="switchGitTab('directories')">Directories</button>
350
- <button id="tab-graph" class="tab-btn" onclick="switchGitTab('graph')">Graph</button>
351
- </div>
352
- <div id="git-content" style="flex: 1; overflow-y: auto; padding: 0; background: var(--bg);">
353
- </div>
354
- </div>
355
-
356
- </main>
357
- </div>
358
-
359
- <!-- Tool Modal -->
360
- <div id="modal-overlay" onclick="closeToolModal(event)">
361
- <div id="tool-modal" onclick="event.stopPropagation()">
362
- <div class="tool-modal-header"><h2>Create Session</h2><button class="icon-btn" type="button" onclick="closeToolModal()" aria-label="Close">×</button></div>
363
- <div style="margin-bottom: 15px;">
364
- <label style="display:block; margin-bottom: 8px; font-size: 14px; color: var(--text-dim);">Working Directory (Optional):</label>
365
- <div style="margin-bottom: 8px; color: var(--text-dim); font-size: 12px; line-height: 1.4;">
366
- Default: <span id="default-cwd" style="font-family: monospace; color: #d1d5db; overflow-wrap: anywhere;">Loading...</span>
367
- </div>
368
- <textarea id="cwd-field" rows="2" placeholder="Optional path" style="width: 100%; background: rgba(255,255,255,0.08); border: 1px solid rgba(255,255,255,0.1); border-radius: 8px; color: #fff; padding: 10px; font-size: 14px; box-sizing: border-box; resize: vertical; font-family: monospace; outline: none;"></textarea>
369
- </div>
370
- <div id="tools-list" style="max-height: 300px; overflow-y: auto;">
371
- <p>Detecting tools...</p>
372
- </div>
373
- </div>
374
- </div>
375
-
376
- <!-- Usage Source Modal -->
377
- <div id="usage-source-overlay" onclick="closeUsageSourceModal(event)">
378
- <div id="usage-source-modal" role="dialog" aria-modal="true" aria-labelledby="usage-source-modal-title" onclick="event.stopPropagation()">
379
- <div class="usage-source-header">
380
- <div>
381
- <h2 id="usage-source-modal-title">Usage Dashboard</h2>
382
- <p>Select a CLI with local usage history.</p>
383
- </div>
384
- <button class="icon-btn" type="button" onclick="closeUsageSourceModal()" aria-label="Close">×</button>
385
- </div>
386
- <div id="usage-sources-list"><p class="usage-modal-state">Reading local usage data...</p></div>
387
- </div>
388
- </div>
389
-
390
- <!-- App Settings Modal -->
391
- <div id="settings-modal-overlay" onclick="closeSettings(event)">
392
- <div id="settings-modal" role="dialog" aria-modal="true" aria-labelledby="settings-modal-title" onclick="event.stopPropagation()">
393
- <div class="settings-modal-header">
394
- <div>
395
- <h2 id="settings-modal-title">Settings</h2>
396
- <p>Manage Glad preferences and integrations.</p>
397
- </div>
398
- <button class="icon-btn settings-close" type="button" onclick="closeSettings()" aria-label="Close">×</button>
399
- </div>
400
- <section class="settings-section" aria-labelledby="appearance-settings-title">
401
- <div class="settings-section-label" id="appearance-settings-title">Appearance</div>
402
- <div class="theme-choice" role="group" aria-label="Color theme">
403
- <button id="theme-light-btn" type="button" onclick="setGladTheme('light')" aria-pressed="false">
404
- <span class="theme-swatch light" aria-hidden="true"></span>
405
- Light
406
- </button>
407
- <button id="theme-dark-btn" type="button" onclick="setGladTheme('dark')" aria-pressed="false">
408
- <span class="theme-swatch dark" aria-hidden="true"></span>
409
- Dark
410
- </button>
411
- </div>
412
- <p class="settings-theme-help">Glad follows your system until you choose a theme.</p>
413
- </section>
414
- <section class="settings-section" aria-labelledby="notifications-settings-title">
415
- <div class="settings-section-label" id="notifications-settings-title">Notifications</div>
416
- <div class="settings-provider-header">
417
- <div>
418
- <h3>ServerChan</h3>
419
- <p>Save the integration here, then enable notifications per chat using its bell.</p>
420
- </div>
421
- </div>
422
- <div class="form-field">
423
- <label for="serverchan-client-type">Delivery</label>
424
- <select id="serverchan-client-type">
425
- <option value="wechat">WeChat</option>
426
- <option value="pushdeer">PushDeer</option>
427
- </select>
428
- <div class="serverchan-help">This choice adjusts message formatting. Delivery is managed by ServerChan.</div>
429
- </div>
430
- <div class="form-field">
431
- <label for="serverchan-send-key">SendKey</label>
432
- <input id="serverchan-send-key" type="password" autocomplete="off" placeholder="SCT...">
433
- <div id="serverchan-key-hint" class="serverchan-help"></div>
434
- </div>
435
- <div id="serverchan-settings-status" class="serverchan-settings-status" aria-live="polite"></div>
436
- <div class="serverchan-modal-actions">
437
- <button id="serverchan-remove-btn" class="small-btn danger" type="button" onclick="removeServerChanSettings()">Remove</button>
438
- <span class="serverchan-action-spacer"></span>
439
- <button id="serverchan-save-btn" class="small-btn primary" type="button" onclick="saveServerChanSettings()">Save</button>
440
- <button id="serverchan-test-btn" class="small-btn" type="button" onclick="testServerChanSettings()">Send Test</button>
441
- </div>
442
- </section>
443
- </div>
444
- </div>
445
- <div id="app-toast" role="status" aria-live="polite"></div>
446
-
447
- <!-- Schedule Modal -->
448
- <div id="schedule-modal-overlay" onclick="closeScheduleModal(event)" style="position: fixed; inset: 0; background: rgba(0,0,0,0.8); z-index: 10001; display: none; align-items: center; justify-content: center; padding: 16px;">
449
- <div id="schedule-modal" onclick="event.stopPropagation()">
450
- <div style="display:flex; justify-content:space-between; align-items:center; gap:12px; margin-bottom:14px;">
451
- <div>
452
- <h2 id="schedule-modal-title" style="margin:0; font-size:20px;">Scheduled Task</h2>
453
- <p class="schedule-modal-subtitle">Runs automatically at the selected time.</p>
454
- </div>
455
- <button class="icon-btn" onclick="closeScheduleModal()" style="color: var(--text-dim); font-size: 24px;">×</button>
456
- </div>
457
- <div class="form-field">
458
- <label>Name</label>
459
- <input id="schedule-name" type="text" placeholder="Scheduled task">
460
- </div>
461
- <div class="form-field">
462
- <label>Tool</label>
463
- <select id="schedule-tool"></select>
464
- </div>
465
- <div class="form-field">
466
- <label>Working Directory</label>
467
- <input id="schedule-cwd" type="text" placeholder="Leave empty for default directory">
468
- </div>
469
- <div class="form-field">
470
- <label>Trigger Time</label>
471
- <input id="schedule-time" type="time" value="09:00">
472
- </div>
473
- <div class="form-field">
474
- <label>Weekdays</label>
475
- <div id="schedule-weekdays" class="weekday-grid"></div>
476
- </div>
477
- <div style="display:flex; justify-content:space-between; align-items:center; gap:10px; margin: 14px 0 8px;">
478
- <div style="font-weight:700;">Steps</div>
479
- <select id="add-step-type" style="background:rgba(255,255,255,0.08); color:#fff; border:1px solid rgba(255,255,255,0.12); border-radius:8px; padding:8px;" onchange="addScheduleStep(this.value); this.value='';">
480
- <option value="">Add step</option>
481
- <option value="sleep">Sleep</option>
482
- <option value="sendText">Send text</option>
483
- <option value="sendKey">Send key</option>
484
- <option value="keyDown">Key down</option>
485
- <option value="keyUp">Key up</option>
486
- <option value="stop">Stop</option>
487
- <option value="closeSession">Close session</option>
488
- </select>
489
- </div>
490
- <div id="schedule-steps"></div>
491
- <div style="display:flex; justify-content:flex-end; gap:10px; margin-top:16px;">
492
- <button class="small-btn" onclick="closeScheduleModal()">Cancel</button>
493
- <button class="small-btn primary" onclick="saveSchedule()">Save</button>
494
- </div>
495
- </div>
496
- </div>
497
-
498
- <script defer src="vendor/xterm.js"></script>
499
- <script defer src="vendor/xterm-addon-fit.js"></script>
500
- <script src="theme.js"></script>
501
- <script src="gitgraph.js"></script>
502
- <script src="core.js"></script>
503
- <script src="layout.js"></script>
504
- <script src="notifications.js"></script>
505
- <script src="claude.js"></script>
506
- <script src="schedules.js"></script>
507
- <script src="shell.js"></script>
508
- <script src="codex.js"></script>
509
- <script src="session.js"></script>
510
- <script src="composer.js"></script>
511
- <script src="timed-inputs.js"></script>
512
- <script src="terminal-scroll.js"></script>
513
- <script src="git.js"></script>
514
- <script src="usage.js"></script>
515
- </body>
516
- </html>
package/lib/web/layout.js DELETED
@@ -1,72 +0,0 @@
1
- const GLAD_SPLIT_QUERY = '(min-width: 920px)';
2
- const GLAD_SIDEBAR_KEY = 'glad-sidebar-width';
3
-
4
- function isSplitLayout() {
5
- return window.matchMedia(GLAD_SPLIT_QUERY).matches;
6
- }
7
-
8
- function clampSidebarWidth(value) {
9
- const max = Math.max(300, Math.min(480, window.innerWidth - 484));
10
- return Math.min(max, Math.max(300, Number(value) || 348));
11
- }
12
-
13
- function applySidebarWidth(value) {
14
- const width = clampSidebarWidth(value);
15
- document.documentElement.style.setProperty('--sidebar-w', `${width}px`);
16
- return width;
17
- }
18
-
19
- function initializeResponsiveLayout() {
20
- const storedWidth = Number(localStorage.getItem(GLAD_SIDEBAR_KEY));
21
- applySidebarWidth(storedWidth || 348);
22
-
23
- const handle = document.getElementById('sidebar-resizer');
24
- if (handle) {
25
- let dragging = false;
26
- const onPointerMove = event => {
27
- if (!dragging) return;
28
- applySidebarWidth(event.clientX);
29
- if (typeof syncLayout === 'function') syncLayout({ keepAtBottom: false });
30
- };
31
- const stopDragging = () => {
32
- if (!dragging) return;
33
- dragging = false;
34
- handle.classList.remove('dragging');
35
- document.body.style.removeProperty('cursor');
36
- document.body.style.removeProperty('user-select');
37
- const width = parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--sidebar-w'));
38
- localStorage.setItem(GLAD_SIDEBAR_KEY, String(clampSidebarWidth(width)));
39
- };
40
- handle.addEventListener('pointerdown', event => {
41
- if (!isSplitLayout()) return;
42
- dragging = true;
43
- handle.classList.add('dragging');
44
- handle.setPointerCapture?.(event.pointerId);
45
- document.body.style.cursor = 'col-resize';
46
- document.body.style.userSelect = 'none';
47
- event.preventDefault();
48
- });
49
- window.addEventListener('pointermove', onPointerMove);
50
- window.addEventListener('pointerup', stopDragging);
51
- window.addEventListener('pointercancel', stopDragging);
52
- }
53
-
54
- const media = window.matchMedia(GLAD_SPLIT_QUERY);
55
- media.addEventListener('change', () => {
56
- applySidebarWidth(parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--sidebar-w')));
57
- if (isSplitLayout()) scheduleSessionPolling();
58
- if (typeof syncLayout === 'function') requestAnimationFrame(() => syncLayout({ keepAtBottom: false }));
59
- });
60
-
61
- const controls = document.getElementById('terminal-controls');
62
- if (controls && typeof ResizeObserver !== 'undefined') {
63
- new ResizeObserver(() => {
64
- if (typeof syncLayout === 'function') syncLayout();
65
- }).observe(controls);
66
- }
67
- }
68
-
69
- window.addEventListener('resize', () => {
70
- if (isSplitLayout()) applySidebarWidth(parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--sidebar-w')));
71
- });
72
- document.addEventListener('DOMContentLoaded', initializeResponsiveLayout);