ai-agent-session-center 2.0.0 → 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.
- package/hooks/setup-wizard.js +101 -2
- package/package.json +2 -1
- package/public/css/animations.css +318 -0
- package/public/css/base.css +137 -0
- package/public/css/card.css +222 -57
- package/public/css/dashboard.css +150 -0
- package/public/css/detail-panel.css +550 -0
- package/public/css/layout.css +626 -40
- package/public/css/modals.css +327 -5
- package/public/css/settings.css +109 -8
- package/public/css/terminal.css +259 -0
- package/public/index.html +81 -2
- package/public/js/alarmManager.js +3 -51
- package/public/js/analyticsPanel.js +13 -7
- package/public/js/app.js +130 -2
- package/public/js/historyPanel.js +57 -52
- package/public/js/quickActions.js +85 -8
- package/public/js/sessionControls.js +9 -4
- package/public/js/timelinePanel.js +4 -3
- package/public/js/wsClient.js +16 -2
- package/server/apiRouter.js +101 -1
- package/server/authManager.js +155 -0
- package/server/db.js +466 -0
- package/server/index.js +66 -3
- package/server/serverConfig.js +1 -0
- package/server/sessionMatcher.js +48 -31
- package/server/sessionStore.js +52 -8
package/hooks/setup-wizard.js
CHANGED
|
@@ -3,6 +3,7 @@ import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs';
|
|
|
3
3
|
import { join, dirname } from 'path';
|
|
4
4
|
import { fileURLToPath } from 'url';
|
|
5
5
|
import { execSync } from 'child_process';
|
|
6
|
+
import { scryptSync, randomBytes } from 'crypto';
|
|
6
7
|
|
|
7
8
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
9
|
const PROJECT_ROOT = join(__dirname, '..');
|
|
@@ -28,6 +29,7 @@ try {
|
|
|
28
29
|
|
|
29
30
|
// ── readline helper ──
|
|
30
31
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
32
|
+
let rlClosed = false;
|
|
31
33
|
|
|
32
34
|
function ask(prompt) {
|
|
33
35
|
return new Promise(resolve => rl.question(prompt, resolve));
|
|
@@ -54,8 +56,46 @@ async function askValue(stepNum, totalSteps, label, defaultVal) {
|
|
|
54
56
|
return answer.trim() || String(defaultVal);
|
|
55
57
|
}
|
|
56
58
|
|
|
59
|
+
// ── Password helper ──
|
|
60
|
+
function hashPassword(password) {
|
|
61
|
+
const salt = randomBytes(16).toString('hex');
|
|
62
|
+
const hash = scryptSync(password, salt, 64).toString('hex');
|
|
63
|
+
return `${salt}:${hash}`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function askPassword(prompt) {
|
|
67
|
+
return new Promise((resolve) => {
|
|
68
|
+
process.stdout.write(prompt);
|
|
69
|
+
const wasRaw = process.stdin.isRaw;
|
|
70
|
+
if (process.stdin.isTTY) process.stdin.setRawMode(true);
|
|
71
|
+
let input = '';
|
|
72
|
+
const onData = (ch) => {
|
|
73
|
+
const c = ch.toString();
|
|
74
|
+
if (c === '\n' || c === '\r') {
|
|
75
|
+
if (process.stdin.isTTY) process.stdin.setRawMode(wasRaw || false);
|
|
76
|
+
process.stdin.removeListener('data', onData);
|
|
77
|
+
process.stdout.write('\n');
|
|
78
|
+
resolve(input);
|
|
79
|
+
} else if (c === '\u007f' || c === '\b') {
|
|
80
|
+
if (input.length > 0) {
|
|
81
|
+
input = input.slice(0, -1);
|
|
82
|
+
process.stdout.write('\b \b');
|
|
83
|
+
}
|
|
84
|
+
} else if (c === '\u0003') {
|
|
85
|
+
// Ctrl+C
|
|
86
|
+
process.exit(1);
|
|
87
|
+
} else {
|
|
88
|
+
input += c;
|
|
89
|
+
process.stdout.write('*');
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
process.stdin.resume();
|
|
93
|
+
process.stdin.on('data', onData);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
57
97
|
// ── Main ──
|
|
58
|
-
const TOTAL =
|
|
98
|
+
const TOTAL = 6;
|
|
59
99
|
|
|
60
100
|
console.log(`\n${CYAN}╭──────────────────────────────────────────────╮${RESET}`);
|
|
61
101
|
console.log(`${CYAN}│${RESET} ${BOLD}AI Agent Session Center — Setup Wizard${RESET} ${CYAN}│${RESET}`);
|
|
@@ -113,7 +153,61 @@ const historyOptions = [
|
|
|
113
153
|
const currentHistIdx = historyOptions.findIndex(o => o.value === (existing.sessionHistoryHours || 24));
|
|
114
154
|
const history = await choose(5, TOTAL, 'Session history retention', historyOptions, currentHistIdx >= 0 ? currentHistIdx : 1);
|
|
115
155
|
|
|
116
|
-
|
|
156
|
+
// 6. Dashboard password
|
|
157
|
+
const hasExistingPassword = Boolean(existing.passwordHash);
|
|
158
|
+
let passwordHash = null;
|
|
159
|
+
|
|
160
|
+
if (hasExistingPassword) {
|
|
161
|
+
const pwOptions = [
|
|
162
|
+
{ label: `Keep current password`, value: 'keep' },
|
|
163
|
+
{ label: `Change password`, value: 'change' },
|
|
164
|
+
{ label: `Remove password ${DIM}— no login required${RESET}`, value: 'remove' },
|
|
165
|
+
];
|
|
166
|
+
const pwChoice = await choose(6, TOTAL, 'Dashboard password', pwOptions, 0);
|
|
167
|
+
if (pwChoice.value === 'keep') {
|
|
168
|
+
passwordHash = existing.passwordHash;
|
|
169
|
+
} else if (pwChoice.value === 'change') {
|
|
170
|
+
rl.close(); rlClosed = true;
|
|
171
|
+
const pw = await askPassword(` ${DIM}New password:${RESET} `);
|
|
172
|
+
if (pw.length < 4) {
|
|
173
|
+
console.log(` ${RED}✗ Password must be at least 4 characters${RESET}`);
|
|
174
|
+
process.exit(1);
|
|
175
|
+
}
|
|
176
|
+
const confirm = await askPassword(` ${DIM}Confirm password:${RESET} `);
|
|
177
|
+
if (pw !== confirm) {
|
|
178
|
+
console.log(` ${RED}✗ Passwords do not match${RESET}`);
|
|
179
|
+
process.exit(1);
|
|
180
|
+
}
|
|
181
|
+
passwordHash = hashPassword(pw);
|
|
182
|
+
ok('Password updated');
|
|
183
|
+
} else {
|
|
184
|
+
passwordHash = null;
|
|
185
|
+
ok('Password removed — no login required');
|
|
186
|
+
}
|
|
187
|
+
} else {
|
|
188
|
+
const pwOptions = [
|
|
189
|
+
{ label: `No password ${DIM}— open access on localhost${RESET}`, value: 'none' },
|
|
190
|
+
{ label: `Set a password ${DIM}— require login${RESET}`, value: 'set' },
|
|
191
|
+
];
|
|
192
|
+
const pwChoice = await choose(6, TOTAL, 'Dashboard password (optional)', pwOptions, 0);
|
|
193
|
+
if (pwChoice.value === 'set') {
|
|
194
|
+
rl.close(); rlClosed = true;
|
|
195
|
+
const pw = await askPassword(` ${DIM}Enter password:${RESET} `);
|
|
196
|
+
if (pw.length < 4) {
|
|
197
|
+
console.log(` ${RED}✗ Password must be at least 4 characters${RESET}`);
|
|
198
|
+
process.exit(1);
|
|
199
|
+
}
|
|
200
|
+
const confirm = await askPassword(` ${DIM}Confirm password:${RESET} `);
|
|
201
|
+
if (pw !== confirm) {
|
|
202
|
+
console.log(` ${RED}✗ Passwords do not match${RESET}`);
|
|
203
|
+
process.exit(1);
|
|
204
|
+
}
|
|
205
|
+
passwordHash = hashPassword(pw);
|
|
206
|
+
ok('Password set — login will be required');
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (!rlClosed) { rl.close(); rlClosed = true; }
|
|
117
211
|
|
|
118
212
|
// ── Save config ──
|
|
119
213
|
const configData = {
|
|
@@ -122,6 +216,7 @@ const configData = {
|
|
|
122
216
|
hookDensity: density.value,
|
|
123
217
|
debug: debug.value,
|
|
124
218
|
sessionHistoryHours: history.value,
|
|
219
|
+
...(passwordHash ? { passwordHash } : {}),
|
|
125
220
|
};
|
|
126
221
|
|
|
127
222
|
const dataDir = join(PROJECT_ROOT, 'data');
|
|
@@ -136,6 +231,7 @@ info(`Enabled CLIs: ${BOLD}${configData.enabledClis.join(', ')}${RESET}`);
|
|
|
136
231
|
info(`Hook density: ${BOLD}${configData.hookDensity}${RESET}`);
|
|
137
232
|
info(`Debug: ${BOLD}${configData.debug ? 'ON' : 'OFF'}${RESET}`);
|
|
138
233
|
info(`History retention: ${BOLD}${configData.sessionHistoryHours}h${RESET}`);
|
|
234
|
+
info(`Password: ${BOLD}${configData.passwordHash ? 'Enabled' : 'Disabled'}${RESET}`);
|
|
139
235
|
|
|
140
236
|
// ── Install hooks with chosen density ──
|
|
141
237
|
console.log('');
|
|
@@ -154,3 +250,6 @@ console.log(` ${GREEN}✓ Setup complete!${RESET}`);
|
|
|
154
250
|
console.log(`${GREEN}────────────────────────────────────────────────${RESET}`);
|
|
155
251
|
console.log(`\n Starting server on port ${BOLD}${configData.port}${RESET}...`);
|
|
156
252
|
console.log(` Browser will open automatically.\n`);
|
|
253
|
+
|
|
254
|
+
// Explicit exit — askPassword's process.stdin.resume() keeps the event loop alive
|
|
255
|
+
process.exit(0);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-agent-session-center",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "A real-time dashboard for monitoring AI agent sessions (Claude Code, Gemini CLI, Codex) with 3D visualization",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "server/index.js",
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"node": ">=18.0.0"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
+
"better-sqlite3": "^12.6.2",
|
|
43
44
|
"express": "^5.0.0",
|
|
44
45
|
"node-pty": "^1.1.0",
|
|
45
46
|
"ws": "^8.18.0"
|
|
@@ -846,3 +846,321 @@ body[data-effect-ended="none"] .css-robot[data-status="ended"] {
|
|
|
846
846
|
flex: 1;
|
|
847
847
|
}
|
|
848
848
|
|
|
849
|
+
/* ============================================================
|
|
850
|
+
Responsive: Animation Mobile & Touch Enhancements
|
|
851
|
+
============================================================ */
|
|
852
|
+
|
|
853
|
+
/* --- Enhanced prefers-reduced-motion support --- */
|
|
854
|
+
@media (prefers-reduced-motion: reduce) {
|
|
855
|
+
/* Override all animation effects with simpler alternatives */
|
|
856
|
+
.css-robot[data-status="working"] .robot-head::after,
|
|
857
|
+
.css-robot[data-status="working"] .robot-head::before,
|
|
858
|
+
.char-cat[data-status="working"] .cat-head::after,
|
|
859
|
+
.char-cat[data-status="working"] .cat-head::before,
|
|
860
|
+
.char-alien[data-status="working"] .alien-dome::after,
|
|
861
|
+
.char-alien[data-status="working"] .alien-dome::before,
|
|
862
|
+
.char-ghost[data-status="working"] .ghost-body::after,
|
|
863
|
+
.char-ghost[data-status="working"] .ghost-body::before,
|
|
864
|
+
.char-orb[data-status="working"] .orb-core::after,
|
|
865
|
+
.char-orb[data-status="working"] .orb-core::before,
|
|
866
|
+
.char-dragon[data-status="working"] .dragon-head::after,
|
|
867
|
+
.char-dragon[data-status="working"] .dragon-head::before {
|
|
868
|
+
animation: none !important;
|
|
869
|
+
opacity: 0 !important;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
/* Disable triggered movement effects */
|
|
873
|
+
.css-robot[data-movement]::before,
|
|
874
|
+
.css-robot[data-movement]::after {
|
|
875
|
+
animation: none !important;
|
|
876
|
+
opacity: 0 !important;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
.css-robot[data-movement] .robot-body-wrap {
|
|
880
|
+
animation: none !important;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
/* Disable configurable status effects */
|
|
884
|
+
body[data-effect-working="energy-ring"] .css-robot[data-status="working"]::before,
|
|
885
|
+
body[data-effect-working="energy-ring"] .css-robot[data-status="working"]::after,
|
|
886
|
+
body[data-effect-working="sparks"] .css-robot[data-status="working"]::before,
|
|
887
|
+
body[data-effect-working="sparks"] .css-robot[data-status="working"]::after,
|
|
888
|
+
body[data-effect-working="steam"] .css-robot[data-status="working"]::before,
|
|
889
|
+
body[data-effect-working="steam"] .css-robot[data-status="working"]::after {
|
|
890
|
+
animation: none !important;
|
|
891
|
+
opacity: 0 !important;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
/* Show static indicators instead of animations for key states */
|
|
895
|
+
.css-robot[data-status="approval"] {
|
|
896
|
+
outline: 2px solid rgba(255, 221, 0, 0.6);
|
|
897
|
+
outline-offset: 4px;
|
|
898
|
+
border-radius: 8px;
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
.css-robot[data-status="working"] {
|
|
902
|
+
outline: 2px solid rgba(255, 152, 0, 0.4);
|
|
903
|
+
outline-offset: 4px;
|
|
904
|
+
border-radius: 8px;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
/* Suppress movement preview animations */
|
|
908
|
+
.movement-preview-viewport .css-robot {
|
|
909
|
+
animation: none !important;
|
|
910
|
+
}
|
|
911
|
+
.movement-preview-viewport .css-robot::before,
|
|
912
|
+
.movement-preview-viewport .css-robot::after {
|
|
913
|
+
animation: none !important;
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
/* --- :active touch feedback on animation settings controls --- */
|
|
918
|
+
.anim-intensity-control input[type="range"]:active {
|
|
919
|
+
opacity: 0.8;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
.movement-preview-viewport:active {
|
|
923
|
+
opacity: 0.9;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
/* --- Touch device enhancements (pointer: coarse) --- */
|
|
927
|
+
@media (pointer: coarse) {
|
|
928
|
+
/* Animation sliders: larger hit area on touch devices */
|
|
929
|
+
.anim-intensity-control input[type="range"] {
|
|
930
|
+
height: 44px;
|
|
931
|
+
cursor: pointer;
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
.anim-intensity-control {
|
|
935
|
+
min-height: 44px;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
/* Movement preview: larger tap targets */
|
|
939
|
+
.movement-preview-viewport {
|
|
940
|
+
min-height: 120px;
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
/* --- Tablet (max-width: 768px) --- */
|
|
945
|
+
@media (max-width: 768px) {
|
|
946
|
+
/* Scale down characters proportionally on tablet */
|
|
947
|
+
.css-robot {
|
|
948
|
+
transform: scale(0.85);
|
|
949
|
+
transform-origin: center bottom;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
/* Movement preview: slightly smaller */
|
|
953
|
+
.movement-preview-viewport {
|
|
954
|
+
height: 140px;
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
.movement-preview-viewport .css-robot {
|
|
958
|
+
transform: scale(0.75);
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
/* --- Small tablet / large phone (max-width: 640px) --- */
|
|
963
|
+
@media (max-width: 640px) {
|
|
964
|
+
/* Scale down characters more on mobile */
|
|
965
|
+
.css-robot {
|
|
966
|
+
transform: scale(0.75);
|
|
967
|
+
transform-origin: center bottom;
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
/* Reduce sweat drops box-shadow complexity for performance */
|
|
971
|
+
.css-robot[data-status="working"] .robot-head::after,
|
|
972
|
+
.char-cat[data-status="working"] .cat-head::after,
|
|
973
|
+
.char-alien[data-status="working"] .alien-dome::after,
|
|
974
|
+
.char-ghost[data-status="working"] .ghost-body::after,
|
|
975
|
+
.char-orb[data-status="working"] .orb-core::after,
|
|
976
|
+
.char-dragon[data-status="working"] .dragon-head::after,
|
|
977
|
+
.char-penguin[data-status="working"] .penguin-head::after,
|
|
978
|
+
.char-octopus[data-status="working"] .octo-head::after,
|
|
979
|
+
.char-mushroom[data-status="working"] .mush-cap::after,
|
|
980
|
+
.char-fox[data-status="working"] .fox-head::after,
|
|
981
|
+
.char-unicorn[data-status="working"] .unicorn-head::after,
|
|
982
|
+
.char-jellyfish[data-status="working"] .jelly-bell::after,
|
|
983
|
+
.char-owl[data-status="working"] .owl-head::after,
|
|
984
|
+
.char-bat[data-status="working"] .bat-head::after,
|
|
985
|
+
.char-cactus[data-status="working"] .cactus-body::after,
|
|
986
|
+
.char-slime[data-status="working"] .slime-body::after,
|
|
987
|
+
.char-pumpkin[data-status="working"] .pumpkin-body::after,
|
|
988
|
+
.char-yeti[data-status="working"] .yeti-head::after,
|
|
989
|
+
.char-crystal[data-status="working"] .crystal-body::after,
|
|
990
|
+
.char-bee[data-status="working"] .bee-head::after {
|
|
991
|
+
/* Reduce from 19 box-shadow drops to 6 for mobile performance */
|
|
992
|
+
box-shadow:
|
|
993
|
+
-4px 4px 0 1px rgba(100,220,255,1),
|
|
994
|
+
2px 14px 0 0 rgba(80,200,255,0.9),
|
|
995
|
+
-2px 24px 0 1px rgba(80,200,255,0.85),
|
|
996
|
+
5px 34px 0 0 rgba(60,190,255,0.8),
|
|
997
|
+
-3px 44px 0 1px rgba(100,220,255,0.75),
|
|
998
|
+
1px 49px 0 0 rgba(80,200,255,0.7);
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
.css-robot[data-status="working"] .robot-head::before,
|
|
1002
|
+
.char-cat[data-status="working"] .cat-head::before,
|
|
1003
|
+
.char-alien[data-status="working"] .alien-dome::before,
|
|
1004
|
+
.char-ghost[data-status="working"] .ghost-body::before,
|
|
1005
|
+
.char-orb[data-status="working"] .orb-core::before,
|
|
1006
|
+
.char-dragon[data-status="working"] .dragon-head::before,
|
|
1007
|
+
.char-penguin[data-status="working"] .penguin-head::before,
|
|
1008
|
+
.char-octopus[data-status="working"] .octo-head::before,
|
|
1009
|
+
.char-mushroom[data-status="working"] .mush-cap::before,
|
|
1010
|
+
.char-fox[data-status="working"] .fox-head::before,
|
|
1011
|
+
.char-unicorn[data-status="working"] .unicorn-head::before,
|
|
1012
|
+
.char-jellyfish[data-status="working"] .jelly-bell::before,
|
|
1013
|
+
.char-owl[data-status="working"] .owl-head::before,
|
|
1014
|
+
.char-bat[data-status="working"] .bat-head::before,
|
|
1015
|
+
.char-cactus[data-status="working"] .cactus-body::before,
|
|
1016
|
+
.char-slime[data-status="working"] .slime-body::before,
|
|
1017
|
+
.char-pumpkin[data-status="working"] .pumpkin-body::before,
|
|
1018
|
+
.char-yeti[data-status="working"] .yeti-head::before,
|
|
1019
|
+
.char-crystal[data-status="working"] .crystal-body::before,
|
|
1020
|
+
.char-bee[data-status="working"] .bee-head::before {
|
|
1021
|
+
/* Reduce from 19 box-shadow drops to 6 for mobile performance */
|
|
1022
|
+
box-shadow:
|
|
1023
|
+
4px 4px 0 1px rgba(100,220,255,1),
|
|
1024
|
+
-2px 14px 0 0 rgba(80,200,255,0.9),
|
|
1025
|
+
2px 24px 0 1px rgba(80,200,255,0.85),
|
|
1026
|
+
-5px 34px 0 0 rgba(60,190,255,0.8),
|
|
1027
|
+
3px 44px 0 1px rgba(100,220,255,0.75),
|
|
1028
|
+
-1px 49px 0 0 rgba(80,200,255,0.7);
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
/* Simplify triggered movement sparks (reduce box-shadow points) */
|
|
1032
|
+
.css-robot[data-movement="sparks"]::before {
|
|
1033
|
+
box-shadow:
|
|
1034
|
+
12px -15px 0 1px #ffcc00,
|
|
1035
|
+
-16px -10px 0 0 #ffa000,
|
|
1036
|
+
20px 6px 0 1px #ff9800,
|
|
1037
|
+
-12px 12px 0 0 #ffcc80;
|
|
1038
|
+
}
|
|
1039
|
+
.css-robot[data-movement="sparks"]::after {
|
|
1040
|
+
box-shadow:
|
|
1041
|
+
-10px -18px 0 1px #ffe082,
|
|
1042
|
+
18px -12px 0 0 #ffca28,
|
|
1043
|
+
-22px 4px 0 1px #ffc107,
|
|
1044
|
+
10px 16px 0 0 #ffe082;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
/* Simplify triggered sparkle effects */
|
|
1048
|
+
.css-robot[data-movement="sparkle"]::before {
|
|
1049
|
+
box-shadow:
|
|
1050
|
+
25px -8px 0 1px rgba(255,255,255,1),
|
|
1051
|
+
-15px 20px 0 0 rgba(255,255,255,0.95),
|
|
1052
|
+
30px 25px 0 1px rgba(255,255,255,0.9),
|
|
1053
|
+
8px 40px 0 0 rgba(255,255,255,0.85);
|
|
1054
|
+
}
|
|
1055
|
+
.css-robot[data-movement="sparkle"]::after {
|
|
1056
|
+
box-shadow:
|
|
1057
|
+
15px 10px 0 1px rgba(255,255,200,0.9),
|
|
1058
|
+
35px 5px 0 0 rgba(255,255,200,0.85),
|
|
1059
|
+
5px 30px 0 1px rgba(255,255,200,0.8);
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
/* Simplify energy-ring effects */
|
|
1063
|
+
body[data-effect-working="energy-ring"] .css-robot[data-status="working"]::before {
|
|
1064
|
+
box-shadow: 0 0 15px rgba(255, 152, 0, 0.5);
|
|
1065
|
+
}
|
|
1066
|
+
body[data-effect-working="energy-ring"] .css-robot[data-status="working"]::after {
|
|
1067
|
+
box-shadow: 0 0 10px rgba(255, 200, 0, 0.3);
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
/* Simplify steam clouds */
|
|
1071
|
+
body[data-effect-working="steam"] .css-robot[data-status="working"]::before {
|
|
1072
|
+
box-shadow:
|
|
1073
|
+
14px -6px 0 4px rgba(220,220,240,0.5),
|
|
1074
|
+
-8px -12px 0 3px rgba(220,220,240,0.4);
|
|
1075
|
+
}
|
|
1076
|
+
body[data-effect-working="steam"] .css-robot[data-status="working"]::after {
|
|
1077
|
+
box-shadow:
|
|
1078
|
+
-12px -8px 0 4px rgba(220,220,240,0.45);
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
/* Movement preview: compact */
|
|
1082
|
+
.movement-preview-viewport {
|
|
1083
|
+
height: 120px;
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
.movement-preview-viewport .css-robot {
|
|
1087
|
+
transform: scale(0.6);
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
/* Animation settings: compact */
|
|
1091
|
+
.movement-preview-label {
|
|
1092
|
+
font-size: 10px;
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
.movement-preview-effect-name {
|
|
1096
|
+
font-size: 9px;
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
/* --- Phone (max-width: 480px) --- */
|
|
1101
|
+
@media (max-width: 480px) {
|
|
1102
|
+
/* Scale down characters further */
|
|
1103
|
+
.css-robot {
|
|
1104
|
+
transform: scale(0.65);
|
|
1105
|
+
transform-origin: center bottom;
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
/* Reduce animation speed on small screens for less jank */
|
|
1109
|
+
.css-robot[data-status="working"] .robot-head::after,
|
|
1110
|
+
.css-robot[data-status="working"] .robot-head::before {
|
|
1111
|
+
animation-duration: 1.2s;
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
/* Movement preview: even more compact */
|
|
1115
|
+
.movement-preview-viewport {
|
|
1116
|
+
height: 100px;
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
.movement-preview-viewport .css-robot {
|
|
1120
|
+
transform: scale(0.5);
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
/* Anim intensity control: compact layout */
|
|
1124
|
+
.anim-intensity-control span {
|
|
1125
|
+
font-size: 10px;
|
|
1126
|
+
min-width: 24px;
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
/* --- Small phone (max-width: 375px) --- */
|
|
1131
|
+
@media (max-width: 375px) {
|
|
1132
|
+
.css-robot {
|
|
1133
|
+
transform: scale(0.55);
|
|
1134
|
+
transform-origin: center bottom;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
.movement-preview-viewport {
|
|
1138
|
+
height: 90px;
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
/* --- Smallest phone (max-width: 320px) --- */
|
|
1143
|
+
@media (max-width: 320px) {
|
|
1144
|
+
.css-robot {
|
|
1145
|
+
transform: scale(0.5);
|
|
1146
|
+
transform-origin: center bottom;
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
.movement-preview-viewport {
|
|
1150
|
+
height: 80px;
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
.anim-intensity-control {
|
|
1154
|
+
gap: 6px;
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
/* --- Scroll-snap for horizontally scrollable containers --- */
|
|
1159
|
+
.terminal-toolbar-actions {
|
|
1160
|
+
scroll-snap-type: x proximity;
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
.terminal-toolbar-actions > * {
|
|
1164
|
+
scroll-snap-align: start;
|
|
1165
|
+
}
|
|
1166
|
+
|
package/public/css/base.css
CHANGED
|
@@ -42,6 +42,18 @@
|
|
|
42
42
|
/* Animation control */
|
|
43
43
|
--anim-intensity: 1;
|
|
44
44
|
--anim-speed: 1;
|
|
45
|
+
|
|
46
|
+
/* Responsive spacing (desktop defaults) */
|
|
47
|
+
--layout-pad: 24px;
|
|
48
|
+
--layout-pad-sm: 16px;
|
|
49
|
+
--layout-gap: 16px;
|
|
50
|
+
--layout-gap-sm: 8px;
|
|
51
|
+
|
|
52
|
+
/* Safe area fallbacks */
|
|
53
|
+
--safe-top: env(safe-area-inset-top, 0px);
|
|
54
|
+
--safe-right: env(safe-area-inset-right, 0px);
|
|
55
|
+
--safe-bottom: env(safe-area-inset-bottom, 0px);
|
|
56
|
+
--safe-left: env(safe-area-inset-left, 0px);
|
|
45
57
|
}
|
|
46
58
|
|
|
47
59
|
/* ---- Reset & Base ---- */
|
|
@@ -148,6 +160,7 @@ body::after {
|
|
|
148
160
|
.modal-panel {
|
|
149
161
|
-webkit-overflow-scrolling: touch;
|
|
150
162
|
touch-action: pan-y;
|
|
163
|
+
overflow-y: auto;
|
|
151
164
|
}
|
|
152
165
|
|
|
153
166
|
/* Ensure all interactive elements meet minimum 44px touch target */
|
|
@@ -168,4 +181,128 @@ body::after {
|
|
|
168
181
|
min-height: 44px;
|
|
169
182
|
min-width: 44px;
|
|
170
183
|
}
|
|
184
|
+
|
|
185
|
+
/* touch-action: manipulation prevents double-tap zoom on interactive elements */
|
|
186
|
+
button,
|
|
187
|
+
a,
|
|
188
|
+
input,
|
|
189
|
+
select,
|
|
190
|
+
textarea,
|
|
191
|
+
.nav-btn,
|
|
192
|
+
.qa-btn,
|
|
193
|
+
.ctrl-btn,
|
|
194
|
+
.tab,
|
|
195
|
+
.nav-actions-toggle,
|
|
196
|
+
.nav-shortcuts-btn,
|
|
197
|
+
.settings-gear,
|
|
198
|
+
.modal-close,
|
|
199
|
+
.theme-swatch,
|
|
200
|
+
.char-swatch,
|
|
201
|
+
.density-btn,
|
|
202
|
+
.ssh-mode-btn,
|
|
203
|
+
.pagination-btn,
|
|
204
|
+
.feed-collapse-btn,
|
|
205
|
+
.session-card,
|
|
206
|
+
.history-row {
|
|
207
|
+
touch-action: manipulation;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/* ---- Disable hover-only effects on touch devices ---- */
|
|
212
|
+
|
|
213
|
+
@media (hover: none) {
|
|
214
|
+
.nav-btn:hover,
|
|
215
|
+
.qa-btn:hover,
|
|
216
|
+
.ctrl-btn:hover,
|
|
217
|
+
.tab:hover,
|
|
218
|
+
.nav-actions-toggle:hover,
|
|
219
|
+
.nav-shortcuts-btn:hover,
|
|
220
|
+
.settings-gear:hover,
|
|
221
|
+
.modal-close:hover,
|
|
222
|
+
.history-row:hover,
|
|
223
|
+
.history-delete:hover,
|
|
224
|
+
.pagination-btn:hover,
|
|
225
|
+
.feed-collapse-btn:hover,
|
|
226
|
+
.hook-stats-reset:hover {
|
|
227
|
+
background: unset;
|
|
228
|
+
color: unset;
|
|
229
|
+
border-color: unset;
|
|
230
|
+
box-shadow: unset;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/* Keep active-state feedback for touch instead */
|
|
234
|
+
.nav-btn:active,
|
|
235
|
+
.qa-btn:active,
|
|
236
|
+
.ctrl-btn:active,
|
|
237
|
+
.tab:active {
|
|
238
|
+
opacity: 0.7;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/* ---- Mobile: enable page scroll, disable zoom ---- */
|
|
243
|
+
|
|
244
|
+
@media (max-width: 768px) {
|
|
245
|
+
html {
|
|
246
|
+
overflow: auto;
|
|
247
|
+
overflow-x: hidden;
|
|
248
|
+
overscroll-behavior-x: none;
|
|
249
|
+
touch-action: pan-x pan-y;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
body {
|
|
253
|
+
overflow: visible;
|
|
254
|
+
overflow-x: hidden;
|
|
255
|
+
height: auto;
|
|
256
|
+
min-height: 100vh;
|
|
257
|
+
min-height: 100dvh;
|
|
258
|
+
overscroll-behavior: none;
|
|
259
|
+
touch-action: pan-x pan-y;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/* ---- Mobile scrollbar hiding ---- */
|
|
264
|
+
|
|
265
|
+
@media (max-width: 768px) {
|
|
266
|
+
.view-panel::-webkit-scrollbar,
|
|
267
|
+
#main-nav::-webkit-scrollbar,
|
|
268
|
+
.modal-panel::-webkit-scrollbar,
|
|
269
|
+
.settings-tab-content::-webkit-scrollbar,
|
|
270
|
+
#feed-entries::-webkit-scrollbar,
|
|
271
|
+
#detail-conversation::-webkit-scrollbar,
|
|
272
|
+
#detail-activity-log::-webkit-scrollbar,
|
|
273
|
+
#notes-list::-webkit-scrollbar,
|
|
274
|
+
#queue-list::-webkit-scrollbar {
|
|
275
|
+
display: none;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
.view-panel,
|
|
279
|
+
#main-nav,
|
|
280
|
+
.modal-panel,
|
|
281
|
+
.settings-tab-content,
|
|
282
|
+
#feed-entries,
|
|
283
|
+
#detail-conversation,
|
|
284
|
+
#detail-activity-log,
|
|
285
|
+
#notes-list,
|
|
286
|
+
#queue-list {
|
|
287
|
+
scrollbar-width: none;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/* Mobile responsive spacing custom properties */
|
|
291
|
+
:root {
|
|
292
|
+
--layout-pad: 12px;
|
|
293
|
+
--layout-pad-sm: 8px;
|
|
294
|
+
--layout-gap: 8px;
|
|
295
|
+
--layout-gap-sm: 4px;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/* ---- Extra-small mobile spacing ---- */
|
|
300
|
+
|
|
301
|
+
@media (max-width: 375px) {
|
|
302
|
+
:root {
|
|
303
|
+
--layout-pad: 8px;
|
|
304
|
+
--layout-pad-sm: 6px;
|
|
305
|
+
--layout-gap: 6px;
|
|
306
|
+
--layout-gap-sm: 4px;
|
|
307
|
+
}
|
|
171
308
|
}
|