labnotes 0.0.52__tar.gz → 0.0.54__tar.gz
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.
- {labnotes-0.0.52 → labnotes-0.0.54}/PKG-INFO +1 -1
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/load_experiment.py +1 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/static/labnotes.css +24 -0
- labnotes-0.0.54/src/labnotes/static/note-edit.js +23 -0
- labnotes-0.0.54/src/labnotes/static/note-editor.js +169 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/tables/open_tables.py +2 -1
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/tables/read_file.py +1 -0
- labnotes-0.0.54/src/labnotes/templates/editor_imports.html +23 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/templates/experiment.html +5 -1
- {labnotes-0.0.52 → labnotes-0.0.54}/.gitignore +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/CHANGELOG.md +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/MIGRATIONS.md +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/README.md +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/pyproject.toml +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/__init__.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/asset_url.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/cell_progress.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/check_counts.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/cli.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/context.md +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/context_guide.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/count_runs.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/create_app.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/create_experiment.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/data_html.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/default_config_text.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/default_counts.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/drain_watch.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/experiment_notes.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/experiment_progress.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/experiment_runs.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/experiments.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/format_duration.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/format_value.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/init.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/is_positive_int.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/list_experiments.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/list_runs.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/load_config.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/models.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/modes.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/notebook_kernel.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/output_html.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/parse_run_view.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/print_rows.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/python_blocks.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/read_config.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/read_path.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/render_markdown.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/runner/__init__.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/runner/describe_event.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/runner/execute_run.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/runner/hold_folder.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/runner/new_run_id.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/runner/new_stamped_run_id.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/runner/param_args.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/runner/param_grid.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/runner/plan_runs.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/runner/read_experiment_config.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/runner/run_experiment.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/runner/run_folder.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/runner/run_headless.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/runner/runs_parent.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/runner/shell_command.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/runner/start_experiment.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/runner/stop_then_kill.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/runner/wait_for_group.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/runner/write_run_json.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/save_note.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/saved_outputs.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/serve_app.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/server.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/sort_href.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/sql_quote.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/static/experiment.js +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/static/row-link.js +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/static/run-cell.js +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/static/view-toggle.js +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/stop_idle_kernels.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/summarize_config.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/tables/__init__.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/tables/experiment_rows.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/tables/file_prefix.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/tables/iso_time.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/tables/list_experiment_runs.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/tables/path_glob.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/tables/path_pattern.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/tables/read_experiment.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/tables/read_run.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/tables/summarize_experiments.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/templates/base.html +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/templates/index.html +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/templates/macros.html +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/tui/__init__.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/tui/dim.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/tui/format_elapsed.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/tui/run_app.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/tui/run_tui.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/src/labnotes/tui/runs_table.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/testing-conventions.toml +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/tests/integration/conftest.py +0 -0
- {labnotes-0.0.52 → labnotes-0.0.54}/uv.lock +0 -0
|
@@ -424,3 +424,27 @@ h3 {
|
|
|
424
424
|
.cell-output pre { background: none; border: 0; padding: 0.3rem 0; }
|
|
425
425
|
.cell-output .stderr, .cell-output .error { color: var(--bad); }
|
|
426
426
|
.cell-output img, .cell-output svg { max-width: 100%; height: auto; }
|
|
427
|
+
|
|
428
|
+
/* Editing a note in place */
|
|
429
|
+
.prose.editable { cursor: text; }
|
|
430
|
+
.edit-status { margin: 0; padding: 0.45rem 1.75rem; font-family: var(--sans); font-size: 0.8rem; color: var(--ink-3); border-bottom: 1px solid var(--rule); }
|
|
431
|
+
.editor { padding: 0.5rem 1.75rem 1.5rem; max-width: 46rem; }
|
|
432
|
+
.editor .cm-editor.cm-focused { outline: none; }
|
|
433
|
+
.editor .cm-content { font-family: var(--serif); font-size: 1.06rem; line-height: 1.68; caret-color: var(--ink); }
|
|
434
|
+
.editor .cm-line { padding: 0; }
|
|
435
|
+
.editor .md-h1 { font-size: 1.6rem; font-weight: 600; }
|
|
436
|
+
.editor .md-h2 { font-size: 1.3rem; font-weight: 600; }
|
|
437
|
+
.editor .md-h3 { font-size: 1.1rem; font-weight: 600; }
|
|
438
|
+
.editor .md-h4 { font-weight: 600; }
|
|
439
|
+
.editor .md-strong { font-weight: 700; }
|
|
440
|
+
.editor .md-em { font-style: italic; }
|
|
441
|
+
.editor .md-strike { text-decoration: line-through; }
|
|
442
|
+
.editor .md-link { color: var(--accent); }
|
|
443
|
+
.editor .md-code { font-family: var(--mono); font-size: 0.86em; }
|
|
444
|
+
.editor .md-quote { color: var(--ink-2); }
|
|
445
|
+
.editor .md-mark { color: var(--ink-3); }
|
|
446
|
+
.editor .md-code-line { font-family: var(--mono); font-size: 0.8rem; line-height: 1.5; background: var(--paper); padding: 0 1rem; }
|
|
447
|
+
.editor .py-keyword { color: var(--accent); }
|
|
448
|
+
.editor .py-string { color: var(--good); }
|
|
449
|
+
.editor .py-comment { color: var(--ink-3); font-style: italic; }
|
|
450
|
+
.editor .py-number { color: var(--bad); }
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const note = document.querySelector(".note[data-save-url]");
|
|
2
|
+
|
|
3
|
+
if (note) {
|
|
4
|
+
const prose = note.querySelector(".prose");
|
|
5
|
+
const status = note.querySelector(".edit-status");
|
|
6
|
+
const editor = import(note.dataset.editor);
|
|
7
|
+
editor.then(
|
|
8
|
+
() => prose.classList.add("editable"),
|
|
9
|
+
() => {
|
|
10
|
+
status.textContent = "Editing needs the network: the editor loads from esm.sh.";
|
|
11
|
+
status.hidden = false;
|
|
12
|
+
},
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
const open = async (event) => {
|
|
16
|
+
if (event.target.closest("a, button, output") || getSelection().toString()) return;
|
|
17
|
+
const { edit } = await editor;
|
|
18
|
+
if (!prose.isConnected) return;
|
|
19
|
+
prose.removeEventListener("click", open);
|
|
20
|
+
edit(note, prose, status);
|
|
21
|
+
};
|
|
22
|
+
prose.addEventListener("click", open);
|
|
23
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { defaultKeymap, history, historyKeymap } from "@codemirror/commands";
|
|
2
|
+
import { pythonLanguage } from "@codemirror/lang-python";
|
|
3
|
+
import {
|
|
4
|
+
HighlightStyle,
|
|
5
|
+
Language,
|
|
6
|
+
defineLanguageFacet,
|
|
7
|
+
languageDataProp,
|
|
8
|
+
syntaxHighlighting,
|
|
9
|
+
syntaxTree,
|
|
10
|
+
} from "@codemirror/language";
|
|
11
|
+
import { EditorState, RangeSetBuilder } from "@codemirror/state";
|
|
12
|
+
import { Decoration, EditorView, ViewPlugin, keymap } from "@codemirror/view";
|
|
13
|
+
import { tags } from "@lezer/highlight";
|
|
14
|
+
import { GFM, parseCode, parser } from "@lezer/markdown";
|
|
15
|
+
|
|
16
|
+
// Built from @lezer/markdown rather than @codemirror/lang-markdown, which
|
|
17
|
+
// imports HTML, CSS and JavaScript support that notes never use.
|
|
18
|
+
const data = defineLanguageFacet();
|
|
19
|
+
const markdown = new Language(
|
|
20
|
+
data,
|
|
21
|
+
parser.configure([
|
|
22
|
+
GFM,
|
|
23
|
+
parseCode({
|
|
24
|
+
codeParser: (info) => (info.split(/\s/)[0] === "python" ? pythonLanguage.parser : null),
|
|
25
|
+
}),
|
|
26
|
+
{ props: [languageDataProp.add({ Document: data })] },
|
|
27
|
+
]),
|
|
28
|
+
[],
|
|
29
|
+
"markdown",
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const style = HighlightStyle.define([
|
|
33
|
+
{ tag: tags.heading1, class: "md-h1" },
|
|
34
|
+
{ tag: tags.heading2, class: "md-h2" },
|
|
35
|
+
{ tag: tags.heading3, class: "md-h3" },
|
|
36
|
+
{ tag: [tags.heading4, tags.heading5, tags.heading6], class: "md-h4" },
|
|
37
|
+
{ tag: tags.strong, class: "md-strong" },
|
|
38
|
+
{ tag: tags.emphasis, class: "md-em" },
|
|
39
|
+
{ tag: tags.strikethrough, class: "md-strike" },
|
|
40
|
+
{ tag: [tags.link, tags.url], class: "md-link" },
|
|
41
|
+
{ tag: tags.monospace, class: "md-code" },
|
|
42
|
+
{ tag: tags.quote, class: "md-quote" },
|
|
43
|
+
{ tag: [tags.processingInstruction, tags.contentSeparator], class: "md-mark" },
|
|
44
|
+
{ tag: [tags.keyword, tags.operatorKeyword, tags.bool, tags.null], class: "py-keyword" },
|
|
45
|
+
{ tag: [tags.string, tags.special(tags.string)], class: "py-string" },
|
|
46
|
+
{ tag: tags.comment, class: "py-comment" },
|
|
47
|
+
{ tag: tags.number, class: "py-number" },
|
|
48
|
+
{ tag: [tags.function(tags.variableName), tags.definition(tags.variableName)], class: "py-name" },
|
|
49
|
+
]);
|
|
50
|
+
|
|
51
|
+
// Markup is hidden except on the lines the cursor is on, as in Obsidian.
|
|
52
|
+
const HIDDEN = new Set(["HeaderMark", "EmphasisMark", "CodeMark", "StrikethroughMark"]);
|
|
53
|
+
const hide = Decoration.replace({});
|
|
54
|
+
const codeLine = Decoration.line({ class: "md-code-line" });
|
|
55
|
+
|
|
56
|
+
function decorate(view) {
|
|
57
|
+
const builder = new RangeSetBuilder();
|
|
58
|
+
const { state } = view;
|
|
59
|
+
const active = new Set();
|
|
60
|
+
for (const range of state.selection.ranges) {
|
|
61
|
+
for (let n = state.doc.lineAt(range.from).number; n <= state.doc.lineAt(range.to).number; n++) active.add(n);
|
|
62
|
+
}
|
|
63
|
+
for (const { from, to } of view.visibleRanges) {
|
|
64
|
+
syntaxTree(state).iterate({
|
|
65
|
+
from,
|
|
66
|
+
to,
|
|
67
|
+
enter(node) {
|
|
68
|
+
if (node.name === "FencedCode") {
|
|
69
|
+
for (let at = node.from; at <= node.to; ) {
|
|
70
|
+
const line = state.doc.lineAt(at);
|
|
71
|
+
builder.add(line.from, line.from, codeLine);
|
|
72
|
+
at = line.to + 1;
|
|
73
|
+
}
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
if (!HIDDEN.has(node.name) || active.has(state.doc.lineAt(node.from).number)) return;
|
|
77
|
+
let end = node.to;
|
|
78
|
+
if (node.name === "HeaderMark" && state.doc.sliceString(end, end + 1) === " ") end += 1;
|
|
79
|
+
builder.add(node.from, end, hide);
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
return builder.finish();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const livePreview = ViewPlugin.fromClass(
|
|
87
|
+
class {
|
|
88
|
+
constructor(view) {
|
|
89
|
+
this.decorations = decorate(view);
|
|
90
|
+
}
|
|
91
|
+
update(update) {
|
|
92
|
+
if (update.docChanged || update.selectionSet || update.viewportChanged) {
|
|
93
|
+
this.decorations = decorate(update.view);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
{ decorations: (plugin) => plugin.decorations },
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
function show(status, message) {
|
|
101
|
+
status.textContent = message;
|
|
102
|
+
status.hidden = false;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function edit(note, prose, status) {
|
|
106
|
+
let base = JSON.parse(note.querySelector(".note-source").textContent);
|
|
107
|
+
let stale = false;
|
|
108
|
+
let timer;
|
|
109
|
+
let queue = Promise.resolve();
|
|
110
|
+
|
|
111
|
+
const host = document.createElement("div");
|
|
112
|
+
host.className = "editor";
|
|
113
|
+
prose.replaceWith(host);
|
|
114
|
+
|
|
115
|
+
const save = () =>
|
|
116
|
+
(queue = queue
|
|
117
|
+
.then(async () => {
|
|
118
|
+
clearTimeout(timer);
|
|
119
|
+
const content = view.state.doc.toString();
|
|
120
|
+
if (stale || content === base) return;
|
|
121
|
+
const response = await fetch(note.dataset.saveUrl, {
|
|
122
|
+
method: "PUT",
|
|
123
|
+
headers: { "Content-Type": "application/json" },
|
|
124
|
+
body: JSON.stringify({ base, content }),
|
|
125
|
+
});
|
|
126
|
+
if (response.status === 409) {
|
|
127
|
+
stale = true;
|
|
128
|
+
show(status, "This note changed on disk, so your edits are not saved. Copy them, then reload.");
|
|
129
|
+
} else if (response.ok) {
|
|
130
|
+
base = content;
|
|
131
|
+
} else {
|
|
132
|
+
show(status, `Not saved: ${response.status} ${response.statusText}`);
|
|
133
|
+
}
|
|
134
|
+
})
|
|
135
|
+
.catch((error) => show(status, `Not saved: ${error.message}`)));
|
|
136
|
+
|
|
137
|
+
const close = async () => {
|
|
138
|
+
await save();
|
|
139
|
+
if (stale) return;
|
|
140
|
+
document.removeEventListener("mousedown", outside);
|
|
141
|
+
location.reload();
|
|
142
|
+
};
|
|
143
|
+
const outside = (event) => {
|
|
144
|
+
if (!note.contains(event.target)) close();
|
|
145
|
+
};
|
|
146
|
+
document.addEventListener("mousedown", outside);
|
|
147
|
+
|
|
148
|
+
const view = new EditorView({
|
|
149
|
+
parent: host,
|
|
150
|
+
state: EditorState.create({
|
|
151
|
+
doc: base,
|
|
152
|
+
extensions: [
|
|
153
|
+
history(),
|
|
154
|
+
keymap.of([{ key: "Escape", run: () => (close(), true) }, ...defaultKeymap, ...historyKeymap]),
|
|
155
|
+
markdown,
|
|
156
|
+
syntaxHighlighting(style),
|
|
157
|
+
livePreview,
|
|
158
|
+
EditorView.lineWrapping,
|
|
159
|
+
EditorView.contentAttributes.of({ "aria-label": "Note" }),
|
|
160
|
+
EditorView.updateListener.of((update) => {
|
|
161
|
+
if (!update.docChanged) return;
|
|
162
|
+
clearTimeout(timer);
|
|
163
|
+
timer = setTimeout(save, 500);
|
|
164
|
+
}),
|
|
165
|
+
],
|
|
166
|
+
}),
|
|
167
|
+
});
|
|
168
|
+
view.focus();
|
|
169
|
+
}
|
|
@@ -28,7 +28,8 @@ CREATE VIEW experiments AS
|
|
|
28
28
|
"""
|
|
29
29
|
FILES = """
|
|
30
30
|
CREATE TABLE files (
|
|
31
|
-
experiment TEXT, filename TEXT, path TEXT PRIMARY KEY, ctime TEXT, mtime TEXT
|
|
31
|
+
experiment TEXT, filename TEXT, path TEXT PRIMARY KEY, ctime TEXT, mtime TEXT,
|
|
32
|
+
mtime_ns INTEGER
|
|
32
33
|
)
|
|
33
34
|
"""
|
|
34
35
|
RUNS = """
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{# Every package is fetched with its imports left bare (the * prefix) and pinned here, so each loads once. Two copies of @codemirror/state break CodeMirror. #}
|
|
2
|
+
<script type="importmap">
|
|
3
|
+
{
|
|
4
|
+
"imports": {
|
|
5
|
+
"@codemirror/autocomplete": "https://esm.sh/*@codemirror/autocomplete@6.20.4",
|
|
6
|
+
"@codemirror/commands": "https://esm.sh/*@codemirror/commands@6.11.1",
|
|
7
|
+
"@codemirror/lang-python": "https://esm.sh/*@codemirror/lang-python@6.2.1",
|
|
8
|
+
"@codemirror/language": "https://esm.sh/*@codemirror/language@6.13.1",
|
|
9
|
+
"@codemirror/state": "https://esm.sh/*@codemirror/state@6.7.6",
|
|
10
|
+
"@codemirror/streamparser": "https://esm.sh/*@codemirror/streamparser@6.0.0",
|
|
11
|
+
"@codemirror/view": "https://esm.sh/*@codemirror/view@6.43.15",
|
|
12
|
+
"@lezer/common": "https://esm.sh/*@lezer/common@1.5.3",
|
|
13
|
+
"@lezer/highlight": "https://esm.sh/*@lezer/highlight@1.2.5",
|
|
14
|
+
"@lezer/lr": "https://esm.sh/*@lezer/lr@1.4.11",
|
|
15
|
+
"@lezer/markdown": "https://esm.sh/*@lezer/markdown@1.8.1",
|
|
16
|
+
"@lezer/python": "https://esm.sh/*@lezer/python@1.1.19",
|
|
17
|
+
"@marijn/find-cluster-break": "https://esm.sh/*@marijn/find-cluster-break@1.0.4",
|
|
18
|
+
"crelt": "https://esm.sh/*crelt@1.0.7",
|
|
19
|
+
"style-mod": "https://esm.sh/*style-mod@4.1.4",
|
|
20
|
+
"w3c-keyname": "https://esm.sh/*w3c-keyname@2.2.8"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
</script>
|
|
@@ -37,11 +37,15 @@
|
|
|
37
37
|
<nav class="tabs sub">
|
|
38
38
|
{% for each in e.notes %}<a href="?tab=notebooks&note={{ each.name | urlencode }}"{% if each.name == note.name %} aria-current="page"{% endif %}>{{ each.name }}</a>{% endfor %}
|
|
39
39
|
</nav>
|
|
40
|
-
<article class="note" data-run-url="{{ url_for('run_block', name=e.name, note=note.name) }}" data-restart-url="{{ url_for('restart_kernel', name=e.name, note=note.name) }}">
|
|
40
|
+
<article class="note" data-run-url="{{ url_for('run_block', name=e.name, note=note.name) }}" data-restart-url="{{ url_for('restart_kernel', name=e.name, note=note.name) }}" data-save-url="{{ url_for('put_note', name=e.name, note=note.name) }}" data-editor="{{ asset('note-editor.js') }}">
|
|
41
41
|
{% if note.runnable %}<header class="note-name"><button type="button" class="restart" title="Stop this note's Python kernel. The next Run starts a fresh one.">Restart</button></header>{% endif %}
|
|
42
|
+
<p class="edit-status" role="status" hidden></p>
|
|
42
43
|
<div class="prose">{{ note.html | safe }}</div>
|
|
44
|
+
<script type="application/json" class="note-source">{{ note.content | tojson }}</script>
|
|
43
45
|
</article>
|
|
46
|
+
{% include "editor_imports.html" %}
|
|
44
47
|
<script src="{{ asset('run-cell.js') }}" defer></script>
|
|
48
|
+
<script type="module" src="{{ asset('note-edit.js') }}"></script>
|
|
45
49
|
{% else %}
|
|
46
50
|
<p class="empty">No notebooks yet.</p>
|
|
47
51
|
{% endif %}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|