Files
Marcos Paulo b562eeafde
ci / ci (macos-latest) (push) Has been cancelled
ci / ci (ubuntu-latest) (push) Has been cancelled
ci / ci (windows-latest) (push) Has been cancelled
feat: Cmd/Ctrl+Enter toggles a task in the Visual block editor
The Visual view's block editor now shares the Source view's task toggle (keys::on_toggle_task).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-19 16:30:30 -03:00

17 KiB
Raw Permalink Blame History

Tasks: Obsidian basics

Order = suggested order (value over size). Every task ends with the crate's tests, clippy -D warnings, fmt; cargo xtask i18n for UI strings. Pure logic first, UI second. "Owner" = needs a decision before work starts.

  • 1. Find and replace in the open note (engine find.* pure matcher in blocks or editor, UI bar with highlight, Next/Previous, Replace, Replace all = one undo step; plain, case, whole word; regex waits on task 11's decision, plain first)
  • 2. Back/forward navigation history (stack of path, scroll, caret; Cmd-[ / Cmd-], menu items, commands nav.back / nav.forward)
  • 3. Auto-pair brackets and quotes (editor/src/keys.rs: pair, step over, Backspace on empty pair, skip in code fences and after a word character for quotes)
  • 4. Footnotes: inline ^[..] scanner and a Footnote item in notescraft-markdown, rendering as superscript with jump and back-link in the rendered view
  • 5. Explorer sort orders (name, modified, created, reverse; setting ui.explorer_sort, remembered)
  • 6. Heading folding by click in the rendered view (done; LIST item folding still open) (fold model shared with callouts and vim za)
  • 7. Search operators section:, block:, task: and match-case switch in notescraft-index (+ results UI switch)
  • 8. Vault-wide search and replace: engine search.replace dry run + apply (one lint_commit per file, content-hash re-check like batch), review view reuse
  • 9. Quick switcher heading and block search (note#heading, note^block) from the existing index
  • 10. Link hover page preview (400 ms, read-only card, heading/block targets show that part)
  • 11. Regex search /…/ and regex in find-and-replace (own linear-time engine in notescraft-regex, so no third-party dependency)
  • 12. Explorer drag and drop to move files and folders (drop targets, note.rename, link update, refuse into itself)
  • 13. Tab pinning and drag reorder
  • 14. Settings batch: default view mode for new tabs, editor font size, readable line length, line numbers, strict line breaks, default location for new notes, link format, deleted files behaviour (each only shown when code reads it). DONE: default view, font size, readable line length, new-note folder. line numbers, link format and path (Copy link, linking a mention), deleted files. strict line breaks, show hidden files
  • 15. Context menu and commands: Open in default app, Copy Obsidian URL, Outgoing links pane, Open random note, Unique note creator. DONE: random, unique, Open in default app, Copy Obsidian URL. Outgoing links section in the Backlinks tab
  • 16. Split view: two panes, per-pane tabs; the second pane is a read-only rendered view, editing stays in the main pane (view.split, tab menu "Open in split view")
  • 17. PDF, audio and video embeds as file cards with an Open button (PDF pages render through the system tool, audio plays through the system player, video plays in the note through ffmpeg when installed (else opens in the system player); no decoder library linked)
  • 18. Spellcheck without a dictionary: the spelling lint rule (off by default, session.lint.spelling) flags words the vault uses in fewer than two notes and suggests the closest known word

Round 2: gaps found after round 1 (checked 2026-10-19; none built yet)

Confirmed missing by grep: no query fence handling, no RTL handling. Pop-out and stacked tabs are listed as missing in audit.md.

  • 19. Embedded search queries: a ```query fence renders the live results of a vault search (same operators as the search panel), refreshes on index change Done when: a driven test shows a note with a query fence listing the matching notes, and the list changes after a matching note is added. DONE: blocks/query/mod.rs, driven test a_query_block_lists_the_matching_notes_and_follows_new_ones; answers refresh every second, rows open the note (a note holding the query text lists itself, as in Obsidian)
  • 20. RTL in the reading view. Owner approved unicode-bidi (2026-10-19). DONE: views/note_view/direction/ orders a row whose first strong letter is right-to-left (whole line at once, styles kept, inline math and footnote marks stay whole) and painting.rs puts the row against the right edge; the UI fonts get the system's Hebrew/Arabic faces as fallbacks (design/fonts.rs); tests direction::tests (8) and a_hebrew_paragraph_sits_against_the_right_edge_and_an_english_one_stays_left; rendered offscreen and viewed. Limits: Arabic stays in isolated letter forms (egui does not join), Source mode and the caret keep logical order, a wrapped right-to-left paragraph keeps its lines left-aligned inside its block, selection and copy on such rows are not mapped back to the source Done when: a unit test resolves direction for Arabic, Hebrew and Latin lines; an offscreen render shows an Arabic paragraph right-aligned
  • 21. Stacked tabs: a tab-group mode where tabs stack as collapsed headers Done when: a driven test toggles stacked mode and the active tab body shows under the other tabs' headers. DONE: view.stacked_tabs (View menu), widgets/stacked.rs (earlier tabs fold left, later ones right, title written upward, click unfolds), saved in the UI state; test stacked_tabs_fold_the_others_into_strips_and_a_click_unfolds_one. The tab bar stays; no stacking with the split view on
  • 22. Pop-out window: move a tab to its own OS window (needs a second egui viewport sharing the session) Done when: a driven test moves a tab to a pop-out and the main window no longer lists it; closing the pop-out returns it. DONE: view.pop_out (View menu), shell/app/popout.rs: an immediate egui viewport (a native window under eframe on desktop) shows the rendered note read-only through the split pane's drawing; links open in the main window; closing the window returns the note as a tab. Where eframe embeds viewports (web, the offscreen harness) the note goes straight back to its tab. NOT checked in a real native window (the live window never paints in the agent session); the driven test covers the tab hand-off only
  • 23. Editor follow-ups. DONE: checkboxes inside embeds (ticking_a_task_inside_an_embed_changes_the_embedded_note), text on an embed's line drawn around it (text_on_the_line_of_an_embed_is_drawn_around_it), embeds follow an edit made in the embedded note's own tab (an_embed_follows_an_edit_made_to_the_embedded_note_in_its_own_tab). block menu by keyboard (Cmd/Ctrl-Alt-B, the_block_menu_opens_from_the_keyboard_at_the_caret; before it was reachable only from the palette) Done when: one driven or unit test per item passes
  • 24. (DMG checked 2026-10-19 without running the UI: mounts, codesign --verify --deep --strict passes, arm64, version 0.1.0, contains view.pop_out and view.stacked_tabs; the window screenshot, a native pop-out window and the Windows run are still open) Hand check of the packaged app: open the DMG build, run the dev hook screenshot and a Windows run of audio/video playback (needs the owner's desktop and a Windows machine) Done when: a PNG of the real window and a Windows result are attached to this file

Round 3: gaps still listed as missing or partial in audit.md (re-audited 2026-10-19; recheck each row before building, some may have been covered by 1-18)

  • 25. (UI DONE too: a selection-toolbar button "Extract to a new note" opens a name prompt (first line offered) and moves the passage through the open editor with a link left behind; Explorer menu "Merge into…" opens the note picker and folds the note into the pick through the open editor, refusing a note with unsaved changes; tests extracting_a_selection_asks_for_a_name_then_moves_it_into_a_new_note_and_links_it and merging_a_note_into_another_goes_through_the_open_editor_and_a_dirty_note_is_refused; ENGINE: vault/composer/mod.rs, commands note.extract and note.merge, tests engine/tests/note_composer.rs (4); open: the UI, a name prompt for extract on the selection toolbar and a note picker for merge, since the palette cannot take required parameters; links to a merged note are not rewritten) Note composer: note.merge (append a note to another, fix links, trash the source) and note.extract (move a text range into a new note, leave a [[link]]) Done when: engine integration tests for both commands pass, and each is in the palette
  • 26. (UI DONE too: a Trash tab, View > Open the trash (view.trash), views/trash_view/, list with age and origin, Restore per item, Empty asking twice, driven test the_trash_tab_lists_a_deleted_note_restores_it_and_empties_on_the_second_click; ENGINE: vault trash.rs records where an item came from (<name>.from beside it), Vault::trash_list/restore_from_trash/empty_trash, engine trash.list, trash.restore, trash.empty, tests in vault/tests/files.rs and engine/tests/note_composer.rs; an item whose old place is taken stays in the trash; items trashed before this change have no recorded origin and cannot be restored; open: the UI, a Trash list with Restore and Empty) Trash restore and empty: list .notescraft/trash, restore a note to its place, empty the trash Done when: engine command tests and a driven test restoring a deleted note
  • 27. Folder notes: a folder opens its note (folder/folder.md) when one exists. DONE: open_folder_or_its_note (double-click on a folder in the Explorer; the context menu's "Open folder view" still opens the view), driven test a_folder_with_a_folder_note_opens_that_note_and_one_without_opens_the_folder_view Done when: a driven test clicking a folder with a folder note opens the note
  • 28. (DONE too: result order (relevance, A to Z, Z to A) and collapse to titles, two pills in the results heading, test search_results_can_be_ordered_by_name_and_collapsed_to_titles; DONE earlier: excluded folders, setting session.files.excluded_folders, applied to plain and /regex/ search, test excluded_folders_hide_notes_from_plain_and_regex_search and config unit test, no Settings control yet; #tag in a note click-to-search, driven test clicking_a_tag_in_a_note_searches_for_it; open: result sort order and collapse) Search: excluded files setting, result sort order, collapse results, #tag in a note body click-to-search Done when: one test per item
  • 29. (DONE both: reading mode view.reading_mode (View menu; per-tab flag TabEdit.reading, test reading_mode_shows_the_note_but_a_click_and_typing_do_not_change_it); INDENT: settings editor.indent_with_tabs and editor.indent_width (1-8), Settings > Editor controls, keys::Indent, tests in editor/tests/keys.rs and editor_settings.rs; open: reading-only open) Reading-only open: "Open in reading mode" (no editing) and an indent-with-tab-or-spaces setting Done when: a driven test shows a reading tab ignoring a click-to-edit; a unit test for the indent setting
  • 30. (ALL BUILT; heading folding in Source mode: a chevron in the left margin beside each heading that has a section; a folded section is laid out at 0.1 pt and transparent, so the text stays one buffer and edits, carets and selections keep working; editor/src/fold.rs, highlight::layout_job_folded, views/center/source_fold.rs, tests fold::tests, hidden_ranges_keep_every_byte_in_the_job_and_shrink_only_those_bytes, driven a_heading_folds_its_section_in_the_source_view_from_the_margin_chevron; folds are per open tab, keyed by the heading line, and not saved; DONE: "Move to…" in the Explorer menu opens a folder picker (palette kind Folders, the vault root included) and moves a note or folder, refusing a folder into itself, test move_to_opens_the_folder_picker_and_moves_a_file_or_folder_but_never_into_itself. STRUCK: spellcheck language (the checker is the vault's own vocabulary, it has no language). DONE too: table helpers ("+ row", "− row", "+ column", "− column" under an editable table, Table::add_row etc. in blocks/table/edit.rs, driven test a_table_gets_a_row_and_a_column_from_the_controls_under_it); user themes (<settings folder>/themes/*.toml, Session::user_themes/theme_named, listed in Settings and pickable, tests in engine/tests/user_themes.rs). DONE too: graph timelapse (a Timelapse toggle in the graph card; notes appear by file creation time over a 12 s run, with play/pause, the date reached and a slider; graph_view/timelapse.rs, tests there and every_note_has_a_creation_time; undated notes always show). - see above) Smaller rows from the audit: heading folding in Source mode, table editing helpers, "Move file to…" folder picker, graph timelapse, user themes in Settings, spellcheck language Done when: each checked against the code first; built or struck out with the reason

Round 4: basic Obsidian features still missing after round 3 (checked 2026-10-19 against the code; none built)

  • 31. (ALREADY BUILT, missed by the round 4 check: Source sidebar tab > History for the open note lists its commits (git.log with the path), a click shows the diff to the current text (panels/source_panel/history.rs, diff_view) and Restore asks first and runs git.restore_version; it needs a git repository (git.init from the same panel). OPEN as 31b: snapshots for a vault without git (a local version list every few minutes, Obsidian's File recovery)) Note history (Obsidian's File recovery): per-note snapshots or git history shown as a list with a diff and Restore. git.log/git.show exist in the engine; there is no per-note history view and no snapshots for vaults without git. Done when: a driven test lists the versions of a note, shows the diff to the current text and restores one as one undoable edit
  • 32. (DONE: view.save_workspace (name prompt) and view.load_workspace (picker, palette kind Workspaces) in the View menu; a layout is the tabs in order, the active one, pins, panel widths, panel visibility and the sidebar tab; engine workspace.list/save/load/delete, files in .notescraft/workspaces/, tests engine/tests/workspaces.rs and a_saved_workspace_brings_the_tabs_and_the_active_one_back_and_skips_notes_that_are_gone; not saved: the split pane and per-note view mode; no delete control in the UI, only the command) Workspaces: save and load the layout (open tabs, split, sidebar tab, panel widths) by name. Done when: a test saves a layout, changes it and loads it back
  • 33. (DONE: commands editor.copy_block_link (adds the ^id once) and editor.copy_heading_link, and "Copy link to this heading" in the Outline row's context menu; blocks::ensure_block_id, FilesConfig::link_text_to (wiki or Markdown links), tests in blocks/src/ids.rs and copying_a_block_link_adds_the_id_once_and_copying_a_heading_link_needs_no_edit) Copy a heading or block link from the editor ([[Note#Heading]], [[Note#^id]]) and a command to add a block id. Done when: a test produces the link text for a heading and for a block and adds a missing ^id once
  • 34. (DEFERRED by the owner 2026-10-19: a click keeps opening the whole block as source) Live Preview editing in the Visual view for inline syntax (the Visual view opens a whole block as source on click; Obsidian edits the rendered line in place). Done when: decided with the owner first (large change to note_view)
  • 35. Hand checks that need a real window: task 24 plus the Linux and Windows items in ROADMAP "Linux and Windows feedback".
  • 31b. (DONE: every save of a changed note keeps a snapshot at most every 5 minutes, newest 20 in .notescraft/history/ (vault/snapshots/, hooked into save_buffer), engine history.list and history.read; View > Note versions opens a Versions tab listing them with Compare (the diff page) and Restore (one undoable edit); tests in snapshots, engine/tests/workspaces.rs and the_versions_tab_lists_a_snapshot_and_restores_it_over_the_current_text; the Source panel's History still needs git) Local snapshots of a note for a vault without git: saved on write when the text changed and the last one is a few minutes old, newest 20 kept in .notescraft/history/, listed with a diff and Restore. Done when: engine tests for snapshot, list and restore (one undoable edit) pass and the Source panel's History lists them when there is no repository

Round 5: small basics found by reading the key handling (2026-10-19)

  • 36. Cmd/Ctrl+Enter toggles a task on the caret line in the Source view: ticks or unticks a box, adds a box to a list item, makes a plain or empty line a - [ ] task (keys::on_toggle_task, test cmd_enter_ticks_unticks_and_makes_tasks).
  • 37. (DONE: read_keys in note_view/edit.rs calls the same on_toggle_task; driven test cmd_enter_in_an_open_visual_block_ticks_the_task) The same key in the Visual view's block editor (it has its own key handling in note_view/edit, so the Source version does not reach it). Done when: a driven test presses Cmd/Ctrl+Enter in an open block and the task box flips