268 Commits

Author SHA1 Message Date
Tom Moor 6461aabc52 feat: Add Catalan language option (#12454)
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 15:56:32 -04:00
Tom Moor ab3994f3f1 feat: Comments sidebar in image lightbox (#12335)
* feat: Toggle comments sidebar in editor lightbox

Adds a new comments toggle button to the lightbox top-right actions. When
toggled the sidebar slides out on the right and shows only the threads
anchored to the active image node. A new comment form at the bottom
creates a thread anchored to the image via a comment mark on the node.

https://claude.ai/code/session_01W3duHkZJ6vgNPCQJL8hQK7

* fix: Make lightbox comments sidebar interactable

The sidebar was being rendered as a sibling of Dialog.Content, so Radix's
focus/click-outside trap blocked all interaction with it. Move it inside
Dialog.Content so clicks and focus stay within the dialog.

Also scope the lightbox handleKeyDown to only preventDefault and act on
arrow/escape keys — and bail out entirely when typing into an input,
textarea, or contenteditable so the comment form receives keystrokes.

https://claude.ai/code/session_01W3duHkZJ6vgNPCQJL8hQK7

* fix: Align lightbox comments header with action buttons

Nudge the sidebar Comments heading 4px down so its baseline lines up
with the lightbox top-right action bar.

https://claude.ai/code/session_01W3duHkZJ6vgNPCQJL8hQK7

* fix: Render lightbox sidebar popovers inside the dialog

Reactions, menus, and tooltips inside the lightbox comments sidebar were
portalling into the editor wrapper via PortalContext — which is hidden
behind the lightbox overlay. Provide a PortalContext that targets the
sidebar element itself so popovers render inside the dialog and remain
visible.

https://claude.ai/code/session_01W3duHkZJ6vgNPCQJL8hQK7

* fix: Prevent lightbox handlers from stealing focus from reply input

Pointer events bubbling out of the comments sidebar were reaching the
ancestor Dialog.Content / lightbox handlers and somehow disrupting focus
on the ProseMirror reply input. Stop propagation of pointer, mouse, and
click events at the CommentsSidebar so the sidebar owns its own
interaction handling.

https://claude.ai/code/session_01W3duHkZJ6vgNPCQJL8hQK7

* fix: Anchor lightbox close animation to current image position

The close animation's translation was calculated relative to the image
position cached when the image first loaded — before the comments
sidebar could shift the image left. Recapture the natural position at
the start of setupZoomOut so the animation correctly starts where the
image actually is when the sidebar is open.

https://claude.ai/code/session_01W3duHkZJ6vgNPCQJL8hQK7

* fix: Fade the comments sidebar with the rest of the lightbox

The sidebar previously had only a slide-in animation on mount and stayed
fully opaque while the rest of the lightbox faded out on close. Wire the
sidebar to the shared fadeOut animation so it disappears in lockstep
with the overlay and action controls.

https://claude.ai/code/session_01W3duHkZJ6vgNPCQJL8hQK7

* Final fixes

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-17 18:57:19 -04:00
Tom Moor 571463710e Add support for data uri for images (#12294)
* Add support for data uri for images

* hoist
2026-05-08 09:10:32 -04:00
Tom Moor 091346dfe8 chore: Migrate to vitest (#12272)
* wip

* Remove obsolete snapshots

* simplify

* chore(test): Convert mocks to TypeScript and tighten fetch mock types

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Remove unneccessary patches

* Migrate to msw instead of custom fetch mock

* Address PR review comments

- Split chained vi.useFakeTimers().setSystemTime() into separate calls.
- Switch test setup to dynamic imports so EventEmitter.defaultMaxListeners
  assignment runs before module init (static imports were hoisted above it).
- Drop redundant NODE_ENV guard in monkeyPatchSequelizeErrorsForJest; its
  sole caller already gates on env.isTest.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-06 21:10:51 -04:00
Tom Moor 0139b91b5d chore: Replace lodash with es-toolkit (#12281)
* chore: Replace lodash with es-toolkit

Migrate all direct lodash imports to es-toolkit/compat for a smaller,
faster, lodash-compatible utility library. Transitive lodash usage from
other packages remains unchanged.

* fix: Restore isPlainObject semantics in CanCan policy

The lodash migration aliased `isObject` to `lodash/isPlainObject` and
the codemod incorrectly mapped the local name to es-toolkit's `isObject`,
which also returns true for arrays and functions. This caused condition
objects in policy definitions to be skipped, breaking authorization
checks across the codebase.

* fix: Restore unicode-aware length counting in validators

es-toolkit/compat's size() returns string.length, while lodash's _.size()
counts unicode code points. Switch to [...value].length to preserve the
previous behavior so multi-byte characters like emoji count as one.
2026-05-06 21:03:47 -04:00
Tom Moor 4c8a1c89b2 chore: resolve no-explicit-any and no-base-to-string lint warnings (#12217) 2026-04-29 17:45:02 -04:00
Tom Moor 0e074321db fix: Add additional validation to table attributes (#12156)
* fix: Add additional validation to table attributes

* fix: Widen isValidCellMarks predicate and test 4-digit hex

* Additional tests

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 08:40:04 -04:00
Tom Moor f9a2cbc1b3 chore: resolve remaining unbound-method lint warnings (#12206)
* chore: resolve remaining unbound-method lint warnings

Apply targeted fixes per call pattern: arrow wrappers when passing a
method as a callback, arrow-function class fields when the method
doesn't depend on `this`, and `.bind()` when capturing for later
invocation.

Also replaces the rfc6902 hasOwnProperty re-export with a small wrapper
function so callers don't reference an unbound prototype method.

* chore: memoize history.goBack callbacks

Stable identity prevents Button re-renders and avoids re-subscribing
the global keydown handler in RegisterKeyDown when the parent renders.
2026-04-28 20:49:35 -04:00
Tom Moor 87bb79250d chore: enable typescript/restrict-template-expressions lint rule (#12199)
* chore: enable typescript/restrict-template-expressions lint rule

Coerce values of unknown type with explicit String() and tighten typing
for template literal expressions across the codebase.

* fix: restore --line-height on Card for fadeOut ::after gradient
2026-04-28 20:11:15 -04:00
Tom Moor adbffc0734 chore: clear mechanical lint warnings (Phase 1) (#12198)
* chore: clear mechanical lint warnings

Drops 44 oxlint warnings (559 → 515) by fixing easy mechanical rules
across the codebase: no-useless-escape, no-duplicate-type-constituents,
no-redundant-type-constituents, no-unused-expressions,
no-meaningless-void-operator, require-array-sort-compare, await-thenable.

* chore: drop callback parameter from useCallback deps

The `open` argument is a parameter of the callback, not a closed-over
variable, so it doesn't belong in the deps array.

* chore: promote cleared lint rules to errors

Promotes the rules cleared in this PR from warn to error so future
violations fail the lint:

- no-unused-expressions
- typescript/await-thenable
- typescript/no-duplicate-type-constituents
- typescript/no-meaningless-void-operator
- typescript/require-array-sort-compare

Removes the override that suppressed no-useless-escape on source
files (the global rule is already error) and fixes the 21 escape
violations that this exposed in regex character classes and template
literals.

* chore: address PR review feedback

- usePinnedDocuments: simplify UrlId to plain string instead of the
  intersection trick.
- PlantUML embed: move - to end of character class so it's a literal
  hyphen rather than a range operator.
- checkboxes: type token params as Token | undefined to match the
  actual call sites that pass tokens[index - 2] etc.
2026-04-28 20:00:03 -04:00
Tom Moor cd9e79b1f1 chore: replace explicit any with concrete types in shared (#12201)
* chore: replace explicit any with concrete types in shared

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* chore: address review feedback

- naturalSort: guard non-string field values instead of asserting string
- ProsemirrorHelper: type stored mark attrs as Partial<CommentMark>
- env: revert to Record<string, any>; safer typing requires fixing many consumers

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-28 19:51:51 -04:00
Tom Moor e034a28242 chore: Address AI code quality findings (#12163)
- Modal: translate default title and bind Dialog.Title to visible text
- Document Header: regroup imports and rename isNew -> wasNew
- Redis adapter: surface error.message and guard pingTimeout cleanup
- urls: fix typo and correct JSDoc @param names

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 08:25:55 -04:00
Tom Moor 4c4649346b feat: Allow geo:, maps:, and magnet: link protocols (#12149)
* feat: Allow geo:, maps:, and magnet: link protocols

* Case-insensitive scheme matching, fix test grammar
2026-04-24 04:10:38 -04:00
Tom Moor 46b040a9f4 fix: Validate move operation path prefix per RFC 6902 (#11835)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-18 21:08:39 -04:00
Tom Moor 49d5052a51 feat: RTL layout (#12107)
* First pass

* Remove prop drilling, fix comment layout

* Revert dev:watch to use dev:backend

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 15:12:57 -04:00
Tom Moor fccc343cb9 feat: Add Hebrew as a language option (#12083)
* feat: Add Hebrew as a language option

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-16 18:37:41 -04:00
Tom Moor 054404d716 fix: Missing + on shared doc shortcut display (#12013)
* fix: Missing + on shared doc shortcut display

* fix: Show "+" between shortcut keys on Windows

Add shared `shortcutSeparator` constant and use it across all shortcut
renderers so Windows displays "Ctrl+K" instead of "CtrlK".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 18:04:18 -04:00
Tom Moor b4c1f88731 feat: Allow document unfurling with shareId (#12007)
* feat: Allow document unfurling with shareId

* fix: Handle collection shares, share-scoped URLs, and unauthenticated unfurls

- Return 204 instead of 404 for collection shares without a document
- Use share-scoped URL in unfurl response so hover previews stay within
  the share context
- Add test coverage for unauthenticated share URL unfurling

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* perf: Only resolve team from context for non-UUID share identifiers

loadPublicShare only requires teamId when the share identifier is a
slug (urlId), not a UUID. Skip the getTeamFromContext DB lookup on the
common UUID path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-12 21:46:36 -04:00
Tom Moor d4dec42bc5 fix: Validate host parameter stored in OAuth state on failure redirect (#11956)
* fix: Validate host parameter stored in OAuth state on auth failure path

* fix: Validate OAuth state host to prevent open redirect

Sanitize the host parameter from OAuth state before using it in error
redirects. Adds userinfo stripping to parseDomain's normalizeUrl to
prevent bypasses like "subdomain.base@evil.com", validates custom
domains against registered teams, and introduces Team.findByDomain
with input normalization for consistent domain lookups.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-04 16:13:54 -04:00
Tom Moor a411e08f1f chore: Address code quality findings (#11960)
* chore: Address code quality findings

* Round 2, quality findings

* fix: Add fallback for MediaQueryList.addEventListener in test env

The jsdom test environment doesn't implement addEventListener on
MediaQueryList. Prefer addEventListener but fall back to the
deprecated addListener when unavailable.
2026-04-04 16:11:10 -04:00
Tom Moor e0021a3d4f Display keyboard shortcuts in menus where available (#11959)
* Display keyboard shortcuts in menus where available

* feat: Display keyboard shortcuts in action menus

Pass shortcut data from Action definitions through to menu items and
render formatted key symbols on the right side of menu entries. Handles
platform differences via normalizeKeyDisplay. Also adds Control key
display support and uppercase formatting for single-letter shortcuts.


Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-04 15:19:11 -04:00
Tom Moor 979d9a412d Mermaid improvements (#11874)
* fix: Upgrade mermaid to 11.13.0

Includes a fix for incorrect viewBox casing in Radar and Packet diagram
renderers (mermaid-js/mermaid#7076) and other improvements.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: Use visibility:hidden for mermaid rendering element

Instead of positioning the temporary render element offscreen at
-9999px, use visibility:hidden with position:fixed so the browser
computes correct bounding boxes for SVG elements. Offscreen elements
can produce incorrect getBBox() results, leading to wrong viewBox
dimensions and diagrams rendering too big or too small.

Fixes #11782

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add session storage for generated diagrams to reduce relayout

* fix: Use LRU eviction for mermaid sessionStorage cache

Track access order via a dedicated LRU index key so the cache evicts
least-recently-used entries rather than arbitrary ones.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 22:59:57 -04:00
Tom Moor 793804cd0d feat: Strip comments from presentation mode (#11860)
* feat: Strip comment marks from documents in presentation mode

Move removeMarks to shared ProsemirrorHelper and use it to strip comment
marks before rendering slides. Make server ProsemirrorHelper extend the
shared class to eliminate duplication and remove SharedProsemirrorHelper
imports from server code.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: Use Set for mark lookup and cloneDeep for browser compat

Use a Set for O(1) mark lookups in removeMarks traversal. Replace
structuredClone with lodash/cloneDeep to support older browsers
that lack the native API.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test: Add tests for ProsemirrorHelper.removeMarks

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 20:21:43 -04:00
Tom Moor 84aed78ee2 fix: Improve performance when editing titles in large open document trees (#11858) 2026-03-23 18:53:37 -04:00
Tom Moor 7dc1d12d3b feat: Support simplified mention syntax in markdown for MCP (#11851)
* feat: Support simplified mention syntax in markdown for MCP clients

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Restore translations

* PR feedback

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 08:08:24 -04:00
Tom Moor 88f7ef9d03 fix: Hide fullscreen control in present mode on mobile iOS (#11737) 2026-03-12 20:50:25 -04:00
Tom Moor 8dbbcb6dce perf: Variety of perf fixes in ProsemirrorHelper (#11554)
* perf: Variety of perf fixes in ProsemirrorHelper

* doc

* feedback
2026-02-28 14:12:13 -05:00
Tom Moor ccde98ce82 fix: Handle template delete and error cases 2026-02-22 16:54:14 -05:00
Tom Moor ac29295dd2 fix: Tighten touch device detection (#11515) 2026-02-21 18:27:00 -05:00
Tom Moor 72c2664478 Allow currency sorting in tables (#11332)
* Allow currency sorting in tables

* Update shared/editor/commands/table.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-01 10:43:10 -05:00
Apoorv Mishra f1e8c0bcc2 fix: Colocate preset color codes and their names in color utils (#11289) 2026-01-27 18:08:19 -05:00
Apoorv Mishra e9ed1ba5d1 Toggle block (#8317)
* fix: modify input rules for heading to wrap it in a toggle block

* fix: leave heading node untouched

* feat: add toggle block menu item

* feat: first prototype

* toggle_head and toggle_body

* fix: indent toggle block

* fix: cleanup

* fix: allow only one heading or one para inside toggle head

* fix: cleanup

* fix: cursor becomes invisible as soon as toggle block is inserted

This happened because the containing paragraph had ~0px as width which
hid the cursor. I attemped setting the `style.minWidth` to 1px for the
containing `span` and cursor became visible. Hence, set the `flexGrow`
prop so that it occupies all the avaible space.

* fix: keep the toggle button vertically center-aligned

Adjusts the toggle button and keeps it center-aligned vertically as the toggle head's
content node changes from, say a paragraph to a heading(of any level),
or the other way around...

* chore: style using css

* fix: nesting of toggle blocks

`toggleWrap` resorted to lifting out the active node when attempting
to create a new nested toggle block inside existing toggle block, which
made it impossible to nest toggle block. Hence, bypassing the
`toggleWrap` flow in favor of `wrapIn`, which provides nesting of toggle
blocks.

* fix: assign unique id to each toggle head node

This will be later used to persist toggle state of the toggle block

* feat: attempt at using node view for toggle block

* fix: get rid of nanoid, we can use existing uuid pkg

* feat: plugin to manage toggling behaviour

This includes a plugin which, for now handles the following behaviours:

1. Sync collapsed state from localStorage, and correspondingly initialize
   decorations for all the toggle block nodes in doc
2. Handle the fold/unfold behaviour of toggle block, triggered through
   the toggle button from within the node view

* fix: don't trigger toggle behavior if secondary button on mouse is pressed

* fix: restore decorations which are removed upon `joinBackward`

When the selection is at the start of block node just
after the toggle block node, pressing backspace triggers `joinBackward`
command which attempts to "join" this block node with the toggle block
node just before it. The `joinBackward` command works by adding a
replacement step, which, in turn drops the decorations in the affected
range. As a result, the toggle block collapses. In order to prevent the
collapse, we restore back the dropped decoration to its corresponding
node(if it exists).

* chore: can find spec using id now

* chore: this.name in favor of hardcoding

* fix: collapse all children of toggle block except the first

As a result of setting `content: block+` for the toggle block node, all
its children are rendered flat in DOM, making it difficult to distinguish which
one corresponds to its first child node. This, in turn, makes it hard to
identify which one should not be collapsed when pressing the toggle
button to collapse. The solution here is to wrap the first child node in
a separate DOM node via a decoration. So, hopefully, we don't need to
break up `toggle_block` node as one containing `toggle_head` and
`toggle_body` nodes and setting `content: block+` should suffice.

* fix: properly restore lost decorations

A weird issue surfaced when a toggle block was erased and then the erase
was undone. It was observed that all the toggle blocks ended up being
collapsed and the one restored by undo had lost the decoration on first
child(which prevents first child from hiding), and, as a result ended up
with all its children hidden.

Here, we collect all dropped decorations in a single array and later add them
back in a single pass.

* fix: we don't yet need  and  as nodes

* fix: command mapped to `Backspace` key for `ToggleBlock` failed to invoke

It happened because of `Math` node being placed before `ToggleBlock` in
the array of exported nodes, which caused the `Math` node's `Backspace`
handling command to _successfully_ run before `ToggleBlock` node's
`Backspace` handling command. Therefore, moving `ToggleBlock` before
`Math` fixed the issue.

* fix: nested toggle blocks behaving differently than top level ones

It was observed that the decoration which wraps the first child of a
toggle block in a separate DOM wasn't being restored as and when
expected for nested toggle blocks while it worked fine for top level
toggle blocks. The reason was that the `findBlockNodes` skips the nested
blocks in its default behavior, so, passing `descend` as `true` fixed
the issue.

* feat: lift all children up upon backspacing at the start of toggle block

* fix: remove/reapply decorations which get misaligned as a result of doc
change

The decoration which is applied to the first child of toggle block, may
get misaligned and go out of sync with the corresponding decoration on
the toggle block as a result of document changes. Here, we simply remove
*all* the decorations and then reapply to their target i.e, first child of each
toggle block.

We're trading off perf with implementation simplicity here since we
don't actually need to remove all decos, only the misaligned ones.

An alternate solution here might be to map the first child decoration
in accordance with corresponding decoration on the toggle block, such
that the first child deco _never_ goes out of sync with its parent
decoration. As for now, not sure how that could be achieved.

* fix: bring cursor out and set at the end of toggle block head upon folding

* fix: cleanup

* fix: if cursor ends up within the hidden range of toggle block while it is folded, immediately unfold it

* fix: check parentNode

* feat: `prependParagraph` and `splitHead`

* fix: cleanup

* fix: handle `Delete` when the node just after the cursor is a toggle
block

* fix: cleanup

* fix: restrict toggle block head to contain heading or paragraph

Certain behavioral aspects of toggle block are implemented assuming head
to be a heading or paragraph, for simplicity. Makes sense for majority
of use cases but still something presumptuous about user expectation.
Proposition is to lauch it with this restriction and see if the users
actually start requesting otherwise. Till then, this keeps things
simple.

* feat: `Tab` into a toggle block, `Shift-Tab` out of toggle block

As part of this, we've modified handling `Enter` within a toggle block
in a way that prevents it to trigger `liftEmptyBlock`, so that the
cursor remains within the toggle block body and is only taken out of it
when pressing `Shift-Tab` combo.

* fix: Toggle block not unfolding when a node having `block+` content
attempted to `Tab` into it

* feat: beautification

* feat: markdown for `toggle_block`

This declares markdown spec for a toggle block, which enables users to
download a doc containing toggle blocks, as markdown. Also, supports
importing a markdown doc containing markdown corresponding to a toggle
block.

* fix: margins between toggle block contents

* fix: `Action.INIT` for publicly shared and deleted docs

It was observed that decorations weren't initialized for publicly shared
and deleted docs because of init being under the `docChanged` cond. This
change fixes the issue.

* fix: all toggle blocks end up folded when navigated from collection to
doc page

This happened because `{ fold: true }` was forcibly set. This is fixed
by applying decorations in accordance with the fold state fetched from local storage.

* fix: disable overflow being set to scroll on Brave

* fix: cleanup

* fix: prevent joining two toggle blocks when backspaced from the start of
a text block between them

Consider two toggle blocks with a text block between
them. If backspaced from the beginning of the text block, the toggle
block below is joined to the toggle block before along with the text
block, because of https://github.com/ProseMirror/prosemirror-commands/blob/20c7d42ab8b5d8642fb9efc6261b7541c9dc23c2/src/commands.ts#L468-L469. On the contrary, what's desirable is just joining back of the text block, retaining the toggle block below as it is.

* fix: sync collapsed state across browser tabs

* fix: cleanup

* fix: upon unfold, append an empty para if toggle block's body is empty

* fix: unfold upon `Enter` if the toggle block body is empty

* feat: placeholder

* feat: inputRule

* feat: `Mod-Enter` shortcut to toggle

* fix: do not split when body is empty

* fix: do not unfold is head is empty

* fix: assign uuid to newly split toggle block

* feat: list keyboard shortcuts

* fix: replace with `wrapIn`

* fix: `container_toggle_block` -> `container_toggle`

* fix: importing a markdown doc with toggle blocks let to them being created without ids

* fix: pressing `Enter` at the end of list item within toggle block should
create a new list item below

* fix: repeated backspacing from an empty list item within toggle block

* fix: prevent joining back when input rule is matched

* fix: prevent button from shrinking when an image is added under content area

* fix: tsc

* Fixes:

1. Toggle block starts off unfolded when created

2. Trigger `liftEmptyBlock` when a toggle block consists of just an empty head, without any body

3. `Shift-Tab` behavior confuses when all nodes following the cursor position, inclusive of the one holding cursor, are empty. It seems at first, that it should simply outdent except it doesn't, because the node holding cursor isn't the `lastChild` of toggle block.

So, `Shift-Tab` behavior is modified such that all nodes following the cursor, up till the last node of toggle block, should be lifted out of it

* Fixes:

1. Upon pressing Enter, lift out all children of toggle block if cursor lies at the start,
   there's no text content
2. Prevent lifting out of an empty block when it's a direct child of a toggle
   block

* fix: lint

* fix: placeholder to inform how to exit toggle block

* fix: prevent tables within toggle block from horizontally scrolling

* fix: align toggle block with lists

* fix: push toggle block down if it ends up as the first child of a list item

* fix: don't consider toggle body empty if it consists of an empty table or notice

* fix: CollapsedIcon

* fix: Delete press

* fix: mainly early return when `deleteBarrier` or `joinMaybeClear`
succeed, rest is cleanup

* fix: rename

* fix: simplify commands

* fix: simplify commands

* fix: remove unused commands

* fix: lint

* chore: cleanup `splitBlockPreservingBody`

* chore: `sinkBlockInto` -> `indentBlock` & `liftConsecutiveBlocks` ->
`dedentBlocks`

* fix: cleanup related to `getUtils`

* fix: simplify `bodyIsEmpty`

* fix: no need of separate func

* fix: `bodyIsEmpty` -> `ToggleBlock.isBodyEmpty` && `headIsEmpty` ->
`ToggleBlock.isHeadEmpty`

* fix: cleanup

* fix: move to utils

* fix: update comment

* fix: rename

* fix: update comment

* fix: update comment

* fix: comments

* fix: cleanup

* fix: `splitBlock` was problematic here because it would run for block
nodes other than toggle block. As an example, consider a `blockquote` containing and empty
para. Here, `liftEmptyBlock` should run upon `Enter`, instead
`splitBlock` runs. Same goes for other nodes like task lists. This
commit fixes the issue.

* fix: wrap heading and its children together within toggle block

* trigger ci

* fix: copy for used funcs from sorted-array-functions

* fix: remove pkg

* fix: remove pkg

* fix: rearrage dep

* fix: restore yarn.lock from main branch

* fix: restore yarn.lock from main branch

* feat: triggering toggle block on an already wrapped toggle block should unwrap it

* fix: get rid of `HeadingTracker` in favor of directly querying doc

* fix: headings under toggle blocks weren't tracked in toc

* fix: don't hide the anchor associated with a heading otherwise the heading can't be scrolled to

* fix: unfold toggle block when hidden heading is clicked from toc

* fix: backspacing into an unfolded toggle block should attempt to join with the last node of body

* `Tab` in a selected `embed` within a folded toggle block. Notice that toggle block remains folded. It should be unfolded as the `embed` is pushed inside. Same goes for some other nodes like `math_block`

* Can only `Tab` in and `Shift-Tab` out once when a node of type `attachment|video|hr` is the last node of a toggle block. Beyond once, pressing those keys have no effect.

* fix: Server build

* refactor

* Combine enum

* perf: Merge plugins, avoid multiple appendTransaction

* Remove getUtils

* fix: Default new toggle blocks to closed

* fix: Infinite loop

* refactor: Separate ToggleBlockView

* Centralize class names

* fix: Align nested headings with lists

* fix: Toggle block disclosure different sizes

* fix: Toggle flash when clicking fold button while focus within toggle content

* refactor: Plugin keys

* Exit toggle block when pressing Enter in the last empty paragraph within the body

* Placeholders

* fix: Fallback line-height, font-size for empty title

* fix: Incorrect decoration on title node change

* doc

* fix: Enter in last list item in toggle body exits

* fix: Allow toggling headings in diff viewer

* fix: Toggle button animation on first load

---------

Co-authored-by: Tom Moor <tom@getoutline.com>
2026-01-26 18:26:41 -05:00
Apoorv Mishra abd7abcc18 Choose table cell background (#10930)
* feat: table cell bgcolor

* fix: review

* fix: cleanup

* fix: new color picker

* fix: transparentize bg preset colors

* fix: show selected color in color list

* fix: pass active color to picker

* fix: make color picker command agnostic

* fix: tsc

* fix: table row and col background menu

* getColorSetForSelectedCells

* toggleCellBackground to toggleCellSelectionBackground

* cellHasBackground to cellSelectionHasBackground

* useless spread

* presetColors

* get rid of hasMultipleColors

* get rid of customColor

* be explicit in passing color

* alpha controls

* remove new highligh command

* DRY DottedCircleIcon

* restore ff fix

* merge createCellBackground into updateCelllBackground

* default color

* Merge

---------

Co-authored-by: Tom Moor <tom@getoutline.com>
2026-01-24 22:13:00 -05:00
Luca Wimmer 06938561a6 fix: add date sorting support to table columns (#11198)
* fix: add date sorting support to table columns

* fix: fixed lint errors, removed unnecessary non-null check

* fix: European slash format should not always be preferred

---------

Co-authored-by: Tom Moor <tom@getoutline.com>
2026-01-17 16:32:35 -05:00
yy 0230e1c5a5 chore: fix typos in comments and messages (#11203) 2026-01-17 10:50:06 -05:00
Copilot 77ad224709 Fix S3 presigned URL expiration exceeding AWS 7-day limit (#11191)
* Initial plan

* Fix S3 presigned URL expiration exceeding AWS 7-day limit

Co-authored-by: tommoor <380914+tommoor@users.noreply.github.com>

* Add comprehensive tests for S3 presigned URL expiration limits

Co-authored-by: tommoor <380914+tommoor@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: tommoor <380914+tommoor@users.noreply.github.com>
2026-01-15 19:51:21 -05:00
Tom Moor ec87cb0308 fix: alt+shift header selection in Safari (#11184)
* fix: Header selection in Safari

* Move browser constants to constants
2026-01-14 21:29:02 -05:00
Tom Moor 831f038e3c perf: Vendorize rfc6902 (#11174)
* perf: Vendorize rfc6902 with fix
2026-01-13 18:13:42 -05:00
Tom Moor 57b6e9aca4 feat: Passkey support (#11065)
closes #6930
2026-01-05 19:58:46 -05:00
Tom Moor 211b747c0a fix: Update email diff rendering to use ChangesetHelper (#11017)
* stash

* toHTML changes property

* stash

* stash

* document access

* test

* fix cleanup

* cleanup, fixes

* Restore styles

* Remove magic number

* wip
2026-01-03 09:52:08 -05:00
Tom Moor fd49602e40 feat: Improved revision viewer (#10824) 2025-12-28 08:56:32 -05:00
Tom Moor 15f2959574 fix: Large base64 images pasted as HTML cause doc not to sync (#10982)
* First pass

* Use placeholders

* tsc
2025-12-23 20:03:37 -05:00
Tom Moor bf45e97641 chore: Enforce type import consistency (#10968)
* Update types

* fix circular dep

* type imports

* lint type imports and --fix
2025-12-19 23:07:02 -05:00
Tom Moor a51188882b fix: isUrl requireHttps option is never hit (#10885) 2025-12-12 22:54:43 -05:00
Copilot 133ec073be Add CSV export for member list (#10803)
* Initial plan

* Add CSV export functionality to members page

Co-authored-by: tommoor <380914+tommoor@users.noreply.github.com>

* Align Export CSV button to the right

Co-authored-by: tommoor <380914+tommoor@users.noreply.github.com>

* Address code review feedback: improve type safety, error handling, and date formatting

Co-authored-by: tommoor <380914+tommoor@users.noreply.github.com>

* Improve CSV utility and date handling consistency

Co-authored-by: tommoor <380914+tommoor@users.noreply.github.com>

* Improve error messages and fix useCallback dependencies

Co-authored-by: tommoor <380914+tommoor@users.noreply.github.com>

* Add comprehensive tests for CSV utility

Co-authored-by: tommoor <380914+tommoor@users.noreply.github.com>

* Refactor: reduce limit to 100, replace lastActiveIp with role, extract ExportCSV component

Co-authored-by: tommoor <380914+tommoor@users.noreply.github.com>

* Improve type safety and extract pagination constant

Co-authored-by: tommoor <380914+tommoor@users.noreply.github.com>

* refactor

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: tommoor <380914+tommoor@users.noreply.github.com>
Co-authored-by: Tom Moor <tom@getoutline.com>
2025-12-05 09:42:36 -05:00
Tom Moor 55f21bfbb3 fix: Rapid retry behavior (#10776) 2025-12-03 20:25:12 -05:00
Tom Moor f009236144 feat: Custom emojis in editor (#10758)
* Working pass, needs refactor

* revert

* fix: Copy/paste behavior

* fix: Public share rendering

* fixes

* fix: Click around emoji atom behavior

* fix: Cannot position caret next to heading

* Update app/scenes/Settings/components/EmojisTable.tsx

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-02 20:17:17 -05:00
Salihu 430883f186 feat: Custom emoji (#10513)
Towards #9278
2025-12-01 02:31:50 +01:00
Tom Moor 7ce407910e fix: Improve validation of urls extracted from data transfer event (#10740) 2025-11-27 20:04:14 +01:00
Apoorv Mishra 06ec6fdfbb Enable commenting on images (#10474)
* feat: enable commenting on image nodes

* chore: make anchorPlugin a top level plugin

* fix: className

* fix: review

* fix: tsc

* fix: checks

* Tweak menu order to match

---------

Co-authored-by: Tom Moor <tom@getoutline.com>
2025-10-28 11:34:40 +05:30