Repository navigation
Conversation
perber
approved these changes
Oct 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Related issue
Fixes #1683
What changed
Fix:
MarkdownLinkre-resolves links when the page tree changes (ui/leafwiki-ui/src/features/preview/MarkdownLink.tsx)MarkdownLinkused to subscribe to the stablegetPageByPathfunction, so a later change to the tree indexes never re-rendered it.MarkdownPreviewmemoizes the body of plain Markdown, so its own re-render did not reach the link either. A link rendered before the tree had loaded stayed a red "create page" button, and a link to a page that was later moved or deleted stayed clickable. The component now computes the lookup path before its early returns and subscribes to the target node itself:useTreeStore((s) => lookupPath === null ? null : (s.byPath[lookupPath] ?? null)). The selector returns either a stable node reference ornull.getPageByPath/getPageByIdremain only for thewikilink-notfoundclick handler. Web, protocol-relative, asset, fragment, relative-path, read-only and navigation-state behaviour is unchanged.Regression tests
MarkdownLink.test.tsx: the target appears after render (absolute and relative link); the target disappears after render (editor, and read-only). Store changes only, norerender, no prop changes.MarkdownPreview.test.tsx:[Target](/target)resolves when the tree arrives later, and unresolves when the target is moved away. This checks across the memoization boundary.E2E (
e2e/tests/page.spec.ts)move-page-updates-incoming-links-via-refactor-api, because it calls the refactor API, not the dialog. Every step and assertion is kept.[target](/target)without the old path. A failed rewrite now fails as such instead of as a missing DOM link.incoming-link-evidence.jsonattachment holds the checkpoint values, the article HTML and the pathname, with no cookies, headers or credentials. The assertion error is rethrown.incoming-link-recovers-after-late-tree-response: holds back onlyGET <base>/api/tree(exact pathname) until the referrer has rendered, then releases it and expects the anchor with no navigation. Gates are released and unrouted infinally.CI (
e2e/playwright.config.ts,.github/workflows/e2e.yml)trace: 'on-first-retry'→'retain-on-failure'. No retries are configured, so the old setting never recorded a trace.if: failure()step uploadse2e/test-results/with the already pinnedactions/upload-artifact@043fb46…(v7.0.1),retention-days: 1. The test step's exit status is unchanged.e2e/run.shand session cookies of the throwaway test server. They contain no real credentials.No backend, auth, flag, schema or dependency change; no timeout increase, retry or skip.
How I tested it
MarkdownLink.tsxtemporarily restored, and passes with the fix.npm testpasses 727/727.npm run buildandnpm run lint -- --max-warnings=0are clean.--grep 'incoming-link' --repeat-each 3passed 6/6.--grep 'move-page-updates-incoming-links-via-refactor-api' --repeat-each 20withE2E_MAX_FAILURES=0passed 20/20. Full one-worker suite: 206 passed, 3 skipped, 0 failed.eslintandprettier --checkare clean on an LF checkout.trace.zip, a screenshot, a video anderror-context.mdundere2e/test-results/.Known, separate: in one earlier 20-repeat run, the new pre-move checkpoint failed once in 33 executions. The referrer had been saved without the typed link.
EditPage.savePagetreats a save button that never becomes enabled as "nothing to save" and returns without saving. The old test would also have failed in that case, at its preview poll, so this is a setup flake in the shared helper and not the CI failure from #1683. Why the typed text was missing is not diagnosed. Not changed here; it can be tracked separately.This PR fixes the late-tree subscription defect it demonstrates. It does not claim that every intermittent failure of this test is gone.
Manual test checklist (still open):
/assets/...) and external links open in a new tab; same-origin full URLs stay in the current tab.Checklist
if featurebranches in shared code (conventions)npm run formatinui/leafwiki-ui(and ine2eif e2e tests changed)