anentrypoint-design 0.0.208 → 0.0.210
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/app-shell.css +268 -93
- package/chat.css +111 -29
- package/colors_and_type.css +34 -1
- package/dist/247420.css +412 -123
- package/dist/247420.js +13 -13
- package/package.json +4 -2
- package/src/components/agent-chat.js +8 -2
- package/src/components/chat.js +26 -9
- package/src/components/content.js +28 -3
- package/src/components/context-pane.js +9 -9
- package/src/components/files-modals.js +3 -3
- package/src/components/files.js +21 -5
- package/src/components/freddie.js +25 -25
- package/src/components/sessions.js +37 -23
- package/src/components/shell.js +67 -5
- package/src/components/theme-toggle.js +12 -5
package/src/components/shell.js
CHANGED
|
@@ -15,7 +15,7 @@ export function Chip({ tone = '', children }) {
|
|
|
15
15
|
return h('span', { class: 'chip' + (tone ? ' tone-' + tone : '') }, children);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
export function Btn({ href, variant = 'default', children, onClick, 'aria-label': ariaLabel, primary, ghost, danger, disabled, className }) {
|
|
18
|
+
export function Btn({ href, variant = 'default', children, onClick, 'aria-label': ariaLabel, primary, ghost, danger, disabled, className, key }) {
|
|
19
19
|
// Support legacy primary/ghost props for backward compatibility, but prefer variant
|
|
20
20
|
const resolvedVariant = variant !== 'default' ? variant : (primary ? 'primary' : (ghost ? 'ghost' : (danger ? 'danger' : 'default')));
|
|
21
21
|
const cls = (resolvedVariant === 'primary' ? 'btn-primary' : (resolvedVariant === 'ghost' ? 'btn-ghost' : (resolvedVariant === 'danger' ? 'btn-primary danger' : 'btn')))
|
|
@@ -37,6 +37,7 @@ export function Btn({ href, variant = 'default', children, onClick, 'aria-label'
|
|
|
37
37
|
const isLink = href != null && href !== '' && href !== '#';
|
|
38
38
|
if (isLink) {
|
|
39
39
|
return h('a', {
|
|
40
|
+
key,
|
|
40
41
|
class: cls, href,
|
|
41
42
|
'aria-label': ariaName,
|
|
42
43
|
'aria-disabled': disabled ? 'true' : null,
|
|
@@ -45,6 +46,7 @@ export function Btn({ href, variant = 'default', children, onClick, 'aria-label'
|
|
|
45
46
|
}, ...kids);
|
|
46
47
|
}
|
|
47
48
|
return h('button', {
|
|
49
|
+
key,
|
|
48
50
|
type: 'button', class: cls,
|
|
49
51
|
disabled: disabled ? true : null,
|
|
50
52
|
'aria-label': ariaName,
|
|
@@ -159,6 +161,9 @@ const ICON_PATHS = {
|
|
|
159
161
|
// Icon(); use innerHTML = iconMarkup(name). Keeps the icon paths upstream so
|
|
160
162
|
// raw-DOM call sites never reintroduce decorative glyph literals.
|
|
161
163
|
export function iconMarkup(name, { size = 16 } = {}) {
|
|
164
|
+
// Accept the props-object shape too - every sibling component takes a
|
|
165
|
+
// single object, so Icon({name}) is what the barrel trains consumers to try.
|
|
166
|
+
if (name && typeof name === 'object') ({ name, size = 16 } = name);
|
|
162
167
|
const inner = ICON_PATHS[name];
|
|
163
168
|
if (!inner) return '';
|
|
164
169
|
return '<svg class="ds-icon ds-icon-' + name + '" width="' + size + '" height="' + size +
|
|
@@ -166,6 +171,7 @@ export function iconMarkup(name, { size = 16 } = {}) {
|
|
|
166
171
|
' stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' + inner + '</svg>';
|
|
167
172
|
}
|
|
168
173
|
export function Icon(name, { size = 16 } = {}) {
|
|
174
|
+
if (name && typeof name === 'object') ({ name, size = 16 } = name);
|
|
169
175
|
const inner = ICON_PATHS[name];
|
|
170
176
|
if (!inner) return h('span', { class: 'glyph', 'aria-hidden': 'true' }, '');
|
|
171
177
|
return h('svg', {
|
|
@@ -299,15 +305,60 @@ export function AppShell({ topbar, crumb, side, main, status, narrow } = {}) {
|
|
|
299
305
|
function toggleWs(which) {
|
|
300
306
|
const shell = document.querySelector('.ws-shell');
|
|
301
307
|
if (!shell) return;
|
|
302
|
-
const cls = which === 'pane' ? 'ws-pane-collapsed'
|
|
308
|
+
const cls = which === 'pane' ? 'ws-pane-collapsed'
|
|
309
|
+
: which === 'sessions' ? 'ws-sessions-collapsed'
|
|
310
|
+
: 'ws-rail-collapsed';
|
|
303
311
|
const nowCollapsed = shell.classList.toggle(cls);
|
|
304
|
-
|
|
305
|
-
|
|
312
|
+
document.querySelectorAll('.ws-' + which + '-toggle').forEach((btn) =>
|
|
313
|
+
btn.setAttribute('aria-expanded', nowCollapsed ? 'false' : 'true'));
|
|
306
314
|
try {
|
|
307
315
|
localStorage.setItem('ds.ws.' + which, nowCollapsed ? 'collapsed' : 'open');
|
|
308
316
|
} catch (_) {}
|
|
309
317
|
}
|
|
310
318
|
|
|
319
|
+
// Column resize: read the current rendered track width and write a clamped inline
|
|
320
|
+
// --ws-<col>-w on .ws-shell (inline overrides the fluid clamp base), persisted.
|
|
321
|
+
const WS_RESIZE_CLAMP = { rail: [60, 360], sessions: [200, 520], pane: [240, 560] };
|
|
322
|
+
function wsResize(col, dx) {
|
|
323
|
+
const shell = document.querySelector('.ws-shell');
|
|
324
|
+
if (!shell) return;
|
|
325
|
+
const track = shell.querySelector('.ws-' + col);
|
|
326
|
+
const cur = track ? track.getBoundingClientRect().width : 0;
|
|
327
|
+
const [lo, hi] = WS_RESIZE_CLAMP[col] || [120, 600];
|
|
328
|
+
const next = Math.max(lo, Math.min(hi, Math.round(cur + dx)));
|
|
329
|
+
shell.style.setProperty('--ws-' + col + '-w', next + 'px');
|
|
330
|
+
try { localStorage.setItem('ds.ws.w.' + col, String(next)); } catch (_) {}
|
|
331
|
+
}
|
|
332
|
+
function seedWsWidths(el) {
|
|
333
|
+
if (!el) return;
|
|
334
|
+
['rail', 'sessions', 'pane'].forEach((col) => {
|
|
335
|
+
try {
|
|
336
|
+
const v = localStorage.getItem('ds.ws.w.' + col);
|
|
337
|
+
if (v && /^\d+$/.test(v)) el.style.setProperty('--ws-' + col + '-w', v + 'px');
|
|
338
|
+
} catch (_) {}
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
function WsResizer(col) {
|
|
342
|
+
const onKey = (e) => {
|
|
343
|
+
if (e.key === 'ArrowLeft') { e.preventDefault(); wsResize(col, -16); }
|
|
344
|
+
else if (e.key === 'ArrowRight') { e.preventDefault(); wsResize(col, 16); }
|
|
345
|
+
};
|
|
346
|
+
const onDown = (e) => {
|
|
347
|
+
e.preventDefault();
|
|
348
|
+
let lastX = e.clientX;
|
|
349
|
+
const move = (ev) => { const dx = ev.clientX - lastX; lastX = ev.clientX; wsResize(col, dx); };
|
|
350
|
+
const up = () => { document.removeEventListener('pointermove', move); document.removeEventListener('pointerup', up); document.body.style.cursor = ''; };
|
|
351
|
+
document.addEventListener('pointermove', move);
|
|
352
|
+
document.addEventListener('pointerup', up);
|
|
353
|
+
document.body.style.cursor = 'col-resize';
|
|
354
|
+
};
|
|
355
|
+
return h('div', {
|
|
356
|
+
class: 'ws-resizer ws-resizer-' + col, role: 'separator', tabindex: '0',
|
|
357
|
+
'aria-orientation': 'vertical', 'aria-label': 'resize ' + col + ' column (arrow keys)',
|
|
358
|
+
onpointerdown: onDown, onkeydown: onKey,
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
|
|
311
362
|
// Toggle a mobile WorkspaceShell DRAWER (sessions or pane). Distinct from the
|
|
312
363
|
// desktop width-collapse (toggleWs): on mobile the columns are fixed overlays
|
|
313
364
|
// revealed by .ws-sessions-open / .ws-pane-open. Opening one closes the other
|
|
@@ -388,7 +439,7 @@ export function WorkspaceShell({ rail, sessions, main, pane, crumb, status, narr
|
|
|
388
439
|
+ (((hasPane && paneIsCollapsed) || keepPaneTrack) ? ' ws-pane-collapsed' : '')
|
|
389
440
|
+ (hasSessions ? '' : ' ws-no-sessions')
|
|
390
441
|
+ (narrow ? ' narrow' : '');
|
|
391
|
-
return h('div', { class: shellCls },
|
|
442
|
+
return h('div', { class: shellCls, ref: seedWsWidths },
|
|
392
443
|
h('a', { href: '#ws-main', class: 'skip-link' }, 'skip to main content'),
|
|
393
444
|
// Left rail column. Its own toggle collapses it to icon-only.
|
|
394
445
|
h('nav', { class: 'ws-rail', role: 'navigation', 'aria-label': railLabel },
|
|
@@ -424,6 +475,13 @@ export function WorkspaceShell({ rail, sessions, main, pane, crumb, status, narr
|
|
|
424
475
|
'aria-label': 'toggle conversations', 'aria-expanded': 'false',
|
|
425
476
|
onclick: () => toggleWsDrawer('sessions'),
|
|
426
477
|
}, Icon('thread')) : null,
|
|
478
|
+
// Desktop-only sessions collapse (reclaims its width for a
|
|
479
|
+
// full-width thread/grid). Hidden on mobile via CSS.
|
|
480
|
+
hasSessions ? h('button', {
|
|
481
|
+
class: 'ws-desktop-toggle ws-sessions-toggle', type: 'button',
|
|
482
|
+
'aria-label': 'collapse conversations', title: 'collapse conversations',
|
|
483
|
+
'aria-expanded': 'true', onclick: () => toggleWs('sessions'),
|
|
484
|
+
}, Icon('chevron-left')) : null,
|
|
427
485
|
h('div', { class: 'ws-crumb-main' }, crumb),
|
|
428
486
|
hasPane ? h('button', {
|
|
429
487
|
class: 'ws-drawer-toggle ws-pane-drawer-toggle', type: 'button',
|
|
@@ -446,6 +504,10 @@ export function WorkspaceShell({ rail, sessions, main, pane, crumb, status, narr
|
|
|
446
504
|
}, Icon(paneIsCollapsed ? 'chevron-left' : 'chevron-right')),
|
|
447
505
|
pane)
|
|
448
506
|
: null,
|
|
507
|
+
// Keyboard/pointer column resize handles (desktop only).
|
|
508
|
+
(!narrow && !railIsCollapsed) ? WsResizer('rail') : null,
|
|
509
|
+
(!narrow && hasSessions) ? WsResizer('sessions') : null,
|
|
510
|
+
(!narrow && hasPane && !paneIsCollapsed) ? WsResizer('pane') : null,
|
|
449
511
|
);
|
|
450
512
|
}
|
|
451
513
|
|
|
@@ -34,19 +34,26 @@ export function ThemeToggle({ compact = false, onChange } = {}) {
|
|
|
34
34
|
const current = getTheme();
|
|
35
35
|
|
|
36
36
|
if (compact) {
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
// Plain words only - 'ink'/'paper' are internal theme codenames a user
|
|
38
|
+
// never chose; the resolved scheme rides in the title, not the label.
|
|
39
|
+
const resolvedWord = resolvedTheme() === 'ink' ? 'dark' : 'light';
|
|
40
|
+
const word = current === 'auto' ? 'auto' : (current === 'ink' ? 'dark' : 'light');
|
|
41
|
+
const label = 'theme: ' + word;
|
|
39
42
|
return h('button', {
|
|
40
43
|
class: 'btn ds-theme-toggle',
|
|
41
44
|
type: 'button',
|
|
42
|
-
'aria-label':
|
|
43
|
-
title: '
|
|
45
|
+
'aria-label': label,
|
|
46
|
+
title: label + (current === 'auto' ? ' (currently ' + resolvedWord + ')' : '') + ' — click to cycle',
|
|
44
47
|
onclick: () => {
|
|
45
48
|
const next = current === 'auto' ? 'paper' : (current === 'paper' ? 'ink' : 'auto');
|
|
46
49
|
applyTheme(next);
|
|
47
50
|
if (onChange) try { onChange(next); } catch {}
|
|
48
51
|
}
|
|
49
|
-
},
|
|
52
|
+
},
|
|
53
|
+
// CSS-drawn half-disc so the control still reads as the theme switch
|
|
54
|
+
// when the label is hidden (icon-only rail strip).
|
|
55
|
+
h('span', { class: 'ds-theme-disc', 'aria-hidden': 'true' }),
|
|
56
|
+
h('span', { class: 'ds-theme-toggle-label' }, label));
|
|
50
57
|
}
|
|
51
58
|
|
|
52
59
|
return h('div', {
|