glad-web 1.0.43 → 1.0.44
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/lib/commands/web.js +2 -0
- package/lib/web/claude.js +16 -6
- package/lib/web/codex.js +3 -3
- package/lib/web/composer.js +3 -9
- package/lib/web/core.js +12 -2
- package/lib/web/index.html +77 -23
- package/lib/web/layout.js +72 -0
- package/lib/web/notifications.js +1 -0
- package/lib/web/session.js +2 -1
- package/lib/web/shell.js +3 -0
- package/lib/web/styles.css +275 -6
- package/lib/web/theme.js +60 -0
- package/lib/web/timed-inputs.js +4 -11
- package/package.json +1 -1
package/lib/commands/web.js
CHANGED
package/lib/web/claude.js
CHANGED
|
@@ -9,13 +9,23 @@
|
|
|
9
9
|
function setClaudeModeEnabled(enabled) {
|
|
10
10
|
const codexChat = isCodexSession() && codexState.presentation === 'structured';
|
|
11
11
|
const structured = enabled || codexChat;
|
|
12
|
+
const actionRail = enabled
|
|
13
|
+
? document.querySelector('.claude-control-rail')
|
|
14
|
+
: codexChat
|
|
15
|
+
? document.getElementById('codex-control-rail')
|
|
16
|
+
: document.getElementById('shortcut-rail');
|
|
17
|
+
const attachButton = document.getElementById('attach-image-btn');
|
|
18
|
+
const scheduleButton = document.getElementById('schedule-send-btn');
|
|
19
|
+
if (actionRail && attachButton && scheduleButton) {
|
|
20
|
+
actionRail.prepend(scheduleButton);
|
|
21
|
+
actionRail.prepend(attachButton);
|
|
22
|
+
}
|
|
12
23
|
document.getElementById('terminal-container').style.display = structured ? 'none' : '';
|
|
13
24
|
document.getElementById('claude-chat-container').style.display = enabled ? 'block' : 'none';
|
|
14
25
|
document.getElementById('codex-chat-container').style.display = codexChat ? 'block' : 'none';
|
|
15
|
-
document.getElementById('claude-control-panel').style.display = enabled ? '
|
|
16
|
-
document.getElementById('codex-control-panel').style.display = codexChat ? '
|
|
26
|
+
document.getElementById('claude-control-panel').style.display = enabled ? 'flex' : 'none';
|
|
27
|
+
document.getElementById('codex-control-panel').style.display = codexChat ? 'flex' : 'none';
|
|
17
28
|
document.getElementById('codex-terminal-switch').style.display = isCodexSession() ? '' : 'none';
|
|
18
|
-
document.getElementById('timer-btn').style.display = '';
|
|
19
29
|
document.getElementById('attach-image-btn').style.display = structured ? '' : 'none';
|
|
20
30
|
document.getElementById('shortcut-rail').style.display = structured ? 'none' : '';
|
|
21
31
|
document.getElementById('scroll-controls').style.display = structured ? 'none' : '';
|
|
@@ -94,7 +104,7 @@
|
|
|
94
104
|
const option = select.selectedOptions && select.selectedOptions[0];
|
|
95
105
|
const label = pickerLabelFromOption(option);
|
|
96
106
|
const fullValue = pickerFullValue(option);
|
|
97
|
-
button
|
|
107
|
+
setActionButtonLabel(button, prefix);
|
|
98
108
|
button.title = type === 'model'
|
|
99
109
|
? `${fullValue || label || prefix} · ${effortLabel(claudeState.effort)}`
|
|
100
110
|
: (fullValue || label || prefix);
|
|
@@ -252,12 +262,12 @@
|
|
|
252
262
|
const usageBtn = document.getElementById('claude-usage-btn');
|
|
253
263
|
if (usageBtn) {
|
|
254
264
|
usageBtn.disabled = claudeUsagePending || claudeStatus === 'thinking';
|
|
255
|
-
usageBtn
|
|
265
|
+
setActionButtonLabel(usageBtn, claudeUsagePending ? 'Loading' : 'Usage');
|
|
256
266
|
}
|
|
257
267
|
const contextBtn = document.getElementById('claude-context-btn');
|
|
258
268
|
if (contextBtn) {
|
|
259
269
|
contextBtn.disabled = claudeContextPending || claudeStatus === 'thinking';
|
|
260
|
-
contextBtn
|
|
270
|
+
setActionButtonLabel(contextBtn, claudeContextPending ? 'Loading' : 'Context');
|
|
261
271
|
}
|
|
262
272
|
renderClaudeStateBar();
|
|
263
273
|
renderClaudeChat();
|
package/lib/web/codex.js
CHANGED
|
@@ -445,14 +445,14 @@
|
|
|
445
445
|
sandbox.value = codexState.sandboxMode || 'default';
|
|
446
446
|
}
|
|
447
447
|
const modelButton = document.getElementById('codex-model-btn');
|
|
448
|
-
if (modelButton) modelButton
|
|
448
|
+
if (modelButton) setActionButtonLabel(modelButton, 'Model');
|
|
449
449
|
const abort = document.getElementById('codex-abort-btn');
|
|
450
450
|
if (abort) {
|
|
451
451
|
abort.disabled = !codexState.canAbort;
|
|
452
|
-
abort
|
|
452
|
+
setActionButtonLabel(abort, codexState.aborting ? 'Aborting…' : 'Abort');
|
|
453
453
|
}
|
|
454
454
|
const compact = document.getElementById('codex-compact-btn');
|
|
455
|
-
if (compact) { compact.disabled = !codexState.canCompact; compact
|
|
455
|
+
if (compact) { compact.disabled = !codexState.canCompact; setActionButtonLabel(compact, codexState.compacting ? 'Compacting' : 'Compact'); }
|
|
456
456
|
const skills = document.getElementById('codex-skills-btn');
|
|
457
457
|
if (skills) { skills.disabled = !codexReadyForInput(); skills.classList.toggle('primary', Boolean(selectedCodexSkill)); }
|
|
458
458
|
const resume = document.getElementById('codex-resume-btn');
|
package/lib/web/composer.js
CHANGED
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
function syncComposerButtonState() {
|
|
12
|
-
const menuOpen = document.getElementById('composer-menu').classList.contains('active');
|
|
13
12
|
const timerOpen = document.getElementById('timed-send-panel').classList.contains('active');
|
|
14
|
-
document.getElementById('
|
|
13
|
+
document.getElementById('schedule-send-btn').classList.toggle('active', timerOpen);
|
|
14
|
+
document.getElementById('attach-image-btn').classList.toggle('active', selectedImageAttachments.length > 0);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
function renderImageAttachments() {
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
`<div class="attachment-chip${item.uploading ? ' uploading' : ''}"><span aria-hidden="true">▧</span><span class="attachment-chip-content"><span class="attachment-chip-name" title="${escapeHtml(item.name)}">${escapeHtml(item.name)}</span>${item.uploading ? `<span class="attachment-progress${item.progressKnown ? '' : ' estimated'}"><span style="width:${Math.max(0, Math.min(100, item.progress || 0))}%"></span></span><span class="attachment-status">${escapeHtml(item.status || (item.progressKnown ? `Uploading ${Math.round(item.progress || 0)}%` : 'Uploading original image…'))}</span>` : ''}</span><button class="attachment-remove" type="button" title="Remove image" aria-label="Remove ${escapeHtml(item.name)}" onclick="removeImageAttachment('${item.id}')">×</button></div>`
|
|
20
20
|
)).join('');
|
|
21
21
|
attachmentStrip.classList.toggle('active', selectedImageAttachments.length > 0);
|
|
22
|
+
syncComposerButtonState();
|
|
22
23
|
updateTerminalControlsHeight();
|
|
23
24
|
}
|
|
24
25
|
|
|
@@ -147,14 +148,7 @@
|
|
|
147
148
|
}
|
|
148
149
|
}
|
|
149
150
|
|
|
150
|
-
function closeComposerMenu() {
|
|
151
|
-
document.getElementById('composer-menu').classList.remove('active');
|
|
152
|
-
syncComposerButtonState();
|
|
153
|
-
updateTerminalControlsHeight();
|
|
154
|
-
}
|
|
155
|
-
|
|
156
151
|
function openTimedSendPanel() {
|
|
157
|
-
closeComposerMenu();
|
|
158
152
|
if (isClaudeSession()) {
|
|
159
153
|
closeClaudePicker();
|
|
160
154
|
claudeResumePanelOpen = false;
|
package/lib/web/core.js
CHANGED
|
@@ -9,6 +9,15 @@
|
|
|
9
9
|
let timedSendRefreshTimer = null;
|
|
10
10
|
let timedTagTimer = null;
|
|
11
11
|
let editingTimedInputId = null;
|
|
12
|
+
function setActionButtonLabel(button, label) {
|
|
13
|
+
if (!button) return;
|
|
14
|
+
const labelNode = button.querySelector('.action-label');
|
|
15
|
+
if (labelNode) {
|
|
16
|
+
labelNode.textContent = label;
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
button.textContent = label;
|
|
20
|
+
}
|
|
12
21
|
let claudeMessages = [];
|
|
13
22
|
let claudePendingPermissions = [];
|
|
14
23
|
let claudeStatus = 'idle';
|
|
@@ -235,7 +244,7 @@
|
|
|
235
244
|
${timedInputCount}
|
|
236
245
|
</span>`
|
|
237
246
|
: '';
|
|
238
|
-
html += `<div class="session-card">
|
|
247
|
+
html += `<div class="session-card${s.id === activeSessionId ? ' selected' : ''}" data-session-id="${escapeHtml(s.id)}">
|
|
239
248
|
<div class="session-info">
|
|
240
249
|
<h3>${escapeHtml(s.name)}${s.hasUnreadCompletion ? '<span class="completion-dot" title="Completed"></span>' : ''}${timerBadge} <button type="button" class="icon-btn session-edit-btn" title="Rename session" aria-label="Rename session" onclick="renameSession('${s.id}', decodePathValue('${encodedName}'), event)"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path></svg></button></h3>
|
|
241
250
|
<p>${escapeHtml(s.tool)}</p>
|
|
@@ -320,7 +329,8 @@
|
|
|
320
329
|
}
|
|
321
330
|
|
|
322
331
|
function isLobbyVisible() {
|
|
323
|
-
return document.getElementById('lobby-view').classList.contains('active')
|
|
332
|
+
return document.getElementById('lobby-view').classList.contains('active')
|
|
333
|
+
|| (typeof isSplitLayout === 'function' && isSplitLayout());
|
|
324
334
|
}
|
|
325
335
|
|
|
326
336
|
function scheduleSessionPolling() {
|
package/lib/web/index.html
CHANGED
|
@@ -5,6 +5,17 @@
|
|
|
5
5
|
<title>Glad - AI Sessions</title>
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
|
|
7
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>
|
|
8
19
|
<link rel="manifest" href="manifest.json">
|
|
9
20
|
<link rel="icon" type="image/svg+xml" href="logo.svg">
|
|
10
21
|
<link rel="apple-touch-icon" href="logo.svg">
|
|
@@ -13,6 +24,25 @@
|
|
|
13
24
|
</head>
|
|
14
25
|
<body>
|
|
15
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-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>
|
|
43
|
+
</svg>
|
|
44
|
+
|
|
45
|
+
<div id="app-shell">
|
|
16
46
|
|
|
17
47
|
<!-- Lobby View -->
|
|
18
48
|
<div id="lobby-view" class="view active">
|
|
@@ -55,6 +85,14 @@
|
|
|
55
85
|
</div>
|
|
56
86
|
</div>
|
|
57
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
|
+
|
|
58
96
|
<!-- Usage Dashboard View -->
|
|
59
97
|
<div id="usage-view" class="view">
|
|
60
98
|
<div class="usage-nav">
|
|
@@ -141,16 +179,13 @@
|
|
|
141
179
|
<div id="timed-tag-rail"></div>
|
|
142
180
|
<div id="input-row">
|
|
143
181
|
<textarea id="cmd-input" rows="1" placeholder="Type a message..."></textarea>
|
|
144
|
-
<button id="
|
|
145
|
-
<button id="send-btn">
|
|
182
|
+
<button id="attach-image-btn" class="composer-action-btn" type="button" title="Add image" aria-label="Add image"><svg class="action-icon" aria-hidden="true"><use href="#icon-image"></use></svg></button>
|
|
183
|
+
<button id="schedule-send-btn" class="composer-action-btn" type="button" title="Schedule send" aria-label="Schedule send"><svg class="action-icon" aria-hidden="true"><use href="#icon-schedule"></use></svg></button>
|
|
184
|
+
<button id="send-btn" type="button" title="Send" aria-label="Send">
|
|
146
185
|
<svg 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>
|
|
147
186
|
</button>
|
|
148
187
|
</div>
|
|
149
188
|
<div id="attachment-strip" aria-live="polite"></div>
|
|
150
|
-
<div id="composer-menu" role="menu">
|
|
151
|
-
<button id="attach-image-btn" class="composer-menu-btn" type="button" role="menuitem">Add image</button>
|
|
152
|
-
<button id="schedule-send-btn" class="composer-menu-btn" type="button" role="menuitem">Schedule send</button>
|
|
153
|
-
</div>
|
|
154
189
|
<input id="image-file-input" type="file" accept="image/png,image/jpeg,image/gif,image/webp" multiple hidden>
|
|
155
190
|
<div id="claude-control-panel">
|
|
156
191
|
<div class="claude-control-rail">
|
|
@@ -166,13 +201,13 @@
|
|
|
166
201
|
<option value="xhigh">Extra high</option>
|
|
167
202
|
<option value="max">Max</option>
|
|
168
203
|
</select>
|
|
169
|
-
<button id="claude-model-picker-btn" class="claude-select claude-picker-btn" onclick="toggleClaudePicker('model')" title="Model and effort">Model</button>
|
|
204
|
+
<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>
|
|
170
205
|
</div>
|
|
171
|
-
<button id="claude-usage-btn" class="claude-ctrl-btn" onclick="requestClaudeUsage()" title="Show Claude usage">Usage</button>
|
|
172
|
-
<button id="claude-context-btn" class="claude-ctrl-btn" onclick="requestClaudeContext()" title="Show Claude context">Context</button>
|
|
173
|
-
<button id="claude-abort-btn" class="claude-ctrl-btn danger" onclick="abortClaudeSession()" title="Abort current Claude turn">Abort</button>
|
|
174
|
-
<button id="claude-resume-btn" class="claude-ctrl-btn primary" onclick="toggleClaudeResumePanel()" title="Choose a Claude session to resume">Resume</button>
|
|
175
|
-
<button id="claude-fork-btn" class="claude-ctrl-btn primary" onclick="toggleClaudeForkPanel()" title="Fork a Claude session">Fork</button>
|
|
206
|
+
<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>
|
|
207
|
+
<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>
|
|
208
|
+
<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>
|
|
209
|
+
<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>
|
|
210
|
+
<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>
|
|
176
211
|
</div>
|
|
177
212
|
<div class="claude-control-page secondary">
|
|
178
213
|
<div class="claude-picker-wrap">
|
|
@@ -182,7 +217,7 @@
|
|
|
182
217
|
<option value="plan">Plan mode</option>
|
|
183
218
|
<option value="bypassPermissions">Bypass</option>
|
|
184
219
|
</select>
|
|
185
|
-
<button id="claude-permission-picker-btn" class="claude-select claude-picker-btn" onclick="toggleClaudePicker('permission')" title="Permission mode">Permission</button>
|
|
220
|
+
<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>
|
|
186
221
|
</div>
|
|
187
222
|
</div>
|
|
188
223
|
</div>
|
|
@@ -194,28 +229,28 @@
|
|
|
194
229
|
<div id="codex-control-panel">
|
|
195
230
|
<div id="codex-control-rail" class="codex-control-rail">
|
|
196
231
|
<div class="codex-control-page">
|
|
197
|
-
<button id="codex-model-btn" class="claude-ctrl-btn" onclick="toggleCodexModelPanel()" title="Model and effort">Model</button>
|
|
198
|
-
<button id="codex-status-btn" class="claude-ctrl-btn" onclick="requestCodexStatus()" title="Show Codex account, limits, and context status">Status</button>
|
|
199
|
-
<button id="codex-abort-btn" class="claude-ctrl-btn danger" onclick="abortCodexSession()" title="Abort current Codex turn">Abort</button>
|
|
200
|
-
<button id="codex-resume-btn" class="claude-ctrl-btn primary" onclick="toggleCodexResumePanel()" title="Resume a Codex thread">Resume</button>
|
|
201
|
-
<button id="codex-fork-btn" class="claude-ctrl-btn primary" onclick="toggleCodexForkPanel()" title="Fork a Codex thread in this conversation">Fork</button>
|
|
232
|
+
<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>
|
|
233
|
+
<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>
|
|
234
|
+
<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>
|
|
235
|
+
<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>
|
|
236
|
+
<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>
|
|
202
237
|
</div>
|
|
203
238
|
<div class="codex-control-page">
|
|
204
|
-
<button id="codex-prompts-btn" class="claude-ctrl-btn" onclick="toggleCodexPromptPanel()" title="Browse and copy recent prompts">Prompts</button>
|
|
205
|
-
<button id="codex-compact-btn" class="claude-ctrl-btn" onclick="compactCodexContext()" title="Compact the current Codex context">Compact</button>
|
|
239
|
+
<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>
|
|
240
|
+
<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>
|
|
206
241
|
<label class="codex-select-control" title="Sandbox mode">
|
|
207
|
-
<span class="codex-select-label" aria-hidden="true">Sandbox</span>
|
|
242
|
+
<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>
|
|
208
243
|
<select id="codex-sandbox-select" class="codex-select" aria-label="Sandbox mode" onchange="updateCodexSettingsFromControls()">
|
|
209
244
|
<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>
|
|
210
245
|
</select>
|
|
211
246
|
</label>
|
|
212
247
|
<label class="codex-select-control" title="Approval policy">
|
|
213
|
-
<span class="codex-select-label" aria-hidden="true">Ask</span>
|
|
248
|
+
<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>
|
|
214
249
|
<select id="codex-permission-select" class="codex-select" aria-label="Approval policy" onchange="updateCodexSettingsFromControls()">
|
|
215
250
|
<option value="default">Default</option><option value="untrusted">Untrusted</option><option value="on-request">On request</option><option value="never">Never ask</option>
|
|
216
251
|
</select>
|
|
217
252
|
</label>
|
|
218
|
-
<button id="codex-skills-btn" class="claude-ctrl-btn" onclick="toggleCodexSkillPanel()" title="Choose a skill for the next message">Skills</button>
|
|
253
|
+
<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>
|
|
219
254
|
</div>
|
|
220
255
|
</div>
|
|
221
256
|
<div id="codex-state-bar"></div>
|
|
@@ -314,6 +349,9 @@
|
|
|
314
349
|
</div>
|
|
315
350
|
</div>
|
|
316
351
|
|
|
352
|
+
</main>
|
|
353
|
+
</div>
|
|
354
|
+
|
|
317
355
|
<!-- Tool Modal -->
|
|
318
356
|
<div id="modal-overlay" onclick="closeToolModal(event)">
|
|
319
357
|
<div id="tool-modal" onclick="event.stopPropagation()">
|
|
@@ -355,6 +393,20 @@
|
|
|
355
393
|
</div>
|
|
356
394
|
<button class="icon-btn settings-close" type="button" onclick="closeSettings()" aria-label="Close">×</button>
|
|
357
395
|
</div>
|
|
396
|
+
<section class="settings-section" aria-labelledby="appearance-settings-title">
|
|
397
|
+
<div class="settings-section-label" id="appearance-settings-title">Appearance</div>
|
|
398
|
+
<div class="theme-choice" role="group" aria-label="Color theme">
|
|
399
|
+
<button id="theme-light-btn" type="button" onclick="setGladTheme('light')" aria-pressed="false">
|
|
400
|
+
<span class="theme-swatch light" aria-hidden="true"></span>
|
|
401
|
+
Light
|
|
402
|
+
</button>
|
|
403
|
+
<button id="theme-dark-btn" type="button" onclick="setGladTheme('dark')" aria-pressed="false">
|
|
404
|
+
<span class="theme-swatch dark" aria-hidden="true"></span>
|
|
405
|
+
Dark
|
|
406
|
+
</button>
|
|
407
|
+
</div>
|
|
408
|
+
<p class="settings-theme-help">Glad follows your system until you choose a theme.</p>
|
|
409
|
+
</section>
|
|
358
410
|
<section class="settings-section" aria-labelledby="notifications-settings-title">
|
|
359
411
|
<div class="settings-section-label" id="notifications-settings-title">Notifications</div>
|
|
360
412
|
<div class="settings-provider-header">
|
|
@@ -441,8 +493,10 @@
|
|
|
441
493
|
|
|
442
494
|
<script defer src="vendor/xterm.js"></script>
|
|
443
495
|
<script defer src="vendor/xterm-addon-fit.js"></script>
|
|
496
|
+
<script src="theme.js"></script>
|
|
444
497
|
<script src="gitgraph.js"></script>
|
|
445
498
|
<script src="core.js"></script>
|
|
499
|
+
<script src="layout.js"></script>
|
|
446
500
|
<script src="notifications.js"></script>
|
|
447
501
|
<script src="claude.js"></script>
|
|
448
502
|
<script src="schedules.js"></script>
|
|
@@ -0,0 +1,72 @@
|
|
|
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);
|
package/lib/web/notifications.js
CHANGED
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
async function openSettings(event = null) {
|
|
50
50
|
event?.stopPropagation();
|
|
51
51
|
document.getElementById('settings-modal-overlay').style.display = 'flex';
|
|
52
|
+
if (typeof syncGladThemeControls === 'function') syncGladThemeControls();
|
|
52
53
|
setServerChanStatus('Loading configuration…');
|
|
53
54
|
try {
|
|
54
55
|
const response = await fetchWithTimeout('/api/notifications/serverchan', {}, 10000);
|
package/lib/web/session.js
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
setClaudeModeEnabled(isClaudeSession());
|
|
18
18
|
if (currentSocket) { currentSocket.close(); currentSocket = null; }
|
|
19
19
|
initTerminal(id);
|
|
20
|
+
if (typeof isSplitLayout === 'function' && isSplitLayout()) refreshSessionsNow();
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
function initTerminal(sessionId) {
|
|
@@ -37,7 +38,7 @@
|
|
|
37
38
|
return /^\x1b\[\?[\d;]*c$/.test(data) || /^\x1b\[>[\d;]*c$/.test(data);
|
|
38
39
|
}
|
|
39
40
|
if (!term) {
|
|
40
|
-
term = new Terminal({ theme: { background: '#000', foreground: '#fff', cursor: '#0f0' }, cursorBlink: true, fontSize: 14, cursorStyle: 'bar', scrollback: 10000 });
|
|
41
|
+
term = new Terminal({ theme: typeof getGladTerminalTheme === 'function' ? getGladTerminalTheme() : { background: '#000', foreground: '#fff', cursor: '#0f0' }, cursorBlink: true, fontSize: 14, cursorStyle: 'bar', scrollback: 10000 });
|
|
41
42
|
fitAddon = new FitAddon.FitAddon();
|
|
42
43
|
term.loadAddon(fitAddon);
|
|
43
44
|
term.open(document.getElementById('terminal'));
|
package/lib/web/shell.js
CHANGED
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
if (currentSocket) { currentSocket.close(); currentSocket = null; }
|
|
8
8
|
closeTimedSendPanel();
|
|
9
9
|
stopTimedInputTimers();
|
|
10
|
+
activeSessionId = null;
|
|
11
|
+
window.activeSessionId = null;
|
|
12
|
+
activeToolKey = null;
|
|
10
13
|
document.querySelectorAll('.view').forEach(v => v.classList.remove('active'));
|
|
11
14
|
document.getElementById('lobby-view').classList.add('active');
|
|
12
15
|
refreshSessionsNow();
|
package/lib/web/styles.css
CHANGED
|
@@ -142,13 +142,7 @@
|
|
|
142
142
|
#cmd-input { flex: 1; min-height: 38px; max-height: 150px; background: rgba(255,255,255,0.08); border: 1px solid rgba(255,255,255,0.1); border-radius: 19px; color: #fff; padding: 9px 16px; font-size: 16px; outline: none; resize: none; overflow-y: auto; line-height: 20px; box-sizing: border-box; transition: background 0.18s ease, border-color 0.18s ease; }
|
|
143
143
|
#cmd-input::placeholder { color: rgba(255,255,255,0.45); }
|
|
144
144
|
#cmd-input:focus { background: rgba(255,255,255,0.1); border-color: rgba(0,122,255,0.42); color: #fff; }
|
|
145
|
-
#timer-btn { width: 38px; height: 38px; margin-left: 8px; background: rgba(255,255,255,0.08); border: 1px solid rgba(255,255,255,0.1); border-radius: 19px; color: #d1d5db; display: flex; align-items: center; justify-content: center; flex-shrink: 0; cursor: pointer; font-size: 24px; line-height: 1; }
|
|
146
|
-
#timer-btn.active { color: #fff; border-color: rgba(0,122,255,0.45); background: rgba(0,122,255,0.22); }
|
|
147
145
|
#send-btn { width: 44px; height: 38px; margin-left: 10px; background: #007aff; border: none; border-radius: 19px; color: #fff; display: flex; align-items: center; justify-content: center; flex-shrink: 0; cursor: pointer; }
|
|
148
|
-
#composer-menu { display: none; width: min(calc(100% - 28px), var(--control-content-max)); margin: -2px auto 8px auto; padding: 6px; border: 1px solid rgba(255,255,255,0.12); border-radius: 12px; background: rgba(38,38,42,0.98); box-shadow: 0 12px 28px rgba(0,0,0,0.28); box-sizing: border-box; }
|
|
149
|
-
#composer-menu.active { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px; }
|
|
150
|
-
.composer-menu-btn { min-height: 42px; border: 0; border-radius: 9px; background: rgba(255,255,255,0.08); color: #fff; font-size: 14px; font-weight: 650; cursor: pointer; }
|
|
151
|
-
.composer-menu-btn:hover { background: rgba(255,255,255,0.14); }
|
|
152
146
|
#attachment-strip { display: none; width: min(calc(100% - 28px), var(--control-content-max)); margin: -3px auto 2px auto; gap: 7px; overflow-x: auto; padding: 0 0 4px 0; box-sizing: border-box; scrollbar-width: none; }
|
|
153
147
|
#attachment-strip.active { display: flex; }
|
|
154
148
|
#attachment-strip::-webkit-scrollbar { display: none; }
|
|
@@ -539,3 +533,278 @@
|
|
|
539
533
|
:root { --chat-content-max: 960px; --control-content-max: 1100px; }
|
|
540
534
|
#codex-chat-container { padding: 18px 22px 34px; }
|
|
541
535
|
}
|
|
536
|
+
|
|
537
|
+
/* Responsive application shell, bottom composer, and color themes. */
|
|
538
|
+
:root {
|
|
539
|
+
--sidebar-w: 348px;
|
|
540
|
+
--surface: #121212;
|
|
541
|
+
--surface-raised: #1c1c1e;
|
|
542
|
+
--surface-soft: rgba(255,255,255,.08);
|
|
543
|
+
--line: rgba(255,255,255,.10);
|
|
544
|
+
--chat-bg: #050505;
|
|
545
|
+
--input-bg: rgba(255,255,255,.08);
|
|
546
|
+
--input-fg: #ffffff;
|
|
547
|
+
--input-placeholder: rgba(255,255,255,.45);
|
|
548
|
+
--overlay-bg: rgba(0,0,0,.80);
|
|
549
|
+
color-scheme: dark;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
html[data-theme="light"] {
|
|
553
|
+
--bg: #f5f6f8;
|
|
554
|
+
--card-bg: #ffffff;
|
|
555
|
+
--text: #172033;
|
|
556
|
+
--text-dim: #667085;
|
|
557
|
+
--surface: #ffffff;
|
|
558
|
+
--surface-raised: #ffffff;
|
|
559
|
+
--surface-soft: rgba(15,23,42,.055);
|
|
560
|
+
--line: rgba(15,23,42,.11);
|
|
561
|
+
--chat-bg: #ffffff;
|
|
562
|
+
--input-bg: #f2f4f7;
|
|
563
|
+
--input-fg: #172033;
|
|
564
|
+
--input-placeholder: #8a94a6;
|
|
565
|
+
--overlay-bg: rgba(15,23,42,.45);
|
|
566
|
+
color-scheme: light;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
html[data-theme="dark"] { color-scheme: dark; }
|
|
570
|
+
|
|
571
|
+
.app-icon-sprite { position: fixed; width: 0; height: 0; overflow: hidden; pointer-events: none; }
|
|
572
|
+
.action-icon { width: 19px; height: 19px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; pointer-events: none; }
|
|
573
|
+
.action-label { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
|
|
574
|
+
|
|
575
|
+
#app-shell { display: flex; width: 100%; height: 100%; min-height: 0; overflow: hidden; background: var(--bg); }
|
|
576
|
+
#detail-pane { position: relative; display: flex; min-width: 0; min-height: 0; flex: 1; overflow: hidden; background: var(--bg); }
|
|
577
|
+
#detail-pane > .view { min-width: 0; min-height: 0; }
|
|
578
|
+
#detail-empty { display: none; flex: 1; flex-direction: column; align-items: center; justify-content: center; padding: 32px; color: var(--text-dim); text-align: center; }
|
|
579
|
+
#detail-empty img { width: 72px; height: 72px; margin-bottom: 14px; opacity: .72; }
|
|
580
|
+
#detail-empty h2 { margin: 0; color: var(--text); font-size: 20px; }
|
|
581
|
+
#detail-empty p { max-width: 360px; margin: 8px 0 0; font-size: 13px; line-height: 1.5; }
|
|
582
|
+
#sidebar-resizer { display: none; }
|
|
583
|
+
|
|
584
|
+
#terminal-view.active { display: grid; grid-template-rows: auto minmax(0, 1fr) auto; }
|
|
585
|
+
#top-ui { display: contents; }
|
|
586
|
+
#nav-bar { grid-row: 1; z-index: 20; padding-top: max(8px, env(safe-area-inset-top)); background: var(--surface); border-color: var(--line); }
|
|
587
|
+
#terminal-controls { position: relative; inset: auto; grid-row: 3; z-index: 30; max-height: min(62dvh, 620px); overflow-y: auto; padding-bottom: env(safe-area-inset-bottom); border-top: 1px solid var(--line); border-bottom: 0; background: color-mix(in srgb, var(--surface) 96%, transparent); box-shadow: 0 -10px 28px rgba(0,0,0,.12); }
|
|
588
|
+
#terminal-controls-spacer { display: none; }
|
|
589
|
+
#terminal-container, #claude-chat-container, #codex-chat-container { grid-row: 2; min-width: 0; min-height: 0; background: var(--chat-bg); }
|
|
590
|
+
#input-row { padding-top: 10px; padding-bottom: 7px; }
|
|
591
|
+
#cmd-input { width: 100%; min-height: 42px; border-color: var(--line); background: var(--input-bg); color: var(--input-fg); }
|
|
592
|
+
#cmd-input::placeholder { color: var(--input-placeholder); }
|
|
593
|
+
#cmd-input:focus { color: var(--input-fg); }
|
|
594
|
+
#send-btn { position: absolute; z-index: 5; bottom: calc(10px + env(safe-area-inset-bottom)); margin: 0; }
|
|
595
|
+
.composer-action-btn { position: relative; width: 40px; min-width: 40px; height: 40px; flex: 0 0 40px; padding: 0; margin: 0; border: 1px solid var(--line); border-radius: 12px; background: var(--surface-soft); color: var(--text-dim); display: flex; align-items: center; justify-content: center; cursor: pointer; }
|
|
596
|
+
.composer-action-btn:hover { color: var(--text); }
|
|
597
|
+
.composer-action-btn.active { color: var(--primary); border-color: color-mix(in srgb, var(--primary) 48%, transparent); background: color-mix(in srgb, var(--primary) 14%, var(--surface)); }
|
|
598
|
+
#send-btn { right: max(12px, calc((100% - var(--control-content-max)) / 2 + 14px)); width: 44px; height: 40px; }
|
|
599
|
+
#claude-control-panel, #codex-control-panel { position: relative; flex-direction: column; width: min(100%, var(--control-content-max)); padding: 0 14px 10px; border: 0; background: transparent; }
|
|
600
|
+
.claude-control-rail, .codex-control-rail { order: 100; min-height: 40px; gap: 7px; margin: 0 58px 0 0; padding: 0; overflow-x: auto; overflow-y: hidden; scroll-snap-type: none; scroll-padding: 0; }
|
|
601
|
+
.claude-control-page, .claude-control-page.secondary, .codex-control-page { display: flex; width: auto; flex: 0 0 auto; gap: 7px; justify-content: flex-start; scroll-snap-align: none; }
|
|
602
|
+
.claude-control-page > *, .codex-control-page > * { width: 40px; min-width: 40px; height: 40px; flex: 0 0 40px; }
|
|
603
|
+
.claude-control-page .claude-ctrl-btn, .claude-control-page .claude-select, .codex-control-page .claude-ctrl-btn, .codex-control-page .codex-select-label,
|
|
604
|
+
.claude-select, .claude-ctrl-btn, .codex-select-control, .codex-select-label { width: 40px; height: 40px; padding: 0; border-radius: 12px; font-size: 0; }
|
|
605
|
+
.claude-picker-btn, .claude-ctrl-btn, .codex-select-label { position: relative; display: inline-flex; align-items: center; justify-content: center; }
|
|
606
|
+
#claude-state-bar, #codex-state-bar { order: 10; }
|
|
607
|
+
#claude-picker-panel, #claude-resume-panel, #claude-fork-panel,
|
|
608
|
+
#codex-model-panel, #codex-resume-panel, #codex-fork-panel, #codex-prompt-panel, #codex-skill-panel { order: 20; }
|
|
609
|
+
#shortcut-rail { width: min(100%, var(--control-content-max)); min-height: 40px; gap: 7px; margin: 0 auto; padding: 0 66px 10px 14px; scroll-snap-type: none; scroll-padding: 0; }
|
|
610
|
+
.shortcut-group { display: contents; }
|
|
611
|
+
.shortcut-group .key-btn, .shortcut-group .command-key { width: auto; min-width: 48px; height: 40px; padding: 0 10px; flex: 0 0 auto; }
|
|
612
|
+
#attachment-strip { border-color: var(--line); background: var(--surface-raised); }
|
|
613
|
+
#timed-send-panel { margin-bottom: 8px; border-color: var(--line); background: var(--surface-raised); }
|
|
614
|
+
#timed-tag-rail { top: auto; bottom: calc(100% + 6px); }
|
|
615
|
+
#scroll-controls { position: absolute; }
|
|
616
|
+
|
|
617
|
+
.theme-choice { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
|
|
618
|
+
.theme-choice button { min-height: 44px; display: flex; align-items: center; justify-content: center; gap: 8px; border: 1px solid var(--line); border-radius: 10px; background: var(--surface-soft); color: var(--text); font-size: 13px; font-weight: 700; cursor: pointer; }
|
|
619
|
+
.theme-choice button[aria-pressed="true"] { border-color: var(--primary); box-shadow: 0 0 0 2px color-mix(in srgb, var(--primary) 18%, transparent); }
|
|
620
|
+
.theme-swatch { width: 18px; height: 18px; border: 1px solid rgba(127,127,127,.35); border-radius: 50%; }
|
|
621
|
+
.theme-swatch.light { background: #fff; }
|
|
622
|
+
.theme-swatch.dark { background: #1c1c1e; }
|
|
623
|
+
.settings-theme-help { margin: 8px 0 0; color: var(--text-dim); font-size: 11px; }
|
|
624
|
+
|
|
625
|
+
@media (min-width: 920px) {
|
|
626
|
+
#lobby-view { display: flex !important; width: min(var(--sidebar-w), calc(100vw - 484px)); flex: 0 0 min(var(--sidebar-w), calc(100vw - 484px)); border-right: 1px solid var(--line); background: var(--bg); }
|
|
627
|
+
#lobby { width: 100%; padding: 18px 14px; }
|
|
628
|
+
#lobby-view .header { margin-bottom: 18px; }
|
|
629
|
+
#lobby-view .header h1 { font-size: 23px; }
|
|
630
|
+
#lobby-view .header-action-btn:not(.icon-only) { width: 36px; padding: 0; font-size: 0; }
|
|
631
|
+
#lobby-view .header-action-btn:not(.icon-only) span:first-child { font-size: 20px; }
|
|
632
|
+
#sidebar-resizer { display: block; width: 4px; flex: 0 0 4px; cursor: col-resize; background: var(--line); touch-action: none; transition: background .15s; }
|
|
633
|
+
#sidebar-resizer:hover, #sidebar-resizer.dragging { background: var(--primary); }
|
|
634
|
+
#detail-pane:not(:has(.view.active)) #detail-empty { display: flex; }
|
|
635
|
+
#back-btn, .usage-nav-button { visibility: hidden; pointer-events: none; }
|
|
636
|
+
.session-card.selected { border: 1px solid color-mix(in srgb, var(--primary) 48%, transparent); background: color-mix(in srgb, var(--primary) 10%, var(--card-bg)); }
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
@media (max-width: 919px) {
|
|
640
|
+
#lobby-view { flex: 1; }
|
|
641
|
+
#detail-pane { display: contents; }
|
|
642
|
+
#detail-empty { display: none !important; }
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
/* Light palette overrides for legacy selectors that still carry dark literals. */
|
|
646
|
+
html[data-theme="light"] body,
|
|
647
|
+
html[data-theme="light"] #terminal-view,
|
|
648
|
+
html[data-theme="light"] #terminal-container,
|
|
649
|
+
html[data-theme="light"] #git-content,
|
|
650
|
+
html[data-theme="light"] #usage-view { background: var(--bg) !important; color: var(--text); }
|
|
651
|
+
html[data-theme="light"] #top-ui,
|
|
652
|
+
html[data-theme="light"] #nav-bar,
|
|
653
|
+
html[data-theme="light"] .usage-nav,
|
|
654
|
+
html[data-theme="light"] #codex-control-panel { background: var(--surface) !important; border-color: var(--line) !important; }
|
|
655
|
+
html[data-theme="light"] .session-card,
|
|
656
|
+
html[data-theme="light"] .schedule-card,
|
|
657
|
+
html[data-theme="light"] .usage-panel,
|
|
658
|
+
html[data-theme="light"] .usage-summary-card,
|
|
659
|
+
html[data-theme="light"] #tool-modal,
|
|
660
|
+
html[data-theme="light"] #settings-modal,
|
|
661
|
+
html[data-theme="light"] #usage-source-modal,
|
|
662
|
+
html[data-theme="light"] #schedule-modal,
|
|
663
|
+
html[data-theme="light"] #claude-picker-panel,
|
|
664
|
+
html[data-theme="light"] #claude-resume-panel,
|
|
665
|
+
html[data-theme="light"] #claude-fork-panel,
|
|
666
|
+
html[data-theme="light"] #codex-model-panel,
|
|
667
|
+
html[data-theme="light"] #codex-resume-panel,
|
|
668
|
+
html[data-theme="light"] #codex-fork-panel,
|
|
669
|
+
html[data-theme="light"] #codex-prompt-panel,
|
|
670
|
+
html[data-theme="light"] #codex-skill-panel { background: var(--card-bg) !important; border-color: var(--line) !important; color: var(--text) !important; }
|
|
671
|
+
html[data-theme="light"] #modal-overlay,
|
|
672
|
+
html[data-theme="light"] #settings-modal-overlay,
|
|
673
|
+
html[data-theme="light"] #usage-source-overlay { background: var(--overlay-bg); }
|
|
674
|
+
html[data-theme="light"] #claude-chat-container,
|
|
675
|
+
html[data-theme="light"] #codex-chat-container,
|
|
676
|
+
html[data-theme="light"] #history-content { background: var(--chat-bg); color: var(--text); }
|
|
677
|
+
html[data-theme="light"] .history-meta { background: #f0f2f5; border-color: var(--line); }
|
|
678
|
+
html[data-theme="light"] .claude-message.assistant,
|
|
679
|
+
html[data-theme="light"] .codex-message,
|
|
680
|
+
html[data-theme="light"] .claude-tool strong,
|
|
681
|
+
html[data-theme="light"] .claude-tool-header,
|
|
682
|
+
html[data-theme="light"] .claude-picker-option,
|
|
683
|
+
html[data-theme="light"] .codex-picker-option,
|
|
684
|
+
html[data-theme="light"] .claude-resume-item,
|
|
685
|
+
html[data-theme="light"] .codex-prompt-text,
|
|
686
|
+
html[data-theme="light"] .usage-nav-title strong { color: var(--text); }
|
|
687
|
+
html[data-theme="light"] .claude-tool,
|
|
688
|
+
html[data-theme="light"] .claude-work-group,
|
|
689
|
+
html[data-theme="light"] .codex-work-group { border-color: var(--line); background: rgba(15,23,42,.035); color: #344054; }
|
|
690
|
+
html[data-theme="light"] .claude-tool-code,
|
|
691
|
+
html[data-theme="light"] .claude-md pre { background: #f4f6f8; color: #27364a; border-color: var(--line); }
|
|
692
|
+
html[data-theme="light"] .claude-select,
|
|
693
|
+
html[data-theme="light"] .claude-ctrl-btn,
|
|
694
|
+
html[data-theme="light"] .codex-select-label,
|
|
695
|
+
html[data-theme="light"] .composer-action-btn,
|
|
696
|
+
html[data-theme="light"] .key-btn,
|
|
697
|
+
html[data-theme="light"] .command-key,
|
|
698
|
+
html[data-theme="light"] .small-btn { border-color: var(--line); background: var(--surface-soft); color: var(--text); }
|
|
699
|
+
html[data-theme="light"] .claude-ctrl-btn.primary { color: #065fbd; background: rgba(0,122,255,.11); }
|
|
700
|
+
html[data-theme="light"] .claude-ctrl-btn.danger,
|
|
701
|
+
html[data-theme="light"] .small-btn.danger { color: #d92d20; background: rgba(217,45,32,.09); }
|
|
702
|
+
html[data-theme="light"] #settings-modal input,
|
|
703
|
+
html[data-theme="light"] #settings-modal select,
|
|
704
|
+
html[data-theme="light"] .form-field input,
|
|
705
|
+
html[data-theme="light"] .form-field select,
|
|
706
|
+
html[data-theme="light"] .form-field textarea,
|
|
707
|
+
html[data-theme="light"] #cwd-field { background: #f2f4f7 !important; border-color: var(--line) !important; color: var(--text) !important; }
|
|
708
|
+
html[data-theme="light"] .tab-bar,
|
|
709
|
+
html[data-theme="light"] .list-item { border-color: var(--line); }
|
|
710
|
+
html[data-theme="light"] .copy-dir-btn,
|
|
711
|
+
html[data-theme="light"] .serverchan-toggle { border-color: var(--line); background: var(--surface-soft); }
|
|
712
|
+
|
|
713
|
+
/* Light theme refinement: stronger hierarchy and readable provider surfaces. */
|
|
714
|
+
html[data-theme="light"] #lobby-view { background: #f0f2f5; }
|
|
715
|
+
html[data-theme="light"] .lobby-tabs { border: 1px solid #e0e5ec; box-shadow: 0 1px 2px rgba(15,23,42,.03); }
|
|
716
|
+
html[data-theme="light"] .session-card,
|
|
717
|
+
html[data-theme="light"] .schedule-card { border: 1px solid #e0e5ec; box-shadow: 0 1px 2px rgba(15,23,42,.04); }
|
|
718
|
+
html[data-theme="light"] .session-card.selected { border-color: #8fc5ff; background: #eaf4ff; box-shadow: 0 0 0 1px rgba(0,122,255,.08); }
|
|
719
|
+
html[data-theme="light"] .btn-join { background: #e8f2ff; color: #006acb; }
|
|
720
|
+
html[data-theme="light"] .weekday-btn { border-color: #d7dde7; background: #f7f8fa; color: #475467; }
|
|
721
|
+
html[data-theme="light"] .weekday-btn.active { border-color: #007aff; background: #007aff; color: #fff; }
|
|
722
|
+
html[data-theme="light"] .step-card { border-color: #dde2ea; background: #f7f8fa; }
|
|
723
|
+
html[data-theme="light"] .small-btn.primary { border-color: #007aff; background: #007aff; color: #fff; }
|
|
724
|
+
|
|
725
|
+
html[data-theme="light"] .claude-message.user,
|
|
726
|
+
html[data-theme="light"] .codex-message.user { border-color: #b8d9fb; background: #e8f3ff; color: #173b61; }
|
|
727
|
+
html[data-theme="light"] .claude-message-attachment { border-color: #c7d9ec; background: rgba(255,255,255,.68); color: #344054; }
|
|
728
|
+
html[data-theme="light"] .claude-md code { border-color: #d9dee7; background: #eef1f5; color: #24364b; }
|
|
729
|
+
html[data-theme="light"] .claude-md blockquote { border-left-color: #aeb8c6; color: #475467; }
|
|
730
|
+
html[data-theme="light"] .claude-md th,
|
|
731
|
+
html[data-theme="light"] .claude-md td { border-color: #d9dee7; }
|
|
732
|
+
html[data-theme="light"] .claude-md th { background: #eef1f5; color: #172033; }
|
|
733
|
+
html[data-theme="light"] .claude-command-chip { border-color: #d9dee7; background: #f2f4f7; color: #27364a; }
|
|
734
|
+
html[data-theme="light"] .claude-picker-option-value { color: #667085; }
|
|
735
|
+
html[data-theme="light"] .claude-picker-column + .claude-picker-column,
|
|
736
|
+
html[data-theme="light"] .codex-picker-column + .codex-picker-column { border-color: #dfe4eb; }
|
|
737
|
+
html[data-theme="light"] .claude-picker-option.selected,
|
|
738
|
+
html[data-theme="light"] .codex-picker-option.selected { background: #dcecff; color: #075fae; }
|
|
739
|
+
|
|
740
|
+
html[data-theme="light"] .codex-status-card { border-color: #cfe0ef; background: #f5f9fc; color: #172033; box-shadow: 0 1px 2px rgba(15,23,42,.03); }
|
|
741
|
+
html[data-theme="light"] .codex-status-title { color: #0070c9; }
|
|
742
|
+
html[data-theme="light"] .codex-status-item { border-color: #dbe3eb; background: #fff; }
|
|
743
|
+
html[data-theme="light"] .codex-status-label { color: #667085; }
|
|
744
|
+
html[data-theme="light"] .codex-status-value { color: #24364b; }
|
|
745
|
+
html[data-theme="light"] .codex-warning-card { border-color: #f2cc60; background: #fff8df; color: #5e4300; }
|
|
746
|
+
html[data-theme="light"] .codex-warning-icon { background: #ffefb3; color: #9a6700; }
|
|
747
|
+
html[data-theme="light"] .codex-warning-title { color: #7a5200; }
|
|
748
|
+
html[data-theme="light"] .codex-warning-text { color: #725d28; }
|
|
749
|
+
html[data-theme="light"] .codex-compaction-card { border-color: #d9c4ef; background: #f7f0ff; color: #3f2a56; }
|
|
750
|
+
html[data-theme="light"] .codex-compaction-meta { color: #725d86; }
|
|
751
|
+
html[data-theme="light"] .codex-context-meter { --context-color: #168447; border-color: #69c98a; background: #effaf3; color: #125f34; }
|
|
752
|
+
html[data-theme="light"] .codex-context-meter::before { background: #bcebc9; }
|
|
753
|
+
html[data-theme="light"] .codex-context-meter.warn { --context-color: #a66f00; border-color: #e0b53e; background: #fff9df; color: #715000; }
|
|
754
|
+
html[data-theme="light"] .codex-context-meter.warn::before { background: #ffe59a; }
|
|
755
|
+
html[data-theme="light"] .codex-context-meter.danger { --context-color: #b53b33; border-color: #e58d86; background: #fff1f0; color: #8f2d27; }
|
|
756
|
+
html[data-theme="light"] .codex-context-meter.danger::before { background: #fac5c1; }
|
|
757
|
+
|
|
758
|
+
html[data-theme="light"] .codex-tool { border-color: #dbe1e8; background: #f7f9fb; color: #344054; }
|
|
759
|
+
html[data-theme="light"] .codex-tool-title,
|
|
760
|
+
html[data-theme="light"] .codex-inline-permission-title,
|
|
761
|
+
html[data-theme="light"] .claude-edit-file > summary,
|
|
762
|
+
html[data-theme="light"] .codex-patch-file > summary { color: #24364b; }
|
|
763
|
+
html[data-theme="light"] .codex-tool-icon,
|
|
764
|
+
html[data-theme="light"] .codex-tool-command,
|
|
765
|
+
html[data-theme="light"] .codex-tool-code,
|
|
766
|
+
html[data-theme="light"] .codex-work-group > summary,
|
|
767
|
+
html[data-theme="light"] .codex-subagent-message { color: #526174; }
|
|
768
|
+
html[data-theme="light"] .codex-tool-body,
|
|
769
|
+
html[data-theme="light"] .codex-patch-file,
|
|
770
|
+
html[data-theme="light"] .claude-edit-body { border-color: #dbe1e8; }
|
|
771
|
+
html[data-theme="light"] .codex-work-group { border-left-color: #cbd3dd; background: transparent; }
|
|
772
|
+
html[data-theme="light"] .codex-subagent-message { border-left-color: #cbd3dd; }
|
|
773
|
+
html[data-theme="light"] .codex-diff { border-color: #d9dee7; background: #f7f8fa; }
|
|
774
|
+
html[data-theme="light"] .codex-diff-line { color: #344054; }
|
|
775
|
+
html[data-theme="light"] .codex-diff-line.add { color: #176b3a; background: #dff4e7; }
|
|
776
|
+
html[data-theme="light"] .codex-diff-line.del { color: #a12b24; background: #fde5e3; }
|
|
777
|
+
html[data-theme="light"] .codex-diff-line.hunk { color: #075fae; background: #e6f2ff; }
|
|
778
|
+
html[data-theme="light"] .claude-permission,
|
|
779
|
+
html[data-theme="light"] .codex-inline-permission { border-color: #edc84f; background: #fff6cf; }
|
|
780
|
+
html[data-theme="light"] .claude-inline-permission-title { color: #795500; }
|
|
781
|
+
html[data-theme="light"] .codex-permission-result { color: #725d28; }
|
|
782
|
+
|
|
783
|
+
html[data-theme="light"] .codex-skill-header,
|
|
784
|
+
html[data-theme="light"] .codex-prompt-header { border-color: #dce2ea; background: #f3f6f9; color: #344054; }
|
|
785
|
+
html[data-theme="light"] .codex-skill-search { border-color: #cbd3dd; background: #fff; color: #172033; }
|
|
786
|
+
html[data-theme="light"] .codex-skill-search::placeholder { color: #8a94a6; }
|
|
787
|
+
html[data-theme="light"] .codex-skill-search:focus { border-color: #007aff; background: #fff; }
|
|
788
|
+
html[data-theme="light"] .codex-skill-section + .codex-skill-section,
|
|
789
|
+
html[data-theme="light"] .codex-prompt-item { border-color: #e1e5eb; }
|
|
790
|
+
html[data-theme="light"] .codex-skill-section-title { background: #e9edf2; color: #475467; }
|
|
791
|
+
html[data-theme="light"] .codex-skill-item { border-color: #e5e9ef; background: #fff; color: #172033; }
|
|
792
|
+
html[data-theme="light"] .codex-skill-item:hover { background: #f7f9fb; }
|
|
793
|
+
html[data-theme="light"] .codex-skill-item.selected { background: #e6f2ff; }
|
|
794
|
+
html[data-theme="light"] .codex-skill-source { border-color: #99c8f7; background: #e8f3ff; color: #006acb; }
|
|
795
|
+
html[data-theme="light"] .codex-skill-description { color: #667085; }
|
|
796
|
+
html[data-theme="light"] .codex-skill-directory { border-color: #d9dee7; background: #f2f4f7; color: #667085; }
|
|
797
|
+
html[data-theme="light"] .codex-prompt-time { color: #667085; }
|
|
798
|
+
|
|
799
|
+
html[data-theme="light"] .usage-period-picker select { border-color: #d9dee7; color: #172033; }
|
|
800
|
+
html[data-theme="light"] .usage-summary-card,
|
|
801
|
+
html[data-theme="light"] .usage-panel { border-color: #dfe4eb; box-shadow: 0 1px 2px rgba(15,23,42,.025); }
|
|
802
|
+
html[data-theme="light"] .usage-summary-card .value,
|
|
803
|
+
html[data-theme="light"] .usage-table tfoot td { color: #172033; }
|
|
804
|
+
html[data-theme="light"] .usage-summary-card .exact,
|
|
805
|
+
html[data-theme="light"] .usage-chart-label,
|
|
806
|
+
html[data-theme="light"] .usage-chart-total,
|
|
807
|
+
html[data-theme="light"] .usage-pricing-note { color: #667085; }
|
|
808
|
+
html[data-theme="light"] .usage-chart-track { background: #e9edf2; }
|
|
809
|
+
html[data-theme="light"] .usage-table th { border-color: #dfe4eb; color: #667085; }
|
|
810
|
+
html[data-theme="light"] .usage-table td { border-color: #edf0f3; color: #344054; }
|
package/lib/web/theme.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
const GLAD_THEME_KEY = 'glad-theme';
|
|
2
|
+
|
|
3
|
+
function getGladTheme() {
|
|
4
|
+
const stored = localStorage.getItem(GLAD_THEME_KEY);
|
|
5
|
+
if (stored === 'light' || stored === 'dark') return stored;
|
|
6
|
+
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function getGladTerminalTheme(theme = getGladTheme()) {
|
|
10
|
+
return theme === 'light'
|
|
11
|
+
? {
|
|
12
|
+
background: '#ffffff', foreground: '#172033', cursor: '#007aff',
|
|
13
|
+
selectionBackground: 'rgba(0, 122, 255, 0.22)', black: '#172033', white: '#f5f6f8',
|
|
14
|
+
brightBlack: '#667085', brightWhite: '#ffffff'
|
|
15
|
+
}
|
|
16
|
+
: {
|
|
17
|
+
background: '#000000', foreground: '#ffffff', cursor: '#34c759',
|
|
18
|
+
selectionBackground: 'rgba(0, 122, 255, 0.32)'
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function syncGladThemeControls(theme = getGladTheme()) {
|
|
23
|
+
['light', 'dark'].forEach(value => {
|
|
24
|
+
const button = document.getElementById(`theme-${value}-btn`);
|
|
25
|
+
if (button) button.setAttribute('aria-pressed', String(value === theme));
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function applyGladTheme(theme, options = {}) {
|
|
30
|
+
const resolved = theme === 'light' ? 'light' : 'dark';
|
|
31
|
+
document.documentElement.dataset.theme = resolved;
|
|
32
|
+
document.documentElement.style.removeProperty('background-color');
|
|
33
|
+
const meta = document.querySelector('meta[name="theme-color"]');
|
|
34
|
+
if (meta) meta.content = resolved === 'light' ? '#f5f6f8' : '#000000';
|
|
35
|
+
syncGladThemeControls(resolved);
|
|
36
|
+
try {
|
|
37
|
+
if (typeof term !== 'undefined' && term) {
|
|
38
|
+
term.options.theme = getGladTerminalTheme(resolved);
|
|
39
|
+
term.refresh(0, Math.max(0, term.rows - 1));
|
|
40
|
+
}
|
|
41
|
+
} catch (_) {}
|
|
42
|
+
if (!options.silent) window.dispatchEvent(new CustomEvent('glad-theme-change', { detail: resolved }));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function setGladTheme(theme) {
|
|
46
|
+
if (theme !== 'light' && theme !== 'dark') return;
|
|
47
|
+
localStorage.setItem(GLAD_THEME_KEY, theme);
|
|
48
|
+
applyGladTheme(theme);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
applyGladTheme(getGladTheme(), { silent: true });
|
|
52
|
+
|
|
53
|
+
const gladSystemTheme = window.matchMedia('(prefers-color-scheme: dark)');
|
|
54
|
+
gladSystemTheme.addEventListener('change', () => {
|
|
55
|
+
if (!localStorage.getItem(GLAD_THEME_KEY)) applyGladTheme(getGladTheme());
|
|
56
|
+
});
|
|
57
|
+
window.addEventListener('storage', event => {
|
|
58
|
+
if (event.key === GLAD_THEME_KEY) applyGladTheme(getGladTheme());
|
|
59
|
+
});
|
|
60
|
+
document.addEventListener('DOMContentLoaded', () => syncGladThemeControls());
|
package/lib/web/timed-inputs.js
CHANGED
|
@@ -115,7 +115,6 @@
|
|
|
115
115
|
function editTimedInput(id) {
|
|
116
116
|
const item = timedInputs.find(value => value.id === id);
|
|
117
117
|
if (!item) return;
|
|
118
|
-
closeComposerMenu();
|
|
119
118
|
initTimedDelaySelectors();
|
|
120
119
|
editingTimedInputId = id;
|
|
121
120
|
inputEl.value = item.text || '';
|
|
@@ -173,19 +172,13 @@
|
|
|
173
172
|
}
|
|
174
173
|
|
|
175
174
|
document.getElementById('send-btn').addEventListener('click', performSend);
|
|
176
|
-
document.getElementById('timer-btn').addEventListener('click', () => {
|
|
177
|
-
const menu = document.getElementById('composer-menu');
|
|
178
|
-
const willOpen = !menu.classList.contains('active');
|
|
179
|
-
document.getElementById('timed-send-panel').classList.remove('active');
|
|
180
|
-
menu.classList.toggle('active', willOpen);
|
|
181
|
-
syncComposerButtonState();
|
|
182
|
-
updateTerminalControlsHeight();
|
|
183
|
-
});
|
|
184
175
|
document.getElementById('attach-image-btn').addEventListener('click', () => {
|
|
185
|
-
closeComposerMenu();
|
|
186
176
|
imageFileInput.click();
|
|
187
177
|
});
|
|
188
|
-
document.getElementById('schedule-send-btn').addEventListener('click',
|
|
178
|
+
document.getElementById('schedule-send-btn').addEventListener('click', () => {
|
|
179
|
+
if (document.getElementById('timed-send-panel').classList.contains('active')) closeTimedSendPanel();
|
|
180
|
+
else openTimedSendPanel();
|
|
181
|
+
});
|
|
189
182
|
imageFileInput.addEventListener('change', () => {
|
|
190
183
|
const files = imageFileInput.files;
|
|
191
184
|
if (files?.length) void uploadImageFiles(files);
|