claude-simple 0.2.1 → 0.2.3
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/client.js +58 -2
- package/dist/app.0.2.3.min.js +924 -0
- package/dist/index.html +2 -2
- package/dist/{style.0.2.1.min.css → style.0.2.3.min.css} +129 -4
- package/gateway.js +7 -2
- package/package.json +1 -1
- package/public/agent-panel.js +10 -1
- package/public/chat.js +9 -1
- package/public/files.js +60 -21
- package/public/index.html +2 -2
- package/public/shell/dashboard.js +125 -4
- package/public/shell/notes.js +24 -9
- package/public/shell/session-nav.js +53 -21
- package/public/shell/templates.js +34 -7
- package/public/shell.js +42 -3
- package/public/style.css +128 -3
- package/public/sw.js +2 -2
- package/src/app.js +27 -9
- package/src/runtime/protocol.js +2 -2
- package/dist/app.0.2.1.min.js +0 -868
package/public/shell.js
CHANGED
|
@@ -24,7 +24,10 @@ import { initSettings, openSettings } from './settings.js';
|
|
|
24
24
|
import { openMetaAgent, toggleMetaAgent } from './agent-panel.js';
|
|
25
25
|
|
|
26
26
|
import { AuthScreen, AppShell } from './shell/templates.js';
|
|
27
|
-
import {
|
|
27
|
+
import {
|
|
28
|
+
updateDashboard, shiftCalMonth, setSelectedCalDate, getDashboardSessions,
|
|
29
|
+
openRecentMenu, toggleRecentMenu, closeRecentMenu, positionRecentMenu,
|
|
30
|
+
} from './shell/dashboard.js';
|
|
28
31
|
import {
|
|
29
32
|
loadAllSessions, loadWorkspaces, renderSessionList, syncSidebarChrome,
|
|
30
33
|
refreshModelSelect, refreshEffortSelect, promptCustomModel,
|
|
@@ -555,6 +558,7 @@ export function initShell(opts = {}) {
|
|
|
555
558
|
});
|
|
556
559
|
delegate.on('click', '#btn-machine-menu', async e => {
|
|
557
560
|
e.stopPropagation();
|
|
561
|
+
closeRecentMenu();
|
|
558
562
|
const menu = $('machine-menu');
|
|
559
563
|
if (!menu) return;
|
|
560
564
|
if (menu.classList.contains('hidden')) {
|
|
@@ -565,9 +569,44 @@ export function initShell(opts = {}) {
|
|
|
565
569
|
menu.classList.add('hidden');
|
|
566
570
|
}
|
|
567
571
|
});
|
|
572
|
+
delegate.on('click', '#btn-recent-sessions', e => {
|
|
573
|
+
e.preventDefault();
|
|
574
|
+
e.stopPropagation();
|
|
575
|
+
closeMachineMenu();
|
|
576
|
+
toggleRecentMenu(getDashboardSessions());
|
|
577
|
+
const open = !$('recent-menu')?.classList.contains('hidden');
|
|
578
|
+
const btn = $('btn-recent-sessions');
|
|
579
|
+
if (btn) btn.setAttribute('aria-expanded', open ? 'true' : 'false');
|
|
580
|
+
});
|
|
581
|
+
delegate.on('click', '#recent-menu-backdrop', e => {
|
|
582
|
+
e.preventDefault();
|
|
583
|
+
e.stopPropagation();
|
|
584
|
+
closeRecentMenu();
|
|
585
|
+
$('btn-recent-sessions')?.setAttribute('aria-expanded', 'false');
|
|
586
|
+
});
|
|
568
587
|
document.addEventListener('click', e => {
|
|
569
|
-
const
|
|
570
|
-
if (
|
|
588
|
+
const machineWrap = $('machine-menu-wrap');
|
|
589
|
+
if (machineWrap && !machineWrap.contains(e.target)) closeMachineMenu();
|
|
590
|
+
const menu = $('recent-menu');
|
|
591
|
+
const btn = $('btn-recent-sessions');
|
|
592
|
+
const t = e.target;
|
|
593
|
+
// Menu is portaled (fixed) outside the button wrap — check both
|
|
594
|
+
if (menu && !menu.classList.contains('hidden')) {
|
|
595
|
+
if (menu.contains(t) || btn?.contains(t)) return;
|
|
596
|
+
closeRecentMenu();
|
|
597
|
+
btn?.setAttribute('aria-expanded', 'false');
|
|
598
|
+
}
|
|
599
|
+
});
|
|
600
|
+
// Keep panel glued under topbar on rotate / URL bar show-hide
|
|
601
|
+
window.addEventListener('resize', () => {
|
|
602
|
+
if ($('recent-menu') && !$('recent-menu').classList.contains('hidden')) {
|
|
603
|
+
positionRecentMenu();
|
|
604
|
+
}
|
|
605
|
+
});
|
|
606
|
+
// After picking a session from the topbar menu, close it (row click also resumes).
|
|
607
|
+
delegate.on('click', '#recent-menu [data-session-id]', () => {
|
|
608
|
+
closeRecentMenu();
|
|
609
|
+
$('btn-recent-sessions')?.setAttribute('aria-expanded', 'false');
|
|
571
610
|
});
|
|
572
611
|
|
|
573
612
|
delegate.on('click', '#btn-edit-project-notes, #project-goal-text, #project-notes-text', () => {
|
package/public/style.css
CHANGED
|
@@ -11,16 +11,66 @@
|
|
|
11
11
|
padding-bottom: env(safe-area-inset-bottom, 0px);
|
|
12
12
|
}
|
|
13
13
|
#sidebar.open { transform: translateX(0); box-shadow: 4px 0 20px rgba(0,0,0,.4); }
|
|
14
|
+
|
|
15
|
+
/* Compact chat composer on phones — less height, less dead space under bar */
|
|
16
|
+
#composer-row {
|
|
17
|
+
gap: 0.4rem;
|
|
18
|
+
padding: 0.4rem 0.55rem 0.35rem !important;
|
|
19
|
+
}
|
|
20
|
+
#chat-input.chat-input,
|
|
21
|
+
#chat-input {
|
|
22
|
+
min-height: 36px !important;
|
|
23
|
+
max-height: 96px !important;
|
|
24
|
+
padding: 0.4rem 0.65rem !important;
|
|
25
|
+
border-radius: 0.65rem;
|
|
26
|
+
/* 16px prevents iOS Safari auto-zoom on focus */
|
|
27
|
+
font-size: 16px !important;
|
|
28
|
+
line-height: 1.35 !important;
|
|
29
|
+
}
|
|
30
|
+
#btn-opts,
|
|
31
|
+
#send-btn,
|
|
32
|
+
#stop-btn {
|
|
33
|
+
height: 36px !important;
|
|
34
|
+
min-height: 36px !important;
|
|
35
|
+
padding-left: 0.65rem;
|
|
36
|
+
padding-right: 0.65rem;
|
|
37
|
+
}
|
|
38
|
+
#chat-opts {
|
|
39
|
+
padding-top: 0.35rem !important;
|
|
40
|
+
padding-left: 0.55rem !important;
|
|
41
|
+
padding-right: 0.55rem !important;
|
|
42
|
+
gap: 0.35rem !important;
|
|
43
|
+
}
|
|
44
|
+
#scroll-nav {
|
|
45
|
+
bottom: 4.5rem !important;
|
|
46
|
+
}
|
|
47
|
+
/* File list actions always visible on touch (no hover) */
|
|
48
|
+
.file-row-action {
|
|
49
|
+
opacity: 1 !important;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/* File row actions: hover-reveal on desktop only */
|
|
54
|
+
@media (min-width: 641px) {
|
|
55
|
+
.file-row-action {
|
|
56
|
+
opacity: 0;
|
|
57
|
+
}
|
|
58
|
+
.group:hover .file-row-action,
|
|
59
|
+
.group:focus-within .file-row-action {
|
|
60
|
+
opacity: 1;
|
|
61
|
+
}
|
|
14
62
|
}
|
|
15
63
|
|
|
16
64
|
/*
|
|
17
|
-
* PWA
|
|
65
|
+
* Safe areas (PWA + mobile Safari with viewport-fit=cover):
|
|
18
66
|
* Do NOT pad the whole body (that leaves a dead empty strip under the chat bar).
|
|
19
67
|
* Paint chrome into the insets; only inset interactive content.
|
|
20
68
|
*/
|
|
21
69
|
html, body {
|
|
22
70
|
height: 100%;
|
|
23
71
|
height: 100dvh;
|
|
72
|
+
max-height: 100dvh;
|
|
73
|
+
overflow: hidden;
|
|
24
74
|
}
|
|
25
75
|
html.pwa-standalone,
|
|
26
76
|
html.pwa-standalone body {
|
|
@@ -34,8 +84,12 @@ html.pwa-standalone #topbar {
|
|
|
34
84
|
padding-left: max(0.75rem, env(safe-area-inset-left, 0px));
|
|
35
85
|
padding-right: max(0.75rem, env(safe-area-inset-right, 0px));
|
|
36
86
|
}
|
|
37
|
-
/*
|
|
38
|
-
|
|
87
|
+
/*
|
|
88
|
+
* Chat composer: background fills home-indicator inset.
|
|
89
|
+
* Inner #composer-row already has small padding; only add safe-area beyond that
|
|
90
|
+
* so we don't stack large empty strips under the input.
|
|
91
|
+
*/
|
|
92
|
+
#chat-composer {
|
|
39
93
|
padding-bottom: env(safe-area-inset-bottom, 0px);
|
|
40
94
|
padding-left: env(safe-area-inset-left, 0px);
|
|
41
95
|
padding-right: env(safe-area-inset-right, 0px);
|
|
@@ -829,6 +883,12 @@ html.pwa-standalone .ma-panel {
|
|
|
829
883
|
|
|
830
884
|
/* ── Chat composer ───────────────────────────────────────────────────────── */
|
|
831
885
|
/* Single outer border only — no DaisyUI nested focus ring / inner outline. */
|
|
886
|
+
#composer-row #btn-opts,
|
|
887
|
+
#composer-row #send-btn,
|
|
888
|
+
#composer-row #stop-btn {
|
|
889
|
+
height: 40px;
|
|
890
|
+
min-height: 40px;
|
|
891
|
+
}
|
|
832
892
|
#chat-input.chat-input,
|
|
833
893
|
#chat-input {
|
|
834
894
|
width: 100%;
|
|
@@ -844,6 +904,71 @@ html.pwa-standalone .ma-panel {
|
|
|
844
904
|
--tw-ring-shadow: 0 0 #0000 !important;
|
|
845
905
|
--tw-ring-offset-shadow: 0 0 #0000 !important;
|
|
846
906
|
transition: border-color .15s ease;
|
|
907
|
+
/* Avoid iOS inherited large text scaling making the box look oversized */
|
|
908
|
+
-webkit-text-size-adjust: 100%;
|
|
909
|
+
}
|
|
910
|
+
/* Kill browser password/contact autofill widgets (side checkmarks / arrows) */
|
|
911
|
+
#chat-input::-webkit-contacts-auto-fill-button,
|
|
912
|
+
#chat-input::-webkit-credentials-auto-fill-button,
|
|
913
|
+
#chat-input::-webkit-strong-password-auto-fill-button,
|
|
914
|
+
#ma-input::-webkit-contacts-auto-fill-button,
|
|
915
|
+
#ma-input::-webkit-credentials-auto-fill-button {
|
|
916
|
+
visibility: hidden !important;
|
|
917
|
+
display: none !important;
|
|
918
|
+
pointer-events: none !important;
|
|
919
|
+
position: absolute !important;
|
|
920
|
+
right: 0 !important;
|
|
921
|
+
width: 0 !important;
|
|
922
|
+
height: 0 !important;
|
|
923
|
+
margin: 0 !important;
|
|
924
|
+
padding: 0 !important;
|
|
925
|
+
opacity: 0 !important;
|
|
926
|
+
}
|
|
927
|
+
#chat-input:-webkit-autofill,
|
|
928
|
+
#chat-input:-webkit-autofill:hover,
|
|
929
|
+
#chat-input:-webkit-autofill:focus {
|
|
930
|
+
-webkit-box-shadow: 0 0 0 1000px var(--color-base-100) inset !important;
|
|
931
|
+
transition: background-color 99999s ease-in-out 0s;
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
/* Recent sessions panel (position set in JS under topbar; never use top:100% with fixed) */
|
|
935
|
+
#recent-menu {
|
|
936
|
+
min-width: 16rem;
|
|
937
|
+
overscroll-behavior: contain;
|
|
938
|
+
-webkit-overflow-scrolling: touch;
|
|
939
|
+
}
|
|
940
|
+
#recent-menu.recent-menu-open {
|
|
941
|
+
display: block; /* beat .hidden if both race */
|
|
942
|
+
}
|
|
943
|
+
@media (max-width: 640px) {
|
|
944
|
+
#recent-menu {
|
|
945
|
+
min-width: 0;
|
|
946
|
+
width: auto;
|
|
947
|
+
}
|
|
948
|
+
/* Keep 最近 + hamburger reachable; tuck secondary actions */
|
|
949
|
+
#topbar {
|
|
950
|
+
overflow: visible;
|
|
951
|
+
gap: 0.35rem;
|
|
952
|
+
}
|
|
953
|
+
#topbar .topbar-actions {
|
|
954
|
+
gap: 0.25rem;
|
|
955
|
+
flex-shrink: 0;
|
|
956
|
+
}
|
|
957
|
+
#btn-theme,
|
|
958
|
+
#btn-settings,
|
|
959
|
+
#btn-logout,
|
|
960
|
+
#btn-pwa-install {
|
|
961
|
+
display: none !important;
|
|
962
|
+
}
|
|
963
|
+
#topbar-project {
|
|
964
|
+
font-size: 11px;
|
|
965
|
+
max-width: 30vw;
|
|
966
|
+
}
|
|
967
|
+
#recent-menu-list .recent-menu-item {
|
|
968
|
+
min-height: 44px; /* comfortable touch targets */
|
|
969
|
+
padding-top: 0.5rem;
|
|
970
|
+
padding-bottom: 0.5rem;
|
|
971
|
+
}
|
|
847
972
|
}
|
|
848
973
|
#chat-input:focus,
|
|
849
974
|
#chat-input:focus-visible {
|
package/public/sw.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/* Agent UI service worker — cache shell assets only.
|
|
2
2
|
* Never intercepts WebSocket or mutates API semantics beyond offline fallback.
|
|
3
3
|
* Bump CACHE when shipping UI that must invalidate old shells. */
|
|
4
|
-
const CACHE = 'agent-ui-shell-
|
|
4
|
+
const CACHE = 'agent-ui-shell-v7';
|
|
5
5
|
const PRECACHE = [
|
|
6
6
|
'/',
|
|
7
7
|
'/index.html',
|
|
8
8
|
'/manifest.webmanifest',
|
|
9
|
-
'/style.css?v=
|
|
9
|
+
'/style.css?v=ui-9',
|
|
10
10
|
'/style.css',
|
|
11
11
|
'/app.js',
|
|
12
12
|
'/icons/icon-192.png',
|
package/src/app.js
CHANGED
|
@@ -383,7 +383,10 @@ function markSessionIdle(sessionId, tempKey) {
|
|
|
383
383
|
}
|
|
384
384
|
}
|
|
385
385
|
|
|
386
|
-
/**
|
|
386
|
+
/** Max JSON chars kept per tool_use input in history payloads (traffic). */
|
|
387
|
+
const HISTORY_TOOL_INPUT_MAX = 100;
|
|
388
|
+
|
|
389
|
+
/** Shrink history payloads: tool inputs dominate mobile / hub transfers. */
|
|
387
390
|
function compactHistoryMessages(messages) {
|
|
388
391
|
if (!Array.isArray(messages)) return messages;
|
|
389
392
|
return messages.map((m) => {
|
|
@@ -392,8 +395,15 @@ function compactHistoryMessages(messages) {
|
|
|
392
395
|
let raw;
|
|
393
396
|
try { raw = typeof m.input === 'string' ? m.input : JSON.stringify(m.input); }
|
|
394
397
|
catch { raw = String(m.input); }
|
|
395
|
-
if (raw.length >
|
|
396
|
-
return {
|
|
398
|
+
if (raw.length > HISTORY_TOOL_INPUT_MAX) {
|
|
399
|
+
return {
|
|
400
|
+
...m,
|
|
401
|
+
input: {
|
|
402
|
+
_truncated: true,
|
|
403
|
+
preview: raw.slice(0, HISTORY_TOOL_INPUT_MAX),
|
|
404
|
+
bytes: raw.length,
|
|
405
|
+
},
|
|
406
|
+
};
|
|
397
407
|
}
|
|
398
408
|
}
|
|
399
409
|
if (m.type === 'text' && typeof m.content === 'string' && m.content.length > 48_000) {
|
|
@@ -407,8 +417,12 @@ function compactHistoryMessages(messages) {
|
|
|
407
417
|
export function createApp() {
|
|
408
418
|
const app = express();
|
|
409
419
|
|
|
410
|
-
// Gzip JSON/text API responses when client accepts it (
|
|
420
|
+
// Gzip JSON/text API responses when client accepts it (mobile / direct browser).
|
|
421
|
+
// Skip for hub-proxied edge traffic: Node fetch auto-decompresses gzip while
|
|
422
|
+
// still advertising Content-Encoding, which breaks the browser after tunnel
|
|
423
|
+
// re-delivery. Control WS already uses perMessageDeflate.
|
|
411
424
|
app.use((req, res, next) => {
|
|
425
|
+
if (req.headers['x-hub-token']) return next();
|
|
412
426
|
const ae = req.headers['accept-encoding'] || '';
|
|
413
427
|
if (!/\bgzip\b/i.test(ae) || req.method === 'HEAD') return next();
|
|
414
428
|
const origJson = res.json.bind(res);
|
|
@@ -548,8 +562,12 @@ export function createApp() {
|
|
|
548
562
|
const stat = await fs.stat(filePath);
|
|
549
563
|
if (req.query.download === 'true') {
|
|
550
564
|
const name = path.basename(filePath);
|
|
551
|
-
|
|
565
|
+
// ASCII fallback + RFC 5987 so Chinese / spaces filenames download correctly.
|
|
566
|
+
const ascii = name.replace(/[^\x20-\x7E]/g, '_').replace(/["\\]/g, '_');
|
|
567
|
+
const star = encodeURIComponent(name).replace(/['()]/g, c => `%${c.charCodeAt(0).toString(16).toUpperCase()}`);
|
|
568
|
+
res.setHeader('Content-Disposition', `attachment; filename="${ascii || 'download'}"; filename*=UTF-8''${star}`);
|
|
552
569
|
res.setHeader('Content-Type', 'application/octet-stream');
|
|
570
|
+
res.setHeader('Cache-Control', 'no-store');
|
|
553
571
|
return fsSync.createReadStream(filePath).pipe(res);
|
|
554
572
|
}
|
|
555
573
|
// Office / PDF / binary previews need more headroom than plain text
|
|
@@ -881,11 +899,11 @@ export function createApp() {
|
|
|
881
899
|
const { sessionId } = req.params;
|
|
882
900
|
if (!/^[0-9a-f-]{36}$/i.test(sessionId)) return res.status(400).json({ error: 'Invalid session id' });
|
|
883
901
|
const preferred = typeof req.query.agent === 'string' ? req.query.agent : null;
|
|
884
|
-
// tail=0 → full history
|
|
885
|
-
let tail = parseInt(req.query.tail ?? '
|
|
886
|
-
if (Number.isNaN(tail)) tail =
|
|
902
|
+
// tail=0 → full history (default). Positive = last N messages only.
|
|
903
|
+
let tail = parseInt(req.query.tail ?? '0', 10);
|
|
904
|
+
if (Number.isNaN(tail)) tail = 0;
|
|
887
905
|
if (tail < 0) tail = 0;
|
|
888
|
-
if (tail >
|
|
906
|
+
if (tail > 50_000) tail = 50_000;
|
|
889
907
|
const compact = req.query.compact !== '0' && req.query.compact !== 'false';
|
|
890
908
|
const opts = {
|
|
891
909
|
claudeConfigDirs: CLAUDE_CONFIG_DIRS,
|
package/src/runtime/protocol.js
CHANGED
|
@@ -138,9 +138,9 @@ export const UI_EVENT = {
|
|
|
138
138
|
/**
|
|
139
139
|
* Compact tool_use inputs before they hit the wire (mobile tunnel cost).
|
|
140
140
|
* @param {unknown} input
|
|
141
|
-
* @param {number} [max=
|
|
141
|
+
* @param {number} [max=100]
|
|
142
142
|
*/
|
|
143
|
-
export function compactToolInput(input, max =
|
|
143
|
+
export function compactToolInput(input, max = 100) {
|
|
144
144
|
if (input == null) return input;
|
|
145
145
|
let raw;
|
|
146
146
|
try {
|