anentrypoint-design 0.0.398 → 0.0.400

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/package.json CHANGED
@@ -1,12 +1,23 @@
1
1
  {
2
2
  "name": "anentrypoint-design",
3
- "version": "0.0.398",
3
+ "version": "0.0.400",
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",
7
7
  "module": "./dist/247420.js",
8
+ "types": "./types/index.d.ts",
9
+ "sideEffects": [
10
+ "*.css",
11
+ "./src/index.js",
12
+ "./src/styles.js",
13
+ "./src/markdown-cache.js",
14
+ "./src/components/chat/stats.js",
15
+ "./src/web-components/freddie-chat.js",
16
+ "./dist/247420.js"
17
+ ],
8
18
  "exports": {
9
19
  ".": {
20
+ "types": "./types/index.d.ts",
10
21
  "import": "./dist/247420.js",
11
22
  "default": "./dist/247420.js"
12
23
  },
@@ -72,6 +83,7 @@
72
83
  "files": [
73
84
  "dist",
74
85
  "src",
86
+ "types",
75
87
  "scripts/lint-glyphs.mjs",
76
88
  "scripts/lint-null-children.mjs",
77
89
  "scripts/lint-shared.mjs",
@@ -97,6 +109,8 @@
97
109
  "sitemap": "node scripts/generate-sitemap.mjs",
98
110
  "docs:components": "node scripts/generate-component-docs.mjs",
99
111
  "lint:component-docs": "node scripts/generate-component-docs.mjs --check",
112
+ "types:components": "node scripts/generate-component-types.mjs",
113
+ "lint:component-types": "node scripts/generate-component-types.mjs --check",
100
114
  "generate:ui-kits": "node scripts/generate-ui-kit-scaffolds.mjs",
101
115
  "generate:preview-index": "node scripts/generate-preview-index.mjs",
102
116
  "a11y": "node scripts/a11y-audit.mjs",
@@ -18,7 +18,11 @@ const root = path.resolve(__dirname, '..');
18
18
  // Directories scanned recursively for source files. dist/ is generated,
19
19
  // node_modules/ is vendored, vendor/ is third-party (webjsx), .gm/ is the
20
20
  // plugkit spool — none are hand-authored design surfaces.
21
- const SCAN_DIRS = ['src', 'ui_kits', 'slides', 'site'];
21
+ // `preview` added 2026-07-28. It had been missing while EXEMPT_FILES below
22
+ // already carried a `preview/icons-unicode.html` entry — an exemption for a
23
+ // directory the walk never entered, i.e. dead code that read as coverage. The
24
+ // preview/ tree ships and is exactly where glyphs accumulate unnoticed.
25
+ const SCAN_DIRS = ['src', 'ui_kits', 'slides', 'site', 'preview'];
22
26
  const SCAN_EXT = new Set(['.js', '.mjs', '.css', '.html']);
23
27
  // The bundled root CSS files (app-shell.css, chat.css, etc.) are the design
24
28
  // system's shipped styling but live at the repo root, outside SCAN_DIRS — they
@@ -22,7 +22,13 @@ import { walkFiles } from './lint-shared.mjs';
22
22
 
23
23
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
24
24
  const root = path.resolve(__dirname, '..');
25
- const SCAN_DIRS = ['src'];
25
+ // `ui_kits`, `site`, `preview`, `slides` added 2026-07-28. The bug this gate
26
+ // catches is a webjsx applyDiff crash on a children array holding a null/false
27
+ // hole, so its true scope is "everywhere h() is called" — and h() is called in
28
+ // 20 files outside src/ (every ui_kits/*/app.js, site/theme.mjs,
29
+ // preview/data-density.js). Scanning only src/ meant the gate never looked at
30
+ // the consumer code most likely to hand-roll a conditional child.
31
+ const SCAN_DIRS = ['src', 'ui_kits', 'site', 'preview', 'slides'];
26
32
  const SCAN_EXT = new Set(['.js', '.mjs']);
27
33
  const SKIP_DIRS = new Set(['node_modules', 'vendor']);
28
34
 
@@ -107,18 +107,21 @@ export function JsonViewer({ value, emptyText = 'no data', maxHeight, mode = 'pl
107
107
  else { try { text = JSON.stringify(value, null, 2); knownJson = text != null; parsed = value; } catch { text = String(value); } }
108
108
  if (!text) return h('div', { class: 'ds-ep-json ds-ep-json-empty' }, emptyText);
109
109
  const style = maxHeight ? ('max-height:' + maxHeight) : null;
110
- // A maxHeight makes this an `overflow: auto` scroll container, so the
111
- // clipped content is reachable by mouse wheel but by nothing a keyboard
112
- // user has WCAG 2.1.1 Keyboard, axe's `scrollable-region-focusable`.
113
- // Same fix the Table wrapper carries: tabindex puts it in the tab order and
114
- // arrow keys then scroll it natively, and a bare focusable region with no
115
- // name is its own violation, so it is labelled. `group` rather than
116
- // `region` so a page rendering several viewers does not gain a landmark
117
- // each. Witnessed unreachable on a real page: 761px of content in a 250px
118
- // box with no focusable descendant.
119
- const scrollable = maxHeight
120
- ? { tabindex: '0', role: 'group', 'aria-label': 'JSON, scrollable' }
121
- : {};
110
+ // Every viewer is a scroll container, so these are UNCONDITIONAL. The
111
+ // `maxHeight` prop only overrides a height the stylesheet already sets:
112
+ // `.ds-ep-json` carries `max-height: 300px; overflow-y: auto` in
113
+ // editor-primitives.css, so a viewer rendered with no prop at all still
114
+ // clips and still scrolls. Gating the attributes on the prop therefore
115
+ // missed the common case measured live: a prop-less viewer reported
116
+ // scrollHeight 1265 against clientHeight 300 while sitting at tabindex -1.
117
+ //
118
+ // Clipped content reachable by wheel but not by keyboard is WCAG 2.1.1
119
+ // Keyboard, axe's `scrollable-region-focusable`. tabindex puts the box in
120
+ // the tab order and arrow keys then scroll it natively; a bare focusable
121
+ // region with no name is its own violation, so it is labelled. `group`
122
+ // rather than `region` so a page rendering several viewers does not gain a
123
+ // landmark each.
124
+ const scrollable = { tabindex: '0', role: 'group', 'aria-label': 'JSON, scrollable' };
122
125
  if (!knownJson && (mode === 'highlight' || mode === 'tree')) {
123
126
  try { parsed = JSON.parse(text); knownJson = true; } catch { /* swallow: not JSON — render plain */ }
124
127
  }
@@ -23,6 +23,35 @@ const h = webjsx.createElement;
23
23
  // CAP and a "show N more" row, mirroring the History tab's "load N older".
24
24
  const FILE_GRID_CAP = 200;
25
25
 
26
+ /**
27
+ * The directory listing.
28
+ *
29
+ * `loading` and `busy` are NOT two spellings of one state -- they are the two
30
+ * halves of this SDK's standing distinction, and FileGrid is the component
31
+ * that takes both because it is the one place both are in play at once:
32
+ *
33
+ * loading -- a DATA FETCH is in flight. Owns which SHAPE renders: with no
34
+ * rows yet it is a cold load and the whole grid is replaced by
35
+ * FileSkeleton; with rows already on screen it is a refresh and
36
+ * the existing rows stay mounted and dim (is-refreshing), because
37
+ * flashing a populated directory back to shimmer reads as data
38
+ * loss.
39
+ * busy -- a USER ACTION is in flight (a rename/move/delete round-trip).
40
+ * Owns INTERACTIVITY, not shape: it is forwarded to each FileRow
41
+ * as `busy`, which disables that row's open + mutation controls
42
+ * so a second click cannot fire the same mutation twice.
43
+ *
44
+ * A grid can be `busy` while not `loading` (a delete is posting, rows fully
45
+ * rendered) and `loading` while not `busy` (a plain refresh). Passing one for
46
+ * the other is a real bug, not a style choice, so they are deliberately not
47
+ * merged and neither is an alias of the other.
48
+ *
49
+ * @param {Array} [files=[]] - the directory entries to render.
50
+ * @param {boolean} [loading=false] - a data fetch is in flight (skeleton when cold, dim when refreshing).
51
+ * @param {boolean} [busy] - a user-initiated mutation is in flight; disables every row's controls. Per-entry `f.busy` is used when this is not passed.
52
+ * @param {string} [emptyText='No files here yet'] - copy for the empty/filtered-miss state.
53
+ * @param {'list'|'compact'|'thumb'} [density='list'] - row density; 'thumb' switches to the multi-column cell grid.
54
+ */
26
55
  export function FileGrid({ files = [], onOpen, onAction, onUp, emptyText = 'No files here yet', emptyAction,
27
56
  sort, filter, loading = false,
28
57
  shown, onShowMore, actions, busy,
@@ -55,7 +55,8 @@ export function SessionDashboard({ sessions = [], onStop, onOpen, onView, onStop
55
55
  sort, filter, errorsOnly = false, onErrorsOnly,
56
56
  selectable = false, selected, onToggleSelect, onSelectAll, onClearSelection,
57
57
  activeSid, streamState,
58
- emptyText = 'No live sessions', emptyAction, offline = false } = {}) {
58
+ emptyText = 'No live sessions', emptyAction, offline = false,
59
+ density = 'comfortable' } = {}) {
59
60
  if (offline) {
60
61
  return h('div', { class: 'ds-dash-state ds-dash-state-error', role: 'status' }, 'Backend offline — live sessions unavailable');
61
62
  }
@@ -171,7 +172,7 @@ export function SessionDashboard({ sessions = [], onStop, onOpen, onView, onStop
171
172
  const grouped = !sort || !sort.value || sort.value === 'status';
172
173
  const cardOf = (s) => h('div', { key: s.sid, role: 'listitem' },
173
174
  SessionCard({ session: s, onStop, onOpen, onView, active: s.sid === activeSid,
174
- selectable, selected: selSet.has(s.sid), onToggleSelect }));
175
+ selectable, selected: selSet.has(s.sid), onToggleSelect, density }));
175
176
  // ONE stable body wrapper across every state (empty / grouped / flat), with
176
177
  // KEYED children - the ConversationList stable-keyed-body rule. Diffing
177
178
  // happens on the children, never by swapping the container's shape.
@@ -193,9 +194,9 @@ export function SessionDashboard({ sessions = [], onStop, onOpen, onView, onStop
193
194
  ].filter((b) => b.rows.length);
194
195
  bodyKids = buckets.map((b) => h('div', { key: 'grp' + b.key, class: 'ds-dash-group', role: 'group', 'aria-label': b.label + ' sessions' },
195
196
  h('div', { key: 'gl', class: 'ds-dash-group-label' }, b.label + ' · ' + b.rows.length),
196
- h('div', { key: 'gg', class: 'ds-dash-grid', role: 'list', 'aria-label': b.label + ' sessions' }, ...b.rows.map(cardOf))));
197
+ h('div', { key: 'gg', class: 'ds-dash-grid' + (density === 'compact' ? ' is-compact' : ''), role: 'list', 'aria-label': b.label + ' sessions' }, ...b.rows.map(cardOf))));
197
198
  } else {
198
- bodyKids = [h('div', { key: 'flat', class: 'ds-dash-grid', role: 'list', 'aria-label': 'live sessions' }, ...sessions.map(cardOf))];
199
+ bodyKids = [h('div', { key: 'flat', class: 'ds-dash-grid' + (density === 'compact' ? ' is-compact' : ''), role: 'list', 'aria-label': 'live sessions' }, ...sessions.map(cardOf))];
199
200
  }
200
201
  const body = h('div', { key: 'body', class: 'ds-dash-groups' }, ...bodyKids);
201
202
  return h('div', { class: 'ds-dash' }, header, body);
@@ -28,9 +28,21 @@ const h = webjsx.createElement;
28
28
  // the card heading so the rail row and its dashboard card share one identity.
29
29
  // `session.elapsedMs` (raw ms) is formatted internally via fmtDuration; the
30
30
  // pre-formatted `elapsed` string remains as a legacy fallback.
31
+ // `density` trades vertical space for scannability, the same list/compact axis
32
+ // FileGrid already exposes:
33
+ // 'comfortable' (default) — the full card: title, head, meta, per-card actions.
34
+ // 'compact' — one line per session. Measured on a real 16-session
35
+ // dashboard the comfortable card renders 208px tall,
36
+ // so a 439px viewport shows two sessions; the point of
37
+ // a command center is scanning many at once. Compact
38
+ // keeps every FACT (status, agent, cwd, elapsed,
39
+ // activity) and drops only the per-card action row,
40
+ // which is reachable by opening the session.
31
41
  export function SessionCard({ session = {}, onStop, onOpen, onView, active = false,
32
- selectable = false, selected = false, onToggleSelect } = {}) {
42
+ selectable = false, selected = false, onToggleSelect,
43
+ density = 'comfortable' } = {}) {
33
44
  const s = session;
45
+ const compact = density === 'compact';
34
46
  const st = s.stopping ? 'stopping' : (s.status === 'error' ? 'error' : (s.status === 'stale' ? 'stale' : 'running'));
35
47
  // The stat line composes elapsed + live counter; the activity line carries the
36
48
  // last-activity time and the current tool so a card shows MOTION, not just a
@@ -47,7 +59,7 @@ export function SessionCard({ session = {}, onStop, onOpen, onView, active = fal
47
59
  s.currentTool ? 'running: ' + s.currentTool : null,
48
60
  s.lastActivity ? 'last ' + s.lastActivity : null,
49
61
  ].filter(Boolean);
50
- const cls = 'ds-dash-card is-' + st + (active ? ' is-active' : '') + (selected ? ' is-selected' : '') + (s.external ? ' is-external' : '') + (s.isNew ? ' is-new' : '');
62
+ const cls = 'ds-dash-card is-' + st + (compact ? ' is-compact' : '') + (active ? ' is-active' : '') + (selected ? ' is-selected' : '') + (s.external ? ' is-external' : '') + (s.isNew ? ' is-new' : '');
51
63
  // EVERY children array is filter(Boolean)'d: webjsx applyDiff crashes
52
64
  // (reading 'key') on a bare null among VElement siblings, so a null cwd /
53
65
  // model / external flag must never reach a positional child slot.
@@ -84,9 +96,15 @@ export function SessionCard({ session = {}, onStop, onOpen, onView, active = fal
84
96
  onClick: () => !s.stopping && onStop(s),
85
97
  children: [Icon('square', { size: 14 }), h('span', {}, s.stopping ? 'stopping…' : 'stop')] }) : null,
86
98
  ].filter(Boolean));
99
+ // Compact keeps head + meta on ONE row and drops the action group; the title
100
+ // rides as the element's own accessible name (already set below) rather than
101
+ // taking a line of its own, so nothing is lost, only re-laid-out.
102
+ const children = compact
103
+ ? [head, meta].filter(Boolean)
104
+ : [
105
+ s.title ? h('div', { class: 'ds-dash-title', title: s.title }, s.title) : null,
106
+ head, meta, actions,
107
+ ].filter(Boolean);
87
108
  return h('div', { class: cls, role: 'group', 'aria-label': 'session ' + (s.title || s.agent || s.sid), 'aria-current': active ? 'true' : null },
88
- ...[
89
- s.title ? h('div', { class: 'ds-dash-title', title: s.title }, s.title) : null,
90
- head, meta, actions,
91
- ].filter(Boolean));
109
+ ...children);
92
110
  }
@@ -165,6 +165,15 @@
165
165
  .chip.tone-wip, .chip.tone-neutral {
166
166
  background: var(--bg-3); color: var(--fg-2);
167
167
  }
168
+ /* tone-dim was applied by five kits (aicat, project_page, signin, system_primer,
169
+ terminal) with no rule anywhere, so a "dim" chip rendered byte-identical to a
170
+ plain one — measured at rgb(234,230,218) on rgb(31,31,38), same opacity. It is
171
+ the recede-into-the-row tone: the tier-3 foreground on the same recessed
172
+ surface tone-wip uses, which is quieter than the default --fg-2 without
173
+ dropping to an opacity fade that would take the border and background with it. */
174
+ .chip.tone-dim {
175
+ background: var(--bg-3); color: var(--fg-3);
176
+ }
168
177
  .chip.tone-purple { background: var(--purple-tint); color: var(--purple-deep); }
169
178
  .chip.tone-mascot { background: var(--mascot-tint); color: var(--ink); }
170
179
  .chip.tone-sun, .chip.tone-yellow {
@@ -23,6 +23,7 @@ let _initPromise = null;
23
23
  let _renderCache = new Map();
24
24
  let _stats = {
25
25
  markdownInitMs: 0,
26
+ totalInitMs: 0,
26
27
  prismInitMs: 0,
27
28
  renderCount: 0,
28
29
  renderTimes: [],
@@ -59,8 +60,13 @@ export async function initializeCachesEagerly() {
59
60
  })(),
60
61
  ]);
61
62
 
62
- const totalMs = performance.now() - startTime;
63
- console.debug(`[247420] markdown/prism caches initialized in ${totalMs.toFixed(1)}ms (markdown: ${_stats.markdownInitMs.toFixed(1)}ms, prism: ${_stats.prismInitMs.toFixed(1)}ms)`);
63
+ // Recorded, not printed. This is library code, so an unconditional
64
+ // console.debug writes into every consuming application's console on
65
+ // every init. The same numbers are already live-inspectable through
66
+ // window.__debug['markdown-cache'] (registered at the bottom of this
67
+ // file), which is the repo's own observability channel and the one a
68
+ // consumer can actually opt into.
69
+ _stats.totalInitMs = performance.now() - startTime;
64
70
 
65
71
  return { markdown: mdOk, prism: prismOk };
66
72
  })();
@@ -140,6 +146,11 @@ export function getCacheStats() {
140
146
  initMs: {
141
147
  markdown: _stats.markdownInitMs,
142
148
  prism: _stats.prismInitMs,
149
+ // Wall-clock for the whole init, which is not the sum of the two
150
+ // above: they run concurrently under Promise.all, so total is the
151
+ // slower of the pair plus overhead. Previously only ever printed
152
+ // to console.debug; exposed here so it is inspectable instead.
153
+ total: _stats.totalInitMs,
143
154
  },
144
155
  renderStats: {
145
156
  count: _stats.renderCount,
@@ -171,6 +182,7 @@ export function resetCacheState() {
171
182
  _renderCache.clear();
172
183
  _stats = {
173
184
  markdownInitMs: 0,
185
+ totalInitMs: 0,
174
186
  prismInitMs: 0,
175
187
  renderCount: 0,
176
188
  renderTimes: [],