anentrypoint-design 0.0.210 → 0.0.212
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 +120 -74
- package/chat.css +94 -20
- package/colors_and_type.css +19 -4
- package/community.css +5 -1
- package/dist/247420.css +489 -100
- package/dist/247420.js +13 -13
- package/package.json +1 -1
- package/src/components/content.js +8 -2
- package/src/components/files.js +24 -28
- package/src/components/sessions.js +9 -4
- package/src/components/shell.js +32 -10
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "anentrypoint-design",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.212",
|
|
4
4
|
"description": "247420 design system SDK — webjsx + modified ripple-ui, single-file ESM bundle for reproducible use of the AnEntrypoint design.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/247420.js",
|
|
@@ -6,8 +6,8 @@ import * as webjsx from '../../vendor/webjsx/index.js';
|
|
|
6
6
|
import { Btn, Heading, Lede, Dot, Icon } from './shell.js';
|
|
7
7
|
const h = webjsx.createElement;
|
|
8
8
|
|
|
9
|
-
export function Panel({ title, count, right, style = '', children, kind, id }) {
|
|
10
|
-
const cls = 'panel' + (kind ? ' panel-' + kind : '');
|
|
9
|
+
export function Panel({ title, count, right, style = '', class: className = '', children, kind, id }) {
|
|
10
|
+
const cls = 'panel' + (kind ? ' panel-' + kind : '') + (className ? ' ' + className : '');
|
|
11
11
|
return h('div', { class: cls, style, id: id || null },
|
|
12
12
|
title != null ? h('div', { class: 'panel-head' },
|
|
13
13
|
h('span', {}, title),
|
|
@@ -100,7 +100,13 @@ export function Row({ code, rank, title, sub, meta, active, state = 'default', o
|
|
|
100
100
|
onkeydown: (e) => { e.stopPropagation(); },
|
|
101
101
|
}, a.label)))
|
|
102
102
|
: null;
|
|
103
|
+
// Color is not the only status channel: emit a visually-hidden word for the
|
|
104
|
+
// meaningful rail tones (error/subagent) so AT and color-blind users get the
|
|
105
|
+
// state. green is the unremarkable default - announcing "ok" everywhere would
|
|
106
|
+
// be AT noise - so it emits nothing.
|
|
107
|
+
const railWord = rail === 'flame' ? 'error' : rail === 'purple' ? 'subagent' : null;
|
|
103
108
|
return h(isLink ? 'a' : 'div', props,
|
|
109
|
+
railWord ? h('span', { class: 'sr-only' }, railWord) : null,
|
|
104
110
|
leading != null ? leading : (codeVal != null ? h('span', { class: 'code' }, codeVal) : null),
|
|
105
111
|
h('span', { class: 'title' }, titleNode, sub ? h('span', { class: 'sub' }, sub) : null),
|
|
106
112
|
trailing != null ? trailing : (meta != null ? h('span', { class: 'meta' }, meta) : null),
|
package/src/components/files.js
CHANGED
|
@@ -4,10 +4,6 @@ import * as webjsx from '../../vendor/webjsx/index.js';
|
|
|
4
4
|
import { Btn, Icon } from './shell.js';
|
|
5
5
|
const h = webjsx.createElement;
|
|
6
6
|
|
|
7
|
-
// Minimum column width for the responsive file grid (minmax floor). Named so
|
|
8
|
-
// the magic 240px isn't buried in the gridTemplateColumns string.
|
|
9
|
-
const FILE_GRID_MIN_COL = '240px';
|
|
10
|
-
|
|
11
7
|
const FILE_TYPES = ['dir', 'image', 'video', 'audio', 'code', 'text', 'archive', 'document', 'symlink', 'other'];
|
|
12
8
|
const TYPE_ICON = {
|
|
13
9
|
dir: 'folder', image: 'file-image', video: 'file-video', audio: 'file-audio', code: 'file-code',
|
|
@@ -97,7 +93,7 @@ export function FileRow({ name, type = 'other', size, modified, code, onOpen, on
|
|
|
97
93
|
'aria-label': (marked ? 'unselect ' : 'select ') + name,
|
|
98
94
|
disabled: (noAccess || busy) ? true : null,
|
|
99
95
|
onclick: onMark ? (e) => onMark({ range: !!e.shiftKey }) : null,
|
|
100
|
-
}, h('span', { 'aria-hidden': 'true' }
|
|
96
|
+
}, h('span', { class: 'ds-check-box', 'aria-hidden': 'true' })) : null;
|
|
101
97
|
// A role=button row containing real <button> action controls is invalid
|
|
102
98
|
// HTML (interactive nesting). Instead the row is a plain container and the
|
|
103
99
|
// primary "open" affordance is itself a real <button> (native keyboard +
|
|
@@ -178,7 +174,7 @@ export function sortFiles(files = [], sort = 'name', dir = 'asc') {
|
|
|
178
174
|
const FILE_GRID_CAP = 200;
|
|
179
175
|
|
|
180
176
|
export function FileGrid({ files = [], onOpen, onAction, onUp, emptyText = 'No files here yet',
|
|
181
|
-
|
|
177
|
+
sort, filter, loading = false,
|
|
182
178
|
shown, onShowMore, actions, busy,
|
|
183
179
|
// Canonical multi-select contract (shared with
|
|
184
180
|
// SessionDashboard): selected/onToggleSelect.
|
|
@@ -200,16 +196,11 @@ export function FileGrid({ files = [], onOpen, onAction, onUp, emptyText = 'No f
|
|
|
200
196
|
const capped = files.length > limit;
|
|
201
197
|
const visible = capped ? files.slice(0, limit) : files;
|
|
202
198
|
const isThumb = density === 'thumb';
|
|
199
|
+
// NOTE: the old `columns`-driven data-columns card-mode was removed - it placed
|
|
200
|
+
// flex list-rows into a 2-4 col grid (squashed rows, mis-sized actions) and was
|
|
201
|
+
// a half-wired third layout never exposed by the density radiogroup (list/
|
|
202
|
+
// compact/thumb). Thumb density is the canonical multi-column grid.
|
|
203
203
|
const gridAttrs = {};
|
|
204
|
-
if (!isThumb && columns !== 'auto' && columns > 0) {
|
|
205
|
-
const col = Math.max(1, Math.min(4, Math.floor(columns)));
|
|
206
|
-
gridAttrs['data-columns'] = String(col);
|
|
207
|
-
gridAttrs.style = {
|
|
208
|
-
display: 'grid',
|
|
209
|
-
gridTemplateColumns: `repeat(${col}, minmax(${FILE_GRID_MIN_COL}, 1fr))`,
|
|
210
|
-
gap: 'var(--space-3)'
|
|
211
|
-
};
|
|
212
|
-
}
|
|
213
204
|
// Multi-select bookkeeping. Entries are keyed by path (fallback name); a
|
|
214
205
|
// locked/EACCES entry is never selectable — bulk mutations would fail on it.
|
|
215
206
|
const entryKeyOf = (f) => f.path || f.name;
|
|
@@ -243,7 +234,7 @@ export function FileGrid({ files = [], onOpen, onAction, onUp, emptyText = 'No f
|
|
|
243
234
|
'aria-checked': allState,
|
|
244
235
|
'aria-label': allState === 'true' ? 'clear selection' : 'select all ' + selectableKeys.length + ' shown files',
|
|
245
236
|
onclick: () => (allState === 'true' && onClearSelection) ? onClearSelection() : onSelectAll(selectableKeys) },
|
|
246
|
-
h('span', {
|
|
237
|
+
h('span', { class: 'ds-check-box', 'aria-hidden': 'true' }),
|
|
247
238
|
h('span', {}, 'all'))
|
|
248
239
|
: null;
|
|
249
240
|
// Density picker — list / compact / thumbnails. A radiogroup, not tabs:
|
|
@@ -257,19 +248,24 @@ export function FileGrid({ files = [], onOpen, onAction, onUp, emptyText = 'No f
|
|
|
257
248
|
onclick: () => { if (density !== k) onDensity(k); },
|
|
258
249
|
}, label)))
|
|
259
250
|
: null;
|
|
260
|
-
|
|
261
|
-
|
|
251
|
+
// One toolbar baseline: filter + select-all + sort sit left, density is
|
|
252
|
+
// pushed right by the spread. The filter used to be a separate right-aligned
|
|
253
|
+
// strip ABOVE controls, giving two strips with conflicting alignment.
|
|
254
|
+
const filterCtl = filter ? h('input', {
|
|
255
|
+
key: 'filter',
|
|
256
|
+
class: 'ds-file-filter-input', type: 'search',
|
|
257
|
+
value: filter.value || '', placeholder: filter.placeholder || 'Filter files',
|
|
258
|
+
'aria-label': filter.placeholder || 'Filter files in this directory',
|
|
259
|
+
oninput: (e) => filter.onInput && filter.onInput(e.target.value),
|
|
260
|
+
}) : null;
|
|
261
|
+
const leftKids = [filterCtl, selectAllCtl, head].filter(Boolean);
|
|
262
|
+
const controlsKids = [
|
|
263
|
+
...leftKids,
|
|
264
|
+
(leftKids.length && densityCtl) ? h('span', { key: 'spread', class: 'spread' }) : null,
|
|
262
265
|
densityCtl].filter(Boolean);
|
|
263
266
|
const controls = controlsKids.length
|
|
264
267
|
? h('div', { class: 'ds-file-controls' }, ...controlsKids)
|
|
265
268
|
: null;
|
|
266
|
-
const filterBar = filter ? h('div', { class: 'ds-file-filter' },
|
|
267
|
-
h('input', {
|
|
268
|
-
class: 'ds-file-filter-input', type: 'search',
|
|
269
|
-
value: filter.value || '', placeholder: filter.placeholder || 'Filter files',
|
|
270
|
-
'aria-label': filter.placeholder || 'Filter files in this directory',
|
|
271
|
-
oninput: (e) => filter.onInput && filter.onInput(e.target.value),
|
|
272
|
-
})) : null;
|
|
273
269
|
// role=group not listbox: the rows contain real <button> action controls, so
|
|
274
270
|
// listbox/option semantics are invalid (an option can't host interactive
|
|
275
271
|
// children). Keyboard nav still works via roving focus over the open buttons.
|
|
@@ -311,8 +307,8 @@ export function FileGrid({ files = [], onOpen, onAction, onUp, emptyText = 'No f
|
|
|
311
307
|
onclick: () => onShowMore(Math.min(files.length, limit + FILE_GRID_CAP)) },
|
|
312
308
|
'show ' + Math.min(FILE_GRID_CAP, files.length - limit) + ' more') : null)
|
|
313
309
|
: null;
|
|
314
|
-
return (controls ||
|
|
315
|
-
? h('div', { class: 'ds-file-listing' },
|
|
310
|
+
return (controls || more)
|
|
311
|
+
? h('div', { class: 'ds-file-listing' }, controls, grid, more)
|
|
316
312
|
: grid;
|
|
317
313
|
}
|
|
318
314
|
|
|
@@ -334,7 +330,7 @@ function FileCell({ key, f = {}, selectable = false, marked = false, onMark, onO
|
|
|
334
330
|
'aria-label': (marked ? 'unselect ' : 'select ') + f.name,
|
|
335
331
|
disabled: noAccess ? true : null,
|
|
336
332
|
onclick: onMark ? (e) => onMark({ range: !!e.shiftKey }) : null,
|
|
337
|
-
}, h('span', { 'aria-hidden': 'true' }
|
|
333
|
+
}, h('span', { class: 'ds-check-box', 'aria-hidden': 'true' })) : null,
|
|
338
334
|
h('button', {
|
|
339
335
|
key: 'open', type: 'button', class: 'ds-file-cell-open',
|
|
340
336
|
onclick: canOpen ? () => onOpen(f) : null,
|
|
@@ -171,7 +171,9 @@ export function SessionCard({ session = {}, onStop, onOpen, onView, active = fal
|
|
|
171
171
|
// sessions with no cost source (external tally rows) simply omit the segment.
|
|
172
172
|
const tokText = s.tokens != null ? (typeof s.tokens === 'number' ? s.tokens.toLocaleString() : s.tokens) + ' tok' : null;
|
|
173
173
|
const costText = s.cost != null ? (typeof s.cost === 'number' ? '$' + s.cost.toFixed(4) : String(s.cost)) : null;
|
|
174
|
-
|
|
174
|
+
// Cost is rendered as its own emphasized segment (not buried in the mono run)
|
|
175
|
+
// so the command-center cost-at-a-glance signal is scannable.
|
|
176
|
+
const statBits = [elapsedText, s.counter != null ? s.counter : null, tokText].filter((x) => x != null && x !== '');
|
|
175
177
|
const activityBits = [
|
|
176
178
|
s.currentTool ? 'running: ' + s.currentTool : null,
|
|
177
179
|
s.lastActivity ? 'last ' + s.lastActivity : null,
|
|
@@ -186,7 +188,7 @@ export function SessionCard({ session = {}, onStop, onOpen, onView, active = fal
|
|
|
186
188
|
'aria-checked': selected ? 'true' : 'false',
|
|
187
189
|
'aria-label': (selected ? 'deselect' : 'select') + ' session ' + (s.title || s.agent || s.sid),
|
|
188
190
|
onclick: () => onToggleSelect && onToggleSelect(s),
|
|
189
|
-
},
|
|
191
|
+
}, h('span', { class: 'ds-check-box', 'aria-hidden': 'true' })) : null,
|
|
190
192
|
h('span', { class: 'status-dot-disc ' + STATUS_DISC[st], 'aria-hidden': 'true' }),
|
|
191
193
|
h('span', { class: 'ds-dash-status is-' + st }, STATUS_WORD[st]),
|
|
192
194
|
s.external ? h('span', { class: 'ds-dash-external' }, 'external') : null,
|
|
@@ -195,7 +197,10 @@ export function SessionCard({ session = {}, onStop, onOpen, onView, active = fal
|
|
|
195
197
|
].filter(Boolean));
|
|
196
198
|
const meta = h('div', { class: 'ds-dash-meta' }, ...[
|
|
197
199
|
s.cwd ? h('span', { class: 'ds-dash-cwd', title: s.cwd }, s.cwd) : null,
|
|
198
|
-
statBits.length ? h('span', { class: 'ds-dash-stat' },
|
|
200
|
+
(statBits.length || costText) ? h('span', { class: 'ds-dash-stat' },
|
|
201
|
+
statBits.join(' · '),
|
|
202
|
+
costText ? h('span', { class: 'ds-dash-stat-cost' }, (statBits.length ? ' · ' : '') + costText) : null
|
|
203
|
+
) : null,
|
|
199
204
|
activityBits.length ? h('span', { class: 'ds-dash-activity' }, activityBits.join(' · ')) : null,
|
|
200
205
|
].filter(Boolean));
|
|
201
206
|
const actions = h('div', { class: 'ds-dash-actions', role: 'group', 'aria-label': 'session actions' }, ...[
|
|
@@ -310,7 +315,7 @@ export function SessionDashboard({ sessions = [], onStop, onOpen, onView, onStop
|
|
|
310
315
|
? h('button', { key: 'selall', type: 'button', class: 'ds-dash-selectall', role: 'checkbox',
|
|
311
316
|
'aria-checked': allState, 'aria-label': allState === 'true' ? 'clear selection' : 'select all sessions',
|
|
312
317
|
onclick: () => (allState === 'true' && onClearSelection) ? onClearSelection() : onSelectAll(selectableSids) },
|
|
313
|
-
h('span', {
|
|
318
|
+
h('span', { class: 'ds-check-box', 'aria-hidden': 'true' }),
|
|
314
319
|
h('span', {}, 'all'))
|
|
315
320
|
: null;
|
|
316
321
|
const clearCtl = (selectable && selCount && onClearSelection)
|
package/src/components/shell.js
CHANGED
|
@@ -318,8 +318,13 @@ function toggleWs(which) {
|
|
|
318
318
|
|
|
319
319
|
// Column resize: read the current rendered track width and write a clamped inline
|
|
320
320
|
// --ws-<col>-w on .ws-shell (inline overrides the fluid clamp base), persisted.
|
|
321
|
-
|
|
322
|
-
|
|
321
|
+
// Bounds are derived from the CSS fluid clamp() floors/ceilings in app-shell.css
|
|
322
|
+
// (--ws-rail-w clamp(200,16vw,260); sessions clamp(248,22vw,360); pane
|
|
323
|
+
// clamp(288,24vw,420)) so a drag/arrow can never shrink a column below its
|
|
324
|
+
// designed floor (the collapsed rail is a SEPARATE class, not a resize target)
|
|
325
|
+
// nor grow past the ultrawide ceiling.
|
|
326
|
+
const WS_RESIZE_CLAMP = { rail: [200, 260], sessions: [248, 360], pane: [288, 420] };
|
|
327
|
+
function wsResize(col, dx, persist = true) {
|
|
323
328
|
const shell = document.querySelector('.ws-shell');
|
|
324
329
|
if (!shell) return;
|
|
325
330
|
const track = shell.querySelector('.ws-' + col);
|
|
@@ -327,7 +332,11 @@ function wsResize(col, dx) {
|
|
|
327
332
|
const [lo, hi] = WS_RESIZE_CLAMP[col] || [120, 600];
|
|
328
333
|
const next = Math.max(lo, Math.min(hi, Math.round(cur + dx)));
|
|
329
334
|
shell.style.setProperty('--ws-' + col + '-w', next + 'px');
|
|
330
|
-
|
|
335
|
+
const handle = shell.querySelector('.ws-resizer-' + col);
|
|
336
|
+
if (handle) handle.setAttribute('aria-valuenow', String(next));
|
|
337
|
+
// Commit to storage only on a settled move (pointerup / keyboard), not on
|
|
338
|
+
// every pointermove frame (that fired dozens of synchronous writes per drag).
|
|
339
|
+
if (persist) { try { localStorage.setItem('ds.ws.w.' + col, String(next)); } catch (_) {} }
|
|
331
340
|
}
|
|
332
341
|
function seedWsWidths(el) {
|
|
333
342
|
if (!el) return;
|
|
@@ -340,22 +349,35 @@ function seedWsWidths(el) {
|
|
|
340
349
|
}
|
|
341
350
|
function WsResizer(col) {
|
|
342
351
|
const onKey = (e) => {
|
|
343
|
-
if (e.key === 'ArrowLeft') { e.preventDefault(); wsResize(col, -16); }
|
|
344
|
-
else if (e.key === 'ArrowRight') { e.preventDefault(); wsResize(col, 16); }
|
|
352
|
+
if (e.key === 'ArrowLeft') { e.preventDefault(); wsResize(col, -16, true); }
|
|
353
|
+
else if (e.key === 'ArrowRight') { e.preventDefault(); wsResize(col, 16, true); }
|
|
345
354
|
};
|
|
346
355
|
const onDown = (e) => {
|
|
347
356
|
e.preventDefault();
|
|
348
357
|
let lastX = e.clientX;
|
|
349
|
-
const move = (ev) => { const dx = ev.clientX - lastX; lastX = ev.clientX; wsResize(col, dx); };
|
|
350
|
-
const up = () => {
|
|
358
|
+
const move = (ev) => { const dx = ev.clientX - lastX; lastX = ev.clientX; wsResize(col, dx, false); };
|
|
359
|
+
const up = () => {
|
|
360
|
+
document.removeEventListener('pointermove', move);
|
|
361
|
+
document.removeEventListener('pointerup', up);
|
|
362
|
+
document.body.style.cursor = '';
|
|
363
|
+
wsResize(col, 0, true); // commit the settled width once
|
|
364
|
+
};
|
|
351
365
|
document.addEventListener('pointermove', move);
|
|
352
366
|
document.addEventListener('pointerup', up);
|
|
353
367
|
document.body.style.cursor = 'col-resize';
|
|
354
368
|
};
|
|
369
|
+
const [lo, hi] = WS_RESIZE_CLAMP[col] || [120, 600];
|
|
370
|
+
// Seed aria-valuenow from the rendered track width so AT announces real widths.
|
|
371
|
+
const seedNow = (el) => {
|
|
372
|
+
if (!el) return;
|
|
373
|
+
const track = el.closest('.ws-shell') && el.closest('.ws-shell').querySelector('.ws-' + col);
|
|
374
|
+
if (track) el.setAttribute('aria-valuenow', String(Math.round(track.getBoundingClientRect().width)));
|
|
375
|
+
};
|
|
355
376
|
return h('div', {
|
|
356
377
|
class: 'ws-resizer ws-resizer-' + col, role: 'separator', tabindex: '0',
|
|
357
378
|
'aria-orientation': 'vertical', 'aria-label': 'resize ' + col + ' column (arrow keys)',
|
|
358
|
-
|
|
379
|
+
'aria-valuemin': String(lo), 'aria-valuemax': String(hi),
|
|
380
|
+
onpointerdown: onDown, onkeydown: onKey, ref: seedNow,
|
|
359
381
|
});
|
|
360
382
|
}
|
|
361
383
|
|
|
@@ -550,8 +572,8 @@ export function WorkspaceRail({ brand = '247420', action, items = [], footer } =
|
|
|
550
572
|
);
|
|
551
573
|
}
|
|
552
574
|
|
|
553
|
-
export function Heading({ level = 1, children, style = '', 'aria-level': ariaLevel }) {
|
|
554
|
-
return h('h' + level, { style, 'aria-level': ariaLevel != null ? String(ariaLevel) : null }, children);
|
|
575
|
+
export function Heading({ level = 1, children, style = '', class: className = '', 'aria-level': ariaLevel }) {
|
|
576
|
+
return h('h' + level, { class: className || null, style, 'aria-level': ariaLevel != null ? String(ariaLevel) : null }, children);
|
|
555
577
|
}
|
|
556
578
|
|
|
557
579
|
export function Lede({ children }) {
|