claude-simple 0.2.2 → 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.
@@ -266,7 +266,70 @@ export function updateRecentSessionsList(sessions) {
266
266
  }
267
267
 
268
268
  export function closeRecentMenu() {
269
- $('recent-menu')?.classList.add('hidden');
269
+ const menu = $('recent-menu');
270
+ if (menu) {
271
+ menu.classList.add('hidden');
272
+ menu.classList.remove('recent-menu-open');
273
+ // clear inline placement for next open
274
+ menu.style.top = '';
275
+ menu.style.left = '';
276
+ menu.style.right = '';
277
+ menu.style.width = '';
278
+ menu.style.maxHeight = '';
279
+ }
280
+ $('recent-menu-backdrop')?.classList.add('hidden');
281
+ }
282
+
283
+ /**
284
+ * Place the panel under the topbar with position:fixed so it is never clipped
285
+ * by #app / #topbar overflow (the previous top-full + fixed combo put it
286
+ * below the viewport on phones — looked like “no response”).
287
+ */
288
+ export function positionRecentMenu() {
289
+ const menu = $('recent-menu');
290
+ const topbar = $('topbar');
291
+ if (!menu || !topbar) return;
292
+ const rect = topbar.getBoundingClientRect();
293
+ const gap = 6;
294
+ const top = Math.round(rect.bottom + gap);
295
+ const side = window.matchMedia('(max-width: 640px)').matches ? 8 : null;
296
+ menu.style.top = `${top}px`;
297
+ if (side != null) {
298
+ menu.style.left = `${side}px`;
299
+ menu.style.right = `${side}px`;
300
+ } else {
301
+ // desktop: align under the “最近” button when possible
302
+ const btn = $('btn-recent-sessions');
303
+ const br = btn?.getBoundingClientRect();
304
+ if (br) {
305
+ const width = Math.min(320, window.innerWidth - 16);
306
+ let left = br.left;
307
+ if (left + width > window.innerWidth - 8) left = window.innerWidth - width - 8;
308
+ if (left < 8) left = 8;
309
+ menu.style.left = `${Math.round(left)}px`;
310
+ menu.style.right = 'auto';
311
+ menu.style.width = `${width}px`;
312
+ }
313
+ }
314
+ const maxH = Math.max(160, window.innerHeight - top - 12);
315
+ menu.style.maxHeight = `${Math.min(maxH, Math.round(window.innerHeight * 0.7))}px`;
316
+ }
317
+
318
+ export function openRecentMenu(sessions) {
319
+ const menu = $('recent-menu');
320
+ if (!menu) return;
321
+ renderRecentSessionsMenu(sessions ?? getDashboardSessions());
322
+ menu.classList.remove('hidden');
323
+ menu.classList.add('recent-menu-open');
324
+ positionRecentMenu();
325
+ $('recent-menu-backdrop')?.classList.remove('hidden');
326
+ }
327
+
328
+ export function toggleRecentMenu(sessions) {
329
+ const menu = $('recent-menu');
330
+ if (!menu) return;
331
+ if (menu.classList.contains('hidden')) openRecentMenu(sessions);
332
+ else closeRecentMenu();
270
333
  }
271
334
 
272
335
  /** Topbar quick-jump: last N sessions by updatedAt. */
@@ -61,24 +61,15 @@ export const AppShell = (opts = {}) => {
61
61
  </div>
62
62
  </div>
63
63
  <div id="recent-menu-wrap" class="relative flex-shrink-0">
64
- <button type="button" id="btn-recent-sessions" class="btn btn-ghost btn-xs border border-base-300 gap-1"
65
- title="最近会话 / 快速跳转">
64
+ <button type="button" id="btn-recent-sessions" class="btn btn-ghost btn-xs border border-base-300 gap-1 min-h-8 px-2"
65
+ title="最近会话 / 快速跳转" aria-haspopup="listbox" aria-expanded="false">
66
66
  <span>最近</span>
67
67
  <span class="text-[10px] opacity-50">▾</span>
68
68
  </button>
69
- <div id="recent-menu" class="hidden absolute left-0 top-full mt-1 z-50 w-80 max-h-[min(24rem,70vh)] overflow-y-auto
70
- rounded-lg border border-base-300 bg-base-100 shadow-xl p-2">
71
- <div class="flex items-center justify-between px-1 pb-1.5 mb-1 border-b border-base-300 sticky top-0 bg-base-100 z-[1]">
72
- <span class="text-[10px] uppercase tracking-wide text-base-content/45 font-semibold">Recent sessions</span>
73
- <span id="recent-menu-count" class="text-[10px] text-base-content/35 font-mono"></span>
74
- </div>
75
- <div id="recent-menu-list" class="flex flex-col gap-0.5"></div>
76
- <p id="recent-menu-empty" class="text-xs text-base-content/40 px-1 py-2 hidden">暂无最近会话</p>
77
- </div>
78
69
  </div>
79
70
  <span id="topbar-project" class="text-sm text-base-content/50 flex-1 truncate min-w-0">Select a session</span>
80
71
  <span id="conn-dot" class="w-2 h-2 rounded-full bg-base-content/30 flex-shrink-0" title="连接状态"></span>
81
- <div class="flex gap-2">
72
+ <div class="flex gap-2 topbar-actions">
82
73
  <button id="btn-refresh-conn" class="btn btn-ghost btn-xs border border-base-300" title="重新连接并同步">↻</button>
83
74
  <button id="btn-pwa-install" class="btn btn-ghost btn-xs border border-base-300 hidden" title="安装到主屏幕">安装</button>
84
75
  <button id="btn-meta-agent" class="btn btn-ghost btn-xs border border-primary/40 text-primary" title="Meta Agent — 报告 / 问答 / VLM">✦ Agent</button>
@@ -88,6 +79,18 @@ export const AppShell = (opts = {}) => {
88
79
  <button id="btn-logout" class="btn btn-ghost btn-xs border border-base-300">Sign out</button>
89
80
  </div>
90
81
  </div>
82
+ <!-- Fixed panel + backdrop: siblings of topbar so flex layout never clips them -->
83
+ <div id="recent-menu-backdrop" class="hidden fixed inset-0 z-[55] bg-black/40" aria-hidden="true"></div>
84
+ <div id="recent-menu" class="hidden fixed z-[60] w-80 max-h-[min(24rem,70vh)] overflow-y-auto
85
+ rounded-lg border border-base-300 bg-base-100 shadow-xl p-2"
86
+ role="listbox" aria-label="最近会话">
87
+ <div class="flex items-center justify-between px-1 pb-1.5 mb-1 border-b border-base-300 sticky top-0 bg-base-100 z-[1]">
88
+ <span class="text-[10px] uppercase tracking-wide text-base-content/45 font-semibold">最近会话</span>
89
+ <span id="recent-menu-count" class="text-[10px] text-base-content/35 font-mono"></span>
90
+ </div>
91
+ <div id="recent-menu-list" class="flex flex-col gap-0.5"></div>
92
+ <p id="recent-menu-empty" class="text-xs text-base-content/40 px-1 py-2 hidden">暂无最近会话</p>
93
+ </div>
91
94
  <!-- Hub: only when no remembered machine (or last one offline) -->
92
95
  <div id="machine-picker" class="hidden fixed inset-0 z-[60] bg-base-100 flex items-center justify-center p-4">
93
96
  <div class="w-full max-w-md">
package/public/shell.js CHANGED
@@ -26,7 +26,7 @@ import { openMetaAgent, toggleMetaAgent } from './agent-panel.js';
26
26
  import { AuthScreen, AppShell } from './shell/templates.js';
27
27
  import {
28
28
  updateDashboard, shiftCalMonth, setSelectedCalDate, getDashboardSessions,
29
- renderRecentSessionsMenu, closeRecentMenu,
29
+ openRecentMenu, toggleRecentMenu, closeRecentMenu, positionRecentMenu,
30
30
  } from './shell/dashboard.js';
31
31
  import {
32
32
  loadAllSessions, loadWorkspaces, renderSessionList, syncSidebarChrome,
@@ -570,26 +570,43 @@ export function initShell(opts = {}) {
570
570
  }
571
571
  });
572
572
  delegate.on('click', '#btn-recent-sessions', e => {
573
+ e.preventDefault();
573
574
  e.stopPropagation();
574
575
  closeMachineMenu();
575
- const menu = $('recent-menu');
576
- if (!menu) return;
577
- if (menu.classList.contains('hidden')) {
578
- renderRecentSessionsMenu(getDashboardSessions());
579
- menu.classList.remove('hidden');
580
- } else {
581
- menu.classList.add('hidden');
582
- }
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');
583
586
  });
584
587
  document.addEventListener('click', e => {
585
588
  const machineWrap = $('machine-menu-wrap');
586
589
  if (machineWrap && !machineWrap.contains(e.target)) closeMachineMenu();
587
- const recentWrap = $('recent-menu-wrap');
588
- if (recentWrap && !recentWrap.contains(e.target)) closeRecentMenu();
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
+ }
589
605
  });
590
606
  // After picking a session from the topbar menu, close it (row click also resumes).
591
607
  delegate.on('click', '#recent-menu [data-session-id]', () => {
592
608
  closeRecentMenu();
609
+ $('btn-recent-sessions')?.setAttribute('aria-expanded', 'false');
593
610
  });
594
611
 
595
612
  delegate.on('click', '#btn-edit-project-notes, #project-goal-text, #project-notes-text', () => {
package/public/style.css CHANGED
@@ -931,17 +931,43 @@ html.pwa-standalone .ma-panel {
931
931
  transition: background-color 99999s ease-in-out 0s;
932
932
  }
933
933
 
934
- /* Recent sessions topbar menu */
934
+ /* Recent sessions panel (position set in JS under topbar; never use top:100% with fixed) */
935
935
  #recent-menu {
936
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 */
937
942
  }
938
943
  @media (max-width: 640px) {
939
- #recent-menu-wrap #recent-menu {
940
- position: fixed;
941
- left: 0.5rem;
942
- right: 0.5rem;
944
+ #recent-menu {
945
+ min-width: 0;
943
946
  width: auto;
944
- max-width: none;
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;
945
971
  }
946
972
  }
947
973
  #chat-input:focus,
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-v6';
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=ui-8',
9
+ '/style.css?v=ui-9',
10
10
  '/style.css',
11
11
  '/app.js',
12
12
  '/icons/icon-192.png',