* fix: Remap internal links when duplicating a document tree
Duplicating a document tree now assigns the identifiers of every duplicate
before any content is written, so links and mentions between the documents
being duplicated point at the copies rather than the originals.
* fix: Remap fully qualified links when duplicating a document tree
Links written as a full url to this installation are now remapped alongside
relative ones, and stay fully qualified. Where a link is displayed as its own
url the text is updated to match.
Also trims surrounding whitespace in sanitizeUrl, which otherwise failed
validation and prepended a second scheme to an already qualified url.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: Remap links across trees when duplicating a collection
The collection duplication task duplicated each root document separately, so
only links within a single tree were remapped. It now duplicates the whole
collection as one unit, with identifiers assigned across every tree before any
content is written.
* fix: Match document links by identifier rather than host when duplicating
An installation can be reached through more than one host, so comparing a
link's host against the configured url left fully qualified links to a
document in the duplicated set unmapped. The document a link identifies now
decides whether it is replaced, and the host it was written with is kept.
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat: Add TextPack (.textpack) import support
Adds a single-document importer for TextPack, the zipped variant of
TextBundle used by Bear, Ulysses, iA Writer and others. Bare .textbundle
directories are not supported, as a directory cannot be delivered through
a browser file input.
The bundle's text entry may use any extension, per the spec, with
info.json's type deciding whether it can be read as markdown. Assets are
inlined as data URIs for the existing attachment pipeline to pick up,
bounded by the attachment size limit and a memory ceiling, and only for
media types markdown-it accepts as a link destination.
Also fixes an existing bug where a file embedded in an HTML or email
import as a data URI was stored in the document as base64 rather than
being uploaded as an attachment.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Refactor to individual converters
* refactor
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: Return a compact success payload from MCP mutation tools
Mutation tools serialized the entire resource — and for documents the
full markdown body — back to the client on every write, which the caller
already has. They now acknowledge the write with the identifying fields
needed to reference the resource.
The one exception is update_document with editMode "patch", which still
echoes the resulting content so the caller can verify what was replaced.
For move_document and restore_document the breadcrumb is now resolved
after the transaction commits, so it reflects the new location rather
than the cached pre-move document structure.
* test: Update MCP scope enforcement tests for the new response shape
* test: Assert the success acknowledgement across all mutation tool variants
The main app scenes were created with bare lazyWithRetry, so the sidebar
had no way to warm a route's chunk ahead of navigation — every click paid
the full fetch. Move them to createLazyComponent in a leaf module that
both the routes and the sidebar can import without a cycle, then preload
on the existing hover intent handlers.
Document and Collection links already prefetched their data on hover, so
the chunk preload rides along on the same event.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
The icon column stretched to the full height of a list item, so on mobile
– where items are tall enough to wrap a context snippet and meta – a large
strip down the left of every item toggled selection instead of navigating.
Hug the icon so that only a click on the icon itself begins a selection.
Also limit revealing the checkbox to real hover. On touch devices `hover`
resolves to `active`, and the rule additionally matched focus, so simply
tapping an item to navigate swapped its icon for an empty checkbox and the
state persisted. There the checkbox now appears once a selection is active.
Claude-Session: https://claude.ai/code/session_01WUpKTb4aEtvUwfQQoMC53v
Co-authored-by: Claude <noreply@anthropic.com>
The user role submenu labelled each item "Promote to…" or "Demote to…"
depending on the target user's current role, and hid the role they
already had. Show the plain role names with a checkmark against the
current one, matching the group member role menu.
Claude-Session: https://claude.ai/code/session_01LfzcABi86HH7n6j9QrY4po
Co-authored-by: Claude <noreply@anthropic.com>
* fix: Invalid sort comparator for frequently used items (#13241)
Extracts frequency tracking for icons, emojis, and code languages into a
shared FrequencyTracker with a spec-compliant, stable sort.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Snapshot frequently used icons while picker is open
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Sync frequent custom emojis to tracked ids, preserving order
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Memberships pointing at soft-deleted or archived documents were still
returned by userMemberships.list and groupMemberships.list, consuming
slots in the first page the sidebar requests. Inner join the document so
they are excluded from the query entirely.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: Bound plain text cache in command bar search index
Adds a shared LRUCache utility with optional sessionStorage persistence
and uses it for the command bar plain text cache, which previously grew
without limit, and for the Mermaid diagram cache, which had its own
bespoke LRU implementation.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: Avoid double lookup in LRUCache.get
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: Read persisted LRUCache values lazily
Hydration now loads only the key index, reading each value from storage
on first access. This avoids a synchronous read and parse of the entire
cache on first use, and keeps in memory only the entries the session
actually touches.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test: Assert lazy LRUCache reads without a storage spy
Spying on Storage.prototype missed the calls under Node 26, where a
native Storage global shadows the jsdom one. Replacing the stored value
after hydration asserts the same behavior independently of environment.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Restores field-level picking in presentURL instead of casting the entire
UnfurlData blob, so transient metadata attached by unfurl plugins or
internal callers is never serialized into API responses.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Widen the narrowed instanceof Error checks introduced in the TypeScript 6
upgrade with a shared errToId helper that accepts any object carrying a
string id, restoring the login email-matching recovery path.
* feat: Add support for web page and email import
* fix: Type mailparser result explicitly
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Combine HTML and Web Page
* fix: Validate mime type before inlining MIME archive parts
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat: Replace import document file picker with a modal
Choosing "Import document" now opens a dialog that lists the supported
file formats with an icon for each, and accepts files either dropped onto
it or chosen with the system file picker.
Also closes the gaps between the formats the dialog advertises and those
the importer actually accepts:
- `.tsv` was offered by the file picker but rejected by the server, it is
now converted to a table like CSV
- `.csv` and `.htm` are now recognised by extension, so they still import
when the browser reports an unhelpful mime type
- `.markdown` is now offered by the file picker, it was already supported
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dj1UnjnRX336CQcdzw3ott
* Add intermediate dialog for file import
* tweaks, polish
* Update failure message
* test
---------
Co-authored-by: Claude <noreply@anthropic.com>
sequelize-strict-attributes defines per-instance accessors for attributes
omitted from a query's selection, which trips Sequelize's public class field
detection. The removed `Fix` decorator used to suppress this per model; do it
once on the base model instead.
* perf: Reduce data loading on revisions.list endpoint
* test: Cover includeContent option on Document.findByPk
Also fixes a JSDoc typo on the includeViews option.
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix: Button elements in DocumentMeta rendered as links
* fix: Expose comments sidebar state on toggle button
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Image and video dimension loading failures now warn and resolve undefined
rather than rejecting, which surfaced as unhandled errors in Sentry.
Co-authored-by: Claude <noreply@anthropic.com>
* fix: Crash when two mermaid diagrams are adjacent
A diagram's widget decoration sits at the end of its code block, which is
also the start position of the following block. When the next block is also
a mermaid diagram and has no decoration of its own yet, it adopted the
preceding diagram's renderer — and so its single DOM element — leaving two
widget decorations sharing one node and crashing prosemirror-view.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: Enter with mermaid selection deletes node
---------
Co-authored-by: Claude <noreply@anthropic.com>
Drop targets that reorder or reparent an item now require the `move`
ability on the dragged item, so no drop cursor or highlight appears
where the move would be rejected by the server.
Co-authored-by: Claude <noreply@anthropic.com>
* Move socket management from client -> server
* feedback
* fix: Reconcile collection rooms on visibility change
Handle collections.update transitions between private and team-visible by
joining or rebuilding the collection channel, route collection events through
the members channel so guests are excluded, and use removeData for the
collection ids cache invalidation.
---------
Co-authored-by: Claude <noreply@anthropic.com>
The `fast-uri@npm:^3.0.1` resolution was redundant. The ^3.0.1 range
naturally resolves to 3.1.4 without the resolution override, so removing
it produces identical yarn.lock output and does not introduce any new
vulnerabilities per `yarn npm audit --severity high --recursive
--environment production`.
All other resolutions remain necessary either to prevent lower/vulnerable
versions from being installed transitively or to enforce a single package
version required for TypeScript type consistency (React, Koa,
prosemirror-transform, markdown-it types).
Co-authored-by: Claude <noreply@anthropic.com>
* wip
* generalize
* refactor
* Extract SearchResultIcon for command bar results
Both search action builders rendered the same document icon inline. Move it
into a shared component alongside the other command bar pieces, forwarding
size so the command bar can continue to size icons via cloneElement.
Also drop the redundant charAt(0).toUpperCase() on the initial, as the Icon
component already normalizes it.
Co-Authored-By: Claude <noreply@anthropic.com>
* PR feedback
Prevent same doc showing twice in cmd k
Improve result ordering in main app command bar
Refactor to flattenTree
* Upgrade Fuse to 7.5.0, use workers
* fix: Re-register search actions when index is enriched
Include result contexts in the command bar registration key so that
snippets refresh when the same documents are enriched, and only mark a
server search as cached once its results are actually fed to the index.
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* chore: Audit and move inline actions to definitions
* fix: Reuse existing smart-quote translation key in emoji delete dialog
Co-Authored-By: Claude <noreply@anthropic.com>
* Remove two usages of 'Link copied' translation
---------
Co-authored-by: Claude <noreply@anthropic.com>