Skip to content

feat(markdown): add offline /insert-toc slash command - #423

Merged
petertzy merged 4 commits into
petertzy:mainfrom
filip-dzivy:feat/insert-toc
Oct 17, 2026
Merged

petertzy merged 4 commits into
petertzy:mainfrom
filip-dzivy:feat/insert-toc

Conversation

@filip-dzivy

Copy link
Copy Markdown
Contributor

Closes #420.

Adds an offline /insert-toc slash command that generates a Markdown
table of contents from the document's headings, complementing the
AI-powered /toc without needing an API key or network access.

Implementation reuses the existing, already-tested offline generator
(backend.ai_logic._generate_markdown_toc) via a new lightweight
POST /api/markdown/toc endpoint, so anchors match the rendered preview
exactly. Frontend wires it through the existing slash-command path
(same as /insert-table).

Validation:

  • uv run ruff check . / ruff format --check . - passed
  • uv run pytest - passed (new tests/test_insert_toc.py)
  • cd frontend && npm run build && npm run lint && npm test - passed

Note: the inserted TOC reuses the AI /toc output format (includes a
"## Table of Contents" header). Happy to make it list-only if preferred.

@petertzy
petertzy requested review from AllCodeBreaker, Eswar0108 and praghnyau and removed request for AllCodeBreaker and praghnyau October 14, 2026 10:35

@karaaslanz karaaslanz left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed PR #423 at exact HEAD dfd69ff63972971245ad8008806c84d7c951ce19. The new asynchronous /insert-toc path can silently insert a TOC into the wrong document when the user switches tabs before the local backend responds.

Source: frontend/src/hooks/editor/useSlashCommandWiring.ts:69-79 captures text from document A, awaits Markdown.toc(text), then calls insertTableOfContents(toc) without checking that the original editor model is still current. frontend/src/hooks/editor/useEditorFormatting.ts:74-79 delegates to replaceSelection, which fetches monacoRef.current.getModel() and getSelection() at insertion time. On a tab switch, those now belong to document B. The generated TOC for A is inserted in B.

Exact-source reproduction (Node 24, no project files changed): fetched the two functions at the PR head (wiring blob 98700b2881f21cb27e5025b67a68f23338d4b9dd; formatting blob 43254c0a31640284412b1903119d20a5c7dbd584), stripped TypeScript with Node's builtin transformer, ran the original functions using deterministic mocked Monaco and a deferred Markdown.toc promise. Invoked the slash command in A (# Alpha), switched the editor ref to B (# Beta) before resolving the response, then resolved the TOC. Result: A only had the slash trigger removed; B received ## Table of Contents\n\n- [Alpha](#alpha) ahead of # Beta. Reproduced, not a speculative race. This is an isolated source-backed execution, not a full browser / end-to-end test.

Narrow fix: capture the initiating Monaco model/document identity (and preferably a version/selection/range snapshot) before the await; on completion, cancel insertion if the active model, buffer version or intended insertion location is no longer valid. The insertion helper should not blindly resolve a different model on the return path. Add a deferred-promise regression test for A?B tab switch, plus an edit/cursor-move while awaiting. Keep the feature offline/provider-free as intended.

Requesting changes for this correctness invariant only.

@petertzy

Copy link
Copy Markdown
Owner

Approved. The branch has been merged with the latest main, and the offline /insert-toc feature meets the issue requirements: it generates TOCs locally through the Markdown endpoint, requires no AI provider or API key, preserves heading hierarchy and compatible anchors, and handles documents without headings gracefully.

The reported async race has been fixed. The insertion target now captures the initiating Monaco editor, model, document version, and selection before awaiting the TOC response. The result is discarded if the user switches tabs, edits the document, or moves the cursor before completion, preventing content from being inserted into the wrong document or location. The AI-panel entry point is also guarded against stale document results.

Regression coverage includes deferred-response tab switching, content/selection changes, and the unchanged successful path. Validation passed: 491 Python tests, 157 frontend tests, frontend lint, production build, and Ruff checks. Ready to merge.

@petertzy
petertzy merged commit 6699229 into petertzy:main Oct 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add an Offline /insert-toc Slash Command for Markdown Table of Contents

3 participants