@eclipse-lyra/extension-notebook 0.0.0
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/dist/i18n.json.d.ts +13 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/notebook-extension-CIJUGgLe.js +87 -0
- package/dist/notebook-extension-CIJUGgLe.js.map +1 -0
- package/dist/notebook-extension.d.ts +3 -0
- package/dist/notebook-extension.d.ts.map +1 -0
- package/dist/notebook-runtime-CFgw3UmB.js +1157 -0
- package/dist/notebook-runtime-CFgw3UmB.js.map +1 -0
- package/dist/notebook-runtime.d.ts +61 -0
- package/dist/notebook-runtime.d.ts.map +1 -0
- package/dist/notebook-types.d.ts +23 -0
- package/dist/notebook-types.d.ts.map +1 -0
- package/package.json +31 -0
|
@@ -0,0 +1,1157 @@
|
|
|
1
|
+
import { html, css } from "lit";
|
|
2
|
+
import { property, state, customElement } from "lit/decorators.js";
|
|
3
|
+
import { createRef, ref } from "lit/directives/ref.js";
|
|
4
|
+
import { repeat } from "lit/directives/repeat.js";
|
|
5
|
+
import { styleMap } from "lit/directives/style-map.js";
|
|
6
|
+
import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
|
7
|
+
import { marked } from "marked";
|
|
8
|
+
import * as monaco from "monaco-editor";
|
|
9
|
+
import monacoStyles from "monaco-editor/min/vs/editor/editor.main.css?raw";
|
|
10
|
+
import { LyraPart, workspaceService } from "@eclipse-lyra/core";
|
|
11
|
+
import { PyEnv, pythonPackageManagerService } from "@eclipse-lyra/extension-python-runtime/api";
|
|
12
|
+
var __defProp = Object.defineProperty;
|
|
13
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
14
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
15
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
16
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
17
|
+
if (decorator = decorators[i])
|
|
18
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
19
|
+
if (kind && result) __defProp(target, key, result);
|
|
20
|
+
return result;
|
|
21
|
+
};
|
|
22
|
+
let LyraNotebookEditor = class extends LyraPart {
|
|
23
|
+
constructor() {
|
|
24
|
+
super(...arguments);
|
|
25
|
+
this.cellOutputs = /* @__PURE__ */ new Map();
|
|
26
|
+
this.executingCells = /* @__PURE__ */ new Set();
|
|
27
|
+
this.pyConnected = false;
|
|
28
|
+
this.pyConnecting = false;
|
|
29
|
+
this.editingMarkdownCells = /* @__PURE__ */ new Set();
|
|
30
|
+
this.executionCounter = 0;
|
|
31
|
+
this.isRunningAll = false;
|
|
32
|
+
this.highlightedCellIndex = -1;
|
|
33
|
+
this.focusedCellIndex = -1;
|
|
34
|
+
this.cancelRunAll = false;
|
|
35
|
+
this.monacoEditors = /* @__PURE__ */ new Map();
|
|
36
|
+
this.cellRefs = /* @__PURE__ */ new Map();
|
|
37
|
+
}
|
|
38
|
+
doClose() {
|
|
39
|
+
this.input = void 0;
|
|
40
|
+
this.notebook = void 0;
|
|
41
|
+
this.cellOutputs.clear();
|
|
42
|
+
this.executingCells.clear();
|
|
43
|
+
this.monacoEditors.forEach((editor) => editor.dispose());
|
|
44
|
+
this.monacoEditors.clear();
|
|
45
|
+
this.cellRefs.clear();
|
|
46
|
+
this.focusedCellIndex = -1;
|
|
47
|
+
if (this.themeObserver) {
|
|
48
|
+
this.themeObserver.disconnect();
|
|
49
|
+
this.themeObserver = void 0;
|
|
50
|
+
}
|
|
51
|
+
if (this.pyenv) {
|
|
52
|
+
this.pyenv.close();
|
|
53
|
+
this.pyenv = void 0;
|
|
54
|
+
}
|
|
55
|
+
this.pyConnected = false;
|
|
56
|
+
this.pyVersion = void 0;
|
|
57
|
+
}
|
|
58
|
+
async save() {
|
|
59
|
+
if (!this.notebook || !this.input) return;
|
|
60
|
+
try {
|
|
61
|
+
this.saveEditorContents();
|
|
62
|
+
this.notebook.cells.forEach((cell, index) => {
|
|
63
|
+
if (cell.cell_type === "code") {
|
|
64
|
+
const output = this.cellOutputs.get(index);
|
|
65
|
+
cell.outputs = output ? this.convertOutputToJupyter(output, cell.execution_count) : [];
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
const notebookJson = JSON.stringify(this.notebook, null, 2);
|
|
69
|
+
const file = this.input.data;
|
|
70
|
+
await file.saveContents(notebookJson);
|
|
71
|
+
this.markDirty(false);
|
|
72
|
+
} catch (error) {
|
|
73
|
+
console.error("Failed to save notebook:", error);
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
doBeforeUI() {
|
|
78
|
+
this.loadNotebook();
|
|
79
|
+
}
|
|
80
|
+
renderToolbar() {
|
|
81
|
+
const connectionTitle = this.pyConnecting ? "Connecting to Python..." : this.pyConnected ? "Python Connected" : "Python Disconnected - Click to connect";
|
|
82
|
+
const connectionText = this.pyConnecting ? "Connecting..." : this.pyConnected && this.pyVersion ? `Python ${this.pyVersion}` : "Not connected";
|
|
83
|
+
const iconColor = this.pyConnected ? "var(--wa-color-green-40)" : this.pyConnecting ? "var(--wa-color-warning-500)" : "var(--wa-color-red-40)";
|
|
84
|
+
const runAllButton = this.isRunningAll ? html`
|
|
85
|
+
<wa-button size="small" appearance="plain" @click=${() => this.cancelAllCells()} title="Cancel running all cells">
|
|
86
|
+
<wa-icon name="stop" label="Stop"></wa-icon>
|
|
87
|
+
Cancel All
|
|
88
|
+
</wa-button>
|
|
89
|
+
` : html`
|
|
90
|
+
<wa-button size="small" appearance="plain" @click=${() => this.runAllCells()} title="Run all code cells sequentially">
|
|
91
|
+
<wa-icon name="play" label="Run"></wa-icon>
|
|
92
|
+
Run All
|
|
93
|
+
</wa-button>
|
|
94
|
+
`;
|
|
95
|
+
return html`
|
|
96
|
+
<wa-button
|
|
97
|
+
appearance="plain"
|
|
98
|
+
size="small"
|
|
99
|
+
style="display: flex; align-items: center; gap: 0.5rem;"
|
|
100
|
+
?disabled=${this.pyConnecting}
|
|
101
|
+
@click=${() => this.connectPython()}
|
|
102
|
+
title=${connectionTitle}>
|
|
103
|
+
<wa-icon
|
|
104
|
+
name="circle"
|
|
105
|
+
label="Python status"
|
|
106
|
+
style=${styleMap({ color: iconColor })}
|
|
107
|
+
></wa-icon>
|
|
108
|
+
${connectionText}
|
|
109
|
+
</wa-button>
|
|
110
|
+
${runAllButton}
|
|
111
|
+
<wa-button
|
|
112
|
+
size="small"
|
|
113
|
+
appearance="plain"
|
|
114
|
+
@click=${() => this.clearAllOutputs()}
|
|
115
|
+
title="Clear all outputs and reset execution counter">
|
|
116
|
+
<wa-icon name="eraser" label="Clear"></wa-icon>
|
|
117
|
+
Clear Outputs
|
|
118
|
+
</wa-button>
|
|
119
|
+
<wa-button
|
|
120
|
+
size="small"
|
|
121
|
+
appearance="plain"
|
|
122
|
+
@click=${() => this.restartKernel()}
|
|
123
|
+
title="Restart Python kernel (clears all variables and state)"
|
|
124
|
+
?disabled=${!this.pyConnected || this.pyConnecting}>
|
|
125
|
+
<wa-icon name="arrows-rotate" label="Restart"></wa-icon>
|
|
126
|
+
Restart Kernel
|
|
127
|
+
</wa-button>
|
|
128
|
+
<wa-button
|
|
129
|
+
size="small"
|
|
130
|
+
appearance="plain"
|
|
131
|
+
@click=${() => this.openPackageManager()}
|
|
132
|
+
title="Manage required packages for this notebook">
|
|
133
|
+
<wa-icon name="box" label="Packages"></wa-icon>
|
|
134
|
+
Packages
|
|
135
|
+
</wa-button>
|
|
136
|
+
`;
|
|
137
|
+
}
|
|
138
|
+
doInitUI() {
|
|
139
|
+
this.setupThemeObserver();
|
|
140
|
+
}
|
|
141
|
+
async loadNotebook() {
|
|
142
|
+
const file = this.input.data;
|
|
143
|
+
const contents = await file.getContents();
|
|
144
|
+
try {
|
|
145
|
+
this.notebook = JSON.parse(contents);
|
|
146
|
+
} catch (e) {
|
|
147
|
+
console.error("Failed to parse notebook:", e);
|
|
148
|
+
this.notebook = {
|
|
149
|
+
cells: [{
|
|
150
|
+
cell_type: "markdown",
|
|
151
|
+
source: ["# Error\nFailed to parse notebook file."]
|
|
152
|
+
}]
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
if (this.notebook?.cells) {
|
|
156
|
+
const maxCount = this.notebook.cells.filter((cell) => cell.cell_type === "code").map((cell) => cell.execution_count ?? 0).reduce((max, count) => Math.max(max, count), 0);
|
|
157
|
+
this.executionCounter = maxCount;
|
|
158
|
+
this.notebook.cells.forEach((cell, index) => {
|
|
159
|
+
if (cell.cell_type === "code" && cell.outputs && cell.outputs.length > 0) {
|
|
160
|
+
const output = this.convertOutputFromJupyter(cell.outputs[0]);
|
|
161
|
+
if (output) {
|
|
162
|
+
this.cellOutputs.set(index, output);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
setupThemeObserver() {
|
|
169
|
+
const rootElement = document.documentElement;
|
|
170
|
+
let currentTheme = this.getMonacoTheme();
|
|
171
|
+
this.themeObserver = new MutationObserver(() => {
|
|
172
|
+
const newTheme = this.getMonacoTheme();
|
|
173
|
+
if (newTheme !== currentTheme) {
|
|
174
|
+
currentTheme = newTheme;
|
|
175
|
+
monaco.editor.setTheme(newTheme);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
this.themeObserver.observe(rootElement, {
|
|
179
|
+
attributes: true,
|
|
180
|
+
attributeFilter: ["class"]
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
getCellSource(cell) {
|
|
184
|
+
return Array.isArray(cell.source) ? cell.source.join("") : cell.source;
|
|
185
|
+
}
|
|
186
|
+
convertOutputToJupyter(output, executionCount) {
|
|
187
|
+
if (output.type === "execute_result") {
|
|
188
|
+
const data = {};
|
|
189
|
+
if (output.imageData) data["image/png"] = output.imageData;
|
|
190
|
+
if (output.data) data["text/plain"] = output.data;
|
|
191
|
+
return [{
|
|
192
|
+
output_type: "execute_result",
|
|
193
|
+
data,
|
|
194
|
+
execution_count: executionCount,
|
|
195
|
+
metadata: {}
|
|
196
|
+
}];
|
|
197
|
+
} else if (output.type === "error") {
|
|
198
|
+
return [{
|
|
199
|
+
output_type: "error",
|
|
200
|
+
ename: "Error",
|
|
201
|
+
evalue: output.data,
|
|
202
|
+
traceback: [output.data]
|
|
203
|
+
}];
|
|
204
|
+
}
|
|
205
|
+
return [];
|
|
206
|
+
}
|
|
207
|
+
convertOutputFromJupyter(jupyterOutput) {
|
|
208
|
+
if (jupyterOutput.output_type === "execute_result" && jupyterOutput.data) {
|
|
209
|
+
return {
|
|
210
|
+
type: "execute_result",
|
|
211
|
+
data: jupyterOutput.data["text/plain"] || "",
|
|
212
|
+
imageData: jupyterOutput.data["image/png"] || void 0
|
|
213
|
+
};
|
|
214
|
+
} else if (jupyterOutput.output_type === "error") {
|
|
215
|
+
return {
|
|
216
|
+
type: "error",
|
|
217
|
+
data: jupyterOutput.evalue || jupyterOutput.traceback?.join("\n") || "Unknown error"
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
renderHeaderActions(index, additionalButton) {
|
|
223
|
+
return html`
|
|
224
|
+
<div class="cell-header-actions">
|
|
225
|
+
${additionalButton || ""}
|
|
226
|
+
${additionalButton ? html`<span class="divider"></span>` : ""}
|
|
227
|
+
<wa-button size="small" appearance="plain" @click=${() => this.addCell(index, "code")} title="Add code cell before">
|
|
228
|
+
<wa-icon name="plus"></wa-icon>
|
|
229
|
+
<wa-icon name="code" label="Code"></wa-icon>
|
|
230
|
+
</wa-button>
|
|
231
|
+
<wa-button size="small" appearance="plain" @click=${() => this.addCell(index, "markdown")} title="Add markdown cell before">
|
|
232
|
+
<wa-icon name="plus"></wa-icon>
|
|
233
|
+
<wa-icon name="font" label="Markdown"></wa-icon>
|
|
234
|
+
</wa-button>
|
|
235
|
+
<span class="divider"></span>
|
|
236
|
+
<wa-button size="small" appearance="plain" @click=${() => this.deleteCell(index)} title="Delete cell" ?disabled=${this.notebook.cells.length <= 1}>
|
|
237
|
+
<wa-icon name="trash" label="Delete cell"></wa-icon>
|
|
238
|
+
</wa-button>
|
|
239
|
+
</div>
|
|
240
|
+
`;
|
|
241
|
+
}
|
|
242
|
+
renderFooterActions(index) {
|
|
243
|
+
return html`
|
|
244
|
+
<div class="cell-footer">
|
|
245
|
+
<wa-button size="small" appearance="plain" @click=${() => this.addCell(index + 1, "code")} title="Add code cell after">
|
|
246
|
+
<wa-icon name="code" label="Code"></wa-icon>
|
|
247
|
+
<wa-icon name="plus"></wa-icon>
|
|
248
|
+
</wa-button>
|
|
249
|
+
<wa-button size="small" appearance="plain" @click=${() => this.addCell(index + 1, "markdown")} title="Add markdown cell after">
|
|
250
|
+
<wa-icon name="font" label="Markdown"></wa-icon>
|
|
251
|
+
<wa-icon name="plus"></wa-icon>
|
|
252
|
+
</wa-button>
|
|
253
|
+
</div>
|
|
254
|
+
`;
|
|
255
|
+
}
|
|
256
|
+
// Helper to convert string to Jupyter source format
|
|
257
|
+
stringToSourceArray(content) {
|
|
258
|
+
if (!content) return [""];
|
|
259
|
+
const lines = content.split("\n").map((line) => line + "\n");
|
|
260
|
+
if (lines.length > 0) {
|
|
261
|
+
lines[lines.length - 1] = lines[lines.length - 1].replace(/\n$/, "");
|
|
262
|
+
}
|
|
263
|
+
return lines;
|
|
264
|
+
}
|
|
265
|
+
// Helper to create a new cell
|
|
266
|
+
createCell(cellType) {
|
|
267
|
+
const newCell = {
|
|
268
|
+
cell_type: cellType,
|
|
269
|
+
source: [""],
|
|
270
|
+
metadata: {}
|
|
271
|
+
};
|
|
272
|
+
if (cellType === "code") {
|
|
273
|
+
newCell.execution_count = null;
|
|
274
|
+
newCell.outputs = [];
|
|
275
|
+
}
|
|
276
|
+
return newCell;
|
|
277
|
+
}
|
|
278
|
+
async initPyodide() {
|
|
279
|
+
if (!this.pyenv) {
|
|
280
|
+
this.pyenv = new PyEnv();
|
|
281
|
+
const workspace = await workspaceService.getWorkspace();
|
|
282
|
+
if (workspace) {
|
|
283
|
+
await this.pyenv.init(workspace);
|
|
284
|
+
this.pyConnected = true;
|
|
285
|
+
try {
|
|
286
|
+
const response = await this.pyenv.execCode("import sys; sys.version.split()[0]");
|
|
287
|
+
this.pyVersion = response?.result || "Unknown";
|
|
288
|
+
} catch (error) {
|
|
289
|
+
console.error("Failed to get Python version:", error);
|
|
290
|
+
this.pyVersion = "Unknown";
|
|
291
|
+
}
|
|
292
|
+
const requiredPackages = this.notebook?.metadata?.required_packages || [];
|
|
293
|
+
if (requiredPackages.length > 0) {
|
|
294
|
+
try {
|
|
295
|
+
await this.pyenv.loadPackages(requiredPackages);
|
|
296
|
+
} catch (error) {
|
|
297
|
+
console.error("Failed to load required packages:", error);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
try {
|
|
301
|
+
await this.pyenv.execCode(`
|
|
302
|
+
try:
|
|
303
|
+
import matplotlib
|
|
304
|
+
matplotlib.use('agg')
|
|
305
|
+
|
|
306
|
+
import matplotlib.pyplot as plt
|
|
307
|
+
import io
|
|
308
|
+
import base64
|
|
309
|
+
|
|
310
|
+
_original_show = plt.show
|
|
311
|
+
_display_data = None
|
|
312
|
+
|
|
313
|
+
def _patched_show(*args, **kwargs):
|
|
314
|
+
"""Patched plt.show() that captures the current figure for notebook display."""
|
|
315
|
+
global _display_data
|
|
316
|
+
if plt.get_fignums():
|
|
317
|
+
fig = plt.gcf()
|
|
318
|
+
buffer = io.BytesIO()
|
|
319
|
+
fig.savefig(buffer, format='png', bbox_inches='tight', dpi=100)
|
|
320
|
+
buffer.seek(0)
|
|
321
|
+
_display_data = base64.b64encode(buffer.read()).decode('utf-8')
|
|
322
|
+
plt.close(fig)
|
|
323
|
+
# Don't call original show() as it would try to display in a window
|
|
324
|
+
|
|
325
|
+
plt.show = _patched_show
|
|
326
|
+
except ImportError:
|
|
327
|
+
# matplotlib not installed - skip configuration
|
|
328
|
+
pass
|
|
329
|
+
`);
|
|
330
|
+
} catch (error) {
|
|
331
|
+
console.error("Failed to configure matplotlib:", error);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
async executeCell(cellIndex) {
|
|
337
|
+
const cell = this.notebook.cells[cellIndex];
|
|
338
|
+
if (cell.cell_type !== "code") return;
|
|
339
|
+
this.executingCells.add(cellIndex);
|
|
340
|
+
this.requestUpdate();
|
|
341
|
+
try {
|
|
342
|
+
await this.initPyodide();
|
|
343
|
+
const editor = this.monacoEditors.get(cellIndex);
|
|
344
|
+
let code = editor ? editor.getValue() : this.getCellSource(cell);
|
|
345
|
+
const response = await this.pyenv.execCode(code);
|
|
346
|
+
const outputParts = [];
|
|
347
|
+
if (response && typeof response === "object" && "console" in response) {
|
|
348
|
+
const consoleOutput = response.console;
|
|
349
|
+
if (Array.isArray(consoleOutput) && consoleOutput.length > 0) {
|
|
350
|
+
const filteredOutput = consoleOutput.filter((s) => s && s.trim());
|
|
351
|
+
if (filteredOutput.length > 0) {
|
|
352
|
+
outputParts.push(...filteredOutput);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
let imageData = void 0;
|
|
357
|
+
try {
|
|
358
|
+
const checkDisplayData = await this.pyenv.execCode('_display_data if "_display_data" in dir() else None');
|
|
359
|
+
const displayResult = checkDisplayData && typeof checkDisplayData === "object" ? checkDisplayData.result : checkDisplayData;
|
|
360
|
+
if (displayResult && String(displayResult) !== "None" && String(displayResult) !== "undefined") {
|
|
361
|
+
imageData = String(displayResult);
|
|
362
|
+
await this.pyenv.execCode("_display_data = None");
|
|
363
|
+
}
|
|
364
|
+
} catch (e) {
|
|
365
|
+
console.debug("No display data to retrieve:", e);
|
|
366
|
+
}
|
|
367
|
+
if (!imageData) {
|
|
368
|
+
const result = response && typeof response === "object" ? response.result : response;
|
|
369
|
+
if (result !== void 0 && result !== null && String(result) !== "undefined") {
|
|
370
|
+
const resultStr = String(result);
|
|
371
|
+
if (resultStr && resultStr !== "undefined") {
|
|
372
|
+
outputParts.push(resultStr);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
this.cellOutputs.set(cellIndex, {
|
|
377
|
+
type: "execute_result",
|
|
378
|
+
data: outputParts.length > 0 ? outputParts.join("\n") : void 0,
|
|
379
|
+
imageData
|
|
380
|
+
});
|
|
381
|
+
this.executionCounter++;
|
|
382
|
+
cell.execution_count = this.executionCounter;
|
|
383
|
+
this.markDirty(true);
|
|
384
|
+
} catch (error) {
|
|
385
|
+
if (this.executingCells.has(cellIndex)) {
|
|
386
|
+
this.cellOutputs.set(cellIndex, {
|
|
387
|
+
type: "error",
|
|
388
|
+
data: String(error)
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
} finally {
|
|
392
|
+
this.executingCells.delete(cellIndex);
|
|
393
|
+
this.requestUpdate();
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
async cancelExecution(cellIndex) {
|
|
397
|
+
if (!this.pyenv) return;
|
|
398
|
+
if (this.pyenv.canInterrupt()) {
|
|
399
|
+
this.pyenv.interrupt();
|
|
400
|
+
} else {
|
|
401
|
+
this.cellOutputs.set(cellIndex, {
|
|
402
|
+
type: "error",
|
|
403
|
+
data: "Execution cancelled by user (worker terminated - SharedArrayBuffer not available for graceful interrupt)"
|
|
404
|
+
});
|
|
405
|
+
this.executingCells.delete(cellIndex);
|
|
406
|
+
this.pyenv.close();
|
|
407
|
+
this.pyenv = void 0;
|
|
408
|
+
this.pyConnected = false;
|
|
409
|
+
this.pyVersion = void 0;
|
|
410
|
+
try {
|
|
411
|
+
await this.initPyodide();
|
|
412
|
+
} catch (error) {
|
|
413
|
+
console.error("Failed to reinitialize Python after cancellation:", error);
|
|
414
|
+
}
|
|
415
|
+
this.requestUpdate();
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
clearAllOutputs() {
|
|
419
|
+
this.cellOutputs.clear();
|
|
420
|
+
this.executionCounter = 0;
|
|
421
|
+
if (this.notebook?.cells) {
|
|
422
|
+
this.notebook.cells.forEach((cell) => {
|
|
423
|
+
if (cell.cell_type === "code") {
|
|
424
|
+
cell.execution_count = null;
|
|
425
|
+
cell.outputs = [];
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
this.markDirty(true);
|
|
430
|
+
this.requestUpdate();
|
|
431
|
+
}
|
|
432
|
+
async restartKernel() {
|
|
433
|
+
if (!this.pyenv || this.pyConnecting) return;
|
|
434
|
+
try {
|
|
435
|
+
this.pyConnecting = true;
|
|
436
|
+
this.pyenv.close();
|
|
437
|
+
this.pyenv = void 0;
|
|
438
|
+
this.pyConnected = false;
|
|
439
|
+
this.pyVersion = void 0;
|
|
440
|
+
this.requestUpdate();
|
|
441
|
+
await this.initPyodide();
|
|
442
|
+
this.requestUpdate();
|
|
443
|
+
} catch (error) {
|
|
444
|
+
console.error("Failed to restart kernel:", error);
|
|
445
|
+
} finally {
|
|
446
|
+
this.pyConnecting = false;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
async runAllCells() {
|
|
450
|
+
if (!this.notebook?.cells) return;
|
|
451
|
+
this.isRunningAll = true;
|
|
452
|
+
this.cancelRunAll = false;
|
|
453
|
+
this.requestUpdate();
|
|
454
|
+
try {
|
|
455
|
+
for (let i = 0; i < this.notebook.cells.length; i++) {
|
|
456
|
+
if (this.cancelRunAll) {
|
|
457
|
+
break;
|
|
458
|
+
}
|
|
459
|
+
const cell = this.notebook.cells[i];
|
|
460
|
+
if (cell.cell_type === "code") {
|
|
461
|
+
await this.executeCell(i);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
} finally {
|
|
465
|
+
this.isRunningAll = false;
|
|
466
|
+
this.cancelRunAll = false;
|
|
467
|
+
this.requestUpdate();
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
cancelAllCells() {
|
|
471
|
+
this.cancelRunAll = true;
|
|
472
|
+
}
|
|
473
|
+
toggleMarkdownEdit(index) {
|
|
474
|
+
if (this.editingMarkdownCells.has(index)) {
|
|
475
|
+
this.editingMarkdownCells.delete(index);
|
|
476
|
+
} else {
|
|
477
|
+
this.editingMarkdownCells.add(index);
|
|
478
|
+
}
|
|
479
|
+
this.requestUpdate();
|
|
480
|
+
}
|
|
481
|
+
saveMarkdownEdit(index, event) {
|
|
482
|
+
const textarea = event.target;
|
|
483
|
+
const newContent = textarea.value;
|
|
484
|
+
if (this.notebook && this.notebook.cells[index]) {
|
|
485
|
+
const cell = this.notebook.cells[index];
|
|
486
|
+
const oldContent = this.getCellSource(cell);
|
|
487
|
+
cell.source = this.stringToSourceArray(newContent);
|
|
488
|
+
if (oldContent !== newContent) {
|
|
489
|
+
this.markDirty(true);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
this.editingMarkdownCells.delete(index);
|
|
493
|
+
this.requestUpdate();
|
|
494
|
+
}
|
|
495
|
+
renderMarkdownCell(cell, index) {
|
|
496
|
+
const source = this.getCellSource(cell);
|
|
497
|
+
const isEmpty = !source || source.trim() === "";
|
|
498
|
+
const isEditing = this.editingMarkdownCells.has(index);
|
|
499
|
+
if (isEditing) {
|
|
500
|
+
const editButtons = html`
|
|
501
|
+
<wa-button
|
|
502
|
+
size="small"
|
|
503
|
+
appearance="plain"
|
|
504
|
+
@click=${(e) => {
|
|
505
|
+
const textarea = e.target.closest(".markdown-cell")?.querySelector("textarea");
|
|
506
|
+
if (textarea) {
|
|
507
|
+
this.saveMarkdownEdit(index, { target: textarea });
|
|
508
|
+
}
|
|
509
|
+
}}
|
|
510
|
+
title="Save changes">
|
|
511
|
+
<wa-icon name="check" label="Save"></wa-icon>
|
|
512
|
+
</wa-button>
|
|
513
|
+
<wa-button
|
|
514
|
+
size="small"
|
|
515
|
+
appearance="plain"
|
|
516
|
+
@click=${() => this.toggleMarkdownEdit(index)}
|
|
517
|
+
title="Cancel editing">
|
|
518
|
+
<wa-icon name="xmark" label="Cancel"></wa-icon>
|
|
519
|
+
</wa-button>
|
|
520
|
+
`;
|
|
521
|
+
return html`
|
|
522
|
+
<div class="cell-wrapper">
|
|
523
|
+
<wa-animation
|
|
524
|
+
name="bounce"
|
|
525
|
+
duration="1000"
|
|
526
|
+
iterations="1"
|
|
527
|
+
?play=${this.highlightedCellIndex === index}
|
|
528
|
+
@wa-finish=${() => this.highlightedCellIndex = -1}>
|
|
529
|
+
<div class="cell markdown-cell editing">
|
|
530
|
+
<div class="cell-header">
|
|
531
|
+
${this.renderHeaderActions(index, editButtons)}
|
|
532
|
+
<span class="cell-label">Markdown</span>
|
|
533
|
+
</div>
|
|
534
|
+
<textarea
|
|
535
|
+
class="markdown-editor"
|
|
536
|
+
.value=${source}
|
|
537
|
+
@blur=${(e) => this.saveMarkdownEdit(index, e)}
|
|
538
|
+
placeholder="Enter markdown content here... (# for headings, ** for bold, etc.)"></textarea>
|
|
539
|
+
${this.renderFooterActions(index)}
|
|
540
|
+
</div>
|
|
541
|
+
</wa-animation>
|
|
542
|
+
</div>
|
|
543
|
+
`;
|
|
544
|
+
}
|
|
545
|
+
const rendered = marked.parse(source);
|
|
546
|
+
const editButton = html`
|
|
547
|
+
<wa-button
|
|
548
|
+
size="small"
|
|
549
|
+
appearance="plain"
|
|
550
|
+
@click=${() => this.toggleMarkdownEdit(index)}
|
|
551
|
+
title="Edit markdown">
|
|
552
|
+
<wa-icon name="pencil" label="Edit"></wa-icon>
|
|
553
|
+
</wa-button>
|
|
554
|
+
`;
|
|
555
|
+
return html`
|
|
556
|
+
<div class="cell-wrapper">
|
|
557
|
+
<wa-animation
|
|
558
|
+
name="bounce"
|
|
559
|
+
duration="1000"
|
|
560
|
+
iterations="1"
|
|
561
|
+
?play=${this.highlightedCellIndex === index}
|
|
562
|
+
@wa-finish=${() => this.highlightedCellIndex = -1}>
|
|
563
|
+
<div class="cell markdown-cell ${isEmpty ? "empty" : ""}" @dblclick=${() => this.toggleMarkdownEdit(index)}>
|
|
564
|
+
<div class="cell-header">
|
|
565
|
+
${this.renderHeaderActions(index, editButton)}
|
|
566
|
+
<span class="cell-label"></span>
|
|
567
|
+
</div>
|
|
568
|
+
<div class="cell-content">
|
|
569
|
+
${isEmpty ? html`
|
|
570
|
+
<div class="markdown-placeholder">
|
|
571
|
+
<wa-icon name="font" label="Markdown"></wa-icon>
|
|
572
|
+
<span>Double-click or click the pencil icon to edit markdown</span>
|
|
573
|
+
</div>
|
|
574
|
+
` : unsafeHTML(rendered)}
|
|
575
|
+
</div>
|
|
576
|
+
${this.renderFooterActions(index)}
|
|
577
|
+
</div>
|
|
578
|
+
</wa-animation>
|
|
579
|
+
</div>
|
|
580
|
+
`;
|
|
581
|
+
}
|
|
582
|
+
renderCodeCell(cell, index) {
|
|
583
|
+
const output = this.cellOutputs.get(index);
|
|
584
|
+
const isExecuting = this.executingCells.has(index);
|
|
585
|
+
if (!this.cellRefs.has(index)) {
|
|
586
|
+
this.cellRefs.set(index, createRef());
|
|
587
|
+
}
|
|
588
|
+
const cellRef = this.cellRefs.get(index);
|
|
589
|
+
const runButton = isExecuting ? html`
|
|
590
|
+
<wa-button
|
|
591
|
+
size="small"
|
|
592
|
+
appearance="plain"
|
|
593
|
+
@click=${() => this.cancelExecution(index)}
|
|
594
|
+
title="Stop execution">
|
|
595
|
+
<wa-icon name="stop" label="Stop" style="color: var(--wa-color-danger-500);"></wa-icon>
|
|
596
|
+
</wa-button>
|
|
597
|
+
` : html`
|
|
598
|
+
<lyra-command
|
|
599
|
+
cmd="python"
|
|
600
|
+
icon="play"
|
|
601
|
+
title="Run cell"
|
|
602
|
+
size="small"
|
|
603
|
+
appearance="plain"
|
|
604
|
+
.params=${{ cellIndex: index }}>
|
|
605
|
+
</lyra-command>
|
|
606
|
+
`;
|
|
607
|
+
return html`
|
|
608
|
+
<div class="cell-wrapper">
|
|
609
|
+
<wa-animation
|
|
610
|
+
name="bounce"
|
|
611
|
+
duration="1000"
|
|
612
|
+
iterations="1"
|
|
613
|
+
?play=${this.highlightedCellIndex === index}
|
|
614
|
+
@wa-finish=${() => this.highlightedCellIndex = -1}>
|
|
615
|
+
<div class="cell code-cell ${isExecuting ? "executing" : ""}">
|
|
616
|
+
<div class="cell-header">
|
|
617
|
+
<span class="cell-label">
|
|
618
|
+
${isExecuting ? html`
|
|
619
|
+
In [<wa-animation name="pulse" duration="1000" iterations="Infinity" ?play=${isExecuting}>
|
|
620
|
+
<span class="executing-indicator">*</span>
|
|
621
|
+
</wa-animation>]
|
|
622
|
+
` : html`
|
|
623
|
+
In [${cell.execution_count ?? " "}]
|
|
624
|
+
`}
|
|
625
|
+
</span>
|
|
626
|
+
${this.renderHeaderActions(index, runButton)}
|
|
627
|
+
</div>
|
|
628
|
+
<div class="cell-input monaco-container" ${ref(cellRef)} data-cell-index="${index}"></div>
|
|
629
|
+
${output ? html`
|
|
630
|
+
<div class="cell-output ${output.type === "error" ? "output-error" : ""}">
|
|
631
|
+
<div class="output-label">Out [${index + 1}]:</div>
|
|
632
|
+
${output.imageData ? html`
|
|
633
|
+
<img src="data:image/png;base64,${output.imageData}" alt="Output image" class="output-image" />
|
|
634
|
+
` : ""}
|
|
635
|
+
${output.data ? html`<pre><code>${output.data}</code></pre>` : ""}
|
|
636
|
+
</div>
|
|
637
|
+
` : ""}
|
|
638
|
+
${this.renderFooterActions(index)}
|
|
639
|
+
</div>
|
|
640
|
+
</wa-animation>
|
|
641
|
+
</div>
|
|
642
|
+
`;
|
|
643
|
+
}
|
|
644
|
+
renderCell(cell, index) {
|
|
645
|
+
if (cell.cell_type === "markdown") {
|
|
646
|
+
return this.renderMarkdownCell(cell, index);
|
|
647
|
+
} else if (cell.cell_type === "code") {
|
|
648
|
+
return this.renderCodeCell(cell, index);
|
|
649
|
+
} else {
|
|
650
|
+
const source = this.getCellSource(cell);
|
|
651
|
+
return html`
|
|
652
|
+
<div class="cell raw-cell">
|
|
653
|
+
<pre><code>${source}</code></pre>
|
|
654
|
+
</div>
|
|
655
|
+
`;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
async connectPython() {
|
|
659
|
+
if (this.pyConnecting || this.pyConnected) {
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
try {
|
|
663
|
+
this.pyConnecting = true;
|
|
664
|
+
await this.initPyodide();
|
|
665
|
+
} catch (error) {
|
|
666
|
+
console.error("Failed to initialize Pyodide:", error);
|
|
667
|
+
} finally {
|
|
668
|
+
this.pyConnecting = false;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
addCell(index, cellType = "code") {
|
|
672
|
+
if (!this.notebook) return;
|
|
673
|
+
this.saveEditorContents();
|
|
674
|
+
this.shiftIndices(index, "up");
|
|
675
|
+
this.notebook.cells.splice(index, 0, this.createCell(cellType));
|
|
676
|
+
if (cellType === "markdown") {
|
|
677
|
+
this.editingMarkdownCells.add(index);
|
|
678
|
+
}
|
|
679
|
+
this.resetCellState();
|
|
680
|
+
this.highlightedCellIndex = index;
|
|
681
|
+
this.updateComplete.then(() => {
|
|
682
|
+
this.scrollToCell(index);
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
scrollToCell(index) {
|
|
686
|
+
const cellWrapper = this.shadowRoot?.querySelectorAll(".cell-wrapper")[index];
|
|
687
|
+
if (!cellWrapper) return;
|
|
688
|
+
const scroller = this.closest("wa-scroller");
|
|
689
|
+
if (!scroller) {
|
|
690
|
+
cellWrapper.scrollIntoView({
|
|
691
|
+
behavior: "smooth",
|
|
692
|
+
block: "center",
|
|
693
|
+
inline: "nearest"
|
|
694
|
+
});
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
const scrollerRect = scroller.getBoundingClientRect();
|
|
698
|
+
const cellRect = cellWrapper.getBoundingClientRect();
|
|
699
|
+
const scrollTop = scroller.scrollTop;
|
|
700
|
+
const targetScroll = scrollTop + (cellRect.top - scrollerRect.top) - scrollerRect.height / 2 + cellRect.height / 2;
|
|
701
|
+
scroller.scrollTo({
|
|
702
|
+
top: targetScroll,
|
|
703
|
+
behavior: "smooth"
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
saveEditorContents() {
|
|
707
|
+
this.monacoEditors.forEach((editor, index) => {
|
|
708
|
+
const cell = this.notebook.cells[index];
|
|
709
|
+
if (cell && cell.cell_type === "code") {
|
|
710
|
+
cell.source = this.stringToSourceArray(editor.getValue());
|
|
711
|
+
}
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
resetCellState() {
|
|
715
|
+
this.monacoEditors.forEach((editor) => editor.dispose());
|
|
716
|
+
this.monacoEditors.clear();
|
|
717
|
+
this.cellRefs.clear();
|
|
718
|
+
this.markDirty(true);
|
|
719
|
+
}
|
|
720
|
+
deleteCell(index) {
|
|
721
|
+
if (!this.notebook || this.notebook.cells.length <= 1) return;
|
|
722
|
+
this.saveEditorContents();
|
|
723
|
+
this.cellOutputs.delete(index);
|
|
724
|
+
this.executingCells.delete(index);
|
|
725
|
+
this.editingMarkdownCells.delete(index);
|
|
726
|
+
this.notebook.cells.splice(index, 1);
|
|
727
|
+
this.shiftIndices(index, "down");
|
|
728
|
+
this.resetCellState();
|
|
729
|
+
}
|
|
730
|
+
shiftIndices(startIndex, direction) {
|
|
731
|
+
const shift = direction === "up" ? 1 : -1;
|
|
732
|
+
const filterFn = direction === "up" ? (idx) => idx >= startIndex : (idx) => idx > startIndex;
|
|
733
|
+
const sortFn = direction === "up" ? (a, b) => b - a : (a, b) => a - b;
|
|
734
|
+
const shiftMap = (map) => {
|
|
735
|
+
const indices = Array.from(map.keys()).filter(filterFn).sort(sortFn);
|
|
736
|
+
indices.forEach((oldIndex) => {
|
|
737
|
+
const value = map.get(oldIndex);
|
|
738
|
+
map.delete(oldIndex);
|
|
739
|
+
map.set(oldIndex + shift, value);
|
|
740
|
+
});
|
|
741
|
+
};
|
|
742
|
+
const shiftSet = (set) => {
|
|
743
|
+
const indices = Array.from(set).filter(filterFn).sort(sortFn);
|
|
744
|
+
indices.forEach((oldIndex) => {
|
|
745
|
+
set.delete(oldIndex);
|
|
746
|
+
set.add(oldIndex + shift);
|
|
747
|
+
});
|
|
748
|
+
};
|
|
749
|
+
shiftMap(this.cellOutputs);
|
|
750
|
+
shiftSet(this.executingCells);
|
|
751
|
+
shiftSet(this.editingMarkdownCells);
|
|
752
|
+
}
|
|
753
|
+
initializeMonacoEditor(container, cell, index) {
|
|
754
|
+
const source = this.getCellSource(cell);
|
|
755
|
+
const lineCount = source.split("\n").length;
|
|
756
|
+
const lineHeight = 19;
|
|
757
|
+
const padding = 10;
|
|
758
|
+
const minHeight = 100;
|
|
759
|
+
const calculatedHeight = Math.max(minHeight, lineCount * lineHeight + padding);
|
|
760
|
+
container.style.height = `${calculatedHeight}px`;
|
|
761
|
+
const editor = monaco.editor.create(container, {
|
|
762
|
+
value: source,
|
|
763
|
+
language: "python",
|
|
764
|
+
theme: this.getMonacoTheme(),
|
|
765
|
+
minimap: { enabled: false },
|
|
766
|
+
scrollBeyondLastLine: false,
|
|
767
|
+
lineNumbers: "on",
|
|
768
|
+
renderLineHighlight: "all",
|
|
769
|
+
automaticLayout: true,
|
|
770
|
+
fontSize: 14,
|
|
771
|
+
tabSize: 4,
|
|
772
|
+
wordWrap: "on"
|
|
773
|
+
});
|
|
774
|
+
let isEditorFocused = false;
|
|
775
|
+
editor.onDidFocusEditorText(() => {
|
|
776
|
+
isEditorFocused = true;
|
|
777
|
+
this.focusedCellIndex = index;
|
|
778
|
+
});
|
|
779
|
+
editor.onDidBlurEditorText(() => {
|
|
780
|
+
isEditorFocused = false;
|
|
781
|
+
if (this.focusedCellIndex === index) {
|
|
782
|
+
this.focusedCellIndex = -1;
|
|
783
|
+
}
|
|
784
|
+
});
|
|
785
|
+
container.addEventListener("wheel", (e) => {
|
|
786
|
+
if (!isEditorFocused) {
|
|
787
|
+
const scrollTop = editor.getScrollTop();
|
|
788
|
+
const scrollHeight = editor.getScrollHeight();
|
|
789
|
+
const contentHeight = editor.getContentHeight();
|
|
790
|
+
const canScroll = scrollHeight > contentHeight;
|
|
791
|
+
const atBoundary = e.deltaY < 0 && scrollTop <= 0 || e.deltaY > 0 && scrollTop + contentHeight >= scrollHeight;
|
|
792
|
+
if (!canScroll || atBoundary) {
|
|
793
|
+
e.stopImmediatePropagation();
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
}, { passive: true, capture: true });
|
|
797
|
+
editor.onDidContentSizeChange(() => {
|
|
798
|
+
const contentHeight = editor.getContentHeight();
|
|
799
|
+
container.style.height = `${Math.max(minHeight, contentHeight + padding)}px`;
|
|
800
|
+
editor.layout();
|
|
801
|
+
});
|
|
802
|
+
editor.onDidChangeModelContent(() => {
|
|
803
|
+
const currentValue = editor.getValue();
|
|
804
|
+
const originalValue = this.getCellSource(cell);
|
|
805
|
+
if (currentValue !== originalValue) {
|
|
806
|
+
this.markDirty(true);
|
|
807
|
+
}
|
|
808
|
+
});
|
|
809
|
+
this.monacoEditors.set(index, editor);
|
|
810
|
+
}
|
|
811
|
+
getMonacoTheme() {
|
|
812
|
+
const rootElement = document.documentElement;
|
|
813
|
+
if (rootElement.classList.contains("wa-dark")) {
|
|
814
|
+
return "vs-dark";
|
|
815
|
+
} else if (rootElement.classList.contains("wa-light")) {
|
|
816
|
+
return "vs";
|
|
817
|
+
}
|
|
818
|
+
return "vs-dark";
|
|
819
|
+
}
|
|
820
|
+
openPackageManager() {
|
|
821
|
+
const packages = this.notebook?.metadata?.required_packages || [];
|
|
822
|
+
pythonPackageManagerService.showPackageManager({
|
|
823
|
+
packages,
|
|
824
|
+
pyenv: this.pyenv,
|
|
825
|
+
onPackageAdded: (packageName) => {
|
|
826
|
+
if (!this.notebook) return;
|
|
827
|
+
if (!this.notebook.metadata) {
|
|
828
|
+
this.notebook.metadata = {};
|
|
829
|
+
}
|
|
830
|
+
if (!this.notebook.metadata.required_packages) {
|
|
831
|
+
this.notebook.metadata.required_packages = [];
|
|
832
|
+
}
|
|
833
|
+
if (!this.notebook.metadata.required_packages.includes(packageName)) {
|
|
834
|
+
this.notebook.metadata.required_packages.push(packageName);
|
|
835
|
+
this.markDirty(true);
|
|
836
|
+
}
|
|
837
|
+
},
|
|
838
|
+
onPackageRemoved: (packageName) => {
|
|
839
|
+
if (!this.notebook?.metadata?.required_packages) return;
|
|
840
|
+
const index = this.notebook.metadata.required_packages.indexOf(packageName);
|
|
841
|
+
if (index > -1) {
|
|
842
|
+
this.notebook.metadata.required_packages.splice(index, 1);
|
|
843
|
+
this.markDirty(true);
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
});
|
|
847
|
+
}
|
|
848
|
+
updated(changedProperties) {
|
|
849
|
+
super.updated(changedProperties);
|
|
850
|
+
if (changedProperties.has("pyConnected") || changedProperties.has("pyConnecting") || changedProperties.has("pyVersion") || changedProperties.has("isRunningAll")) {
|
|
851
|
+
this.updateToolbar();
|
|
852
|
+
}
|
|
853
|
+
if (this.notebook?.cells) {
|
|
854
|
+
this.notebook.cells.forEach((cell, index) => {
|
|
855
|
+
if (cell.cell_type === "code") {
|
|
856
|
+
const ref2 = this.cellRefs.get(index);
|
|
857
|
+
if (ref2?.value && !this.monacoEditors.has(index)) {
|
|
858
|
+
this.initializeMonacoEditor(ref2.value, cell, index);
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
});
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
render() {
|
|
865
|
+
if (!this.notebook) {
|
|
866
|
+
return html`<div class="loading">Loading notebook...</div>`;
|
|
867
|
+
}
|
|
868
|
+
return html`
|
|
869
|
+
<style>
|
|
870
|
+
${monacoStyles}
|
|
871
|
+
</style>
|
|
872
|
+
<div class="noteboocells">
|
|
873
|
+
${repeat(
|
|
874
|
+
this.notebook.cells,
|
|
875
|
+
(_cell, index) => index,
|
|
876
|
+
(cell, index) => this.renderCell(cell, index)
|
|
877
|
+
)}
|
|
878
|
+
</div>
|
|
879
|
+
`;
|
|
880
|
+
}
|
|
881
|
+
};
|
|
882
|
+
LyraNotebookEditor.styles = css`
|
|
883
|
+
:host {
|
|
884
|
+
display: block;
|
|
885
|
+
width: 100%;
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
.python-status {
|
|
889
|
+
display: flex;
|
|
890
|
+
align-items: center;
|
|
891
|
+
gap: 0.5rem;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
.python-version {
|
|
895
|
+
font-size: 0.9rem;
|
|
896
|
+
opacity: 0.8;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
.noteboocells {
|
|
900
|
+
display: flex;
|
|
901
|
+
flex-direction: column;
|
|
902
|
+
gap: 3rem;
|
|
903
|
+
max-width: 1200px;
|
|
904
|
+
margin: 0 auto;
|
|
905
|
+
padding: 2rem 1rem;
|
|
906
|
+
width: 100%;
|
|
907
|
+
box-sizing: border-box;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
.cell-wrapper {
|
|
911
|
+
position: relative;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
.cell {
|
|
915
|
+
border-radius: 4px;
|
|
916
|
+
overflow: hidden;
|
|
917
|
+
opacity: 0.9;
|
|
918
|
+
position: relative;
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
.cell-header-actions {
|
|
922
|
+
display: flex;
|
|
923
|
+
gap: 0.25rem;
|
|
924
|
+
align-items: center;
|
|
925
|
+
opacity: 0.5;
|
|
926
|
+
transition: opacity 0.2s;
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
.cell-header-actions .divider {
|
|
930
|
+
width: 1px;
|
|
931
|
+
height: 1rem;
|
|
932
|
+
background: var(--wa-color-outline);
|
|
933
|
+
margin: 0 0.25rem;
|
|
934
|
+
opacity: 0.5;
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
.cell-header:hover .cell-header-actions {
|
|
938
|
+
opacity: 1;
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
.cell-footer {
|
|
942
|
+
display: flex;
|
|
943
|
+
gap: 0.5rem;
|
|
944
|
+
align-items: center;
|
|
945
|
+
justify-content: flex-start;
|
|
946
|
+
padding: 0.5rem;
|
|
947
|
+
border-top: 1px solid var(--wa-color-outline);
|
|
948
|
+
opacity: 0.5;
|
|
949
|
+
transition: opacity 0.2s;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
.cell-footer:hover {
|
|
953
|
+
opacity: 1;
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
.markdown-cell {
|
|
957
|
+
cursor: pointer;
|
|
958
|
+
transition: opacity 0.2s;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
.markdown-cell:hover:not(.editing) {
|
|
962
|
+
opacity: 0.9;
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
.markdown-cell .cell-content {
|
|
966
|
+
padding: 1rem;
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
.markdown-cell.editing {
|
|
970
|
+
cursor: default;
|
|
971
|
+
padding: 0;
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
.markdown-cell.editing .cell-actions {
|
|
975
|
+
display: none;
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
.markdown-editor {
|
|
979
|
+
width: 100%;
|
|
980
|
+
min-height: 200px;
|
|
981
|
+
padding: 1rem;
|
|
982
|
+
font-family: monospace;
|
|
983
|
+
font-size: 0.95rem;
|
|
984
|
+
line-height: 1.6;
|
|
985
|
+
border: none;
|
|
986
|
+
outline: none;
|
|
987
|
+
resize: vertical;
|
|
988
|
+
background: transparent;
|
|
989
|
+
color: inherit;
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
.code-cell {
|
|
993
|
+
border-left: 3px solid var(--wa-color-primary-500);
|
|
994
|
+
transition: all 0.3s ease;
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
.code-cell.executing {
|
|
998
|
+
border-left: 4px solid var(--wa-color-primary-500);
|
|
999
|
+
box-shadow: 0 0 0 2px var(--wa-color-primary-500, rgba(59, 130, 246, 0.3));
|
|
1000
|
+
animation: pulse-cell 2s ease-in-out infinite;
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
@keyframes pulse-cell {
|
|
1004
|
+
0%, 100% {
|
|
1005
|
+
box-shadow: 0 0 0 2px var(--wa-color-primary-500, rgba(59, 130, 246, 0.3));
|
|
1006
|
+
opacity: 1;
|
|
1007
|
+
}
|
|
1008
|
+
50% {
|
|
1009
|
+
box-shadow: 0 0 0 4px var(--wa-color-primary-500, rgba(59, 130, 246, 0.5));
|
|
1010
|
+
opacity: 0.95;
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
.cell-header {
|
|
1015
|
+
display: flex;
|
|
1016
|
+
align-items: center;
|
|
1017
|
+
justify-content: flex-start;
|
|
1018
|
+
gap: 0.5rem;
|
|
1019
|
+
padding: 0.5rem 1rem;
|
|
1020
|
+
flex-wrap: nowrap;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
.cell-label {
|
|
1024
|
+
font-family: monospace;
|
|
1025
|
+
font-weight: bold;
|
|
1026
|
+
flex-shrink: 0;
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
.executing-indicator {
|
|
1030
|
+
display: inline-block;
|
|
1031
|
+
color: var(--wa-color-primary-500);
|
|
1032
|
+
font-weight: bold;
|
|
1033
|
+
font-size: 1.2em;
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
.cell-input {
|
|
1037
|
+
margin: 0;
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
.monaco-container {
|
|
1041
|
+
min-height: 100px;
|
|
1042
|
+
height: auto;
|
|
1043
|
+
width: 100%;
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
.cell-output {
|
|
1047
|
+
padding: 1rem;
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
.output-label {
|
|
1051
|
+
font-family: monospace;
|
|
1052
|
+
font-weight: bold;
|
|
1053
|
+
margin-bottom: 0.5rem;
|
|
1054
|
+
opacity: 0.7;
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
.cell-output pre {
|
|
1058
|
+
margin: 0;
|
|
1059
|
+
overflow-x: auto;
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
.cell-output code {
|
|
1063
|
+
font-family: 'Courier New', monospace;
|
|
1064
|
+
font-size: 0.9rem;
|
|
1065
|
+
line-height: 1.5;
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
.output-image {
|
|
1069
|
+
max-width: 100%;
|
|
1070
|
+
height: auto;
|
|
1071
|
+
display: block;
|
|
1072
|
+
margin: 0.5rem 0;
|
|
1073
|
+
border-radius: 4px;
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
.output-error {
|
|
1077
|
+
border-left: 3px solid var(--wa-color-danger-500);
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
.raw-cell {
|
|
1081
|
+
padding: 1rem;
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
.raw-cell pre {
|
|
1085
|
+
margin: 0;
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
.loading {
|
|
1089
|
+
display: flex;
|
|
1090
|
+
align-items: center;
|
|
1091
|
+
justify-content: center;
|
|
1092
|
+
height: 100%;
|
|
1093
|
+
font-size: 1.2rem;
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
.markdown-placeholder {
|
|
1097
|
+
display: flex;
|
|
1098
|
+
align-items: center;
|
|
1099
|
+
justify-content: center;
|
|
1100
|
+
gap: 0.75rem;
|
|
1101
|
+
padding: 3rem 1rem;
|
|
1102
|
+
opacity: 0.5;
|
|
1103
|
+
font-style: italic;
|
|
1104
|
+
transition: opacity 0.2s;
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
.markdown-cell.empty:hover .markdown-placeholder {
|
|
1108
|
+
opacity: 0.8;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
.markdown-placeholder wa-icon {
|
|
1112
|
+
font-size: 1.5rem;
|
|
1113
|
+
}
|
|
1114
|
+
`;
|
|
1115
|
+
__decorateClass([
|
|
1116
|
+
property({ attribute: false })
|
|
1117
|
+
], LyraNotebookEditor.prototype, "input", 2);
|
|
1118
|
+
__decorateClass([
|
|
1119
|
+
state()
|
|
1120
|
+
], LyraNotebookEditor.prototype, "notebook", 2);
|
|
1121
|
+
__decorateClass([
|
|
1122
|
+
state()
|
|
1123
|
+
], LyraNotebookEditor.prototype, "cellOutputs", 2);
|
|
1124
|
+
__decorateClass([
|
|
1125
|
+
state()
|
|
1126
|
+
], LyraNotebookEditor.prototype, "executingCells", 2);
|
|
1127
|
+
__decorateClass([
|
|
1128
|
+
state()
|
|
1129
|
+
], LyraNotebookEditor.prototype, "pyenv", 2);
|
|
1130
|
+
__decorateClass([
|
|
1131
|
+
state()
|
|
1132
|
+
], LyraNotebookEditor.prototype, "pyConnected", 2);
|
|
1133
|
+
__decorateClass([
|
|
1134
|
+
state()
|
|
1135
|
+
], LyraNotebookEditor.prototype, "pyConnecting", 2);
|
|
1136
|
+
__decorateClass([
|
|
1137
|
+
state()
|
|
1138
|
+
], LyraNotebookEditor.prototype, "pyVersion", 2);
|
|
1139
|
+
__decorateClass([
|
|
1140
|
+
state()
|
|
1141
|
+
], LyraNotebookEditor.prototype, "editingMarkdownCells", 2);
|
|
1142
|
+
__decorateClass([
|
|
1143
|
+
state()
|
|
1144
|
+
], LyraNotebookEditor.prototype, "executionCounter", 2);
|
|
1145
|
+
__decorateClass([
|
|
1146
|
+
state()
|
|
1147
|
+
], LyraNotebookEditor.prototype, "isRunningAll", 2);
|
|
1148
|
+
__decorateClass([
|
|
1149
|
+
state()
|
|
1150
|
+
], LyraNotebookEditor.prototype, "highlightedCellIndex", 2);
|
|
1151
|
+
LyraNotebookEditor = __decorateClass([
|
|
1152
|
+
customElement("lyra-notebook-editor")
|
|
1153
|
+
], LyraNotebookEditor);
|
|
1154
|
+
export {
|
|
1155
|
+
LyraNotebookEditor
|
|
1156
|
+
};
|
|
1157
|
+
//# sourceMappingURL=notebook-runtime-CFgw3UmB.js.map
|