@webmcp-auto-ui/ui 2.5.38 → 2.5.39

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": "@webmcp-auto-ui/ui",
3
- "version": "2.5.38",
3
+ "version": "2.5.39",
4
4
  "description": "Svelte 5 UI components — primitives, widgets, window manager",
5
5
  "license": "AGPL-3.0-or-later",
6
6
  "type": "module",
@@ -79,8 +79,11 @@ export async function render(container: HTMLElement, data: Record<string, unknow
79
79
  hidePublishBadge: (data as any).hidePublishBadge === true,
80
80
  } as any);
81
81
 
82
- // Default src visibility per mode (toggle remains usable for per-cell override).
83
- for (const c of state.cells) c.hideSource = state.mode === 'view';
82
+ // Default src + logs visibility per mode (toggle remains usable for per-cell override).
83
+ for (const c of state.cells) {
84
+ c.hideSource = state.mode === 'view';
85
+ if (state.mode === 'view' && c.hideLogs === undefined) c.hideLogs = true;
86
+ }
84
87
 
85
88
  // Live mode runtime overlay (created lazily). Never mutates state.
86
89
  let overlay: RuntimeOverlay | null = null;
@@ -282,7 +285,7 @@ export async function render(container: HTMLElement, data: Record<string, unknow
282
285
  const viewBtn = shell.querySelector('.nb-mode-view') as HTMLElement;
283
286
  editBtn.addEventListener('click', () => {
284
287
  state.mode = 'edit';
285
- for (const c of state.cells) c.hideSource = false;
288
+ for (const c of state.cells) { c.hideSource = false; c.hideLogs = false; }
286
289
  container.classList.remove('nb-view-mode');
287
290
  editBtn.classList.add('nb-on'); viewBtn.classList.remove('nb-on');
288
291
  // Leaving view: stop live refresh and clear overlay so frozen snapshots show.
@@ -291,7 +294,7 @@ export async function render(container: HTMLElement, data: Record<string, unknow
291
294
  });
292
295
  viewBtn.addEventListener('click', () => {
293
296
  state.mode = 'view';
294
- for (const c of state.cells) c.hideSource = true;
297
+ for (const c of state.cells) { c.hideSource = true; c.hideLogs = true; }
295
298
  container.classList.add('nb-view-mode');
296
299
  viewBtn.classList.add('nb-on'); editBtn.classList.remove('nb-on');
297
300
  if (state.autoRun === true) bootstrapLive();
@@ -454,17 +457,25 @@ function renderCell(cell: NotebookCell, state: NotebookState, overlay: RuntimeOv
454
457
  liveBadge = `<span class="nbe-cell-badge nbe-cell-frozen" title="JS cells are not re-executed in live mode">frozen</span>`;
455
458
  }
456
459
  }
460
+ const lastRes = effectiveResult(cell, overlay) ?? cell.lastResult;
461
+ const logCount = (lastRes?.logs as string[] | undefined)?.length ?? 0;
462
+ const logsToggle = logCount > 0
463
+ ? `<button class="nb-icon-btn nb-toggle-logs" title="toggle console">${cell.hideLogs ? '▸' : '▾'} ${logCount}</button>`
464
+ : '';
457
465
  head.innerHTML = `
458
466
  <span class="nbe-run-controls"></span>
459
467
  <span class="nbe-type-${cell.type}">${cell.type}</span>
460
468
  <span class="nbe-meta-info">${escapeHtml(metaInfoFor(cell, overlay))}</span>
461
469
  ${liveBadge}
462
470
  <div class="nbe-actions">
471
+ ${logsToggle}
463
472
  <button class="nb-icon-btn nb-toggle-src">${cell.hideSource ? '▸ src' : '◂ src'}</button>
464
473
  <button class="nb-icon-btn nb-toggle-res">${cell.hideResult ? '▸ res' : '◂ res'}</button>
465
474
  </div>`;
466
475
  codeCell.appendChild(head);
467
476
  mountRunControls(head.querySelector('.nbe-run-controls') as HTMLElement, cell, wrap, state, rerender);
477
+ const logsBtn = head.querySelector('.nb-toggle-logs') as HTMLElement | null;
478
+ if (logsBtn) logsBtn.addEventListener('click', () => { cell.hideLogs = !cell.hideLogs; rerender(); });
468
479
 
469
480
  const body = document.createElement('div');
470
481
  body.className = 'nbe-code-body' + (cell.hideSource ? ' nbe-hidden' : '');
@@ -847,12 +858,25 @@ function renderResultInto(el: HTMLElement, cell: NotebookCell, overlay: RuntimeO
847
858
  const r = effectiveResult(cell, overlay) ?? cell.lastResult;
848
859
  el.innerHTML = '';
849
860
  if (!r) {
850
- el.innerHTML = `<div class="nbe-result-empty">press to run</div>`;
861
+ const isView = stateRef?.mode === 'view';
862
+ const rtStatus = cellRuntimeStatus(cell, overlay);
863
+ // In view mode (autoRun), any unresolved cell is effectively loading —
864
+ // the auto-runner will pick it up shortly. Show a continuous spinner so
865
+ // users don't see a static "—" placeholder during the idle→running gap.
866
+ if (isView && rtStatus !== 'frozen') {
867
+ el.innerHTML = `<div class="nbe-result-running"><span class="nbe-spinner"></span> running</div>`;
868
+ } else if (rtStatus === 'running') {
869
+ el.innerHTML = `<div class="nbe-result-running"><span class="nbe-spinner"></span> running</div>`;
870
+ } else {
871
+ el.innerHTML = `<div class="nbe-result-empty">press ▶ to run</div>`;
872
+ }
851
873
  return;
852
874
  }
853
875
  // Logs panel (shared across all widgets), prepended above the main result
854
- const logsEl = renderCellLogs(r);
855
- if (logsEl) el.appendChild(logsEl);
876
+ if (!cell.hideLogs) {
877
+ const logsEl = renderCellLogs(r);
878
+ if (logsEl) el.appendChild(logsEl);
879
+ }
856
880
  if (!r.ok) {
857
881
  const err = document.createElement('div');
858
882
  err.className = 'nbe-result-error';
@@ -1080,6 +1104,30 @@ function injectLayoutStyles(): void {
1080
1104
  .nbe-code-body { padding: 14px 16px; }
1081
1105
  .nbe-hidden { display: none !important; }
1082
1106
 
1107
+ /* View-mode Option B: chrome hidden by default, revealed on hover in top-right corner. */
1108
+ .nb-root.nb-view-mode .nbe-code-cell { position: relative; min-height: 28px; }
1109
+ .nb-root.nb-view-mode .nbe-cell-head {
1110
+ position: absolute; top: 0; right: 0; z-index: 2;
1111
+ padding: 4px 8px; gap: 6px;
1112
+ background: var(--color-surface2);
1113
+ border: 1px solid var(--color-border);
1114
+ border-top: none; border-right: none;
1115
+ border-bottom-left-radius: 6px;
1116
+ opacity: 0; pointer-events: none;
1117
+ transition: opacity 120ms ease;
1118
+ font-size: 9.5px;
1119
+ }
1120
+ .nb-root.nb-view-mode .nbe-code-cell:hover .nbe-cell-head,
1121
+ .nb-root.nb-view-mode .nbe-cell-head:focus-within {
1122
+ opacity: 0.95; pointer-events: auto;
1123
+ }
1124
+ .nb-root.nb-view-mode .nbe-cell-head .nbe-meta-info,
1125
+ .nb-root.nb-view-mode .nbe-cell-head .nbe-type-sql,
1126
+ .nb-root.nb-view-mode .nbe-cell-head .nbe-type-js,
1127
+ .nb-root.nb-view-mode .nbe-cell-head .nbe-type-md { display: none; }
1128
+ .nb-root.nb-view-mode .nbe-code-body { padding: 10px 14px; }
1129
+ .nb-root.nb-view-mode .nbe-result { padding: 8px 14px; }
1130
+
1083
1131
  .nbe-result {
1084
1132
  background: var(--color-bg);
1085
1133
  border-top: 1px solid var(--color-border);
@@ -1090,6 +1138,10 @@ function injectLayoutStyles(): void {
1090
1138
  .nbe-result-empty {
1091
1139
  color: var(--color-text2); font-style: italic; font-size: 11.5px;
1092
1140
  }
1141
+ .nbe-result-running {
1142
+ display: inline-flex; align-items: center; gap: 6px;
1143
+ color: #2ea043; font-size: 11.5px;
1144
+ }
1093
1145
  .nbe-result-error {
1094
1146
  color: var(--color-accent2); white-space: pre-wrap; font-size: 12px;
1095
1147
  }
@@ -4,7 +4,7 @@
4
4
  // 4 formats: JSON, Markdown, Hyperskill link (+ short), PNG snapshot.
5
5
  // ---------------------------------------------------------------------------
6
6
 
7
- import { encode, buildShortUrl } from '@webmcp-auto-ui/sdk';
7
+ import { encode, buildShortUrl, pickFence } from '@webmcp-auto-ui/sdk';
8
8
  import { canvasVanilla } from '@webmcp-auto-ui/sdk/canvas-vanilla';
9
9
  import type { NotebookState, NotebookCell } from './shared.js';
10
10
 
@@ -56,7 +56,9 @@ export function serializeToMarkdown(state: NotebookState): string {
56
56
  const metaLine = cell.args && Object.keys(cell.args).length > 0
57
57
  ? `${commentPrefix} @meta ${JSON.stringify(cell.args)}\n`
58
58
  : '';
59
- parts.push('```' + lang + varname, metaLine + cell.content.trim(), '```', '');
59
+ const body = metaLine + cell.content.trim();
60
+ const fence = pickFence(body);
61
+ parts.push(fence + lang + varname, body, fence, '');
60
62
  }
61
63
  }
62
64
  return parts.join('\n').trim() + '\n';
@@ -47,6 +47,8 @@ export interface NotebookCell {
47
47
  varname?: string; // named output (compact)
48
48
  hideSource?: boolean;
49
49
  hideResult?: boolean;
50
+ /** view-mode: collapse the console logs panel under the head bar. Default true in view mode. */
51
+ hideLogs?: boolean;
50
52
  runState?: RunState;
51
53
  lastMs?: number;
52
54
  status?: 'fresh' | 'stale';
@@ -1248,8 +1250,6 @@ const NOTEBOOK_STYLES = `
1248
1250
 
1249
1251
  .nb-root.nb-view-mode .nb-drag-handle,
1250
1252
  .nb-root.nb-view-mode .nb-icon-btn.nb-danger,
1251
- .nb-root.nb-view-mode .nb-toggle-src,
1252
- .nb-root.nb-view-mode .nb-toggle-res,
1253
1253
  .nb-root.nb-view-mode .nb-add-cell,
1254
1254
  .nb-root.nb-view-mode .nbe-cell-actionbar { display: none !important; }
1255
1255