ai-or-die 0.1.75 → 0.1.76

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-or-die",
3
- "version": "0.1.75",
3
+ "version": "0.1.76",
4
4
  "description": "Universal AI coding terminal — Claude, Copilot, Gemini & more in your browser",
5
5
  "main": "src/server.js",
6
6
  "bin": {
Binary file
package/src/public/app.js CHANGED
@@ -191,6 +191,18 @@ class ClaudeCodeWebInterface {
191
191
  }
192
192
 
193
193
  await this.loadConfig();
194
+ // Apply per-machine identity (`[HOST] ai-or-die`) to the tab/window
195
+ // title, mobile menu header, aria-label, and PWA meta tags now that
196
+ // this.hostname is populated. Must run before the first notification
197
+ // flash, which saves/restores the then-current document.title.
198
+ if (window.AppIdentity) {
199
+ const identity = window.AppIdentity.formatAppIdentity({ hostname: this.hostname });
200
+ window.AppIdentity.applyAppIdentity(identity);
201
+ // Surface the machine identity on the start screen too.
202
+ const spIdText = document.getElementById('startPromptIdentityText');
203
+ const spId = document.getElementById('startPromptIdentity');
204
+ if (spIdText && spId) { spIdText.textContent = identity; spId.hidden = false; }
205
+ }
194
206
  this.setupTerminal();
195
207
  this._setupExtraKeys();
196
208
  this._setupOrientationHandler();
@@ -1929,19 +1941,34 @@ class ClaudeCodeWebInterface {
1929
1941
  });
1930
1942
  }
1931
1943
 
1932
- // Section collapse/expand (keyboard accessible)
1933
- modal.querySelectorAll('.setting-section-header').forEach((header) => {
1934
- const toggle = () => {
1935
- const section = header.parentElement;
1936
- const isCollapsed = section.classList.toggle('collapsed');
1937
- header.setAttribute('aria-expanded', String(!isCollapsed));
1938
- };
1939
- header.addEventListener('click', toggle);
1940
- header.addEventListener('keydown', (e) => {
1941
- if (e.key === 'Enter' || e.key === ' ') {
1942
- e.preventDefault();
1943
- toggle();
1944
- }
1944
+ // Two-pane settings nav (ARIA tablist): switch panes, roving tabindex,
1945
+ // arrow/Home/End keyboard support. Replaces the old collapsible sections.
1946
+ const settingsTabs = Array.from(modal.querySelectorAll('.settings-tab'));
1947
+ const selectSettingsTab = (tab, focus = true) => {
1948
+ settingsTabs.forEach((t) => {
1949
+ const selected = t === tab;
1950
+ t.setAttribute('aria-selected', String(selected));
1951
+ t.tabIndex = selected ? 0 : -1;
1952
+ const pane = document.getElementById(t.getAttribute('aria-controls'));
1953
+ if (pane) pane.hidden = !selected;
1954
+ });
1955
+ if (focus && tab) tab.focus();
1956
+ };
1957
+ settingsTabs.forEach((tab) => {
1958
+ tab.addEventListener('click', () => selectSettingsTab(tab, false));
1959
+ tab.addEventListener('keydown', (e) => {
1960
+ // Navigate among VISIBLE tabs only (the Install tab is hidden
1961
+ // when running as an installed PWA) so focus never lands on an
1962
+ // invisible element and escapes the modal focus trap.
1963
+ const visible = settingsTabs.filter((t) => t.style.display !== 'none' && !t.hidden);
1964
+ const idx = visible.indexOf(tab);
1965
+ if (idx === -1) return;
1966
+ let next = null;
1967
+ if (e.key === 'ArrowDown' || e.key === 'ArrowRight') next = visible[(idx + 1) % visible.length];
1968
+ else if (e.key === 'ArrowUp' || e.key === 'ArrowLeft') next = visible[(idx - 1 + visible.length) % visible.length];
1969
+ else if (e.key === 'Home') next = visible[0];
1970
+ else if (e.key === 'End') next = visible[visible.length - 1];
1971
+ if (next) { e.preventDefault(); selectSettingsTab(next); }
1945
1972
  });
1946
1973
  });
1947
1974
 
@@ -3917,11 +3944,15 @@ class ClaudeCodeWebInterface {
3917
3944
  content.classList.remove('closing');
3918
3945
  overlay.classList.remove('closing');
3919
3946
  overlay.classList.remove('active');
3920
- overlay.style.display = 'none';
3947
+ // Clear (don't set) the inline display so the modal hides via its
3948
+ // base CSS rule (.<modal> { display: none }). Setting an inline
3949
+ // display:none would win over `.active { display: flex }` and
3950
+ // permanently block reopening the modal.
3951
+ overlay.style.removeProperty('display');
3921
3952
  }, 150);
3922
3953
  } else {
3923
3954
  overlay.classList.remove('active');
3924
- overlay.style.display = 'none';
3955
+ overlay.style.removeProperty('display');
3925
3956
  }
3926
3957
  }
3927
3958
 
@@ -4196,10 +4227,17 @@ class ClaudeCodeWebInterface {
4196
4227
  if (installBtn) installBtn.style.display = 'none';
4197
4228
  if (iosInstructions) iosInstructions.style.display = 'none';
4198
4229
 
4199
- // If running inside installed PWA, hide the entire section
4200
- const section = document.querySelector('[data-section="install"]');
4201
- if (section) {
4202
- section.style.display = this._isInstalled ? 'none' : '';
4230
+ // If running inside an installed PWA, hide the Install tab + pane.
4231
+ const installTab = document.getElementById('settingsTab-install');
4232
+ const installPane = document.getElementById('settingsPane-install');
4233
+ if (installTab) installTab.style.display = this._isInstalled ? 'none' : '';
4234
+ if (installPane && this._isInstalled) installPane.hidden = true;
4235
+ // If the Install tab was the active one and is now hidden, fall back to
4236
+ // the first visible tab so the pane area is never left blank/unreachable.
4237
+ if (this._isInstalled && installTab && installTab.getAttribute('aria-selected') === 'true') {
4238
+ const firstVisible = Array.from(document.querySelectorAll('.settings-tab'))
4239
+ .find((t) => t.style.display !== 'none');
4240
+ if (firstVisible) firstVisible.click();
4203
4241
  }
4204
4242
 
4205
4243
  switch (this._installState) {
@@ -133,7 +133,7 @@ class AuthManager {
133
133
  left: 0;
134
134
  right: 0;
135
135
  bottom: 0;
136
- background: rgba(0, 0, 0, 0.95);
136
+ background: var(--overlay-backdrop-strong, rgba(0, 0, 0, 0.95));
137
137
  display: flex;
138
138
  align-items: center;
139
139
  justify-content: center;
@@ -143,12 +143,12 @@ class AuthManager {
143
143
  const loginForm = document.createElement('div');
144
144
  loginForm.style.cssText = `
145
145
  background: var(--bg-secondary, #1c2128);
146
- border: 1px solid var(--border-color, #30363d);
146
+ border: 1px solid var(--border-default, #30363d);
147
147
  border-radius: 12px;
148
148
  padding: 32px;
149
149
  max-width: 400px;
150
150
  width: 90%;
151
- box-shadow: 0 10px 50px rgba(0, 0, 0, 0.5);
151
+ box-shadow: var(--shadow-xl);
152
152
  `;
153
153
 
154
154
  loginForm.innerHTML = `
@@ -157,7 +157,7 @@ class AuthManager {
157
157
  Authentication Required
158
158
  </h2>
159
159
  <p style="color: var(--text-secondary, #8b949e); margin: 0 0 24px 0; font-size: 14px;">
160
- This ai-or-die instance requires authentication.
160
+ This instance requires authentication.
161
161
  </p>
162
162
  <form id="auth-form">
163
163
  <div style="margin-bottom: 16px;">
@@ -172,7 +172,7 @@ class AuthManager {
172
172
  width: 100%;
173
173
  padding: 10px 12px;
174
174
  background: var(--bg-primary, #0d1117);
175
- border: 1px solid var(--border-color, #30363d);
175
+ border: 1px solid var(--border-default, #30363d);
176
176
  border-radius: 6px;
177
177
  color: var(--text-primary, #f0f6fc);
178
178
  font-family: 'Inter', system-ui, -apple-system, Segoe UI, Roboto, 'JetBrains Mono', monospace;
@@ -183,7 +183,7 @@ class AuthManager {
183
183
  required
184
184
  />
185
185
  </div>
186
- <div id="auth-error" style="color: #f85149; margin-bottom: 16px; font-size: 14px; display: none;"></div>
186
+ <div id="auth-error" style="color: var(--status-error, #f85149); margin-bottom: 16px; font-size: 14px; display: none;"></div>
187
187
  <button
188
188
  type="submit"
189
189
  style="
@@ -163,7 +163,7 @@
163
163
  border: 2px solid var(--border);
164
164
  border-radius: 50%;
165
165
  cursor: pointer;
166
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
166
+ box-shadow: var(--shadow-md);
167
167
  transition: all var(--duration-slow, 300ms) var(--ease-default, cubic-bezier(0.4, 0, 0.2, 1));
168
168
  width: 56px;
169
169
  height: 56px;
@@ -173,7 +173,7 @@
173
173
  background-color: var(--bg-tertiary);
174
174
  border-color: var(--accent);
175
175
  transform: translateY(-2px);
176
- box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4);
176
+ box-shadow: var(--shadow-lg);
177
177
  }
178
178
 
179
179
  .mode-switcher-btn:active {
@@ -212,7 +212,7 @@
212
212
  border: 2px solid var(--error);
213
213
  border-radius: 50%;
214
214
  cursor: pointer;
215
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
215
+ box-shadow: var(--shadow-md);
216
216
  transition: all var(--duration-slow, 300ms) var(--ease-default, cubic-bezier(0.4, 0, 0.2, 1));
217
217
  width: 56px;
218
218
  height: 56px;
@@ -222,6 +222,7 @@
222
222
  background-color: var(--bg-tertiary);
223
223
  border-color: var(--color-red-400);
224
224
  transform: translateY(-2px);
225
+ /* Intentional red destructive-escape glow (not a neutral elevation shadow). */
225
226
  box-shadow: 0 6px 16px rgba(248, 113, 113, 0.2);
226
227
  }
227
228
 
@@ -58,26 +58,26 @@
58
58
  transform: translateY(-1px);
59
59
  }
60
60
 
61
- /* Per-tool brand color hover tints */
61
+ /* Per-tool brand color hover tints (tokens shared with the tab badges) */
62
62
  .tool-card[data-tool="terminal"]:hover:not(.disabled) {
63
- border-color: rgba(113, 113, 122, 0.5);
64
- background: rgba(113, 113, 122, 0.08);
63
+ border-color: rgba(var(--tool-terminal-rgb), 0.5);
64
+ background: rgba(var(--tool-terminal-rgb), 0.08);
65
65
  }
66
66
  .tool-card[data-tool="claude"]:hover:not(.disabled) {
67
- border-color: rgba(217, 119, 6, 0.5);
68
- background: rgba(217, 119, 6, 0.08);
67
+ border-color: rgba(var(--tool-claude-rgb), 0.5);
68
+ background: rgba(var(--tool-claude-rgb), 0.08);
69
69
  }
70
70
  .tool-card[data-tool="codex"]:hover:not(.disabled) {
71
- border-color: rgba(5, 150, 105, 0.5);
72
- background: rgba(5, 150, 105, 0.08);
71
+ border-color: rgba(var(--tool-codex-rgb), 0.5);
72
+ background: rgba(var(--tool-codex-rgb), 0.08);
73
73
  }
74
74
  .tool-card[data-tool="copilot"]:hover:not(.disabled) {
75
- border-color: rgba(99, 102, 241, 0.5);
76
- background: rgba(99, 102, 241, 0.08);
75
+ border-color: rgba(var(--tool-copilot-rgb), 0.5);
76
+ background: rgba(var(--tool-copilot-rgb), 0.08);
77
77
  }
78
78
  .tool-card[data-tool="gemini"]:hover:not(.disabled) {
79
- border-color: rgba(37, 99, 235, 0.5);
80
- background: rgba(37, 99, 235, 0.08);
79
+ border-color: rgba(var(--tool-gemini-rgb), 0.5);
80
+ background: rgba(var(--tool-gemini-rgb), 0.08);
81
81
  }
82
82
 
83
83
  /* Focus visible for keyboard navigation */
@@ -112,20 +112,20 @@
112
112
 
113
113
  /* Per-tool brand color hover for installable cards (softer than available) */
114
114
  .tool-card.installable[data-tool="claude"]:hover {
115
- border-color: rgba(217, 119, 6, 0.3);
116
- background: rgba(217, 119, 6, 0.04);
115
+ border-color: rgba(var(--tool-claude-rgb), 0.3);
116
+ background: rgba(var(--tool-claude-rgb), 0.04);
117
117
  }
118
118
  .tool-card.installable[data-tool="codex"]:hover {
119
- border-color: rgba(5, 150, 105, 0.3);
120
- background: rgba(5, 150, 105, 0.04);
119
+ border-color: rgba(var(--tool-codex-rgb), 0.3);
120
+ background: rgba(var(--tool-codex-rgb), 0.04);
121
121
  }
122
122
  .tool-card.installable[data-tool="copilot"]:hover {
123
- border-color: rgba(99, 102, 241, 0.3);
124
- background: rgba(99, 102, 241, 0.04);
123
+ border-color: rgba(var(--tool-copilot-rgb), 0.3);
124
+ background: rgba(var(--tool-copilot-rgb), 0.04);
125
125
  }
126
126
  .tool-card.installable[data-tool="gemini"]:hover {
127
- border-color: rgba(37, 99, 235, 0.3);
128
- background: rgba(37, 99, 235, 0.04);
127
+ border-color: rgba(var(--tool-gemini-rgb), 0.3);
128
+ background: rgba(var(--tool-gemini-rgb), 0.04);
129
129
  }
130
130
 
131
131
  /* Expanded state */
@@ -474,6 +474,30 @@
474
474
  margin-bottom: var(--space-2);
475
475
  }
476
476
 
477
+ /* Machine-identity chip — reads like a live terminal status badge */
478
+ .start-prompt-identity {
479
+ display: inline-flex;
480
+ align-items: center;
481
+ gap: var(--space-2);
482
+ margin-bottom: var(--space-3);
483
+ padding: var(--space-1) var(--space-3);
484
+ border: 1px solid var(--border-subtle);
485
+ border-radius: var(--radius-full);
486
+ background: var(--surface-secondary);
487
+ color: var(--text-secondary);
488
+ font-family: var(--font-mono);
489
+ font-size: var(--text-sm);
490
+ font-weight: var(--weight-medium);
491
+ }
492
+ .start-prompt-identity[hidden] { display: none; }
493
+ .start-prompt-identity-dot {
494
+ width: 7px;
495
+ height: 7px;
496
+ border-radius: 50%;
497
+ background: var(--status-success);
498
+ flex: 0 0 auto;
499
+ }
500
+
477
501
  /* Start prompt subtitle */
478
502
  .start-prompt-subtitle {
479
503
  color: var(--text-muted);