labnotes 0.0.98__tar.gz → 0.0.99__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.98 → labnotes-0.0.99}/PKG-INFO +1 -1
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/static/labnotes.css +49 -17
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/static/new-note.js +0 -2
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/static/note-edit.js +9 -16
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/static/note-editor.js +113 -69
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/templates/editor_imports.html +1 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/.gitignore +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/CHANGELOG.md +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/MIGRATIONS.md +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/README.md +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/pyproject.toml +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/__init__.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/__main__.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/asset_url.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/check_counts.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/cli.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/context.md +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/context_guide.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/count_groups.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/count_runs.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/create_app.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/create_experiment.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/create_note.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/daemon/__init__.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/daemon/control_run.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/daemon/daemon_app.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/daemon/daemon_client.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/daemon/daemon_running.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/daemon/daemon_server.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/daemon/describe_status.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/daemon/ensure_daemon.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/daemon/exit_when_idle.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/daemon/remote_start.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/daemon/run_daemon.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/daemon/run_logs.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/daemon/serve_daemon.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/daemon/socket_path.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/daemon/stop_runs.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/daemon/watch_daemon.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/daemon/watch_status.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/daemon_command.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/data_html.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/default_config_text.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/default_counts.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/drain_watch.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/experiment_notes.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/experiment_runs.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/experiments.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/file_changes.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/format_duration.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/format_started.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/format_value.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/group_runs.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/init.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/invocation_started.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/is_positive_int.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/list_experiments.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/list_runs.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/load_config.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/load_experiment.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/load_run.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/log.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/log_path.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/models.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/modes.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/nests_runs.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/note_path.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/notebook_kernel.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/output_html.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/parse_run_view.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/print_rows.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/python_blocks.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/read_config.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/read_path.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/render_markdown.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/run_command.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/run_control.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/run_output.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/run_status.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/runner/__init__.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/runner/describe_event.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/runner/execute_run.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/runner/hold_folder.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/runner/new_param_run_id.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/runner/new_run_id.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/runner/new_stamped_run_id.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/runner/param_args.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/runner/param_grid.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/runner/param_run_name.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/runner/plan_runs.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/runner/prepare_experiment.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/runner/read_experiment_config.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/runner/run_experiment.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/runner/run_folder.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/runner/run_headless.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/runner/runs_parent.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/runner/shell_command.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/runner/signal_tree.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/runner/stop_then_kill.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/runner/wait_for_tree.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/runner/write_run_json.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/save_conflict_copy.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/save_note.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/saved_outputs.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/serve_app.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/server.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/shared_params.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/sort_href.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/sql_quote.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/static/elapsed.js +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/static/experiment.js +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/static/local-time.js +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/static/row-link.js +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/static/run-cell.js +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/static/run-output.js +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/static/view-toggle.js +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/summarize_config.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/tables/__init__.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/tables/experiment_rows.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/tables/experiment_run_ids.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/tables/file_prefix.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/tables/file_text.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/tables/iso_time.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/tables/list_experiment_runs.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/tables/note_text.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/tables/open_tables.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/tables/path_glob.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/tables/path_pattern.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/tables/read_experiment.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/tables/read_file.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/tables/read_log.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/tables/read_run.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/tables/summarize_experiments.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/templates/base.html +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/templates/experiment.html +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/templates/index.html +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/templates/note.html +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/templates/run.html +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/tui/__init__.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/tui/browse_app.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/tui/browse_tui.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/tui/dim.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/tui/experiment_screen.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/tui/experiments_screen.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/tui/fill_table.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/tui/format_elapsed.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/tui/group_cells.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/tui/invocation_columns.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/tui/invocation_rows.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/tui/leave_prompt.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/tui/reload_on_change.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/tui/run_app.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/tui/run_cells.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/tui/run_output.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/tui/run_screen.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/tui/run_tui.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/tui/runs_table.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/tui/stopped_title.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/wait_for_note.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/src/labnotes/watch_rows.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/testing-conventions.toml +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/tests/integration/conftest.py +0 -0
- {labnotes-0.0.98 → labnotes-0.0.99}/uv.lock +0 -0
|
@@ -418,7 +418,7 @@ h3 {
|
|
|
418
418
|
top: 0.5rem;
|
|
419
419
|
right: 0.5rem;
|
|
420
420
|
}
|
|
421
|
-
.cell .run, .note-name .restart {
|
|
421
|
+
.cell .run, .editor .run, .note-name .restart {
|
|
422
422
|
font: inherit;
|
|
423
423
|
font-family: var(--sans);
|
|
424
424
|
font-size: 0.75rem;
|
|
@@ -429,8 +429,8 @@ h3 {
|
|
|
429
429
|
color: var(--ink-2);
|
|
430
430
|
cursor: pointer;
|
|
431
431
|
}
|
|
432
|
-
.cell .run:hover, .note-name .restart:hover { color: var(--accent); border-color: var(--accent); }
|
|
433
|
-
.cell .run:disabled, .note-name .restart:disabled { cursor: progress; opacity: 0.6; }
|
|
432
|
+
.cell .run:hover, .editor .run:hover, .note-name .restart:hover { color: var(--accent); border-color: var(--accent); }
|
|
433
|
+
.cell .run:disabled, .editor .run:disabled, .note-name .restart:disabled { cursor: progress; opacity: 0.6; }
|
|
434
434
|
.note-name:has(.restart) { display: flex; align-items: center; justify-content: flex-end; padding-top: 0.45rem; padding-bottom: 0.45rem; }
|
|
435
435
|
.cell-output { display: block; }
|
|
436
436
|
.cell-output:not(:empty) { margin-top: 0.4rem; padding-left: 0.9rem; border-left: 2px solid var(--accent-soft); overflow-x: auto; }
|
|
@@ -438,25 +438,57 @@ h3 {
|
|
|
438
438
|
.cell-output .stderr, .cell-output .error { color: var(--bad); }
|
|
439
439
|
.cell-output img, .cell-output svg { max-width: 100%; height: auto; }
|
|
440
440
|
|
|
441
|
-
/* Editing a note in place */
|
|
442
|
-
.prose.editable { cursor: text; }
|
|
441
|
+
/* Editing a note in place: CodeMirror styled to match .prose */
|
|
443
442
|
.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); }
|
|
444
443
|
.editor { padding: 0.5rem 1.75rem 1.5rem; max-width: 46rem; }
|
|
445
444
|
.editor .cm-editor.cm-focused { outline: none; }
|
|
446
|
-
.editor .cm-content { font-family: var(--serif); font-size: 1.06rem; line-height: 1.68; caret-color: var(--ink); }
|
|
445
|
+
.editor .cm-content { padding: 0.8em 0; font-family: var(--serif); font-size: 1.06rem; line-height: 1.68; caret-color: var(--ink); }
|
|
447
446
|
.editor .cm-line { padding: 0; }
|
|
448
|
-
.editor .
|
|
449
|
-
.editor .
|
|
450
|
-
.editor .
|
|
451
|
-
.editor .
|
|
452
|
-
.editor .
|
|
453
|
-
.editor .
|
|
454
|
-
.editor .
|
|
455
|
-
.editor .
|
|
456
|
-
.editor .
|
|
457
|
-
.editor .
|
|
447
|
+
.editor .cm-line:has(> br:only-child):not(.cm-atomic-fenced-code) { line-height: 0.8em; }
|
|
448
|
+
.editor .cm-atomic-h1, .editor .cm-atomic-h2, .editor .cm-atomic-h3, .editor .cm-atomic-h4, .editor .cm-atomic-h5, .editor .cm-atomic-h6 { font-weight: 600; letter-spacing: -0.01em; line-height: 1.3; padding: 1em 0 0.5em; }
|
|
449
|
+
.editor .cm-line.cm-atomic-h1 { font-size: 1.6rem; }
|
|
450
|
+
.editor .cm-line.cm-atomic-h2 { font-size: 1.3rem; }
|
|
451
|
+
.editor .cm-line.cm-atomic-h3 { font-size: 1.1rem; }
|
|
452
|
+
.editor .cm-line.cm-atomic-h4, .editor .cm-line.cm-atomic-h5, .editor .cm-line.cm-atomic-h6 { font-size: 1rem; }
|
|
453
|
+
.editor .cm-atomic-strong { font-weight: 700; }
|
|
454
|
+
.editor .cm-atomic-em { font-style: italic; }
|
|
455
|
+
.editor .cm-atomic-strike { text-decoration: line-through; }
|
|
456
|
+
.editor .cm-atomic-inline-code { font-family: var(--mono); font-size: 0.86em; background: var(--chip); padding: 0.1em 0.35em; border-radius: 4px; }
|
|
457
|
+
.editor .cm-atomic-link { text-decoration: underline; text-decoration-color: var(--rule-strong); text-underline-offset: 3px; }
|
|
458
|
+
.editor .cm-atomic-link::after {
|
|
459
|
+
content: "↗";
|
|
460
|
+
margin-left: 0.15em;
|
|
461
|
+
font-family: var(--sans);
|
|
462
|
+
font-size: 0.75em;
|
|
463
|
+
color: var(--ink-3);
|
|
464
|
+
cursor: pointer;
|
|
465
|
+
}
|
|
466
|
+
.editor .cm-atomic-link:hover::after { color: var(--accent); }
|
|
467
|
+
.editor .cm-line.cm-atomic-blockquote { padding-left: 1rem; border-left: 2px solid var(--rule-strong); color: var(--ink-2); }
|
|
468
|
+
.editor .cm-atomic-list-marker { display: inline-block; width: 0.9em; margin-right: 0.3em; text-align: right; }
|
|
469
|
+
.editor .cm-atomic-list-marker, .editor .cm-atomic-list-marker .md-mark { color: var(--ink); }
|
|
470
|
+
.editor .cm-line.cm-atomic-hr { position: relative; }
|
|
471
|
+
.editor .cm-line.cm-atomic-hr::after { content: ""; position: absolute; left: 0; right: 0; top: 50%; border-top: 1px solid var(--rule); }
|
|
472
|
+
.editor .cm-line.cm-atomic-fenced-code {
|
|
473
|
+
position: relative;
|
|
474
|
+
font-family: var(--mono);
|
|
475
|
+
font-size: 0.8rem;
|
|
476
|
+
line-height: 1.5;
|
|
477
|
+
background: var(--paper);
|
|
478
|
+
padding: 0 1rem;
|
|
479
|
+
border: solid var(--rule);
|
|
480
|
+
border-width: 0 1px;
|
|
481
|
+
}
|
|
482
|
+
.editor .cm-line:not(.cm-atomic-fenced-code) + .cm-atomic-fenced-code, .editor .cm-atomic-fenced-code:first-child { border-top-width: 1px; border-radius: 6px 6px 0 0; }
|
|
483
|
+
.editor .cm-atomic-fenced-code:not(:has(+ .cm-atomic-fenced-code)) { border-bottom-width: 1px; border-radius: 0 0 6px 6px; }
|
|
484
|
+
.editor .cm-line:not(.cm-atomic-fenced-code) + .cm-atomic-fenced-code:not(:has(+ .cm-atomic-fenced-code)) { border-radius: 6px; }
|
|
485
|
+
.editor .cm-atomic-fenced-selection { background: var(--accent-soft); }
|
|
486
|
+
.editor .run { position: absolute; z-index: 1; top: 0.5rem; right: 0.5rem; }
|
|
487
|
+
/* CodeMirror measures block widgets without their margins. */
|
|
488
|
+
.editor .cell-below { display: flow-root; padding-bottom: 0.8em; }
|
|
489
|
+
.editor .cell-output pre { margin: 0; font-size: 0.8rem; line-height: 1.5; }
|
|
490
|
+
.editor .cm-line .cm-widgetBuffer { vertical-align: top; }
|
|
458
491
|
.editor .md-mark { color: var(--ink-3); }
|
|
459
|
-
.editor .md-code-line { font-family: var(--mono); font-size: 0.8rem; line-height: 1.5; background: var(--paper); padding: 0 1rem; }
|
|
460
492
|
.editor .py-keyword { color: var(--accent); }
|
|
461
493
|
.editor .py-string { color: var(--good); }
|
|
462
494
|
.editor .py-comment { color: var(--ink-3); font-style: italic; }
|
|
@@ -22,8 +22,6 @@ function show(note, button) {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
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
25
|
button.addEventListener("click", async () => {
|
|
28
26
|
button.disabled = true;
|
|
29
27
|
const response = await fetch(button.dataset.url, {
|
|
@@ -1,35 +1,28 @@
|
|
|
1
1
|
let detach = () => {};
|
|
2
2
|
|
|
3
|
-
export function attach(note,
|
|
3
|
+
export function attach(note, focus) {
|
|
4
4
|
detach();
|
|
5
5
|
const prose = note.querySelector(".prose");
|
|
6
6
|
const status = note.querySelector(".edit-status");
|
|
7
|
-
const editor = import(note.dataset.editor);
|
|
8
7
|
const source = JSON.parse(note.querySelector(".note-source").textContent);
|
|
9
8
|
let editing = null;
|
|
10
9
|
let attached = true;
|
|
11
10
|
|
|
12
|
-
editor.then(
|
|
13
|
-
() =>
|
|
11
|
+
import(note.dataset.editor).then(
|
|
12
|
+
({ edit }) => {
|
|
13
|
+
if (!attached) return;
|
|
14
|
+
editing = edit(note, prose, status);
|
|
15
|
+
if (focus) editing.view.focus();
|
|
16
|
+
},
|
|
14
17
|
() => {},
|
|
15
18
|
);
|
|
16
19
|
|
|
17
|
-
const open = async (event) => {
|
|
18
|
-
if (event.target.closest("a, button, output") || getSelection().toString()) return;
|
|
19
|
-
const { edit } = await editor;
|
|
20
|
-
if (editing || !attached) return;
|
|
21
|
-
prose.removeEventListener("click", open);
|
|
22
|
-
editing = edit(note, prose, status);
|
|
23
|
-
};
|
|
24
|
-
prose.addEventListener("click", open);
|
|
25
|
-
if (openNow) open({ target: prose });
|
|
26
|
-
|
|
27
20
|
const changes = new EventSource(note.dataset.changesUrl);
|
|
28
21
|
changes.addEventListener("message", (event) => {
|
|
29
22
|
if (JSON.parse(event.data) !== note.dataset.note) return;
|
|
30
23
|
if (editing) return editing.pull();
|
|
31
|
-
//
|
|
32
|
-
//
|
|
24
|
+
// Without the editor the note is rendered HTML. A touch or a duplicate
|
|
25
|
+
// event leaves the text alone, and reloading then is wasted.
|
|
33
26
|
fetch(note.dataset.saveUrl, { cache: "no-store" })
|
|
34
27
|
.then((response) => response.text())
|
|
35
28
|
.then((text) => {
|
|
@@ -4,12 +4,14 @@ import {
|
|
|
4
4
|
HighlightStyle,
|
|
5
5
|
Language,
|
|
6
6
|
defineLanguageFacet,
|
|
7
|
+
ensureSyntaxTree,
|
|
7
8
|
languageDataProp,
|
|
8
9
|
syntaxHighlighting,
|
|
9
10
|
syntaxTree,
|
|
10
11
|
} from "@codemirror/language";
|
|
11
|
-
import { EditorState,
|
|
12
|
-
import { Decoration, EditorView,
|
|
12
|
+
import { EditorState, StateField } from "@codemirror/state";
|
|
13
|
+
import { Decoration, EditorView, WidgetType, keymap } from "@codemirror/view";
|
|
14
|
+
import { inlinePreview } from "@atomic-editor/editor/inline-preview";
|
|
13
15
|
import { tags } from "@lezer/highlight";
|
|
14
16
|
import { GFM, parseCode, parser } from "@lezer/markdown";
|
|
15
17
|
import { DIFF_DELETE, DIFF_EQUAL, diff_match_patch } from "diff-match-patch";
|
|
@@ -31,16 +33,6 @@ const markdown = new Language(
|
|
|
31
33
|
);
|
|
32
34
|
|
|
33
35
|
const style = HighlightStyle.define([
|
|
34
|
-
{ tag: tags.heading1, class: "md-h1" },
|
|
35
|
-
{ tag: tags.heading2, class: "md-h2" },
|
|
36
|
-
{ tag: tags.heading3, class: "md-h3" },
|
|
37
|
-
{ tag: [tags.heading4, tags.heading5, tags.heading6], class: "md-h4" },
|
|
38
|
-
{ tag: tags.strong, class: "md-strong" },
|
|
39
|
-
{ tag: tags.emphasis, class: "md-em" },
|
|
40
|
-
{ tag: tags.strikethrough, class: "md-strike" },
|
|
41
|
-
{ tag: [tags.link, tags.url], class: "md-link" },
|
|
42
|
-
{ tag: tags.monospace, class: "md-code" },
|
|
43
|
-
{ tag: tags.quote, class: "md-quote" },
|
|
44
36
|
{ tag: [tags.processingInstruction, tags.contentSeparator], class: "md-mark" },
|
|
45
37
|
{ tag: [tags.keyword, tags.operatorKeyword, tags.bool, tags.null], class: "py-keyword" },
|
|
46
38
|
{ tag: [tags.string, tags.special(tags.string)], class: "py-string" },
|
|
@@ -49,54 +41,86 @@ const style = HighlightStyle.define([
|
|
|
49
41
|
{ tag: [tags.function(tags.variableName), tags.definition(tags.variableName)], class: "py-name" },
|
|
50
42
|
]);
|
|
51
43
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
function decorate(view) {
|
|
58
|
-
const builder = new RangeSetBuilder();
|
|
59
|
-
const { state } = view;
|
|
60
|
-
const active = new Set();
|
|
61
|
-
for (const range of state.selection.ranges) {
|
|
62
|
-
for (let n = state.doc.lineAt(range.from).number; n <= state.doc.lineAt(range.to).number; n++) active.add(n);
|
|
44
|
+
class RunButton extends WidgetType {
|
|
45
|
+
constructor(block, run) {
|
|
46
|
+
super();
|
|
47
|
+
this.block = block;
|
|
48
|
+
this.run = run;
|
|
63
49
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
if (!HIDDEN.has(node.name) || active.has(state.doc.lineAt(node.from).number)) return;
|
|
78
|
-
let end = node.to;
|
|
79
|
-
if (node.name === "HeaderMark" && state.doc.sliceString(end, end + 1) === " ") end += 1;
|
|
80
|
-
builder.add(node.from, end, hide);
|
|
81
|
-
},
|
|
82
|
-
});
|
|
50
|
+
eq(other) {
|
|
51
|
+
return other.block === this.block;
|
|
52
|
+
}
|
|
53
|
+
toDOM() {
|
|
54
|
+
const button = document.createElement("button");
|
|
55
|
+
button.type = "button";
|
|
56
|
+
button.className = "run";
|
|
57
|
+
button.textContent = "Run";
|
|
58
|
+
button.addEventListener("click", () => this.run(this.block, button));
|
|
59
|
+
return button;
|
|
60
|
+
}
|
|
61
|
+
ignoreEvent() {
|
|
62
|
+
return true;
|
|
83
63
|
}
|
|
84
|
-
return builder.finish();
|
|
85
64
|
}
|
|
86
65
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
66
|
+
class CellOutput extends WidgetType {
|
|
67
|
+
constructor(block, outputs) {
|
|
68
|
+
super();
|
|
69
|
+
this.block = block;
|
|
70
|
+
this.outputs = outputs;
|
|
71
|
+
}
|
|
72
|
+
eq(other) {
|
|
73
|
+
return other.block === this.block;
|
|
74
|
+
}
|
|
75
|
+
toDOM() {
|
|
76
|
+
const cell = document.createElement("div");
|
|
77
|
+
cell.className = "cell-below";
|
|
78
|
+
if (!this.outputs.has(this.block)) {
|
|
79
|
+
const output = document.createElement("output");
|
|
80
|
+
output.className = "cell-output";
|
|
81
|
+
this.outputs.set(this.block, output);
|
|
91
82
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
83
|
+
cell.append(this.outputs.get(this.block));
|
|
84
|
+
return cell;
|
|
85
|
+
}
|
|
86
|
+
ignoreEvent() {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Numbered in document order, as python_blocks numbers them on the server.
|
|
92
|
+
function cellWidgets(state, run, outputs) {
|
|
93
|
+
const widgets = [];
|
|
94
|
+
let block = 0;
|
|
95
|
+
const tree = ensureSyntaxTree(state, state.doc.length, 200) ?? syntaxTree(state);
|
|
96
|
+
tree.iterate({
|
|
97
|
+
enter(node) {
|
|
98
|
+
if (node.name !== "FencedCode") return;
|
|
99
|
+
const info = node.node.getChild("CodeInfo");
|
|
100
|
+
if (info && state.doc.sliceString(info.from, info.to).split(/\s/)[0] === "python") {
|
|
101
|
+
const open = state.doc.lineAt(node.from);
|
|
102
|
+
widgets.push(Decoration.widget({ widget: new RunButton(block, run), side: 1 }).range(open.to));
|
|
103
|
+
widgets.push(
|
|
104
|
+
Decoration.widget({ widget: new CellOutput(block, outputs), block: true, side: 1 }).range(node.to),
|
|
105
|
+
);
|
|
106
|
+
block += 1;
|
|
95
107
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
);
|
|
108
|
+
return false;
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
return Decoration.set(widgets, true);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function cells(run, outputs) {
|
|
115
|
+
return StateField.define({
|
|
116
|
+
create: (state) => cellWidgets(state, run, outputs),
|
|
117
|
+
update(widgets, tr) {
|
|
118
|
+
if (!tr.docChanged && syntaxTree(tr.state) === syntaxTree(tr.startState)) return widgets;
|
|
119
|
+
return cellWidgets(tr.state, run, outputs);
|
|
120
|
+
},
|
|
121
|
+
provide: (field) => EditorView.decorations.from(field),
|
|
122
|
+
});
|
|
123
|
+
}
|
|
100
124
|
|
|
101
125
|
function show(status, message) {
|
|
102
126
|
status.textContent = message;
|
|
@@ -196,15 +220,33 @@ export function edit(note, prose, status) {
|
|
|
196
220
|
|
|
197
221
|
const save = () => run(push);
|
|
198
222
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
223
|
+
// The rendered note's outputs move into the editor, so a run started
|
|
224
|
+
// before the editor loaded still shows its result.
|
|
225
|
+
const outputs = new Map();
|
|
226
|
+
for (const cell of prose.querySelectorAll(".cell")) {
|
|
227
|
+
outputs.set(Number(cell.dataset.block), cell.querySelector(".cell-output"));
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// The server runs the block from the file, so unsaved edits go first.
|
|
231
|
+
const runCell = async (block, button) => {
|
|
232
|
+
button.disabled = true;
|
|
233
|
+
button.textContent = "Running";
|
|
234
|
+
try {
|
|
235
|
+
await save();
|
|
236
|
+
const response = await fetch(note.dataset.runUrl, {
|
|
237
|
+
method: "POST",
|
|
238
|
+
headers: { "Content-Type": "application/json" },
|
|
239
|
+
body: JSON.stringify({ block }),
|
|
240
|
+
});
|
|
241
|
+
outputs.get(block).innerHTML = response.ok
|
|
242
|
+
? await response.text()
|
|
243
|
+
: `<pre class="error">${response.status} ${response.statusText}</pre>`;
|
|
244
|
+
view.requestMeasure();
|
|
245
|
+
} finally {
|
|
246
|
+
button.disabled = false;
|
|
247
|
+
button.textContent = "Run";
|
|
248
|
+
}
|
|
206
249
|
};
|
|
207
|
-
document.addEventListener("mousedown", outside);
|
|
208
250
|
|
|
209
251
|
const view = new EditorView({
|
|
210
252
|
parent: host,
|
|
@@ -212,10 +254,15 @@ export function edit(note, prose, status) {
|
|
|
212
254
|
doc: base,
|
|
213
255
|
extensions: [
|
|
214
256
|
history(),
|
|
215
|
-
keymap.of([
|
|
257
|
+
keymap.of([
|
|
258
|
+
{ key: "Escape", run: () => (view.contentDOM.blur(), true) },
|
|
259
|
+
...defaultKeymap,
|
|
260
|
+
...historyKeymap,
|
|
261
|
+
]),
|
|
216
262
|
markdown,
|
|
217
263
|
syntaxHighlighting(style),
|
|
218
|
-
|
|
264
|
+
inlinePreview({ onLinkClick: (url) => location.assign(new URL(url, location.href)) }),
|
|
265
|
+
cells(runCell, outputs),
|
|
219
266
|
EditorView.lineWrapping,
|
|
220
267
|
EditorView.contentAttributes.of({ "aria-label": "Note" }),
|
|
221
268
|
EditorView.updateListener.of((update) => {
|
|
@@ -227,12 +274,9 @@ export function edit(note, prose, status) {
|
|
|
227
274
|
],
|
|
228
275
|
}),
|
|
229
276
|
});
|
|
230
|
-
view.focus();
|
|
231
277
|
return {
|
|
278
|
+
view,
|
|
232
279
|
pull: () => run(pull),
|
|
233
|
-
detach: () =>
|
|
234
|
-
document.removeEventListener("mousedown", outside);
|
|
235
|
-
save();
|
|
236
|
-
},
|
|
280
|
+
detach: () => save(),
|
|
237
281
|
};
|
|
238
282
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
<script type="importmap">
|
|
3
3
|
{
|
|
4
4
|
"imports": {
|
|
5
|
+
"@atomic-editor/editor/inline-preview": "https://esm.sh/*@atomic-editor/editor@0.6.2/dist/inline-preview.js",
|
|
5
6
|
"@codemirror/autocomplete": "https://esm.sh/*@codemirror/autocomplete@6.20.4",
|
|
6
7
|
"@codemirror/commands": "https://esm.sh/*@codemirror/commands@6.11.1",
|
|
7
8
|
"@codemirror/lang-python": "https://esm.sh/*@codemirror/lang-python@6.2.1",
|
|
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
|
|
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
|