Repository navigation
Texodus v4.4.0
✨ New
Insert Table of Contents
A new command in the Format menu builds a table of contents from the
document's headings: a nested markdown list of links, indented relative to the
shallowest heading present, walking headings inside blockquotes and list items
too.
The result is ordinary markdown that you own — it renders the same here, on
GitHub, and in exported HTML and PDF. The command inserts a fresh list each
time it runs; it never rewrites a list it produced earlier, so edits you make
to it are yours to keep.
Document titles instead of file names
A new documentTitleMode setting lets the sidebar, tab bar and Quick Open show
a document's H1 (or its frontmatter title:) instead of its file name. Titles
are cached and refresh when the file changes on disk.
The workspace comes back the way you left it
The sidebar's expanded folders and selection are now persisted per workspace
root, and the last workspace is restored silently on startup. Watcher-driven
refreshes run in the background, so the tree no longer blanks out into a
loading state while it reloads.
🐛 Fixes
Windows: "File changed on disk" immediately after opening a file (#7)
Opening a CRLF file on Windows marked the tab modified — the title showed *
— and then raised a conflict prompt for a file nobody had touched.
CodeMirror stores every document as LF, so feeding it raw CRLF text came back
through the editor as an "edit": the tab went dirty on open, and from then on
the file watcher's byte-for-byte comparison of buffer against disk could never
match again. Saving would have silently rewritten the whole file to LF.
The buffer is now always normalised, and each tab remembers its file's own
line ending, which is re-applied on every write — manual save, Save As,
auto-save, and the sidebar's save-before-rename alike. Your line endings are
preserved instead of being converted. A file whose endings are rewritten by
an external tool hands its new ending to the tab, so the next save doesn't flip
it back. New documents and .txt exports follow the host OS.
Table-of-contents links did nothing (#8)
Clicking an entry in a hand-written table of contents — or any #section link,
including ones copied from GitHub — did nothing at all: the cursor turned into
a hand and the page stayed put, in the preview and in exported HTML.
Headings simply had no anchors to jump to. They now carry GitHub-compatible
slugs (lowercased, punctuation dropped, spaces turned into dashes, repeats
numbered in document order), so a document that works on github.com works here
and back again. The preview resolves in-page links itself and scrolls both
panes to the heading.
PDF export: dead links and missing bold, italics and strikethrough
Fixing the anchors surfaced a much older problem. In exported PDFs, no link
inside a paragraph, list or table ever produced a clickable annotation, and
**bold**, *italics* and ~~strikethrough~~ rendered as plain text — the
exported file didn't even embed the bold and italic faces.
The cause was in how the PDF layer flattens nested inline content: it discards
the properties of the wrappers it collapses, so styling and links attached to
them never reached the renderer. Inline markup is now resolved into per-run
properties, and the result is verified against a real rendered PDF rather than
the document description. Internal links jump to their heading, external links
open in your browser, formatting inside a link travels with the jump, and a
struck-through link keeps both its lines.
A #fragment that names no heading in the document is deliberately rendered as
plain text — a link that looks clickable and goes nowhere is worse than no link.
Two tabs over one file
Duplicating a tab copied the file path along with the text, leaving two buffers
over one file. They diverged as soon as you typed, raced to overwrite each
other on save, and session restore brought the pair back on the next launch.
A duplicate is now an unsaved draft of the text, and Save As gives it its own
path.
Windows that refused to close
The file watcher's conflict prompt could land in the middle of the
close-window walk over unsaved tabs, take over the modal, cancel the walk, and
leave a window that simply would not close. Prompts are queued now, and the
watcher re-checks the document before showing a conflict that may already have
been resolved.
Unsaved changes skipped by a race
Closing, switching or opening a document waits for pending auto-saves to
flush. That wait could report "nothing to do" while a write was still in the
air, letting those paths read a stale state and skip the unsaved-changes
prompt. The flush now joins the write in flight and keeps draining edits made
while it runs.
Formatting commands only ever added markup
Pressing Cmd+1 twice gave you # # title, Paragraph never removed a marker,
Cmd+B over already-bold text nested another pair, and line prefixes touched
only the line with the cursor. Both command families toggle now, and line
prefixes apply to every selected line, renumbering ordered lists and preserving
indentation and blockquote context.
Context menus pushed off screen
A right-click near the bottom of the tab strip put menu items past the edge of
the window. The sidebar and tab-bar menus now share one component that clamps
to the viewport on open and on resize.
Recent files that no longer exist
Deleted files lingered in the Recent Files menu and failed on click. They are
pruned before the menu is built, and a failed open drops the entry — but only
for a definitely-missing file, so permission and scope problems still reach the
existing recovery flow.
🎨 Improvements
- The split-pane divider stays where you put it. The ratio is persisted per
window and survives switching layout modes and restarting the app. Dragging
moved to pointer capture, so it survives the pointer leaving the window, and
resizing the window mid-drag no longer makes the ratio drift. - Context menus are keyboard-reachable. Entries are real menu items with
focus handling, Escape, arrows, Home and End. - One list of supported file extensions. Four separate copies of the
.md/.markdown/.txtcheck (drag-drop, sidebar, preview links, and a separate
Rust list) had drifted apart; both sides now read a single list, which also
fixes a case where the check could fire on a directory name. - A corrupt saved session no longer reaches the loader. The persisted tab
list is validated and falls back to an empty session.
🔒 Security
- "Reveal in Finder/Explorer" no longer goes through the
openerplugin, which
required a**path permission. It is now a Rust command that reveals only
paths inside the granted filesystem scope, and the plugin — along with its
permission — has been removed. - Persisted settings are validated on load, so a hand-edited or corrupt
localStoragevalue can't push an invalid layout or document mode into the
UI and the Rust window routing.
🧰 Under the hood
- Vue components are testable now (
@vue/test-utilsplus the Vue plugin in the
Vitest config), and the suite has grown to 530 tests covering line
endings, heading anchors, the table-of-contents builder, PDF link generation,
context menus, the divider drag, file watching, session restore, Quick Open,
menu memoization and theme tokens. - The About dialog reads the version from a build-time constant instead of
importingpackage.json, which used to pull the whole dependency manifest
into the bundle.