anentrypoint-design 0.0.215 → 0.0.217

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,6 +1,6 @@
1
1
  {
2
2
  "name": "anentrypoint-design",
3
- "version": "0.0.215",
3
+ "version": "0.0.217",
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",
@@ -182,7 +182,9 @@ export function AgentChat(props = {}) {
182
182
  // string OR at least one part. Used for the empty-shell skip + working tail
183
183
  // so an interleaved turn (parts-only, no m.content) is not treated as empty.
184
184
  const msgHasBody = (m) => !!(m.content || (Array.isArray(m.parts) && m.parts.length));
185
- const showWorkingTail = busy && lastMsg && lastMsg.role === 'assistant' && msgHasBody(lastMsg);
185
+ const lastMsgLastPart = lastMsg && Array.isArray(lastMsg.parts) && lastMsg.parts.length ? lastMsg.parts[lastMsg.parts.length - 1] : null;
186
+ const showWorkingTail = busy && lastMsg && lastMsg.role === 'assistant' && msgHasBody(lastMsg)
187
+ && lastMsgLastPart && lastMsgLastPart.kind === 'tool' && lastMsgLastPart.status === 'running';
186
188
  const rows = messages.slice(msgStart).map((m, wi) => {
187
189
  const i = wi + msgStart; // absolute index — streaming/caret/actions logic keys off the real lastIdx
188
190
  const isAssistant = m.role === 'assistant';
@@ -411,6 +411,7 @@ function toggleWsDrawer(which, open) {
411
411
  const focusable = drawer && drawer.querySelector('button, a, input, [tabindex]');
412
412
  if (focusable) try { focusable.focus(); } catch (_) {}
413
413
  const onKey = (e) => { if (e.key === 'Escape') { toggleWsDrawer(which, false); document.removeEventListener('keydown', onKey); if (btn) try { btn.focus(); } catch (_) {} } };
414
+ shell._wsEscHandler = onKey;
414
415
  document.addEventListener('keydown', onKey);
415
416
  }
416
417
  }
@@ -419,6 +420,8 @@ function closeWsDrawers() {
419
420
  if (!shell) return;
420
421
  shell.classList.remove('ws-sessions-open', 'ws-pane-open');
421
422
  document.querySelectorAll('.ws-sessions-drawer-toggle, .ws-pane-drawer-toggle').forEach((b) => b.setAttribute('aria-expanded', 'false'));
423
+ // Remove Esc handler armed by toggleWsDrawer to prevent ghost close on next Esc.
424
+ if (shell._wsEscHandler) { document.removeEventListener('keydown', shell._wsEscHandler); shell._wsEscHandler = null; }
422
425
  }
423
426
 
424
427
  // Read persisted collapse state for a WorkspaceShell column so the layout is
@@ -464,6 +467,7 @@ export function WorkspaceShell({ rail, sessions, main, pane, crumb, status, narr
464
467
  const keepPaneTrack = stableFrame && !hasPane;
465
468
  const railIsCollapsed = wsCollapsed('rail', railCollapsed);
466
469
  const paneIsCollapsed = hasPane ? wsCollapsed('pane', paneCollapsed) : true;
470
+ const sessionsIsCollapsed = wsCollapsed('sessions', false);
467
471
  const shellCls = 'ws-shell'
468
472
  + (railIsCollapsed ? ' ws-rail-collapsed' : '')
469
473
  + ((hasPane || keepPaneTrack) ? '' : ' ws-no-pane')
@@ -492,7 +496,7 @@ export function WorkspaceShell({ rail, sessions, main, pane, crumb, status, narr
492
496
  // (any button click inside) auto-closes it, mirroring AppShell.
493
497
  hasSessions
494
498
  ? h('div', { class: 'ws-sessions', role: 'complementary', 'aria-label': 'conversations',
495
- onclick: (e) => { if (narrow && e.target.closest('button, a, [role="button"]')) closeWsDrawers(); } }, sessions)
499
+ onclick: (e) => { if (window.innerWidth <= 1100 && e.target.closest('button, a, [role="button"]')) closeWsDrawers(); } }, sessions)
496
500
  : null,
497
501
  // Primary content column, with an optional thin crumb bar on top. On
498
502
  // mobile the crumb hosts the drawer toggles (sessions on the left, pane
@@ -510,8 +514,9 @@ export function WorkspaceShell({ rail, sessions, main, pane, crumb, status, narr
510
514
  // full-width thread/grid). Hidden on mobile via CSS.
511
515
  hasSessions ? h('button', {
512
516
  class: 'ws-desktop-toggle ws-sessions-toggle', type: 'button',
513
- 'aria-label': 'collapse conversations', title: 'collapse conversations',
514
- 'aria-expanded': 'true', onclick: () => toggleWs('sessions'),
517
+ 'aria-label': sessionsIsCollapsed ? 'expand conversations' : 'collapse conversations',
518
+ title: sessionsIsCollapsed ? 'expand conversations' : 'collapse conversations',
519
+ 'aria-expanded': sessionsIsCollapsed ? 'false' : 'true', onclick: () => toggleWs('sessions'),
515
520
  }, Icon('chevron-left')) : null,
516
521
  h('div', { class: 'ws-crumb-main' }, crumb),
517
522
  // Desktop-only context-pane collapse, on the same crumb-level
package/src/highlight.js CHANGED
@@ -1,40 +1,35 @@
1
1
  // Syntax highlighting — lazy-loads Prism + common language grammars on first call. No-op safe.
2
+ // Grammars are injected as <script src="..."> so the browser's CSP allowlist
3
+ // (cdn.jsdelivr.net) covers them without needing 'unsafe-eval'. Prism core
4
+ // must finish before grammars; grammars within each tier are parallel.
2
5
 
3
6
  let _prism = null;
4
7
  let _ready = null;
5
8
 
6
9
  const PRISM_BASE = 'https://cdn.jsdelivr.net/npm/prismjs@1.30.0/components/';
7
10
  const PRISM_CORE = PRISM_BASE + 'prism-core.min.js';
8
- const PRISM_LANGS = [
9
- 'prism-markup.min.js',
10
- 'prism-css.min.js',
11
- 'prism-clike.min.js',
12
- 'prism-javascript.min.js',
13
- 'prism-typescript.min.js',
14
- 'prism-json.min.js',
15
- 'prism-bash.min.js',
16
- 'prism-yaml.min.js',
17
- 'prism-markdown.min.js',
18
- 'prism-python.min.js',
19
- 'prism-rust.min.js',
20
- 'prism-go.min.js',
21
- 'prism-jsx.min.js',
22
- 'prism-tsx.min.js',
23
- 'prism-diff.min.js',
24
- 'prism-sql.min.js',
25
- 'prism-toml.min.js',
11
+ // Dependency tiers: each tier loads in parallel; the next tier waits for the previous.
12
+ // (clike must precede javascript; javascript must precede typescript/jsx/tsx.)
13
+ const PRISM_TIERS = [
14
+ ['prism-markup.min.js', 'prism-css.min.js', 'prism-clike.min.js'],
15
+ ['prism-javascript.min.js', 'prism-json.min.js', 'prism-bash.min.js', 'prism-yaml.min.js',
16
+ 'prism-markdown.min.js', 'prism-python.min.js', 'prism-rust.min.js', 'prism-go.min.js',
17
+ 'prism-diff.min.js', 'prism-sql.min.js', 'prism-toml.min.js'],
18
+ ['prism-typescript.min.js', 'prism-jsx.min.js'],
19
+ ['prism-tsx.min.js'],
26
20
  ];
27
21
 
28
- async function loadIntoGlobal(url) {
29
- try {
30
- const res = await fetch(url);
31
- if (!res.ok) throw new Error('http ' + res.status);
32
- const code = await res.text();
33
- // eslint-disable-next-line no-new-func
34
- new Function('window', code)(window);
35
- } catch (err) {
36
- console.warn('[247420] prism part failed:', url, err.message);
37
- }
22
+ function loadScript(url) {
23
+ return new Promise((resolve) => {
24
+ const existing = document.querySelector('script[src="' + url + '"]');
25
+ if (existing) { resolve(); return; }
26
+ const s = document.createElement('script');
27
+ s.src = url;
28
+ s.crossOrigin = 'anonymous';
29
+ s.onload = resolve;
30
+ s.onerror = () => { console.warn('[247420] prism part failed:', url); resolve(); };
31
+ document.head.appendChild(s);
32
+ });
38
33
  }
39
34
 
40
35
  export async function ensurePrism() {
@@ -42,11 +37,11 @@ export async function ensurePrism() {
42
37
  if (_ready) return _ready;
43
38
  _ready = (async () => {
44
39
  try {
45
- await loadIntoGlobal(PRISM_CORE);
40
+ await loadScript(PRISM_CORE);
46
41
  _prism = window.Prism || null;
47
42
  if (_prism) {
48
- for (const f of PRISM_LANGS) {
49
- await loadIntoGlobal(PRISM_BASE + f);
43
+ for (const tier of PRISM_TIERS) {
44
+ await Promise.all(tier.map(f => loadScript(PRISM_BASE + f)));
50
45
  }
51
46
  }
52
47
  return _prism;
package/src/markdown.js CHANGED
@@ -10,8 +10,12 @@ let _purify = null;
10
10
  let _failedAt = 0;
11
11
  const RETRY_BACKOFF_MS = 30000;
12
12
 
13
- const MARKED_URL = 'https://cdn.jsdelivr.net/npm/marked@15/+esm';
14
- const PURIFY_URL = 'https://cdn.jsdelivr.net/npm/dompurify@3/+esm';
13
+ // Pin to exact semver so the CDN cannot silently swap code under us.
14
+ // SRI cannot be applied to dynamic ESM imports in browsers (no importmap
15
+ // integrity support at design time); pinning the version is the best available
16
+ // mitigation for CDN-supply-chain risk on these two dependencies.
17
+ const MARKED_URL = 'https://cdn.jsdelivr.net/npm/marked@15.0.12/+esm';
18
+ const PURIFY_URL = 'https://cdn.jsdelivr.net/npm/dompurify@3.2.6/+esm';
15
19
 
16
20
  // True while the markdown stack is unavailable (escaped-fallback rendering).
17
21
  // Consumers (markdown-cache) use this to avoid caching degraded output.
@@ -50,7 +54,7 @@ export async function renderMarkdown(src) {
50
54
  const ok = await ensureReady();
51
55
  if (!ok) return escapeHtml(src).replace(/\n/g, '<br>');
52
56
  const raw = _marked.parse(String(src));
53
- return _purify.sanitize(raw);
57
+ return _purify.sanitize(raw, { FORCE_BODY: true });
54
58
  }
55
59
 
56
60
  // Sanitize already-rendered HTML before it touches innerHTML. For any surface
@@ -60,5 +64,5 @@ export async function renderMarkdown(src) {
60
64
  export async function sanitizeHtml(html) {
61
65
  const ok = await ensureReady();
62
66
  if (!ok) return escapeHtml(html);
63
- return _purify.sanitize(String(html));
67
+ return _purify.sanitize(String(html), { FORCE_BODY: true });
64
68
  }