anentrypoint-design 0.0.229 → 0.0.230

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.
@@ -3,6 +3,7 @@
3
3
  // webjsx vnode out. CSS in app-shell.css uses these class names.
4
4
 
5
5
  import * as webjsx from '../../vendor/webjsx/index.js';
6
+ import { trapTab } from './overlay-primitives.js';
6
7
  const h = webjsx.createElement;
7
8
 
8
9
  export function Brand({ name = '247420', leaf } = {}) {
@@ -324,8 +325,12 @@ function toggleWs(which) {
324
325
  : which === 'sessions' ? 'ws-sessions-collapsed'
325
326
  : 'ws-rail-collapsed';
326
327
  const nowCollapsed = shell.classList.toggle(cls);
327
- document.querySelectorAll('.ws-' + which + '-toggle').forEach((btn) =>
328
- btn.setAttribute('aria-expanded', nowCollapsed ? 'false' : 'true'));
328
+ document.querySelectorAll('.ws-' + which + '-toggle').forEach((btn) => {
329
+ btn.setAttribute('aria-expanded', nowCollapsed ? 'false' : 'true');
330
+ const nextLabel = nowCollapsed ? 'expand ' + which : 'collapse ' + which;
331
+ btn.setAttribute('aria-label', nextLabel);
332
+ btn.setAttribute('title', nextLabel);
333
+ });
329
334
  try {
330
335
  localStorage.setItem('ds.ws.' + which, nowCollapsed ? 'collapsed' : 'open');
331
336
  } catch (_) {}
@@ -416,12 +421,18 @@ function toggleWsDrawer(which, open) {
416
421
  if (next) shell.classList.remove(other);
417
422
  const btn = document.querySelector('.ws-' + which + '-drawer-toggle');
418
423
  if (btn) btn.setAttribute('aria-expanded', next ? 'true' : 'false');
419
- // When opening, move focus into the drawer and arm an Esc-to-close once.
424
+ // When opening, move focus into the drawer, arm an Esc-to-close, and trap
425
+ // Tab/Shift+Tab inside the drawer (a real focus trap, matching the kit's
426
+ // own dialogs - Tab from inside an open drawer previously walked focus out
427
+ // into the scrim/background content behind it).
420
428
  if (next) {
421
429
  const drawer = shell.querySelector(which === 'pane' ? '.ws-pane' : '.ws-sessions');
422
430
  const focusable = drawer && drawer.querySelector('button, a, input, [tabindex]');
423
431
  if (focusable) try { focusable.focus(); } catch (_) {}
424
- const onKey = (e) => { if (e.key === 'Escape') { toggleWsDrawer(which, false); document.removeEventListener('keydown', onKey); if (btn) try { btn.focus(); } catch (_) {} } };
432
+ const onKey = (e) => {
433
+ if (e.key === 'Escape') { toggleWsDrawer(which, false); document.removeEventListener('keydown', onKey); if (btn) try { btn.focus(); } catch (_) {} return; }
434
+ if (drawer) trapTab(drawer, e);
435
+ };
425
436
  shell._wsEscHandler = onKey;
426
437
  document.addEventListener('keydown', onKey);
427
438
  }
@@ -474,16 +485,20 @@ export function WorkspaceShell({ rail, sessions, main, pane, crumb, status, narr
474
485
  // pane, so the shell does not re-flow its column count (4/3/2) on every tab
475
486
  // switch - the loudest "separate pages" tell. The track collapses to width 0
476
487
  // (ws-pane-collapsed) instead of being removed (ws-no-pane), so chat/history/
477
- // files/live/settings all keep the same column geometry.
488
+ // files/live/settings all keep the same column geometry. The sessions column
489
+ // gets the identical treatment (ws-sessions-collapsed instead of ws-no-sessions)
490
+ // so files/live/settings do not shift the main column when sessions is null.
478
491
  const keepPaneTrack = stableFrame && !hasPane;
492
+ const keepSessionsTrack = stableFrame && !hasSessions;
479
493
  const railIsCollapsed = wsCollapsed('rail', railCollapsed);
480
494
  const paneIsCollapsed = hasPane ? wsCollapsed('pane', paneCollapsed) : true;
481
- const sessionsIsCollapsed = wsCollapsed('sessions', false);
495
+ const sessionsIsCollapsed = hasSessions ? wsCollapsed('sessions', false) : true;
482
496
  const shellCls = 'ws-shell'
483
497
  + (railIsCollapsed ? ' ws-rail-collapsed' : '')
484
498
  + ((hasPane || keepPaneTrack) ? '' : ' ws-no-pane')
485
499
  + (((hasPane && paneIsCollapsed) || keepPaneTrack) ? ' ws-pane-collapsed' : '')
486
- + (hasSessions ? '' : ' ws-no-sessions')
500
+ + ((hasSessions || keepSessionsTrack) ? '' : ' ws-no-sessions')
501
+ + (((hasSessions && sessionsIsCollapsed) || keepSessionsTrack) ? ' ws-sessions-collapsed' : '')
487
502
  + (narrow ? ' narrow' : '');
488
503
  return h('div', { class: shellCls, ref: seedWsWidths },
489
504
  h('a', { href: '#ws-main', class: 'skip-link' }, 'skip to main content'),
@@ -506,7 +521,7 @@ export function WorkspaceShell({ rail, sessions, main, pane, crumb, status, narr
506
521
  // Optional sessions column. On mobile it is a drawer; selecting a row
507
522
  // (any button click inside) auto-closes it, mirroring AppShell.
508
523
  hasSessions
509
- ? h('div', { class: 'ws-sessions', role: 'complementary', 'aria-label': 'conversations',
524
+ ? h('div', { id: 'ws-sessions-col', class: 'ws-sessions', role: 'complementary', 'aria-label': 'conversations',
510
525
  onclick: (e) => { if (window.innerWidth <= 1100 && e.target.closest('button, a, [role="button"]')) closeWsDrawers(); } }, sessions)
511
526
  : null,
512
527
  // Primary content column, with an optional thin crumb bar on top. On
@@ -519,6 +534,7 @@ export function WorkspaceShell({ rail, sessions, main, pane, crumb, status, narr
519
534
  hasSessions ? h('button', {
520
535
  class: 'ws-drawer-toggle ws-sessions-drawer-toggle', type: 'button',
521
536
  'aria-label': 'toggle conversations', 'aria-expanded': 'false',
537
+ 'aria-controls': 'ws-sessions-col',
522
538
  onclick: () => toggleWsDrawer('sessions'),
523
539
  }, Icon('thread')) : null,
524
540
  // Desktop-only sessions collapse (reclaims its width for a
@@ -528,7 +544,7 @@ export function WorkspaceShell({ rail, sessions, main, pane, crumb, status, narr
528
544
  'aria-label': sessionsIsCollapsed ? 'expand conversations' : 'collapse conversations',
529
545
  title: sessionsIsCollapsed ? 'expand conversations' : 'collapse conversations',
530
546
  'aria-expanded': sessionsIsCollapsed ? 'false' : 'true', onclick: () => toggleWs('sessions'),
531
- }, Icon('chevron-left')) : null,
547
+ }, Icon(sessionsIsCollapsed ? 'chevron-right' : 'chevron-left')) : null,
532
548
  h('div', { class: 'ws-crumb-main' }, crumb),
533
549
  // Desktop-only context-pane collapse, on the same crumb-level
534
550
  // chrome idiom as the sessions toggle. Hidden on mobile via CSS.
@@ -542,6 +558,7 @@ export function WorkspaceShell({ rail, sessions, main, pane, crumb, status, narr
542
558
  hasPane ? h('button', {
543
559
  class: 'ws-drawer-toggle ws-pane-drawer-toggle', type: 'button',
544
560
  'aria-label': 'toggle context pane', 'aria-expanded': 'false',
561
+ 'aria-controls': 'ws-pane-col',
545
562
  onclick: () => toggleWsDrawer('pane'),
546
563
  }, Icon('page')) : null)
547
564
  : null,
@@ -551,13 +568,13 @@ export function WorkspaceShell({ rail, sessions, main, pane, crumb, status, narr
551
568
  // Optional right context pane. Its desktop collapse toggle now lives in
552
569
  // the crumb cluster, alongside the sessions toggle.
553
570
  hasPane
554
- ? h('aside', { class: 'ws-pane', role: 'complementary', 'aria-label': paneLabel },
571
+ ? h('aside', { id: 'ws-pane-col', class: 'ws-pane', role: 'complementary', 'aria-label': paneLabel },
555
572
  pane)
556
573
  : null,
557
574
  // Keyboard/pointer column resize handles (desktop only).
558
575
  (!narrow && !railIsCollapsed) ? WsResizer('rail') : null,
559
- (!narrow && hasSessions) ? WsResizer('sessions') : null,
560
- (!narrow && hasPane && !paneIsCollapsed) ? WsResizer('pane') : null,
576
+ (!narrow && (hasSessions || keepSessionsTrack) && !sessionsIsCollapsed) ? WsResizer('sessions') : null,
577
+ (!narrow && (hasPane || keepPaneTrack) && !paneIsCollapsed) ? WsResizer('pane') : null,
561
578
  );
562
579
  }
563
580
 
@@ -568,7 +585,11 @@ export function WorkspaceShell({ rail, sessions, main, pane, crumb, status, narr
568
585
  //
569
586
  // brand : short product name shown in the rail header.
570
587
  // action : { label, icon, onClick } a prominent primary button (New chat).
571
- // items : [{ key, label, icon, active, count, onClick }] nav entries.
588
+ // items : [{ key, label, icon, active, count, rail, onClick }] nav entries.
589
+ // `rail` (optional tone e.g. 'flame') paints an attention dot on the
590
+ // item — used when something in that surface needs the user's eyes
591
+ // even though they are looking at a different tab (e.g. a live
592
+ // session in error while the user is in Chat).
572
593
  // footer : optional vnode pinned to the rail bottom (e.g. settings/theme).
573
594
  export function WorkspaceRail({ brand = '247420', action, items = [], footer } = {}) {
574
595
  return h('div', { class: 'ws-rail-inner' },
@@ -585,9 +606,9 @@ export function WorkspaceRail({ brand = '247420', action, items = [], footer } =
585
606
  ...items.map((it) => h('li', { key: it.key || it.label, role: 'listitem' },
586
607
  h('button', {
587
608
  type: 'button',
588
- class: 'ws-rail-item' + (it.active ? ' active' : ''),
609
+ class: 'ws-rail-item' + (it.active ? ' active' : '') + (it.rail ? ' has-rail-flag' : ''),
589
610
  'aria-current': it.active ? 'page' : null,
590
- 'aria-label': it.label + (it.count ? ' (' + it.count + ')' : ''),
611
+ 'aria-label': it.label + (it.count ? ' (' + it.count + ')' : '') + (it.rail === 'flame' ? ', needs attention' : ''),
591
612
  title: it.label,
592
613
  onclick: it.onClick || null,
593
614
  },
@@ -595,7 +616,8 @@ export function WorkspaceRail({ brand = '247420', action, items = [], footer } =
595
616
  h('span', { class: 'ws-rail-item-label' }, it.label),
596
617
  (it.count != null && it.count !== 0 && it.count !== '0')
597
618
  ? h('span', { class: 'ws-rail-item-count', 'aria-hidden': 'true' }, String(it.count))
598
- : null)))),
619
+ : null,
620
+ it.rail ? h('span', { class: 'ws-rail-item-flag tone-' + it.rail, 'aria-hidden': 'true' }) : null)))),
599
621
  footer ? h('div', { class: 'ws-rail-foot' }, footer) : null,
600
622
  );
601
623
  }
@@ -1299,6 +1299,10 @@ html.ds-247420 { touch-action: pan-x pan-y; overscroll-behavior: none; -webkit-t
1299
1299
  cursor: not-allowed;
1300
1300
  background: color-mix(in oklab, var(--fg) 25%, transparent);
1301
1301
  }
1302
+ .ds-247420 .chat-composer.is-disabled {
1303
+ opacity: .55;
1304
+ pointer-events: none;
1305
+ }
1302
1306
 
1303
1307
  .ds-247420 .ds-icon { --ds-icon-stroke: 1.7; }
1304
1308