labnotes 0.0.66__tar.gz → 0.0.67__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.66 → labnotes-0.0.67}/PKG-INFO +1 -1
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/create_app.py +33 -11
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/create_note.py +2 -2
- labnotes-0.0.67/src/labnotes/note_path.py +16 -0
- labnotes-0.0.67/src/labnotes/static/new-note.js +43 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/static/note-edit.js +17 -8
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/static/note-editor.js +7 -1
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/templates/experiment.html +6 -11
- labnotes-0.0.67/src/labnotes/templates/note.html +6 -0
- labnotes-0.0.66/src/labnotes/static/new-note.js +0 -12
- labnotes-0.0.66/src/labnotes/wait_for_note.py +0 -13
- {labnotes-0.0.66 → labnotes-0.0.67}/.gitignore +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/CHANGELOG.md +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/MIGRATIONS.md +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/README.md +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/pyproject.toml +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/__init__.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/asset_url.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/check_counts.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/cli.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/context.md +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/context_guide.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/count_runs.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/create_experiment.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/data_html.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/default_config_text.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/default_counts.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/drain_watch.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/experiment_notes.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/experiment_runs.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/experiments.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/file_changes.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/format_duration.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/format_value.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/group_runs.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/init.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/invocation_started.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/is_positive_int.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/list_experiments.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/list_runs.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/load_config.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/load_experiment.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/models.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/modes.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/nests_runs.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/notebook_kernel.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/output_html.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/parse_run_view.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/print_rows.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/python_blocks.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/read_config.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/read_path.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/render_markdown.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/runner/__init__.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/runner/describe_event.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/runner/execute_run.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/runner/hold_folder.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/runner/new_param_run_id.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/runner/new_run_id.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/runner/new_stamped_run_id.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/runner/param_args.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/runner/param_grid.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/runner/param_run_name.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/runner/plan_runs.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/runner/read_experiment_config.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/runner/run_experiment.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/runner/run_folder.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/runner/run_headless.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/runner/runs_parent.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/runner/shell_command.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/runner/start_experiment.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/runner/stop_then_kill.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/runner/wait_for_group.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/runner/write_run_json.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/save_conflict_copy.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/save_note.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/saved_outputs.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/serve_app.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/server.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/sort_href.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/sql_quote.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/static/experiment.js +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/static/labnotes.css +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/static/row-link.js +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/static/run-cell.js +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/static/view-toggle.js +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/summarize_config.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/tables/__init__.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/tables/experiment_rows.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/tables/file_prefix.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/tables/iso_time.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/tables/list_experiment_runs.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/tables/open_tables.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/tables/path_glob.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/tables/path_pattern.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/tables/read_experiment.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/tables/read_file.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/tables/read_run.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/tables/summarize_experiments.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/templates/base.html +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/templates/editor_imports.html +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/templates/index.html +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/tui/__init__.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/tui/dim.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/tui/format_elapsed.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/tui/run_app.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/tui/run_tui.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/src/labnotes/tui/runs_table.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/testing-conventions.toml +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/tests/integration/conftest.py +0 -0
- {labnotes-0.0.66 → labnotes-0.0.67}/uv.lock +0 -0
|
@@ -28,13 +28,14 @@ from labnotes.format_value import format_value
|
|
|
28
28
|
from labnotes.list_experiments import list_experiments
|
|
29
29
|
from labnotes.load_experiment import load_experiment
|
|
30
30
|
from labnotes.models import LabnotesConfig
|
|
31
|
+
from labnotes.note_path import note_path
|
|
31
32
|
from labnotes.notebook_kernel import NotebookKernel
|
|
32
33
|
from labnotes.python_blocks import python_blocks
|
|
34
|
+
from labnotes.render_markdown import render_markdown
|
|
33
35
|
from labnotes.save_conflict_copy import save_conflict_copy
|
|
34
36
|
from labnotes.save_note import save_note
|
|
35
37
|
from labnotes.saved_outputs import saved_outputs
|
|
36
38
|
from labnotes.tables.open_tables import open_tables
|
|
37
|
-
from labnotes.wait_for_note import wait_for_note
|
|
38
39
|
|
|
39
40
|
HERE = Path(__file__).parent
|
|
40
41
|
templates = Jinja2Templates(directory=HERE / "templates")
|
|
@@ -100,13 +101,24 @@ def create_app(config: LabnotesConfig) -> Starlette:
|
|
|
100
101
|
if request.headers.get("content-type") != "application/json":
|
|
101
102
|
raise HTTPException(415, "Send JSON")
|
|
102
103
|
name = request.path_params["name"]
|
|
103
|
-
|
|
104
|
-
if
|
|
104
|
+
path = create_note(config, name)
|
|
105
|
+
if path is None:
|
|
105
106
|
raise HTTPException(404, "No such experiment")
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
107
|
+
content = path.read_text(encoding="utf-8")
|
|
108
|
+
return templates.TemplateResponse(
|
|
109
|
+
request,
|
|
110
|
+
"note.html",
|
|
111
|
+
{
|
|
112
|
+
"e": {"name": name},
|
|
113
|
+
"note": {
|
|
114
|
+
"name": path.name,
|
|
115
|
+
"content": content,
|
|
116
|
+
"html": render_markdown(content, ()),
|
|
117
|
+
"runnable": bool(python_blocks(content)),
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
status_code=201,
|
|
121
|
+
)
|
|
110
122
|
|
|
111
123
|
async def get_note(request: Request) -> Response:
|
|
112
124
|
note = await read_note(request)
|
|
@@ -118,23 +130,33 @@ def create_app(config: LabnotesConfig) -> Starlette:
|
|
|
118
130
|
media_type="text/event-stream",
|
|
119
131
|
)
|
|
120
132
|
|
|
133
|
+
def note_file(request: Request) -> Path:
|
|
134
|
+
if request.headers.get("content-type") != "application/json":
|
|
135
|
+
raise HTTPException(415, "Send JSON")
|
|
136
|
+
path = note_path(
|
|
137
|
+
config, request.path_params["name"], request.path_params["note"]
|
|
138
|
+
)
|
|
139
|
+
if path is None:
|
|
140
|
+
raise HTTPException(404, "No such note")
|
|
141
|
+
return path
|
|
142
|
+
|
|
121
143
|
async def put_note(request: Request) -> Response:
|
|
122
|
-
|
|
144
|
+
path = note_file(request)
|
|
123
145
|
body = await request.json()
|
|
124
146
|
base, content = body.get("base"), body.get("content")
|
|
125
147
|
if not isinstance(base, str) or not isinstance(content, str):
|
|
126
148
|
raise HTTPException(400, "Send base and content as strings")
|
|
127
|
-
current = save_note(
|
|
149
|
+
current = save_note(path, base, content)
|
|
128
150
|
if current is not None:
|
|
129
151
|
return JSONResponse({"content": current}, status_code=409)
|
|
130
152
|
return Response(status_code=204)
|
|
131
153
|
|
|
132
154
|
async def post_conflict(request: Request) -> Response:
|
|
133
|
-
|
|
155
|
+
path = note_file(request)
|
|
134
156
|
content = (await request.json()).get("content")
|
|
135
157
|
if not isinstance(content, str):
|
|
136
158
|
raise HTTPException(400, "Send content as a string")
|
|
137
|
-
copy = save_conflict_copy(
|
|
159
|
+
copy = save_conflict_copy(path, content, datetime.now())
|
|
138
160
|
return JSONResponse({"name": copy.name}, status_code=201)
|
|
139
161
|
|
|
140
162
|
async def run_block(request: Request) -> Response:
|
|
@@ -4,7 +4,7 @@ from labnotes.models import LabnotesConfig
|
|
|
4
4
|
from labnotes.tables.file_prefix import file_prefix
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
def create_note(config: LabnotesConfig, experiment: str) ->
|
|
7
|
+
def create_note(config: LabnotesConfig, experiment: str) -> Path | None:
|
|
8
8
|
def folder(template: str) -> Path:
|
|
9
9
|
return config.root / template.replace("{experiment}", experiment)
|
|
10
10
|
|
|
@@ -17,7 +17,7 @@ def create_note(config: LabnotesConfig, experiment: str) -> str | None:
|
|
|
17
17
|
try:
|
|
18
18
|
with (notes / filename).open("x", encoding="utf-8") as note:
|
|
19
19
|
note.write("# Untitled\n")
|
|
20
|
-
return filename
|
|
20
|
+
return notes / filename
|
|
21
21
|
except FileExistsError:
|
|
22
22
|
n += 1
|
|
23
23
|
filename = f"Untitled {n}.md"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from pathlib import Path, PurePosixPath
|
|
2
|
+
|
|
3
|
+
from labnotes.models import LabnotesConfig
|
|
4
|
+
from labnotes.tables.file_prefix import file_prefix
|
|
5
|
+
from labnotes.tables.path_pattern import path_pattern
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def note_path(config: LabnotesConfig, experiment: str, note: str) -> Path | None:
|
|
9
|
+
relative = file_prefix(config.files).replace("{experiment}", experiment) + note
|
|
10
|
+
if ".." in PurePosixPath(relative).parts or not note.endswith(".md"):
|
|
11
|
+
return None
|
|
12
|
+
found = path_pattern(config.files).fullmatch(relative)
|
|
13
|
+
if found is None or found["experiment"] != experiment:
|
|
14
|
+
return None
|
|
15
|
+
path = config.root / relative
|
|
16
|
+
return path if path.is_relative_to(config.root) and path.is_file() else None
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
function show(note, button) {
|
|
2
|
+
let tabs = document.querySelector(".tabs.sub");
|
|
3
|
+
if (!tabs) {
|
|
4
|
+
tabs = document.createElement("nav");
|
|
5
|
+
tabs.className = "tabs sub";
|
|
6
|
+
button.closest(".empty").replaceWith(tabs);
|
|
7
|
+
tabs.append(button);
|
|
8
|
+
}
|
|
9
|
+
const search = `?tab=notebooks¬e=${encodeURIComponent(note.dataset.note)}`;
|
|
10
|
+
const link = document.createElement("a");
|
|
11
|
+
link.href = search;
|
|
12
|
+
link.textContent = note.dataset.note;
|
|
13
|
+
link.setAttribute("aria-current", "page");
|
|
14
|
+
for (const other of tabs.querySelectorAll("a")) other.removeAttribute("aria-current");
|
|
15
|
+
button.before(link);
|
|
16
|
+
const count = document.querySelector(".tabs:not(.sub) [aria-current] .count");
|
|
17
|
+
count.textContent = Number(count.textContent) + 1;
|
|
18
|
+
const old = document.querySelector(".note");
|
|
19
|
+
if (old) old.replaceWith(note);
|
|
20
|
+
else tabs.after(note);
|
|
21
|
+
history.replaceState(null, "", search);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
for (const button of document.querySelectorAll(".new-note")) {
|
|
25
|
+
// An open editor closes, and reloads the page, on a mousedown outside it.
|
|
26
|
+
button.addEventListener("mousedown", (event) => event.stopPropagation());
|
|
27
|
+
button.addEventListener("click", async () => {
|
|
28
|
+
button.disabled = true;
|
|
29
|
+
const response = await fetch(button.dataset.url, {
|
|
30
|
+
method: "POST",
|
|
31
|
+
headers: { "Content-Type": "application/json" },
|
|
32
|
+
body: "{}",
|
|
33
|
+
});
|
|
34
|
+
button.disabled = false;
|
|
35
|
+
if (!response.ok) return;
|
|
36
|
+
const holder = document.createElement("template");
|
|
37
|
+
holder.innerHTML = await response.text();
|
|
38
|
+
const note = holder.content.querySelector(".note");
|
|
39
|
+
const { attach } = await import(button.dataset.module);
|
|
40
|
+
show(note, button);
|
|
41
|
+
attach(note, true);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
let detach = () => {};
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
export function attach(note, openNow) {
|
|
4
|
+
detach();
|
|
4
5
|
const prose = note.querySelector(".prose");
|
|
5
6
|
const status = note.querySelector(".edit-status");
|
|
6
7
|
const editor = import(note.dataset.editor);
|
|
7
8
|
const source = JSON.parse(note.querySelector(".note-source").textContent);
|
|
8
9
|
let editing = null;
|
|
10
|
+
let attached = true;
|
|
9
11
|
|
|
10
12
|
editor.then(
|
|
11
13
|
() => prose.classList.add("editable"),
|
|
@@ -18,17 +20,15 @@ if (note) {
|
|
|
18
20
|
const open = async (event) => {
|
|
19
21
|
if (event.target.closest("a, button, output") || getSelection().toString()) return;
|
|
20
22
|
const { edit } = await editor;
|
|
21
|
-
if (editing) return;
|
|
23
|
+
if (editing || !attached) return;
|
|
22
24
|
prose.removeEventListener("click", open);
|
|
23
25
|
editing = edit(note, prose, status);
|
|
24
26
|
};
|
|
25
27
|
prose.addEventListener("click", open);
|
|
26
|
-
if (
|
|
27
|
-
history.replaceState(null, "", location.pathname + location.search);
|
|
28
|
-
open({ target: prose });
|
|
29
|
-
}
|
|
28
|
+
if (openNow) open({ target: prose });
|
|
30
29
|
|
|
31
|
-
new EventSource(note.dataset.changesUrl)
|
|
30
|
+
const changes = new EventSource(note.dataset.changesUrl);
|
|
31
|
+
changes.addEventListener("message", (event) => {
|
|
32
32
|
if (JSON.parse(event.data) !== note.dataset.note) return;
|
|
33
33
|
if (editing) return editing.pull();
|
|
34
34
|
// A touch or a duplicate event leaves the text alone, and reloading then
|
|
@@ -39,4 +39,13 @@ if (note) {
|
|
|
39
39
|
if (text !== source) location.reload();
|
|
40
40
|
});
|
|
41
41
|
});
|
|
42
|
+
|
|
43
|
+
detach = () => {
|
|
44
|
+
attached = false;
|
|
45
|
+
changes.close();
|
|
46
|
+
editing?.detach();
|
|
47
|
+
};
|
|
42
48
|
}
|
|
49
|
+
|
|
50
|
+
const note = document.querySelector(".note[data-save-url]");
|
|
51
|
+
if (note) attach(note, false);
|
|
@@ -228,5 +228,11 @@ export function edit(note, prose, status) {
|
|
|
228
228
|
}),
|
|
229
229
|
});
|
|
230
230
|
view.focus();
|
|
231
|
-
return {
|
|
231
|
+
return {
|
|
232
|
+
pull: () => run(pull),
|
|
233
|
+
detach: () => {
|
|
234
|
+
document.removeEventListener("mousedown", outside);
|
|
235
|
+
save();
|
|
236
|
+
},
|
|
237
|
+
};
|
|
232
238
|
}
|
|
@@ -35,21 +35,16 @@
|
|
|
35
35
|
{% set note = (e.notes | selectattr('name', 'equalto', chosen) | first) or (e.notes | first) %}
|
|
36
36
|
{% if note %}
|
|
37
37
|
<nav class="tabs sub">
|
|
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 %}<button type="button" class="new-note" data-url="{{ url_for('post_note', name=e.name) }}" aria-label="New note" title="New note">+</button>
|
|
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 %}<button type="button" class="new-note" data-url="{{ url_for('post_note', name=e.name) }}" data-module="{{ asset('note-edit.js') }}" aria-label="New note" title="New note">+</button>
|
|
39
39
|
</nav>
|
|
40
|
-
|
|
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>
|
|
43
|
-
<div class="prose">{{ note.html | safe }}</div>
|
|
44
|
-
<script type="application/json" class="note-source">{{ note.content | tojson }}</script>
|
|
45
|
-
</article>
|
|
46
|
-
{% include "editor_imports.html" %}
|
|
40
|
+
{% include "note.html" %}
|
|
47
41
|
<script src="{{ asset('run-cell.js') }}" defer></script>
|
|
48
|
-
<script type="module" src="{{ asset('note-edit.js') }}"></script>
|
|
49
42
|
{% else %}
|
|
50
|
-
<p class="empty">No notebooks yet. <button type="button" class="new-note" data-url="{{ url_for('post_note', name=e.name) }}" aria-label="New note" title="New note">+</button></p>
|
|
43
|
+
<p class="empty">No notebooks yet. <button type="button" class="new-note" data-url="{{ url_for('post_note', name=e.name) }}" data-module="{{ asset('note-edit.js') }}" aria-label="New note" title="New note">+</button></p>
|
|
51
44
|
{% endif %}
|
|
52
|
-
|
|
45
|
+
{% include "editor_imports.html" %}
|
|
46
|
+
<script type="module" src="{{ asset('note-edit.js') }}"></script>
|
|
47
|
+
<script type="module" src="{{ asset('new-note.js') }}"></script>
|
|
53
48
|
{% else %}
|
|
54
49
|
<section id="runs">
|
|
55
50
|
{% set view = e.run_view %}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<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-conflict-url="{{ url_for('post_conflict', name=e.name, note=note.name) }}" data-editor="{{ asset('note-editor.js') }}" data-changes-url="{{ url_for('changes', name=e.name) }}" data-note="{{ note.name }}">
|
|
2
|
+
{% 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 %}
|
|
3
|
+
<p class="edit-status" role="status" hidden></p>
|
|
4
|
+
<div class="prose">{{ note.html | safe }}</div>
|
|
5
|
+
<script type="application/json" class="note-source">{{ note.content | tojson }}</script>
|
|
6
|
+
</article>
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
for (const button of document.querySelectorAll(".new-note")) {
|
|
2
|
-
button.addEventListener("click", async () => {
|
|
3
|
-
button.disabled = true;
|
|
4
|
-
const response = await fetch(button.dataset.url, {
|
|
5
|
-
method: "POST",
|
|
6
|
-
headers: { "Content-Type": "application/json" },
|
|
7
|
-
body: "{}",
|
|
8
|
-
});
|
|
9
|
-
if (response.ok) location.assign((await response.json()).url);
|
|
10
|
-
else button.disabled = false;
|
|
11
|
-
});
|
|
12
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
from pathlib import Path
|
|
3
|
-
|
|
4
|
-
from dirsql import DirSQL
|
|
5
|
-
|
|
6
|
-
from labnotes.experiment_notes import experiment_notes
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
async def wait_for_note(tables: DirSQL, root: Path, experiment: str, note: str) -> None:
|
|
10
|
-
for _ in range(40):
|
|
11
|
-
if await experiment_notes(tables, root, experiment, note):
|
|
12
|
-
return
|
|
13
|
-
await asyncio.sleep(0.05)
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|