@webmcp-auto-ui/agent 2.5.26 → 2.5.27
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 +1 -1
- package/src/autoui-server.ts +27 -0
- package/src/index.ts +20 -6
- package/src/loop.ts +4 -12
- package/src/notebook-widgets/compact.ts +312 -0
- package/src/notebook-widgets/document.ts +372 -0
- package/src/notebook-widgets/editorial.ts +348 -0
- package/src/notebook-widgets/recipes/compact.md +104 -0
- package/src/notebook-widgets/recipes/document.md +100 -0
- package/src/notebook-widgets/recipes/editorial.md +104 -0
- package/src/notebook-widgets/recipes/workspace.md +94 -0
- package/src/notebook-widgets/shared.ts +1064 -0
- package/src/notebook-widgets/workspace.ts +328 -0
- package/src/prompts/claude-prompt-builder.ts +81 -0
- package/src/prompts/gemma4-prompt-builder.ts +205 -0
- package/src/prompts/index.ts +55 -0
- package/src/prompts/mistral-prompt-builder.ts +90 -0
- package/src/prompts/qwen-prompt-builder.ts +90 -0
- package/src/prompts/tool-call-parsers.ts +322 -0
- package/src/prompts/tool-refs.ts +196 -0
- package/src/providers/factory.ts +20 -3
- package/src/providers/transformers-models.ts +143 -0
- package/src/providers/transformers-serialize.ts +81 -0
- package/src/providers/transformers.ts +329 -0
- package/src/providers/transformers.worker.ts +667 -0
- package/src/providers/wasm.ts +132 -332
- package/src/recipes/_generated.ts +242 -0
- package/src/recipes/hackathon-assemblee-nationale.md +111 -0
- package/src/recipes/notebook-playbook.md +129 -0
- package/src/tool-layers.ts +7 -403
- package/src/trace-observer.ts +669 -0
- package/src/types.ts +17 -7
- package/src/util/opfs-cache.ts +265 -0
- package/tests/gemma-prompt.test.ts +472 -0
- package/tests/loop.test.ts +3 -3
- package/tests/transformers-serialize.test.ts +103 -0
package/package.json
CHANGED
package/src/autoui-server.ts
CHANGED
|
@@ -5,6 +5,22 @@
|
|
|
5
5
|
import { createWebMcpServer, parseFrontmatter } from '@webmcp-auto-ui/core';
|
|
6
6
|
import { RAW_RECIPES } from './recipes/_generated.js';
|
|
7
7
|
|
|
8
|
+
// Notebook widget recipes (vanilla renderers)
|
|
9
|
+
// @ts-ignore — Vite raw imports, not resolved by tsc
|
|
10
|
+
import compactRecipe from './notebook-widgets/recipes/compact.md?raw';
|
|
11
|
+
// @ts-ignore
|
|
12
|
+
import workspaceRecipe from './notebook-widgets/recipes/workspace.md?raw';
|
|
13
|
+
// @ts-ignore
|
|
14
|
+
import documentRecipe from './notebook-widgets/recipes/document.md?raw';
|
|
15
|
+
// @ts-ignore
|
|
16
|
+
import editorialRecipe from './notebook-widgets/recipes/editorial.md?raw';
|
|
17
|
+
|
|
18
|
+
// Notebook widget renderers (vanilla JS)
|
|
19
|
+
import { render as renderCompact } from './notebook-widgets/compact.js';
|
|
20
|
+
import { render as renderWorkspace } from './notebook-widgets/workspace.js';
|
|
21
|
+
import { render as renderDocument } from './notebook-widgets/document.js';
|
|
22
|
+
import { render as renderEditorial } from './notebook-widgets/editorial.js';
|
|
23
|
+
|
|
8
24
|
// ---------------------------------------------------------------------------
|
|
9
25
|
// Inline recipes (frontmatter + body)
|
|
10
26
|
// ---------------------------------------------------------------------------
|
|
@@ -1008,6 +1024,17 @@ for (const recipe of RECIPES) {
|
|
|
1008
1024
|
autoui.registerWidget(recipe, undefined);
|
|
1009
1025
|
}
|
|
1010
1026
|
|
|
1027
|
+
// Notebook widgets — vanilla renderers (resolved via WidgetRenderer vanilla path)
|
|
1028
|
+
const NOTEBOOK_WIDGETS: Array<[string, (container: HTMLElement, data: any) => any]> = [
|
|
1029
|
+
[compactRecipe as string, renderCompact],
|
|
1030
|
+
[workspaceRecipe as string, renderWorkspace],
|
|
1031
|
+
[documentRecipe as string, renderDocument],
|
|
1032
|
+
[editorialRecipe as string, renderEditorial],
|
|
1033
|
+
];
|
|
1034
|
+
for (const [recipe, renderer] of NOTEBOOK_WIDGETS) {
|
|
1035
|
+
autoui.registerWidget(recipe, renderer as any);
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1011
1038
|
// Register flow recipes (multi-step procedures) from the global recipe registry
|
|
1012
1039
|
// that declare this server (autoui) in their frontmatter.
|
|
1013
1040
|
for (const [key, rawMd] of Object.entries(RAW_RECIPES)) {
|
package/src/index.ts
CHANGED
|
@@ -3,8 +3,18 @@
|
|
|
3
3
|
// Providers
|
|
4
4
|
export { RemoteLLMProvider } from './providers/remote.js';
|
|
5
5
|
export type { RemoteLLMProviderOptions } from './providers/remote.js';
|
|
6
|
-
export { WasmProvider
|
|
7
|
-
export type { WasmProviderOptions, WasmStatus
|
|
6
|
+
export { WasmProvider } from './providers/wasm.js';
|
|
7
|
+
export type { WasmProviderOptions, WasmStatus } from './providers/wasm.js';
|
|
8
|
+
export { TransformersProvider } from './providers/transformers.js';
|
|
9
|
+
export type { TransformersProviderOptions, TransformersStatus } from './providers/transformers.js';
|
|
10
|
+
export { TRANSFORMERS_MODELS, getTransformersModel, listTransformersModels } from './providers/transformers-models.js';
|
|
11
|
+
export type { TransformersModelEntry, TransformersFamily, ToolCallFormat } from './providers/transformers-models.js';
|
|
12
|
+
export { parseToolCalls } from './prompts/tool-call-parsers.js';
|
|
13
|
+
export type { ParseResult } from './prompts/tool-call-parsers.js';
|
|
14
|
+
export { loadOrDownloadModel, clearModelCache } from './util/opfs-cache.js';
|
|
15
|
+
export type { ModelFileSpec, CacheProgress } from './util/opfs-cache.js';
|
|
16
|
+
export { buildGemmaPrompt } from './prompts/index.js';
|
|
17
|
+
export type { BuildGemmaPromptInput } from './prompts/index.js';
|
|
8
18
|
export { LocalLLMProvider } from './providers/local.js';
|
|
9
19
|
export type { LocalLLMProviderOptions, LocalBackend } from './providers/local.js';
|
|
10
20
|
export { createProvider } from './providers/factory.js';
|
|
@@ -18,15 +28,16 @@ export type { GemmaProviderOptions, GemmaStatus } from './providers/gemma.js';
|
|
|
18
28
|
|
|
19
29
|
// Agent loop
|
|
20
30
|
export { runAgentLoop, toProviderTools, fromMcpTools, trimConversationHistory } from './loop.js';
|
|
21
|
-
export { buildSystemPrompt } from './
|
|
31
|
+
export { buildSystemPrompt, buildSystemPromptWithAliases } from './prompts/index.js';
|
|
32
|
+
export type { SystemPromptResult } from './prompts/index.js';
|
|
22
33
|
export type { AgentLoopOptions } from './loop.js';
|
|
23
34
|
|
|
24
35
|
// autoui — built-in WebMCP server
|
|
25
36
|
export { autoui, NATIVE_WIDGET_NAMES } from './autoui-server.js';
|
|
26
37
|
|
|
27
38
|
// Tool layers
|
|
28
|
-
export { buildToolsFromLayers, buildDiscoveryTools, buildDiscoveryToolsWithAliases, activateServerTools, resolveCanonicalTools, toolAliasMap,
|
|
29
|
-
export type { ToolLayer, McpLayer, WebMcpLayer,
|
|
39
|
+
export { buildToolsFromLayers, buildDiscoveryTools, buildDiscoveryToolsWithAliases, activateServerTools, resolveCanonicalTools, toolAliasMap, flattenPathMaps, buildDiscoveryCache } from './tool-layers.js';
|
|
40
|
+
export type { ToolLayer, McpLayer, WebMcpLayer, DiscoveryToolsResult, SchemaTransformOptions, BuildToolsResult, ProviderKind } from './tool-layers.js';
|
|
30
41
|
|
|
31
42
|
// Discovery cache
|
|
32
43
|
export { DiscoveryCache, DISCOVERY_TOOL_NAMES } from './discovery-cache.js';
|
|
@@ -63,12 +74,15 @@ export type { RepairResult } from './auto-repair.js';
|
|
|
63
74
|
// Pipeline trace
|
|
64
75
|
export { PipelineTrace, type TraceEntry } from './pipeline-trace.js';
|
|
65
76
|
|
|
77
|
+
// Trace observer — live visual trace for runAgentLoop
|
|
78
|
+
export { createTraceObserver, type TraceObserver, type TraceObserverContext, type RoundTripDetail } from './trace-observer.js';
|
|
79
|
+
|
|
66
80
|
// Nano-RAG — context compaction
|
|
67
81
|
export { ContextRAG, type ContextRAGOptions } from './nano-rag/mod.js';
|
|
68
82
|
|
|
69
83
|
// Types
|
|
70
84
|
export type {
|
|
71
|
-
RemoteModelId, WasmModelId, LLMId, ModelId,
|
|
85
|
+
RemoteModelId, WasmModelId, TransformersModelId, LLMId, ModelId,
|
|
72
86
|
ChatMessage, ContentBlock, McpToolDef, ProviderTool,
|
|
73
87
|
LLMProvider, LLMResponse, ToolCall, AgentMetrics, AgentResult, AgentCallbacks,
|
|
74
88
|
Recipe, McpRecipe,
|
package/src/loop.ts
CHANGED
|
@@ -9,7 +9,8 @@ import type {
|
|
|
9
9
|
LLMProvider, ProviderTool, McpToolDef, AgentCallbacks,
|
|
10
10
|
} from './types.js';
|
|
11
11
|
import type { ToolLayer, SchemaTransformOptions } from './tool-layers.js';
|
|
12
|
-
import { buildToolsFromLayers,
|
|
12
|
+
import { buildToolsFromLayers, buildDiscoveryToolsWithAliases, activateServerTools, toProviderTools, sanitizeServerName, flattenPathMaps } from './tool-layers.js';
|
|
13
|
+
import { buildSystemPromptWithAliases, buildSystemPrompt } from './prompts/index.js';
|
|
13
14
|
import type { DiscoveryCache } from './discovery-cache.js';
|
|
14
15
|
import { unflattenParams, validateJsonSchema } from '@webmcp-auto-ui/core';
|
|
15
16
|
import type { JsonSchema } from '@webmcp-auto-ui/core';
|
|
@@ -17,7 +18,7 @@ import { autoRepairParams } from './auto-repair.js';
|
|
|
17
18
|
import { PipelineTrace } from './pipeline-trace.js';
|
|
18
19
|
|
|
19
20
|
// Re-export buildSystemPrompt for backward compat
|
|
20
|
-
export { buildSystemPrompt } from './
|
|
21
|
+
export { buildSystemPrompt } from './prompts/index.js';
|
|
21
22
|
|
|
22
23
|
const MAX_RESULT_LEN = 10_000;
|
|
23
24
|
|
|
@@ -122,7 +123,7 @@ export interface AgentLoopOptions {
|
|
|
122
123
|
}
|
|
123
124
|
|
|
124
125
|
export async function runAgentLoop(
|
|
125
|
-
userMessage: string,
|
|
126
|
+
userMessage: string | ContentBlock[],
|
|
126
127
|
options: AgentLoopOptions
|
|
127
128
|
): Promise<AgentResult> {
|
|
128
129
|
const {
|
|
@@ -209,7 +210,6 @@ export async function runAgentLoop(
|
|
|
209
210
|
const allToolCalls: ToolCall[] = [];
|
|
210
211
|
let lastText = '';
|
|
211
212
|
let finishedNormally = false;
|
|
212
|
-
let discoveryPhase = false;
|
|
213
213
|
let iterationsWithoutRender = 0;
|
|
214
214
|
let nudgedOnce = false;
|
|
215
215
|
let hasRendered = false;
|
|
@@ -320,14 +320,6 @@ export async function runAgentLoop(
|
|
|
320
320
|
const toolResults: ContentBlock[] = [];
|
|
321
321
|
for (const block of toolBlocks) {
|
|
322
322
|
const call: ToolCall = { id: block.id, name: block.name, args: block.input };
|
|
323
|
-
const wasDiscovering = discoveryPhase;
|
|
324
|
-
if (isDiscoveryTool(block.name)) {
|
|
325
|
-
discoveryPhase = true;
|
|
326
|
-
call.guided = false;
|
|
327
|
-
} else {
|
|
328
|
-
call.guided = wasDiscovering;
|
|
329
|
-
discoveryPhase = false;
|
|
330
|
-
}
|
|
331
323
|
const t1 = performance.now();
|
|
332
324
|
|
|
333
325
|
try {
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
// ---------------------------------------------------------------------------
|
|
3
|
+
// notebook-compact — reactive minimalist layout (marimo-like)
|
|
4
|
+
// Left gutter with type label + vertical line, named outputs, fresh/stale status.
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
createState, injectStyles, mountRunControls, mountHistoryPanel,
|
|
9
|
+
setupDnD, deleteCellWithConfirm, restoreCellFromSnapshot, addCell,
|
|
10
|
+
logHistory, autosize, openShareModal, registerHistoryObserver,
|
|
11
|
+
buildServersButton,
|
|
12
|
+
type NotebookState, type NotebookCell,
|
|
13
|
+
} from './shared.js';
|
|
14
|
+
|
|
15
|
+
export async function render(container: HTMLElement, data: Record<string, unknown>): Promise<() => void> {
|
|
16
|
+
injectStyles();
|
|
17
|
+
injectLayoutStyles();
|
|
18
|
+
|
|
19
|
+
const state: NotebookState = createState({
|
|
20
|
+
id: data.id as string,
|
|
21
|
+
title: data.title as string,
|
|
22
|
+
mode: (data.mode as any) ?? 'edit',
|
|
23
|
+
cells: data.cells as any,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
container.classList.add('nb-root');
|
|
27
|
+
container.classList.toggle('nb-view-mode', state.mode === 'view');
|
|
28
|
+
|
|
29
|
+
container.innerHTML = `
|
|
30
|
+
<div class="nbc-shell">
|
|
31
|
+
<div class="nbc-toolbar">
|
|
32
|
+
<div class="nbc-status">
|
|
33
|
+
<span class="nbc-status-dot"></span>
|
|
34
|
+
<span class="nbc-status-text">reactive · 0 cells</span>
|
|
35
|
+
</div>
|
|
36
|
+
<div class="nbc-actions">
|
|
37
|
+
<div class="nb-mode-switch">
|
|
38
|
+
<button class="nb-mode-edit nb-on">edit</button>
|
|
39
|
+
<button class="nb-mode-view">view</button>
|
|
40
|
+
</div>
|
|
41
|
+
<button class="nb-btn nb-add-cell" data-add="md">+ md</button>
|
|
42
|
+
<button class="nb-btn nb-add-cell" data-add="sql">+ sql</button>
|
|
43
|
+
<button class="nb-btn nb-add-cell" data-add="js">+ js</button>
|
|
44
|
+
<button class="nb-btn nbc-history-btn">⟲ history</button>
|
|
45
|
+
<span class="nbc-servers-slot"></span>
|
|
46
|
+
<button class="nb-btn nbc-share-btn">share</button>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
<div class="nb-history-panel nbc-history-panel"></div>
|
|
50
|
+
<div class="nbc-cells"></div>
|
|
51
|
+
</div>`;
|
|
52
|
+
|
|
53
|
+
const shell = container.querySelector('.nbc-shell') as HTMLElement;
|
|
54
|
+
const cellsEl = shell.querySelector('.nbc-cells') as HTMLElement;
|
|
55
|
+
const historyPanel = shell.querySelector('.nbc-history-panel') as HTMLElement;
|
|
56
|
+
|
|
57
|
+
function renderCells() {
|
|
58
|
+
cellsEl.innerHTML = '';
|
|
59
|
+
state.cells.forEach((cell) => cellsEl.appendChild(renderCell(cell, state, rerender)));
|
|
60
|
+
updateStatus();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function updateStatus() {
|
|
64
|
+
const n = state.cells.length;
|
|
65
|
+
const stale = state.cells.filter((c) => c.status === 'stale').length;
|
|
66
|
+
(shell.querySelector('.nbc-status-text') as HTMLElement).textContent =
|
|
67
|
+
stale > 0 ? `reactive · ${n} cells · ${stale} stale` : `reactive · ${n} cells · synced`;
|
|
68
|
+
(shell.querySelector('.nbc-status-dot') as HTMLElement).classList.toggle('nbc-stale', stale > 0);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function rerender() {
|
|
72
|
+
mountHistoryPanel(historyPanel, state, (snap) => { restoreCellFromSnapshot(state, snap); rerender(); });
|
|
73
|
+
renderCells();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Toolbar bindings
|
|
77
|
+
shell.querySelectorAll<HTMLElement>('[data-add]').forEach((btn) => {
|
|
78
|
+
btn.addEventListener('click', () => {
|
|
79
|
+
const type = btn.dataset.add as any;
|
|
80
|
+
addCell(state, type, { varname: type === 'sql' ? 'rows_' + (state.cells.length + 1) : undefined });
|
|
81
|
+
rerender();
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
(shell.querySelector('.nbc-history-btn') as HTMLElement).addEventListener('click', () => {
|
|
85
|
+
historyPanel.classList.toggle('nb-open');
|
|
86
|
+
});
|
|
87
|
+
(shell.querySelector('.nbc-share-btn') as HTMLElement).addEventListener('click', () => {
|
|
88
|
+
openShareModal(state, (fmt) => console.log('[notebook-compact] share as', fmt, state));
|
|
89
|
+
});
|
|
90
|
+
const editBtn = shell.querySelector('.nb-mode-edit') as HTMLElement;
|
|
91
|
+
const viewBtn = shell.querySelector('.nb-mode-view') as HTMLElement;
|
|
92
|
+
editBtn.addEventListener('click', () => {
|
|
93
|
+
state.mode = 'edit';
|
|
94
|
+
container.classList.remove('nb-view-mode');
|
|
95
|
+
editBtn.classList.add('nb-on'); viewBtn.classList.remove('nb-on');
|
|
96
|
+
});
|
|
97
|
+
viewBtn.addEventListener('click', () => {
|
|
98
|
+
state.mode = 'view';
|
|
99
|
+
container.classList.add('nb-view-mode');
|
|
100
|
+
viewBtn.classList.add('nb-on'); editBtn.classList.remove('nb-on');
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
buildServersButton(state, shell.querySelector('.nbc-servers-slot') as HTMLElement, data, rerender);
|
|
104
|
+
|
|
105
|
+
setupDnD(cellsEl, state, rerender);
|
|
106
|
+
const unsubHistory = registerHistoryObserver(() => mountHistoryPanel(historyPanel, state, (snap) => { restoreCellFromSnapshot(state, snap); rerender(); }));
|
|
107
|
+
|
|
108
|
+
rerender();
|
|
109
|
+
|
|
110
|
+
return () => { unsubHistory(); };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
// Cell rendering (compact layout)
|
|
115
|
+
// ---------------------------------------------------------------------------
|
|
116
|
+
|
|
117
|
+
function renderCell(cell: NotebookCell, state: NotebookState, rerender: () => void): HTMLElement {
|
|
118
|
+
const wrap = document.createElement('div');
|
|
119
|
+
wrap.className = 'nb-cell-wrapper nbc-cell';
|
|
120
|
+
wrap.dataset.id = cell.id;
|
|
121
|
+
|
|
122
|
+
const row = document.createElement('div');
|
|
123
|
+
row.className = 'nbc-row';
|
|
124
|
+
|
|
125
|
+
const handle = document.createElement('span');
|
|
126
|
+
handle.className = 'nb-drag-handle';
|
|
127
|
+
handle.draggable = true;
|
|
128
|
+
handle.textContent = '⋮⋮';
|
|
129
|
+
row.appendChild(handle);
|
|
130
|
+
|
|
131
|
+
const gutter = document.createElement('div');
|
|
132
|
+
gutter.className = `nbc-gutter nbc-gutter-${cell.type}`;
|
|
133
|
+
gutter.innerHTML = `<span class="nbc-type-label">${cell.type}</span><span class="nbc-line"></span>`;
|
|
134
|
+
row.appendChild(gutter);
|
|
135
|
+
|
|
136
|
+
const body = document.createElement('div');
|
|
137
|
+
body.className = 'nbc-body';
|
|
138
|
+
body.style.minWidth = '0';
|
|
139
|
+
|
|
140
|
+
if (cell.type === 'md') {
|
|
141
|
+
const mdBody = document.createElement('div');
|
|
142
|
+
mdBody.className = 'nbc-md-body';
|
|
143
|
+
const ta = document.createElement('textarea');
|
|
144
|
+
ta.className = 'nb-md-edit';
|
|
145
|
+
ta.value = cell.content;
|
|
146
|
+
ta.rows = 2;
|
|
147
|
+
ta.placeholder = 'write markdown…';
|
|
148
|
+
ta.addEventListener('input', () => { cell.content = ta.value; autosize(ta); });
|
|
149
|
+
mdBody.appendChild(ta);
|
|
150
|
+
body.appendChild(mdBody);
|
|
151
|
+
setTimeout(() => autosize(ta), 0);
|
|
152
|
+
|
|
153
|
+
const del = document.createElement('button');
|
|
154
|
+
del.className = 'nb-icon-btn nb-danger nbc-md-del';
|
|
155
|
+
del.textContent = '✕';
|
|
156
|
+
del.title = 'delete cell';
|
|
157
|
+
del.addEventListener('click', () => deleteCellWithConfirm(state, cell, (c) => 'markdown cell', rerender));
|
|
158
|
+
wrap.appendChild(del);
|
|
159
|
+
} else {
|
|
160
|
+
const codeCell = document.createElement('div');
|
|
161
|
+
codeCell.className = 'nb-code-cell nbc-code-cell';
|
|
162
|
+
|
|
163
|
+
// Cell title row with run controls FIRST (left), then meta
|
|
164
|
+
const titleRow = document.createElement('div');
|
|
165
|
+
titleRow.className = 'nbc-title-row';
|
|
166
|
+
titleRow.innerHTML = `
|
|
167
|
+
<span class="nbc-run-controls"></span>
|
|
168
|
+
${cell.varname ? `<span class="nbc-arrow-var">→ ${cell.varname}</span>` : ''}
|
|
169
|
+
<span class="nbc-meta-info">${cell.type === 'sql' ? '4 rows' : 'depends on rows'}</span>
|
|
170
|
+
<button class="nb-icon-btn nb-toggle-src">${cell.hideSource ? '▸ src' : '◂ src'}</button>
|
|
171
|
+
<button class="nb-icon-btn nb-toggle-res">${cell.hideResult ? '▸ res' : '◂ res'}</button>
|
|
172
|
+
<button class="nb-icon-btn nb-danger nbc-code-del">✕</button>
|
|
173
|
+
`;
|
|
174
|
+
codeCell.appendChild(titleRow);
|
|
175
|
+
mountRunControls(titleRow.querySelector('.nbc-run-controls') as HTMLElement, cell, wrap, rerender);
|
|
176
|
+
|
|
177
|
+
const codeBody = document.createElement('div');
|
|
178
|
+
codeBody.className = 'nbc-code-body' + (cell.hideSource ? ' nbc-hidden' : '');
|
|
179
|
+
const ta = document.createElement('textarea');
|
|
180
|
+
ta.className = 'nb-code-edit';
|
|
181
|
+
ta.value = cell.content;
|
|
182
|
+
ta.rows = 1;
|
|
183
|
+
ta.spellcheck = false;
|
|
184
|
+
ta.addEventListener('input', () => { cell.content = ta.value; autosize(ta); cell.status = 'stale'; });
|
|
185
|
+
codeBody.appendChild(ta);
|
|
186
|
+
codeCell.appendChild(codeBody);
|
|
187
|
+
setTimeout(() => autosize(ta), 0);
|
|
188
|
+
|
|
189
|
+
const result = document.createElement('div');
|
|
190
|
+
result.className = 'nbc-result-body' + (cell.hideResult ? ' nbc-hidden' : '');
|
|
191
|
+
if (cell.type === 'sql') {
|
|
192
|
+
result.innerHTML = `
|
|
193
|
+
<div class="nbc-result-row">
|
|
194
|
+
<span>row_1</span><span>42</span>
|
|
195
|
+
<span>row_2</span><span>17</span>
|
|
196
|
+
<span>row_3</span><span>8</span>
|
|
197
|
+
<span>row_4</span><span>3</span>
|
|
198
|
+
</div>`;
|
|
199
|
+
} else {
|
|
200
|
+
result.className = 'nbc-chart-result' + (cell.hideResult ? ' nbc-hidden' : '');
|
|
201
|
+
result.innerHTML = `
|
|
202
|
+
<div class="nbc-bar" style="height:100%"></div>
|
|
203
|
+
<div class="nbc-bar" style="height:68%"></div>
|
|
204
|
+
<div class="nbc-bar" style="height:52%"></div>
|
|
205
|
+
<div class="nbc-bar" style="height:22%"></div>`;
|
|
206
|
+
}
|
|
207
|
+
codeCell.appendChild(result);
|
|
208
|
+
body.appendChild(codeCell);
|
|
209
|
+
|
|
210
|
+
(titleRow.querySelector('.nb-toggle-src') as HTMLElement).addEventListener('click', () => { cell.hideSource = !cell.hideSource; rerender(); });
|
|
211
|
+
(titleRow.querySelector('.nb-toggle-res') as HTMLElement).addEventListener('click', () => { cell.hideResult = !cell.hideResult; rerender(); });
|
|
212
|
+
(titleRow.querySelector('.nbc-code-del') as HTMLElement).addEventListener('click', () =>
|
|
213
|
+
deleteCellWithConfirm(state, cell, (c) => `${c.type} cell${c.varname ? ' → ' + c.varname : ''}`, rerender)
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
row.appendChild(body);
|
|
218
|
+
wrap.appendChild(row);
|
|
219
|
+
return wrap;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// ---------------------------------------------------------------------------
|
|
223
|
+
// Layout-specific styles
|
|
224
|
+
// ---------------------------------------------------------------------------
|
|
225
|
+
|
|
226
|
+
function injectLayoutStyles(): void {
|
|
227
|
+
if (document.getElementById('nbc-styles')) return;
|
|
228
|
+
const style = document.createElement('style');
|
|
229
|
+
style.id = 'nbc-styles';
|
|
230
|
+
style.textContent = `
|
|
231
|
+
.nbc-shell {
|
|
232
|
+
background: var(--color-surface); border: 1px solid var(--color-border);
|
|
233
|
+
border-radius: 12px; padding: 18px;
|
|
234
|
+
}
|
|
235
|
+
.nbc-toolbar {
|
|
236
|
+
display: flex; align-items: center; justify-content: space-between; margin-bottom: 20px;
|
|
237
|
+
}
|
|
238
|
+
.nbc-status {
|
|
239
|
+
font-family: var(--font-mono, 'IBM Plex Mono', monospace);
|
|
240
|
+
font-size: 11px; color: var(--color-text2);
|
|
241
|
+
display: inline-flex; align-items: center; gap: 8px;
|
|
242
|
+
}
|
|
243
|
+
.nbc-status-dot {
|
|
244
|
+
width: 6px; height: 6px; border-radius: 50%;
|
|
245
|
+
background: var(--color-teal);
|
|
246
|
+
}
|
|
247
|
+
.nbc-status-dot.nbc-stale { background: var(--color-amber); }
|
|
248
|
+
.nbc-actions { display: flex; gap: 6px; align-items: center; }
|
|
249
|
+
.nbc-history-panel { margin-bottom: 12px; }
|
|
250
|
+
|
|
251
|
+
.nbc-cell { margin-bottom: 14px; position: relative; }
|
|
252
|
+
.nbc-cell:last-child { margin-bottom: 0; }
|
|
253
|
+
.nbc-row {
|
|
254
|
+
display: grid; grid-template-columns: 20px 34px 1fr; gap: 6px;
|
|
255
|
+
}
|
|
256
|
+
.nbc-gutter {
|
|
257
|
+
display: flex; flex-direction: column; align-items: center; gap: 6px;
|
|
258
|
+
}
|
|
259
|
+
.nbc-type-label {
|
|
260
|
+
font-family: var(--font-mono, 'IBM Plex Mono', monospace);
|
|
261
|
+
font-size: 10px; letter-spacing: 0.12em; color: var(--color-text2);
|
|
262
|
+
}
|
|
263
|
+
.nbc-gutter-sql .nbc-type-label { color: var(--color-accent); }
|
|
264
|
+
.nbc-gutter-js .nbc-type-label { color: var(--color-teal); }
|
|
265
|
+
.nbc-line { width: 1px; flex: 1; background: var(--color-border); }
|
|
266
|
+
.nbc-md-body { padding: 4px 2px; border: 1px dashed transparent; border-radius: 4px; }
|
|
267
|
+
.nbc-md-body:focus-within { border-color: var(--color-border); background: var(--color-bg); }
|
|
268
|
+
.nbc-md-del {
|
|
269
|
+
position: absolute; top: 4px; right: 4px;
|
|
270
|
+
opacity: 0; transition: opacity 0.15s;
|
|
271
|
+
}
|
|
272
|
+
.nbc-cell:hover .nbc-md-del { opacity: 0.5; }
|
|
273
|
+
.nbc-md-del:hover { opacity: 1 !important; }
|
|
274
|
+
|
|
275
|
+
.nbc-code-cell {
|
|
276
|
+
background: var(--color-bg); border: 1px solid var(--color-border);
|
|
277
|
+
border-radius: 8px; overflow: hidden;
|
|
278
|
+
transition: border-color 0.15s;
|
|
279
|
+
}
|
|
280
|
+
.nbc-code-cell:focus-within { border-color: var(--color-border2); }
|
|
281
|
+
.nbc-title-row {
|
|
282
|
+
display: flex; align-items: center; gap: 8px;
|
|
283
|
+
padding: 8px 12px; border-bottom: 1px solid var(--color-border);
|
|
284
|
+
background: var(--color-surface2);
|
|
285
|
+
font-family: var(--font-mono, 'IBM Plex Mono', monospace);
|
|
286
|
+
font-size: 10.5px; color: var(--color-text2);
|
|
287
|
+
}
|
|
288
|
+
.nbc-title-row .nbc-arrow-var { color: var(--color-accent); }
|
|
289
|
+
.nbc-title-row .nbc-meta-info { margin-right: auto; }
|
|
290
|
+
.nbc-code-body { padding: 10px 14px; }
|
|
291
|
+
.nbc-hidden { display: none !important; }
|
|
292
|
+
.nbc-result-body {
|
|
293
|
+
padding: 10px 14px;
|
|
294
|
+
font-family: var(--font-mono, 'IBM Plex Mono', monospace);
|
|
295
|
+
font-size: 12px;
|
|
296
|
+
border-top: 1px solid var(--color-border);
|
|
297
|
+
}
|
|
298
|
+
.nbc-result-row {
|
|
299
|
+
display: grid; grid-template-columns: 1fr auto; gap: 3px 24px;
|
|
300
|
+
color: var(--color-text2);
|
|
301
|
+
}
|
|
302
|
+
.nbc-result-row span:nth-child(even) {
|
|
303
|
+
color: var(--color-text1); font-variant-numeric: tabular-nums;
|
|
304
|
+
}
|
|
305
|
+
.nbc-chart-result {
|
|
306
|
+
padding: 14px; display: flex; align-items: flex-end; gap: 6px;
|
|
307
|
+
height: 92px; border-top: 1px solid var(--color-border);
|
|
308
|
+
}
|
|
309
|
+
.nbc-bar { flex: 1; background: var(--color-accent); border-radius: 2px 2px 0 0; opacity: 0.55; }
|
|
310
|
+
`;
|
|
311
|
+
document.head.appendChild(style);
|
|
312
|
+
}
|