Commit Graph

2093 Commits

Author SHA1 Message Date
Wars 38eda7fa61 fix: correct Safari heading widget handling for Chinese IME (#12453)
Safari keeps the heading actions widget at the end of headings to avoid
selection issues, but the widget was still using side metadata suited to
the leading placement.

With Chinese IME composition at the end of a heading, same-position
insertion could interact with the contentEditable=false widget and leave
the editor selection stuck. After that, Backspace stopped working until
the page was refreshed.

Use positive side metadata for the Safari trailing widget so composed
text stays before it, allow relaxed selection around the widget, and add
a narrow Safari-only ArrowLeft fallback for the heading-end boundary.

Chinese IME has been manually verified. Other composition-based IMEs may
follow a similar path, but are not claimed as verified here.
2026-05-25 16:51:35 -04:00
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 1a4033dd2d fix: Sporadic infinite loop rendering document with imported code blocks that have unknown languages (#12444) 2026-05-24 12:03:02 -04:00
dependabot[bot] 620c654b26 chore(deps): bump react-colorful from 5.6.1 to 5.7.0 (#12386)
* chore(deps): bump react-colorful from 5.6.1 to 5.7.0

Bumps [react-colorful](https://github.com/omgovich/react-colorful) from 5.6.1 to 5.7.0.
- [Release notes](https://github.com/omgovich/react-colorful/releases)
- [Changelog](https://github.com/omgovich/react-colorful/blob/master/CHANGELOG.md)
- [Commits](https://github.com/omgovich/react-colorful/commits/5.7.0)

---
updated-dependencies:
- dependency-name: react-colorful
  dependency-version: 5.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Use new prop

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Tom Moor <tom@getoutline.com>
2026-05-18 22:57:52 -04:00
Translate-O-Tron 16470f0b8d New Crowdin updates (#12166)
* fix: New French translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New Czech translations from Crowdin [ci skip]

* fix: New Danish translations from Crowdin [ci skip]

* fix: New German translations from Crowdin [ci skip]

* fix: New Hebrew translations from Crowdin [ci skip]

* fix: New Hungarian translations from Crowdin [ci skip]

* fix: New Italian translations from Crowdin [ci skip]

* fix: New Japanese translations from Crowdin [ci skip]

* fix: New Korean translations from Crowdin [ci skip]

* fix: New Dutch translations from Crowdin [ci skip]

* fix: New Polish translations from Crowdin [ci skip]

* fix: New Portuguese translations from Crowdin [ci skip]

* fix: New Swedish translations from Crowdin [ci skip]

* fix: New Turkish translations from Crowdin [ci skip]

* fix: New Ukrainian translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Chinese Traditional translations from Crowdin [ci skip]

* fix: New Vietnamese translations from Crowdin [ci skip]

* fix: New Portuguese, Brazilian translations from Crowdin [ci skip]

* fix: New Indonesian translations from Crowdin [ci skip]

* fix: New Persian translations from Crowdin [ci skip]

* fix: New Thai translations from Crowdin [ci skip]

* fix: New English, United Kingdom translations from Crowdin [ci skip]

* fix: New Norwegian Bokmal translations from Crowdin [ci skip]

* fix: New Romanian translations from Crowdin [ci skip]

* fix: New Catalan translations from Crowdin [ci skip]

* fix: New Italian translations from Crowdin [ci skip]

* fix: New French translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New Czech translations from Crowdin [ci skip]

* fix: New Danish translations from Crowdin [ci skip]

* fix: New German translations from Crowdin [ci skip]

* fix: New Hebrew translations from Crowdin [ci skip]

* fix: New Hungarian translations from Crowdin [ci skip]

* fix: New Italian translations from Crowdin [ci skip]

* fix: New Japanese translations from Crowdin [ci skip]

* fix: New Korean translations from Crowdin [ci skip]

* fix: New Dutch translations from Crowdin [ci skip]

* fix: New Polish translations from Crowdin [ci skip]

* fix: New Portuguese translations from Crowdin [ci skip]

* fix: New Swedish translations from Crowdin [ci skip]

* fix: New Turkish translations from Crowdin [ci skip]

* fix: New Ukrainian translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Chinese Traditional translations from Crowdin [ci skip]

* fix: New Vietnamese translations from Crowdin [ci skip]

* fix: New Portuguese, Brazilian translations from Crowdin [ci skip]

* fix: New Indonesian translations from Crowdin [ci skip]

* fix: New Persian translations from Crowdin [ci skip]

* fix: New Thai translations from Crowdin [ci skip]

* fix: New English, United Kingdom translations from Crowdin [ci skip]

* fix: New Norwegian Bokmal translations from Crowdin [ci skip]

* fix: New Romanian translations from Crowdin [ci skip]

* fix: New Catalan translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New German translations from Crowdin [ci skip]

* fix: New German translations from Crowdin [ci skip]

* fix: New Korean translations from Crowdin [ci skip]

* fix: New Korean translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New Italian translations from Crowdin [ci skip]

* fix: New German translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New French translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New Czech translations from Crowdin [ci skip]

* fix: New Danish translations from Crowdin [ci skip]

* fix: New German translations from Crowdin [ci skip]

* fix: New Hebrew translations from Crowdin [ci skip]

* fix: New Hungarian translations from Crowdin [ci skip]

* fix: New Italian translations from Crowdin [ci skip]

* fix: New Japanese translations from Crowdin [ci skip]

* fix: New Korean translations from Crowdin [ci skip]

* fix: New Dutch translations from Crowdin [ci skip]

* fix: New Polish translations from Crowdin [ci skip]

* fix: New Portuguese translations from Crowdin [ci skip]

* fix: New Swedish translations from Crowdin [ci skip]

* fix: New Turkish translations from Crowdin [ci skip]

* fix: New Ukrainian translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Chinese Traditional translations from Crowdin [ci skip]

* fix: New Vietnamese translations from Crowdin [ci skip]

* fix: New Portuguese, Brazilian translations from Crowdin [ci skip]

* fix: New Indonesian translations from Crowdin [ci skip]

* fix: New Persian translations from Crowdin [ci skip]

* fix: New Thai translations from Crowdin [ci skip]

* fix: New English, United Kingdom translations from Crowdin [ci skip]

* fix: New Norwegian Bokmal translations from Crowdin [ci skip]

* fix: New Romanian translations from Crowdin [ci skip]

* fix: New Catalan translations from Crowdin [ci skip]

* fix: New French translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New Czech translations from Crowdin [ci skip]

* fix: New Danish translations from Crowdin [ci skip]

* fix: New German translations from Crowdin [ci skip]

* fix: New Hebrew translations from Crowdin [ci skip]

* fix: New Hungarian translations from Crowdin [ci skip]

* fix: New Italian translations from Crowdin [ci skip]

* fix: New Japanese translations from Crowdin [ci skip]

* fix: New Korean translations from Crowdin [ci skip]

* fix: New Dutch translations from Crowdin [ci skip]

* fix: New Polish translations from Crowdin [ci skip]

* fix: New Portuguese translations from Crowdin [ci skip]

* fix: New Swedish translations from Crowdin [ci skip]

* fix: New Turkish translations from Crowdin [ci skip]

* fix: New Ukrainian translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Chinese Traditional translations from Crowdin [ci skip]

* fix: New Vietnamese translations from Crowdin [ci skip]

* fix: New Portuguese, Brazilian translations from Crowdin [ci skip]

* fix: New Indonesian translations from Crowdin [ci skip]

* fix: New Persian translations from Crowdin [ci skip]

* fix: New Thai translations from Crowdin [ci skip]

* fix: New English, United Kingdom translations from Crowdin [ci skip]

* fix: New Norwegian Bokmal translations from Crowdin [ci skip]

* fix: New Romanian translations from Crowdin [ci skip]

* fix: New Catalan translations from Crowdin [ci skip]

* fix: New French translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New Czech translations from Crowdin [ci skip]

* fix: New Danish translations from Crowdin [ci skip]

* fix: New German translations from Crowdin [ci skip]

* fix: New Hebrew translations from Crowdin [ci skip]

* fix: New Hungarian translations from Crowdin [ci skip]

* fix: New Italian translations from Crowdin [ci skip]

* fix: New Japanese translations from Crowdin [ci skip]

* fix: New Korean translations from Crowdin [ci skip]

* fix: New Dutch translations from Crowdin [ci skip]

* fix: New Polish translations from Crowdin [ci skip]

* fix: New Portuguese translations from Crowdin [ci skip]

* fix: New Swedish translations from Crowdin [ci skip]

* fix: New Turkish translations from Crowdin [ci skip]

* fix: New Ukrainian translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Chinese Traditional translations from Crowdin [ci skip]

* fix: New Vietnamese translations from Crowdin [ci skip]

* fix: New Portuguese, Brazilian translations from Crowdin [ci skip]

* fix: New Indonesian translations from Crowdin [ci skip]

* fix: New Persian translations from Crowdin [ci skip]

* fix: New Thai translations from Crowdin [ci skip]

* fix: New English, United Kingdom translations from Crowdin [ci skip]

* fix: New Norwegian Bokmal translations from Crowdin [ci skip]

* fix: New Romanian translations from Crowdin [ci skip]

* fix: New Catalan translations from Crowdin [ci skip]

* fix: New Catalan translations from Crowdin [ci skip]

* fix: New Catalan translations from Crowdin [ci skip]

* fix: New Catalan translations from Crowdin [ci skip]

* fix: New French translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New Czech translations from Crowdin [ci skip]

* fix: New Danish translations from Crowdin [ci skip]

* fix: New German translations from Crowdin [ci skip]

* fix: New Hebrew translations from Crowdin [ci skip]

* fix: New Hungarian translations from Crowdin [ci skip]

* fix: New Italian translations from Crowdin [ci skip]

* fix: New Japanese translations from Crowdin [ci skip]

* fix: New Korean translations from Crowdin [ci skip]

* fix: New Dutch translations from Crowdin [ci skip]

* fix: New Polish translations from Crowdin [ci skip]

* fix: New Portuguese translations from Crowdin [ci skip]

* fix: New Swedish translations from Crowdin [ci skip]

* fix: New Turkish translations from Crowdin [ci skip]

* fix: New Ukrainian translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Chinese Traditional translations from Crowdin [ci skip]

* fix: New Vietnamese translations from Crowdin [ci skip]

* fix: New Portuguese, Brazilian translations from Crowdin [ci skip]

* fix: New Indonesian translations from Crowdin [ci skip]

* fix: New Persian translations from Crowdin [ci skip]

* fix: New Thai translations from Crowdin [ci skip]

* fix: New English, United Kingdom translations from Crowdin [ci skip]

* fix: New Norwegian Bokmal translations from Crowdin [ci skip]

* fix: New Romanian translations from Crowdin [ci skip]

* fix: New Catalan translations from Crowdin [ci skip]

* fix: New Italian translations from Crowdin [ci skip]

* fix: New Italian translations from Crowdin [ci skip]

* fix: New German translations from Crowdin [ci skip]

* fix: New French translations from Crowdin [ci skip]

* fix: New Korean translations from Crowdin [ci skip]

* fix: New Catalan translations from Crowdin [ci skip]
2026-05-18 22:10:30 -04:00
Tom Moor d2b719a5a9 fix: Not possible to collapse code block at beginning of doc (#12381) 2026-05-18 22:00:45 -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 82d7041b6b chore: Refactor Markdown importer to use new import pipeline (#12361)
* chore: Refactor Markdown importer to use new import pipeline

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 14:10:15 -04:00
Tom Moor 246aa83071 fix: Potential fix for mis-sized mermaid diagrams (#12354)
closes #11874
2026-05-14 19:34:40 -04:00
Tom Moor 0d76dfc9f4 fix: Automatically expand code block if find result is within (#12346) 2026-05-13 17:18:16 -04:00
Tom Moor 9603e6d7c8 fix: Mermaid diagrams inside toggle containers do not render correctly (#12343) 2026-05-13 08:53:43 -04:00
Tom Moor bc6aa11f5d fix: Table cell selection should not show in print (#12334) 2026-05-13 08:24:49 -04:00
Tom Moor 935e0bb7b9 chore: Fix all no-misused-spread lint warnings (#12327) 2026-05-12 17:30:08 -04:00
dependabot[bot] fc01deeefd chore(deps-dev): bump oxlint-tsgolint from 0.14.2 to 0.22.1 (#12320)
* chore(deps-dev): bump oxlint-tsgolint from 0.14.2 to 0.22.1

Bumps [oxlint-tsgolint](https://github.com/oxc-project/tsgolint) from 0.14.2 to 0.22.1.
- [Release notes](https://github.com/oxc-project/tsgolint/releases)
- [Commits](https://github.com/oxc-project/tsgolint/compare/v0.14.2...v0.22.1)

---
updated-dependencies:
- dependency-name: oxlint-tsgolint
  dependency-version: 0.22.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore: Switch tsconfig to bundler resolution for tsgolint 0.22.1

oxlint-tsgolint 0.22.1 removed support for moduleResolution=node10
(the alias for "node"). Switch to "bundler" with resolvePackageJsonExports
disabled so packages whose exports field omits a types condition still
resolve. Update markdown-it type imports to sub-paths since the package's
.d.mts entry only re-exports a subset of named types.

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

* fix: Resolve type-aware lint errors caught by tsgolint 0.22.1

oxlint-tsgolint 0.22.1 catches several await-thenable, no-floating-promises,
and no-meaningless-void-operator cases the prior 0.14.2 missed:

- Drop redundant inner `await` from Promise.all([await x, await y]) call sites
  so the array entries are real Promises rather than already-resolved values.
- Replace Promise.all wrappers around synchronous presenters (presentEvent,
  presentTemplate, presentPublicTeam) with plain map / direct calls.
- Wrap non-promise branches of ternaries inside Promise.all with
  Promise.resolve so the array remains thenable across both arms.
- Add `void` to the unawaited provider.connect() in the auth-failed retry
  chain, and remove `void` from the disconnect() call which returns void.

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

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Tom Moor <tom@getoutline.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 07:59:13 -04:00
Tom Moor 51a1d3bf50 perf: Cache decorations in editor plugins (#12030)
Avoid full document traversal on every keystroke by mapping decorations
through the transaction when no relevant nodes changed. Uses
changedDescendants to detect when a heading, image, or code_inline-marked
text actually changes; otherwise the existing DecorationSet is mapped to
new positions cheaply.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 11:09:57 -04:00
Tom Moor ff3b3ce552 fix: Allow empty string in optional MCP fields (#12310)
* fix: Allow empty string in optional fields

* fix: Preserve empty strings for content fields in MCP tools

Address review feedback by reverting content/text fields (description, document
text, comment text) back to z.string().optional() so callers can intentionally
clear values via "". optionalString() is reserved for identifier and query
fields where "" is not a meaningful input.
2026-05-10 10:47:24 -04:00
Salihu b4cbb39f17 feat: request document access (#10825)
* feat: Request document access

Allow users without permission to a document to request access. Notifies
document managers via in-app notification and email; managers can grant
or dismiss the request.

- Adds AccessRequest model, migration, policy, presenter
- Adds accessRequests.create/info/approve/dismiss endpoints
- Adds DocumentAccessRequestNotificationsTask + email
- Adds Error403 request flow with loading state and pending indicator
- Auto-opens notifications popover via ?notifications=true (used in email)
- Adds SplitButton primitive for permission selection in notifications
- Refactors useConsumeQueryParam hook

* refactor

* fix: Make approve/dismiss idempotent on access requests

Return success when the access request has already been dismissed, or
when the user already has document membership at approve time, instead
of throwing 400. Avoids racy double-clicks on notification actions
producing user-visible errors.

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

* Minor fixes

---------

Co-authored-by: Tom Moor <tom@getoutline.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 08:42:47 -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 0f3f7b8da7 refactor: Remove useDictionary hook in favor of i18next t directly (#12282)
Plumbed `dictionary` props through editor components, menus, extensions,
and nodes. Replaces with `useTranslation()` in React contexts and direct
`t` imports from i18next elsewhere.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-06 20:24:50 -04:00
Tom Moor e0bc08478d feat: Add system preference to open desktop app on startup (#12279)
* feat: Adds toggle to open desktop app on startup

* Remove name hardcoding

* refactor
2026-05-06 19:29:42 -04:00
Tom Moor 913322c0d5 fix: Search highlights not rendering in Firefox (#12273)
@emotion/stylis compiles top-level `::highlight(name)` rules inside a
styled component as a compound selector `.parent::highlight(name)`,
which only matches highlights on the editor container element itself.
Chrome applies these leniently, but Firefox correctly requires the
originating element to contain the highlighted text. Prefixing with
`&` forces a descendant combinator so descendant elements containing
the highlighted text are matched.

Closes #12270
2026-05-05 17:44:10 -04:00
Tom Moor 3562056d72 Reduce minimum table col width to 25px (#12269) 2026-05-05 08:40:36 -04:00
Tom Moor e7623eeade fix: Cannot select doc text in version history (#12268)
* fix: Cannot select doc text in version history

* PR feedback, use deco cache
2026-05-05 08:35:26 -04:00
dependabot[bot] 77aee86c01 chore(deps): bump prosemirror-changeset from 2.3.1 to 2.4.1 (#12261)
* chore(deps): bump prosemirror-changeset from 2.3.1 to 2.4.1

Bumps [prosemirror-changeset](https://github.com/prosemirror/prosemirror-changeset) from 2.3.1 to 2.4.1.
- [Changelog](https://github.com/ProseMirror/prosemirror-changeset/blob/master/CHANGELOG.md)
- [Commits](https://github.com/prosemirror/prosemirror-changeset/commits)

---
updated-dependencies:
- dependency-name: prosemirror-changeset
  dependency-version: 2.4.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix: ExtendedChange type for prosemirror-changeset 2.4.1

The new Change class adds a toJSON() method, which broke `extends Change`
since ExtendedChange values are built via object spread and have no
prototype methods. Pick only the data properties instead.

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

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Tom Moor <tom@getoutline.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 21:11:09 -04:00
dependabot[bot] bee5945c0b chore(deps-dev): bump @types/markdown-it from 14.1.1 to 14.1.2 (#12260)
* chore(deps-dev): bump @types/markdown-it from 14.1.1 to 14.1.2

Bumps [@types/markdown-it](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/markdown-it) from 14.1.1 to 14.1.2.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/markdown-it)

---
updated-dependencies:
- dependency-name: "@types/markdown-it"
  dependency-version: 14.1.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix: Drop removed `jump` field from mark delimiter

@types/markdown-it 14.1.2 removed `jump` from the `Delimiter` interface
to match upstream markdown-it, which tracks jumps in a local array
inside balance_pairs rather than on each delimiter.

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

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Tom Moor <tom@getoutline.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 21:10:58 -04:00
Tom Moor 8c716b173a chore: Update editor generics (#12247)
* chore: Update editor generics

* fix: Address PR review on editor generics

- Restore null-guard on Link click handler so anchors aren't inert when no onClickLink is provided
- Mark onClickLink optional in LinkOptions and openLink command to match runtime
- Remove dead `collapsed` option from HeadingOptions
- Make ToggleBlock dictionary optional and restore optional-chained access for server-side schema instantiation

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

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 18:54:27 -04:00
Tom Moor fca10221b9 chore: promote no-explicit-any from warn to error (#12244)
* chore: promote no-explicit-any from warn to error and resolve violations

Upgrades the oxlint rule severity and removes all 40 existing
`no-explicit-any` warnings across the codebase. Most call sites gained
proper types (SharedEditor refs, JSONNode/JSONMark for ProseMirror JSON
walking, DocumentsStore, dd-trace `Span` parameter inference, prosemirror
Fragment public API in place of internal `(fragment as any).content`).
A few load-bearing `any` uses were preserved with scoped disable
comments where changing the type would cascade widely (Sequelize JSONB
columns on `Event`, the `withTracing` higher-order function generic,
`Extension.options` consumed by many subclasses, dd-trace's `req`
patching).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 12:14:23 -04:00
Tom Moor 903ce856ec perf: Fix exhaustive dep warnings in editor resize hook (#12238) 2026-05-01 08:47:13 -04:00
Tom Moor 1f097b0fdd chore: resolve no-explicit-any lint warnings in plugins (#12237)
* chore: resolve no-explicit-any lint warnings in plugins

Replaces uses of `any` in the plugins directory with concrete types,
`unknown`, or structured type assertions, addressing the remaining
typescript-eslint(no-explicit-any) warnings flagged by oxlint.

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

* chore: address review feedback in GitLabIssueProvider

Drop trailing semicolon from log string and add early return in
`destroyNamespace` when neither `user_id` nor `full_path` is present
to avoid an unnecessary full-scan transaction.

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

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-01 08:29:58 -04:00
Tom Moor f3f9b3e705 fix: Incorrect behavior of mod-left in heading node in FF (#12219) 2026-04-30 06:59:32 -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 57308c46af chore: resolve lint warnings (no-explicit-any, no-redundant-type-constituents, no-base-to-string) (#12209)
* chore: resolve no-redundant-type-constituents and test/mock no-explicit-any warnings

Clears 36 lint warnings: all 5 no-redundant-type-constituents, 6
no-misused-spread (via narrowing getPartitionWhereClause's return type
to WhereAttributeHash), and 25 no-explicit-any in test/mock files.

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

* chore: resolve no-base-to-string warnings in tests

Convert userProvisioner try/catch error assertions to Jest's
.rejects.toThrow() idiom, and cast webhook test body to string.

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

* chore: resolve no-explicit-any warnings in cancan and tracing

Tighten types in the cancan policy framework and tracing decorators.
Constructor / generic-function upper bounds keep `any` where TypeScript
variance requires it, scoped to single-line oxlint-disable comments.

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

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-28 22:55:30 -04:00
Tom Moor f8e70c2c39 chore: resolve mechanical react-hooks/exhaustive-deps warnings (#12207)
Adds missing stable dependencies (e.g. `t`, prop callbacks, store refs,
`setFocusedCommentId`) and removes unnecessary ones across hooks where the
fix is straightforward. For the two MobX-observed `.orderedData` deps in
`History.tsx`, keeps the original deps and silences the false positive
with `eslint-disable-next-line` so the memos still recompute when the
underlying observable arrays change.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-28 22:06:09 -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 9b7ccf8cb5 fix: Resolve no-floating-promises lint errors (#12196)
* fix: Resolve no-floating-promises lint errors

Adds await or void to 10 unhandled promises. Notable fixes: a test
assertion using `.resolves` was never awaited, and a custom emoji
fetch with setState was running during render instead of in an effect.

* chore: Promote no-floating-promises to error

Now that all occurrences are fixed, prevent regressions.
2026-04-28 18:13:46 -04:00
dependabot[bot] 816a474a46 chore(deps-dev): bump oxlint and tsgolint (#12127)
* chore(deps-dev): bump oxlint-tsgolint from 0.1.6 to 0.21.1

Bumps [oxlint-tsgolint](https://github.com/oxc-project/tsgolint) from 0.1.6 to 0.21.1.
- [Release notes](https://github.com/oxc-project/tsgolint/releases)
- [Commits](https://github.com/oxc-project/tsgolint/compare/v0.1.6...v0.21.1)

---
updated-dependencies:
- dependency-name: oxlint-tsgolint
  dependency-version: 0.21.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore: Adjust lint config for newer oxlint-tsgolint

Pin oxlint to 1.50.0 and oxlint-tsgolint to 0.14.2. Older oxlint
can't parse newer tsgolint diagnostic payloads, and tsgolint >=0.15
rejects moduleResolution: "node" — moving off it requires either
"bundler" (currently breaks @hocuspocus@1.1.3 typings, which lack a
types condition in their package.json exports) or "node16"/"nodenext"
(would require explicit .js extensions on every relative import).

Add per-package ignorePatterns since they no longer propagate from
the root config when nested configs are present.

Drop tsconfig baseUrl (typescript-go in tsgolint rejects it) and add
a plugins/* path alias so cross-plugin imports keep resolving. The
babel resolver is switched from babel-plugin-tsconfig-paths-module-
resolver (which required baseUrl) to babel-plugin-module-resolver
with explicit aliases.

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Tom Moor <tom@getoutline.com>
2026-04-28 13:50:42 -04:00
Tom Moor af77bd6474 fix: Uncollapse code when printing (#12193)
closes #12192
2026-04-27 22:55:16 -04:00
Copilot 7ed41eadc6 Add per-share branding: title and logoUrl overrides (#12003)
* feat: add title and logoUrl to Share model

Agent-Logs-Url: https://github.com/outline/outline/sessions/9bc9d438-6892-4903-9d32-6b6868f4fd97

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

* fix: use STRING(4096) for logoUrl column in migration

Agent-Logs-Url: https://github.com/outline/outline/sessions/9bc9d438-6892-4903-9d32-6b6868f4fd97

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

* feat: use share title and logoUrl to override team branding on shared page

Agent-Logs-Url: https://github.com/outline/outline/sessions/854d6d22-e80b-4673-b3b2-0f9cf43a3246

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

* refactor: use ShareValidation class constants for title/logoUrl max lengths

Agent-Logs-Url: https://github.com/outline/outline/sessions/ea462d6a-d4d3-4882-ab8e-88060bf64877

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

* fix: use ShareValidation constants in @Length msg template literals

Agent-Logs-Url: https://github.com/outline/outline/sessions/694116c2-47e8-4001-a103-c8a62c7ac71e

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

* feat: add display settings popover with custom title and icon for shares

Move share toggles (search indexing, email subscriptions, show last
modified, show TOC) into a popover triggered by a settings cog. The
popover also includes inputs for a custom site title and icon upload
to override team branding on shared pages. Rename logoUrl to iconUrl,
loosen URL validation to allow relative attachment paths, and surface
the popover in the shared page header for users with edit permission.

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

* styling

* Display branding on single shared pages

* Review comments

* refactor

* PR feedback

* Lose 'Remove icon' button

---------

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>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-26 21:23:13 -04:00
Tom Moor c4d764e243 feat: Show a chip in header with search term when highlighted in doc (#12165)
* feat: Show a chip in header with search term when highlighted in doc

* theme
2026-04-25 10:59:30 -04:00
Translate-O-Tron 1d0a611ea3 New Crowdin updates (#12094)
* fix: New Ukrainian translations from Crowdin [ci skip]

* fix: New Ukrainian translations from Crowdin [ci skip]

* fix: New Italian translations from Crowdin [ci skip]

* fix: New French translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New Czech translations from Crowdin [ci skip]

* fix: New Danish translations from Crowdin [ci skip]

* fix: New German translations from Crowdin [ci skip]

* fix: New Hebrew translations from Crowdin [ci skip]

* fix: New Hungarian translations from Crowdin [ci skip]

* fix: New Italian translations from Crowdin [ci skip]

* fix: New Japanese translations from Crowdin [ci skip]

* fix: New Korean translations from Crowdin [ci skip]

* fix: New Dutch translations from Crowdin [ci skip]

* fix: New Polish translations from Crowdin [ci skip]

* fix: New Portuguese translations from Crowdin [ci skip]

* fix: New Swedish translations from Crowdin [ci skip]

* fix: New Turkish translations from Crowdin [ci skip]

* fix: New Ukrainian translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Chinese Traditional translations from Crowdin [ci skip]

* fix: New Vietnamese translations from Crowdin [ci skip]

* fix: New Portuguese, Brazilian translations from Crowdin [ci skip]

* fix: New Indonesian translations from Crowdin [ci skip]

* fix: New Persian translations from Crowdin [ci skip]

* fix: New Thai translations from Crowdin [ci skip]

* fix: New English, United Kingdom translations from Crowdin [ci skip]

* fix: New Norwegian Bokmal translations from Crowdin [ci skip]

* fix: New Romanian translations from Crowdin [ci skip]

* fix: New French translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New Czech translations from Crowdin [ci skip]

* fix: New Danish translations from Crowdin [ci skip]

* fix: New German translations from Crowdin [ci skip]

* fix: New Hebrew translations from Crowdin [ci skip]

* fix: New Hungarian translations from Crowdin [ci skip]

* fix: New Italian translations from Crowdin [ci skip]

* fix: New Japanese translations from Crowdin [ci skip]

* fix: New Korean translations from Crowdin [ci skip]

* fix: New Dutch translations from Crowdin [ci skip]

* fix: New Polish translations from Crowdin [ci skip]

* fix: New Portuguese translations from Crowdin [ci skip]

* fix: New Swedish translations from Crowdin [ci skip]

* fix: New Turkish translations from Crowdin [ci skip]

* fix: New Ukrainian translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Chinese Traditional translations from Crowdin [ci skip]

* fix: New Vietnamese translations from Crowdin [ci skip]

* fix: New Portuguese, Brazilian translations from Crowdin [ci skip]

* fix: New Indonesian translations from Crowdin [ci skip]

* fix: New Persian translations from Crowdin [ci skip]

* fix: New Thai translations from Crowdin [ci skip]

* fix: New English, United Kingdom translations from Crowdin [ci skip]

* fix: New Norwegian Bokmal translations from Crowdin [ci skip]

* fix: New Romanian translations from Crowdin [ci skip]

* fix: New French translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New Czech translations from Crowdin [ci skip]

* fix: New Danish translations from Crowdin [ci skip]

* fix: New German translations from Crowdin [ci skip]

* fix: New Hebrew translations from Crowdin [ci skip]

* fix: New Hungarian translations from Crowdin [ci skip]

* fix: New Italian translations from Crowdin [ci skip]

* fix: New Japanese translations from Crowdin [ci skip]

* fix: New Korean translations from Crowdin [ci skip]

* fix: New Dutch translations from Crowdin [ci skip]

* fix: New Polish translations from Crowdin [ci skip]

* fix: New Portuguese translations from Crowdin [ci skip]

* fix: New Swedish translations from Crowdin [ci skip]

* fix: New Turkish translations from Crowdin [ci skip]

* fix: New Ukrainian translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Chinese Traditional translations from Crowdin [ci skip]

* fix: New Vietnamese translations from Crowdin [ci skip]

* fix: New Portuguese, Brazilian translations from Crowdin [ci skip]

* fix: New Indonesian translations from Crowdin [ci skip]

* fix: New Persian translations from Crowdin [ci skip]

* fix: New Thai translations from Crowdin [ci skip]

* fix: New English, United Kingdom translations from Crowdin [ci skip]

* fix: New Norwegian Bokmal translations from Crowdin [ci skip]

* fix: New Romanian translations from Crowdin [ci skip]

* fix: New French translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New Czech translations from Crowdin [ci skip]

* fix: New Danish translations from Crowdin [ci skip]

* fix: New German translations from Crowdin [ci skip]

* fix: New Hebrew translations from Crowdin [ci skip]

* fix: New Hungarian translations from Crowdin [ci skip]

* fix: New Italian translations from Crowdin [ci skip]

* fix: New Japanese translations from Crowdin [ci skip]

* fix: New Korean translations from Crowdin [ci skip]

* fix: New Dutch translations from Crowdin [ci skip]

* fix: New Polish translations from Crowdin [ci skip]

* fix: New Portuguese translations from Crowdin [ci skip]

* fix: New Swedish translations from Crowdin [ci skip]

* fix: New Turkish translations from Crowdin [ci skip]

* fix: New Ukrainian translations from Crowdin [ci skip]

* fix: New Chinese Simplified translations from Crowdin [ci skip]

* fix: New Chinese Traditional translations from Crowdin [ci skip]

* fix: New Vietnamese translations from Crowdin [ci skip]

* fix: New Portuguese, Brazilian translations from Crowdin [ci skip]

* fix: New Indonesian translations from Crowdin [ci skip]

* fix: New Persian translations from Crowdin [ci skip]

* fix: New Thai translations from Crowdin [ci skip]

* fix: New English, United Kingdom translations from Crowdin [ci skip]

* fix: New Norwegian Bokmal translations from Crowdin [ci skip]

* fix: New Romanian translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New Catalan translations from Crowdin [ci skip]

* fix: New Catalan translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New Catalan translations from Crowdin [ci skip]

* fix: New Catalan translations from Crowdin [ci skip]

* fix: New Spanish translations from Crowdin [ci skip]

* fix: New Catalan translations from Crowdin [ci skip]

* fix: New Catalan translations from Crowdin [ci skip]
2026-04-25 08:52:46 -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 9c2d8d9279 fix: Shift-Tab on list item inside toggle block outdents entire block (#12162)
Mirrors the v1.7.0 Tab fix by skipping the toggle block's dedent command
when the selection is inside a list, so the list's Shift-Tab handler can
outdent the list item instead.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 11:46:39 +00:00
Tom Moor f3f97cc3ea feat: Add hex swatch previews (#12150)
* feat: Add hex previews, closes #860

* PR feedback
2026-04-24 04:29:13 -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