fix(review): address #230 re-review — stale breadcrumb, swallowed error, i18n, docs

Approve-with-comments follow-ups:

- breadcrumb: fix the reverse regression where navigating A->B to a page absent
  from the lazily-built tree (before its ancestors load) left the previous
  page's clickable chain on screen. New pure computeBreadcrumbState clears a
  stale chain that doesn't end at the current page, while keeping one that does
  (no blank flash for an already-resolved page); unit-tested for the
  navigated-to-absent-page case.
- share.service: getShareAncestorPage no longer swallows DB errors silently —
  now a live public-share path (isPageReachableThroughShare), so a transient
  error is logged with ancestor/child ids and still fails closed (caller 404s)
  instead of becoming a traceless misleading "not found".
- i18n: register the new "Connecting… (read-only)" key (U+2026 ellipsis) in
  en-US (source of truth) and ru-RU (Подключение… (только чтение)).
- share.service: correct the FUTURE note — 3 callers pass no shareId
  (share-alias.controller/.service, share-seo.controller); the two ai-chat
  callers already pass a real shareId.
- CHANGELOG: add Unreleased Changed/Fixed/Security entries for #216 opt-in
  sub-pages default, #218 trimmed page-info payload + forged-shareId 404, #204
  export internal-link name, #206/#218 breadcrumb, #192 callout paste, #218
  editor pre-sync read-only gate.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
a
2026-06-27 21:31:49 +03:00
parent c9d252cf2a
commit 525172104a
7 changed files with 130 additions and 19 deletions

View File

@@ -42,8 +42,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
catalog's raw files; the image ships a per-branch default baked in CI, and it
can be overridden at runtime via the env var (see `.env.example`). (#222)
### Changed
- **Enabling a public share no longer auto-shares the whole sub-tree.** Turning
a page "Shared to web" now defaults to the page alone; descendant pages become
public only when you explicitly turn on the dedicated "Include sub-pages"
toggle. Previously the create call defaulted to including sub-pages, silently
exposing every child of a freshly shared page. (#216)
### Fixed
- **Internal links in exported Markdown no longer lose their visible text.** A
link whose target page name had no file extension (e.g. a bare title) was
collapsed to empty text during export, producing an unclickable, label-less
link; the page name is now preserved. (#204)
- **Deep pages no longer render a blank breadcrumb while the sidebar tree loads.**
The breadcrumb now falls back to the page's own ancestor chain (fetched
independently of the lazily-built sidebar tree) so a deep page resolves its
trail immediately; navigating away no longer leaves the previously-viewed
page's breadcrumb showing until the new one resolves. (#206, #218)
- **Pasted GitHub-style callouts (`> [!NOTE]` …) now convert to real callouts.**
GitHub admonition blocks pasted as Markdown are recognized and rendered as
callout blocks instead of plain block-quotes. (#192)
- **The editor stays read-only until collaboration has synced.** While a page is
connecting, the body is shown as a non-editable static view with a
"Connecting… (read-only)" banner, so edits typed before the document finishes
syncing can no longer be silently dropped. (#218)
- **A shared page now keeps EXACTLY ONE custom address (`/l/:alias`).** Editing a
page's vanity slug previously inserted a second `share_aliases` row instead of
renaming the existing one, leaving the old `/l/<old>` link live forever and
@@ -63,6 +87,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
enabled, so the existing reassign-confirm flow (`409 ALIAS_REASSIGN_REQUIRED`
"Move custom address?") is discoverable instead of reading as terminal. (#227)
### Security
- **The anonymous public-share page payload is trimmed to an explicit allowlist.**
The `/shares/page-info` route (the only unauthenticated path serializing a
page + its share) now returns only the fields the public renderer needs;
internal metadata — creator/last-updater/contributor ids, space/workspace ids,
AI/source bookkeeping, lock/template flags, parent/position and raw timestamps
— is no longer exposed to anonymous viewers. (#218)
- **A forged or mismatched share id can no longer render a page off its slug
alone.** When the public URL carries a share id/key, the page must be reachable
through that exact share (its own share or an ancestor `includeSubPages`
share); any other value now returns the generic "not found" instead of
serving the page. (#218)
## [0.94.0] - 2026-06-26
This release makes AI chat durable and fast: assistant turns are persisted to