42 Commits

Author SHA1 Message Date
dependabot[bot] d89d624c56 chore(deps)(deps-dev): bump vitest from 4.1.11 to 5.0.0 in /web (#1277)
* chore(deps)(deps-dev): bump vitest from 4.1.11 to 5.0.0 in /web

Bumps [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) from 4.1.11 to 5.0.0.
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v5.0.0/packages/vitest)

---
updated-dependencies:
- dependency-name: vitest
  dependency-version: 5.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

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

* chore: pin the repo's Node floor to 24 (TASK-2971)

vitest 5 declares `engines.node = "^22.12.0 || ^24.0.0 || >=26.0.0"`, so this
bump makes the Node version a thing the repo cares about rather than a detail of
whoever's box. Measured before Dave moved this machine's default: on Node 25.8.1
with `engine-strict=true`, `npm ci` against this branch failed outright —

    npm error notsup Required: {"node":"^22.12.0 || ^24.0.0 || >=26.0.0"}
    npm error notsup Actual:   {"node":"v25.8.1","npm":"11.11.0"}

— which takes out every make target whose chain reaches `npm ci`: web, build,
install, serve, web-check, check. That includes `make install`, the dev-server
refresh. CI was unaffected throughout, because `ci.yml` pins node-version 24;
the failure was only ever local, and it was invisible from CI by construction.

`24` matches what CI already pins, so the file documents the version the project
actually builds against rather than introducing a second opinion. It is not wired
into the workflows here — they name 24 explicitly and changing them to read this
file is a separate decision.

Verified against the MERGE RESULT of this branch and main, not the branch alone:
`npm ci` exits 0 under Node 24.21.0, and the suite is 152 files / 2332 tests
passing on vitest 5 — the same counts as main on vitest 4, which is the guard
against a suite that goes green by collecting less.

Claude-Session: https://claude.ai/code/session_01WS9QAnxk1gA3LBha3PvKVm

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: xarmian <xarmian@gmail.com>
2026-09-09 18:12:06 -04:00
dependabot[bot] bc543a1d37 chore(deps)(deps): bump the npm-minor-and-patch group (#1276)
Bumps the npm-minor-and-patch group in /web with 14 updates:

| Package | From | To |
| --- | --- | --- |
| [@tiptap/core](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/core) | `3.30.5` | `3.31.3` |
| [@tiptap/extension-bubble-menu](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-bubble-menu) | `3.30.5` | `3.31.3` |
| [@tiptap/extension-code-block-lowlight](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-code-block-lowlight) | `3.30.5` | `3.31.3` |
| [@tiptap/extension-collaboration](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-collaboration) | `3.30.5` | `3.31.3` |
| [@tiptap/extension-collaboration-caret](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-collaboration-caret) | `3.30.5` | `3.31.3` |
| [@tiptap/extension-link](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-link) | `3.30.5` | `3.31.3` |
| [@tiptap/extension-placeholder](https://github.com/ueberdosis/tiptap/tree/HEAD/packages-deprecated/extension-placeholder) | `3.30.5` | `3.31.3` |
| [@tiptap/extension-table](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-table) | `3.30.5` | `3.31.3` |
| [@tiptap/extension-task-item](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-task-item) | `3.30.5` | `3.31.3` |
| [@tiptap/extension-task-list](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-task-list) | `3.30.5` | `3.31.3` |
| [@tiptap/pm](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/pm) | `3.30.5` | `3.31.3` |
| [@tiptap/starter-kit](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/starter-kit) | `3.30.5` | `3.31.3` |
| [@tiptap/suggestion](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/suggestion) | `3.30.5` | `3.31.3` |
| [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) | `5.56.10` | `5.57.0` |


Updates `@tiptap/core` from 3.30.5 to 3.31.3
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/v3.31.3/packages/core/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.31.3/packages/core)

Updates `@tiptap/extension-bubble-menu` from 3.30.5 to 3.31.3
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/v3.31.3/packages/extension-bubble-menu/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.31.3/packages/extension-bubble-menu)

Updates `@tiptap/extension-code-block-lowlight` from 3.30.5 to 3.31.3
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/v3.31.3/packages/extension-code-block-lowlight/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.31.3/packages/extension-code-block-lowlight)

Updates `@tiptap/extension-collaboration` from 3.30.5 to 3.31.3
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/v3.31.3/packages/extension-collaboration/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.31.3/packages/extension-collaboration)

Updates `@tiptap/extension-collaboration-caret` from 3.30.5 to 3.31.3
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/v3.31.3/packages/extension-collaboration-caret/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.31.3/packages/extension-collaboration-caret)

Updates `@tiptap/extension-link` from 3.30.5 to 3.31.3
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/v3.31.3/packages/extension-link/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.31.3/packages/extension-link)

Updates `@tiptap/extension-placeholder` from 3.30.5 to 3.31.3
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/v3.31.3/packages-deprecated/extension-placeholder/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.31.3/packages-deprecated/extension-placeholder)

Updates `@tiptap/extension-table` from 3.30.5 to 3.31.3
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/v3.31.3/packages/extension-table/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.31.3/packages/extension-table)

Updates `@tiptap/extension-task-item` from 3.30.5 to 3.31.3
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.31.3/packages/extension-task-item)

Updates `@tiptap/extension-task-list` from 3.30.5 to 3.31.3
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.31.3/packages/extension-task-list)

Updates `@tiptap/pm` from 3.30.5 to 3.31.3
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/v3.31.3/packages/pm/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.31.3/packages/pm)

Updates `@tiptap/starter-kit` from 3.30.5 to 3.31.3
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/v3.31.3/packages/starter-kit/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.31.3/packages/starter-kit)

Updates `@tiptap/suggestion` from 3.30.5 to 3.31.3
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/v3.31.3/packages/suggestion/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.31.3/packages/suggestion)

Updates `svelte` from 5.56.10 to 5.57.0
- [Release notes](https://github.com/sveltejs/svelte/releases)
- [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.57.0/packages/svelte)

---
updated-dependencies:
- dependency-name: "@tiptap/core"
  dependency-version: 3.31.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-bubble-menu"
  dependency-version: 3.31.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-code-block-lowlight"
  dependency-version: 3.31.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-collaboration"
  dependency-version: 3.31.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-collaboration-caret"
  dependency-version: 3.31.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-link"
  dependency-version: 3.31.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-placeholder"
  dependency-version: 3.31.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-table"
  dependency-version: 3.31.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-task-item"
  dependency-version: 3.31.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-task-list"
  dependency-version: 3.31.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/pm"
  dependency-version: 3.31.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/starter-kit"
  dependency-version: 3.31.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/suggestion"
  dependency-version: 3.31.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: svelte
  dependency-version: 5.57.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-08 20:43:24 -04:00
dependabot[bot] 459ba370ec chore(deps)(deps): bump the npm-minor-and-patch group (#1223)
Bumps the npm-minor-and-patch group in /web with 16 updates:

| Package | From | To |
| --- | --- | --- |
| [@tiptap/core](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/core) | `3.30.2` | `3.30.5` |
| [@tiptap/extension-bubble-menu](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-bubble-menu) | `3.30.2` | `3.30.5` |
| [@tiptap/extension-code-block-lowlight](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-code-block-lowlight) | `3.30.2` | `3.30.5` |
| [@tiptap/extension-collaboration](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-collaboration) | `3.30.2` | `3.30.5` |
| [@tiptap/extension-collaboration-caret](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-collaboration-caret) | `3.30.2` | `3.30.5` |
| [@tiptap/extension-link](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-link) | `3.30.2` | `3.30.5` |
| [@tiptap/extension-placeholder](https://github.com/ueberdosis/tiptap/tree/HEAD/packages-deprecated/extension-placeholder) | `3.30.2` | `3.30.5` |
| [@tiptap/extension-table](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-table) | `3.30.2` | `3.30.5` |
| [@tiptap/extension-task-item](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-task-item) | `3.30.2` | `3.30.5` |
| [@tiptap/extension-task-list](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-task-list) | `3.30.2` | `3.30.5` |
| [@tiptap/pm](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/pm) | `3.30.2` | `3.30.5` |
| [@tiptap/starter-kit](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/starter-kit) | `3.30.2` | `3.30.5` |
| [@tiptap/suggestion](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/suggestion) | `3.30.2` | `3.30.5` |
| [mermaid](https://github.com/mermaid-js/mermaid) | `11.17.0` | `11.17.2` |
| [svelte-dnd-action](https://github.com/isaacHagoel/svelte-dnd-action) | `0.9.78` | `0.9.79` |
| [marked](https://github.com/markedjs/marked) | `18.0.10` | `18.0.11` |


Updates `@tiptap/core` from 3.30.2 to 3.30.5
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/v3.30.5/packages/core/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.30.5/packages/core)

Updates `@tiptap/extension-bubble-menu` from 3.30.2 to 3.30.5
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/v3.30.5/packages/extension-bubble-menu/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.30.5/packages/extension-bubble-menu)

Updates `@tiptap/extension-code-block-lowlight` from 3.30.2 to 3.30.5
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/v3.30.5/packages/extension-code-block-lowlight/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.30.5/packages/extension-code-block-lowlight)

Updates `@tiptap/extension-collaboration` from 3.30.2 to 3.30.5
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/v3.30.5/packages/extension-collaboration/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.30.5/packages/extension-collaboration)

Updates `@tiptap/extension-collaboration-caret` from 3.30.2 to 3.30.5
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/v3.30.5/packages/extension-collaboration-caret/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.30.5/packages/extension-collaboration-caret)

Updates `@tiptap/extension-link` from 3.30.2 to 3.30.5
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/v3.30.5/packages/extension-link/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.30.5/packages/extension-link)

Updates `@tiptap/extension-placeholder` from 3.30.2 to 3.30.5
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/v3.30.5/packages-deprecated/extension-placeholder/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.30.5/packages-deprecated/extension-placeholder)

Updates `@tiptap/extension-table` from 3.30.2 to 3.30.5
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/v3.30.5/packages/extension-table/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.30.5/packages/extension-table)

Updates `@tiptap/extension-task-item` from 3.30.2 to 3.30.5
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.30.5/packages/extension-task-item)

Updates `@tiptap/extension-task-list` from 3.30.2 to 3.30.5
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.30.5/packages/extension-task-list)

Updates `@tiptap/pm` from 3.30.2 to 3.30.5
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/v3.30.5/packages/pm/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.30.5/packages/pm)

Updates `@tiptap/starter-kit` from 3.30.2 to 3.30.5
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/v3.30.5/packages/starter-kit/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.30.5/packages/starter-kit)

Updates `@tiptap/suggestion` from 3.30.2 to 3.30.5
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/v3.30.5/packages/suggestion/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.30.5/packages/suggestion)

Updates `mermaid` from 11.17.0 to 11.17.2
- [Release notes](https://github.com/mermaid-js/mermaid/releases)
- [Commits](https://github.com/mermaid-js/mermaid/compare/mermaid@11.17.0...mermaid@11.17.2)

Updates `svelte-dnd-action` from 0.9.78 to 0.9.79
- [Changelog](https://github.com/isaacHagoel/svelte-dnd-action/blob/master/release-notes.md)
- [Commits](https://github.com/isaacHagoel/svelte-dnd-action/commits)

Updates `marked` from 18.0.10 to 18.0.11
- [Release notes](https://github.com/markedjs/marked/releases)
- [Commits](https://github.com/markedjs/marked/compare/v18.0.10...v18.0.11)

---
updated-dependencies:
- dependency-name: "@tiptap/core"
  dependency-version: 3.30.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-bubble-menu"
  dependency-version: 3.30.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-code-block-lowlight"
  dependency-version: 3.30.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-collaboration"
  dependency-version: 3.30.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-collaboration-caret"
  dependency-version: 3.30.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-link"
  dependency-version: 3.30.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-placeholder"
  dependency-version: 3.30.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-table"
  dependency-version: 3.30.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-task-item"
  dependency-version: 3.30.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-task-list"
  dependency-version: 3.30.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/pm"
  dependency-version: 3.30.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/starter-kit"
  dependency-version: 3.30.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/suggestion"
  dependency-version: 3.30.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: mermaid
  dependency-version: 11.17.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: svelte-dnd-action
  dependency-version: 0.9.79
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: marked
  dependency-version: 18.0.11
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-31 17:15:38 -04:00
dependabot[bot] 3f9daa5c7a chore(deps)(deps): bump the npm-minor-and-patch group (#1190)
Bumps the npm-minor-and-patch group in /web with 21 updates:

| Package | From | To |
| --- | --- | --- |
| [@tiptap/core](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/core) | `3.30.1` | `3.30.2` |
| [@tiptap/extension-bubble-menu](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-bubble-menu) | `3.30.1` | `3.30.2` |
| [@tiptap/extension-code-block-lowlight](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-code-block-lowlight) | `3.30.1` | `3.30.2` |
| [@tiptap/extension-collaboration](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-collaboration) | `3.30.1` | `3.30.2` |
| [@tiptap/extension-collaboration-caret](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-collaboration-caret) | `3.30.1` | `3.30.2` |
| [@tiptap/extension-link](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-link) | `3.30.1` | `3.30.2` |
| [@tiptap/extension-placeholder](https://github.com/ueberdosis/tiptap/tree/HEAD/packages-deprecated/extension-placeholder) | `3.30.1` | `3.30.2` |
| [@tiptap/extension-table](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-table) | `3.30.1` | `3.30.2` |
| [@tiptap/extension-task-item](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-task-item) | `3.30.1` | `3.30.2` |
| [@tiptap/extension-task-list](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-task-list) | `3.30.1` | `3.30.2` |
| [@tiptap/pm](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/pm) | `3.30.1` | `3.30.2` |
| [@tiptap/starter-kit](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/starter-kit) | `3.30.1` | `3.30.2` |
| [@tiptap/suggestion](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/suggestion) | `3.30.1` | `3.30.2` |
| [@tiptap/y-tiptap](https://github.com/ueberdosis/y-tiptap) | `3.0.8` | `3.0.9` |
| [dompurify](https://github.com/cure53/DOMPurify) | `3.4.13` | `3.4.14` |
| [mermaid](https://github.com/mermaid-js/mermaid) | `11.16.1` | `11.17.0` |
| [@sveltejs/kit](https://github.com/sveltejs/kit/tree/HEAD/packages/kit) | `2.70.2` | `2.70.3` |
| [marked](https://github.com/markedjs/marked) | `18.0.9` | `18.0.10` |
| [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) | `5.56.9` | `5.56.10` |
| [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) | `8.2.1` | `8.2.2` |
| [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) | `4.1.10` | `4.1.11` |


Updates `@tiptap/core` from 3.30.1 to 3.30.2
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/core/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.30.2/packages/core)

Updates `@tiptap/extension-bubble-menu` from 3.30.1 to 3.30.2
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-bubble-menu/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.30.2/packages/extension-bubble-menu)

Updates `@tiptap/extension-code-block-lowlight` from 3.30.1 to 3.30.2
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-code-block-lowlight/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.30.2/packages/extension-code-block-lowlight)

Updates `@tiptap/extension-collaboration` from 3.30.1 to 3.30.2
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-collaboration/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.30.2/packages/extension-collaboration)

Updates `@tiptap/extension-collaboration-caret` from 3.30.1 to 3.30.2
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-collaboration-caret/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.30.2/packages/extension-collaboration-caret)

Updates `@tiptap/extension-link` from 3.30.1 to 3.30.2
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-link/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.30.2/packages/extension-link)

Updates `@tiptap/extension-placeholder` from 3.30.1 to 3.30.2
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages-deprecated/extension-placeholder/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.30.2/packages-deprecated/extension-placeholder)

Updates `@tiptap/extension-table` from 3.30.1 to 3.30.2
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-table/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.30.2/packages/extension-table)

Updates `@tiptap/extension-task-item` from 3.30.1 to 3.30.2
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.30.2/packages/extension-task-item)

Updates `@tiptap/extension-task-list` from 3.30.1 to 3.30.2
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.30.2/packages/extension-task-list)

Updates `@tiptap/pm` from 3.30.1 to 3.30.2
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/pm/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.30.2/packages/pm)

Updates `@tiptap/starter-kit` from 3.30.1 to 3.30.2
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/starter-kit/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.30.2/packages/starter-kit)

Updates `@tiptap/suggestion` from 3.30.1 to 3.30.2
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/suggestion/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.30.2/packages/suggestion)

Updates `@tiptap/y-tiptap` from 3.0.8 to 3.0.9
- [Changelog](https://github.com/ueberdosis/y-tiptap/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/y-tiptap/commits)

Updates `dompurify` from 3.4.13 to 3.4.14
- [Release notes](https://github.com/cure53/DOMPurify/releases)
- [Commits](https://github.com/cure53/DOMPurify/compare/3.4.13...3.4.14)

Updates `mermaid` from 11.16.1 to 11.17.0
- [Release notes](https://github.com/mermaid-js/mermaid/releases)
- [Commits](https://github.com/mermaid-js/mermaid/compare/mermaid@11.16.1...mermaid@11.17.0)

Updates `@sveltejs/kit` from 2.70.2 to 2.70.3
- [Release notes](https://github.com/sveltejs/kit/releases)
- [Changelog](https://github.com/sveltejs/kit/blob/@sveltejs/kit@2.70.3/packages/kit/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/kit/commits/@sveltejs/kit@2.70.3/packages/kit)

Updates `marked` from 18.0.9 to 18.0.10
- [Release notes](https://github.com/markedjs/marked/releases)
- [Commits](https://github.com/markedjs/marked/compare/v18.0.9...v18.0.10)

Updates `svelte` from 5.56.9 to 5.56.10
- [Release notes](https://github.com/sveltejs/svelte/releases)
- [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.56.10/packages/svelte)

Updates `vite` from 8.2.1 to 8.2.2
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v8.2.2/packages/vite)

Updates `vitest` from 4.1.10 to 4.1.11
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.11/packages/vitest)

---
updated-dependencies:
- dependency-name: "@tiptap/core"
  dependency-version: 3.30.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-bubble-menu"
  dependency-version: 3.30.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-code-block-lowlight"
  dependency-version: 3.30.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-collaboration"
  dependency-version: 3.30.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-collaboration-caret"
  dependency-version: 3.30.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-link"
  dependency-version: 3.30.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-placeholder"
  dependency-version: 3.30.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-table"
  dependency-version: 3.30.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-task-item"
  dependency-version: 3.30.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-task-list"
  dependency-version: 3.30.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/pm"
  dependency-version: 3.30.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/starter-kit"
  dependency-version: 3.30.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/suggestion"
  dependency-version: 3.30.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/y-tiptap"
  dependency-version: 3.0.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: dompurify
  dependency-version: 3.4.14
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: mermaid
  dependency-version: 11.17.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@sveltejs/kit"
  dependency-version: 2.70.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: marked
  dependency-version: 18.0.10
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: svelte
  dependency-version: 5.56.10
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: vite
  dependency-version: 8.2.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: vitest
  dependency-version: 4.1.11
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-24 11:58:36 -04:00
dependabot[bot] 0ccf178e91 chore(deps)(deps-dev): bump jsdom from 26.1.0 to 29.1.1 in /web (#1141)
* chore(deps)(deps-dev): bump jsdom from 26.1.0 to 29.1.1 in /web

Bumps [jsdom](https://github.com/jsdom/jsdom) from 26.1.0 to 29.1.1.
- [Release notes](https://github.com/jsdom/jsdom/releases)
- [Commits](https://github.com/jsdom/jsdom/compare/v26.1.0...v29.1.1)

---
updated-dependencies:
- dependency-name: jsdom
  dependency-version: 29.1.1
  dependency-type: direct:development
  update-type: version-update:semver-major
...

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

* test(a11y): emulate the :modal-unsupported engine explicitly under jsdom 29

jsdom 26 threw on the :modal pseudo-class, so the fallback-path tests in
viewerBackdrop.svelte.test.ts ran their premise on the bare environment
for free. jsdom 29 PARSES :modal but never matches it (the setup-jsdom
showModal polyfill sets no top-layer state), which the module's probe
reads as a supporting engine — flipping three tests off the path their
titles name and silently shifting a fourth.

The unsupported engine is now emulated the same way the supporting one
always was: mockModalUnsupported() throws SyntaxError from every probe
the module makes (querySelector, querySelectorAll, Element.matches),
per the re-take note on TASK-2586.

Claude-Session: https://claude.ai/code/session_01HvAuiZ7JaWyCqqyV99LyWt

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: xarmian <xarmian@gmail.com>
2026-08-23 08:46:16 -04:00
dependabot[bot] ac0f1180a9 chore(deps)(deps): bump the npm-minor-and-patch group across 1 directory with 17 updates (#1140)
Bumps the npm-minor-and-patch group with 16 updates in the /web directory:

| Package | From | To |
| --- | --- | --- |
| [@dagrejs/dagre](https://github.com/dagrejs/dagre) | `3.1.0` | `3.1.1` |
| [@tiptap/core](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/core) | `3.29.2` | `3.30.1` |
| [@tiptap/extension-bubble-menu](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-bubble-menu) | `3.29.2` | `3.30.1` |
| [@tiptap/extension-code-block-lowlight](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-code-block-lowlight) | `3.29.2` | `3.30.1` |
| [@tiptap/extension-collaboration](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-collaboration) | `3.29.2` | `3.30.1` |
| [@tiptap/extension-collaboration-caret](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-collaboration-caret) | `3.29.2` | `3.30.1` |
| [@tiptap/extension-placeholder](https://github.com/ueberdosis/tiptap/tree/HEAD/packages-deprecated/extension-placeholder) | `3.29.2` | `3.30.1` |
| [@tiptap/extension-table](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-table) | `3.29.2` | `3.30.1` |
| [@tiptap/extension-task-item](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-task-item) | `3.29.2` | `3.30.1` |
| [@tiptap/extension-task-list](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-task-list) | `3.29.2` | `3.30.1` |
| [@tiptap/pm](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/pm) | `3.29.2` | `3.30.1` |
| [@tiptap/starter-kit](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/starter-kit) | `3.29.2` | `3.30.1` |
| [@tiptap/suggestion](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/suggestion) | `3.29.2` | `3.30.1` |
| [@sveltejs/vite-plugin-svelte](https://github.com/sveltejs/vite-plugin-svelte/tree/HEAD/packages/vite-plugin-svelte) | `7.2.0` | `7.3.0` |
| [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) | `5.56.8` | `5.56.9` |
| [svelte-check](https://github.com/sveltejs/language-tools) | `4.7.5` | `4.7.6` |



Updates `@dagrejs/dagre` from 3.1.0 to 3.1.1
- [Release notes](https://github.com/dagrejs/dagre/releases)
- [Changelog](https://github.com/dagrejs/dagre/blob/master/changelog.md)
- [Commits](https://github.com/dagrejs/dagre/compare/v3.1.0...v3.1.1)

Updates `@tiptap/core` from 3.29.2 to 3.30.1
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/core/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.30.1/packages/core)

Updates `@tiptap/extension-bubble-menu` from 3.29.2 to 3.30.1
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-bubble-menu/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.30.1/packages/extension-bubble-menu)

Updates `@tiptap/extension-code-block-lowlight` from 3.29.2 to 3.30.1
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-code-block-lowlight/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.30.1/packages/extension-code-block-lowlight)

Updates `@tiptap/extension-collaboration` from 3.29.2 to 3.30.1
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-collaboration/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.30.1/packages/extension-collaboration)

Updates `@tiptap/extension-collaboration-caret` from 3.29.2 to 3.30.1
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-collaboration-caret/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.30.1/packages/extension-collaboration-caret)

Updates `@tiptap/extension-link` from 3.29.2 to 3.30.1
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-link/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.30.1/packages/extension-link)

Updates `@tiptap/extension-placeholder` from 3.29.2 to 3.30.1
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages-deprecated/extension-placeholder/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.30.1/packages-deprecated/extension-placeholder)

Updates `@tiptap/extension-table` from 3.29.2 to 3.30.1
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-table/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.30.1/packages/extension-table)

Updates `@tiptap/extension-task-item` from 3.29.2 to 3.30.1
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.30.1/packages/extension-task-item)

Updates `@tiptap/extension-task-list` from 3.29.2 to 3.30.1
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.30.1/packages/extension-task-list)

Updates `@tiptap/pm` from 3.29.2 to 3.30.1
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/pm/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.30.1/packages/pm)

Updates `@tiptap/starter-kit` from 3.29.2 to 3.30.1
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/starter-kit/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.30.1/packages/starter-kit)

Updates `@tiptap/suggestion` from 3.29.2 to 3.30.1
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/suggestion/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.30.1/packages/suggestion)

Updates `@sveltejs/vite-plugin-svelte` from 7.2.0 to 7.3.0
- [Release notes](https://github.com/sveltejs/vite-plugin-svelte/releases)
- [Changelog](https://github.com/sveltejs/vite-plugin-svelte/blob/main/packages/vite-plugin-svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/vite-plugin-svelte/commits/@sveltejs/vite-plugin-svelte@7.3.0/packages/vite-plugin-svelte)

Updates `svelte` from 5.56.8 to 5.56.9
- [Release notes](https://github.com/sveltejs/svelte/releases)
- [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.56.9/packages/svelte)

Updates `svelte-check` from 4.7.5 to 4.7.6
- [Release notes](https://github.com/sveltejs/language-tools/releases)
- [Commits](https://github.com/sveltejs/language-tools/compare/svelte-check@4.7.5...svelte-check@4.7.6)

---
updated-dependencies:
- dependency-name: "@dagrejs/dagre"
  dependency-version: 3.1.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: "@sveltejs/vite-plugin-svelte"
  dependency-version: 7.3.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/core"
  dependency-version: 3.30.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-bubble-menu"
  dependency-version: 3.30.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-code-block-lowlight"
  dependency-version: 3.30.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-collaboration"
  dependency-version: 3.30.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-collaboration-caret"
  dependency-version: 3.30.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-link"
  dependency-version: 3.30.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-placeholder"
  dependency-version: 3.30.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-table"
  dependency-version: 3.30.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-task-item"
  dependency-version: 3.30.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-task-list"
  dependency-version: 3.30.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/pm"
  dependency-version: 3.30.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/starter-kit"
  dependency-version: 3.30.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/suggestion"
  dependency-version: 3.30.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: svelte
  dependency-version: 5.56.9
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: svelte-check
  dependency-version: 4.7.6
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-23 08:28:19 -04:00
xarmian b759898d5b test(web): fake-indexeddb harness for the localIndex persistence layer (PLAN-2636 unit 1) (#1156)
The localIndex IndexedDB persistence layer ran as a no-op under vitest —
there is no IndexedDB in the node test environment, so `isSupported()`
returned false and every persist/hydrate call short-circuited. The entire
layer, and BUG-2609's seq-guard fix, shipped on live-browser evidence runs
only (the #1148 finding). This adds the harness that makes it testable, the
prerequisite for unit 2's order-and-merge regression matrix.

- fake-indexeddb dev dependency (exact-pinned).
- A dedicated `idb` vitest project (glob `*.idb.test.ts`, node env) whose
  setup installs fake-indexeddb's globals and a fresh IDBFactory per test.
  It self-disables when the dep can't be resolved — mirroring the jsdom
  project — so a symlinked worktree without it keeps `npm run test` green,
  and CI activates it once installed. The idb glob is excluded from the node
  project so the persistence layer can't no-op there and pass vacuously.
- Harness helpers unit 2 builds on: a second cross-tab connection to the
  same database (2635), a v1-database seed + higher-format-version reopen +
  downgrade VersionError (the v1→v2 migration exercise), a fresh-module
  loader that clears the connection cache, and raw ground-truth reads.
  `harnessDbName` mirrors the module's `dbName` exactly, pinned by a test so
  a drift can't make assertions read an empty sibling database.
- BUG-2609's evidence run is ported as a deterministic sequential regression:
  a newer delta commits its atomic rows+cursor transaction, then a stale
  older-seq snapshot lands last and is refused (IDB serializes overlapping
  transactions, so no interleaving control is needed). Plus a raw
  serialization characterization pinning that platform guarantee.

No production code changes.

Claude-Session: https://claude.ai/code/session_017jD6t1zjxGSq47SQpZfp1V
2026-08-18 15:20:13 -04:00
dependabot[bot] 312f28dd14 chore(deps)(deps-dev): bump vitest from 3.2.6 to 4.1.10 in /web (#1045)
Bumps [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) from 3.2.6 to 4.1.10.
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.10/packages/vitest)

---
updated-dependencies:
- dependency-name: vitest
  dependency-version: 4.1.10
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-15 12:34:23 -04:00
dependabot[bot] a7b70c2092 chore(deps)(deps-dev): bump @testing-library/jest-dom in /web (#1044)
Bumps [@testing-library/jest-dom](https://github.com/testing-library/jest-dom) from 6.9.1 to 7.0.1.
- [Release notes](https://github.com/testing-library/jest-dom/releases)
- [Changelog](https://github.com/testing-library/jest-dom/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testing-library/jest-dom/compare/v6.9.1...v7.0.1)

---
updated-dependencies:
- dependency-name: "@testing-library/jest-dom"
  dependency-version: 7.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-15 11:46:01 -04:00
dependabot[bot] 8ba08c7164 chore(deps)(deps): bump the npm-minor-and-patch group (#1072)
Bumps the npm-minor-and-patch group in /web with 7 updates:

| Package | From | To |
| --- | --- | --- |
| [@dagrejs/dagre](https://github.com/dagrejs/dagre) | `3.0.0` | `3.1.0` |
| [mermaid](https://github.com/mermaid-js/mermaid) | `11.16.0` | `11.16.1` |
| [svelte-dnd-action](https://github.com/isaacHagoel/svelte-dnd-action) | `0.9.77` | `0.9.78` |
| [yjs](https://github.com/yjs/yjs) | `13.6.31` | `13.6.32` |
| [marked](https://github.com/markedjs/marked) | `18.0.7` | `18.0.9` |
| [svelte-check](https://github.com/sveltejs/language-tools) | `4.7.4` | `4.7.5` |
| [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) | `8.2.0` | `8.2.1` |


Updates `@dagrejs/dagre` from 3.0.0 to 3.1.0
- [Release notes](https://github.com/dagrejs/dagre/releases)
- [Changelog](https://github.com/dagrejs/dagre/blob/master/changelog.md)
- [Commits](https://github.com/dagrejs/dagre/compare/v3.0.0...v3.1.0)

Updates `mermaid` from 11.16.0 to 11.16.1
- [Release notes](https://github.com/mermaid-js/mermaid/releases)
- [Commits](https://github.com/mermaid-js/mermaid/compare/mermaid@11.16.0...mermaid@11.16.1)

Updates `svelte-dnd-action` from 0.9.77 to 0.9.78
- [Changelog](https://github.com/isaacHagoel/svelte-dnd-action/blob/master/release-notes.md)
- [Commits](https://github.com/isaacHagoel/svelte-dnd-action/commits)

Updates `yjs` from 13.6.31 to 13.6.32
- [Release notes](https://github.com/yjs/yjs/releases)
- [Commits](https://github.com/yjs/yjs/compare/v13.6.31...v13.6.32)

Updates `marked` from 18.0.7 to 18.0.9
- [Release notes](https://github.com/markedjs/marked/releases)
- [Commits](https://github.com/markedjs/marked/compare/v18.0.7...v18.0.9)

Updates `svelte-check` from 4.7.4 to 4.7.5
- [Release notes](https://github.com/sveltejs/language-tools/releases)
- [Commits](https://github.com/sveltejs/language-tools/compare/svelte-check@4.7.4...svelte-check@4.7.5)

Updates `vite` from 8.2.0 to 8.2.1
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v8.2.1/packages/vite)

---
updated-dependencies:
- dependency-name: "@dagrejs/dagre"
  dependency-version: 3.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: mermaid
  dependency-version: 11.16.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: svelte-dnd-action
  dependency-version: 0.9.78
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: yjs
  dependency-version: 13.6.32
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: marked
  dependency-version: 18.0.9
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: svelte-check
  dependency-version: 4.7.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: vite
  dependency-version: 8.2.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-15 11:24:28 -04:00
xarmian 0a547283db fix(deps): keep @dagrejs/dagre pinned at 3.0.0
The group bump to 3.1.0 reproducibly breaks the graph-pane anchor E2E
(pane-content-link-anchors.spec.ts:238, node click timeout, 3/3 runs) —
layout changes move the node hit-target. dagre was exact-pinned at 3.0.0
by the renderer's author (TASK-1783); keeping it that way. Follow-up for
the 3.1 upgrade tracked separately.

Claude-Session: https://claude.ai/code/session_01RNcrc3CtXwJwreubtHTgN6
2026-08-06 23:52:06 +00:00
dependabot[bot] 3f0b390288 chore(deps)(deps): bump the npm-minor-and-patch group across 1 directory with 24 updates
Bumps the npm-minor-and-patch group with 23 updates in the /web directory:

| Package | From | To |
| --- | --- | --- |
| [@dagrejs/dagre](https://github.com/dagrejs/dagre) | `3.0.0` | `3.1.0` |
| [@tiptap/core](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/core) | `3.22.5` | `3.29.2` |
| [@tiptap/extension-bubble-menu](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-bubble-menu) | `3.22.5` | `3.29.2` |
| [@tiptap/extension-code-block-lowlight](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-code-block-lowlight) | `3.22.5` | `3.29.2` |
| [@tiptap/extension-collaboration](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-collaboration) | `3.22.5` | `3.29.2` |
| [@tiptap/extension-collaboration-caret](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-collaboration-caret) | `3.22.5` | `3.29.2` |
| [@tiptap/extension-placeholder](https://github.com/ueberdosis/tiptap/tree/HEAD/packages-deprecated/extension-placeholder) | `3.22.5` | `3.29.2` |
| [@tiptap/extension-table](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-table) | `3.22.5` | `3.29.2` |
| [@tiptap/extension-task-item](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-task-item) | `3.22.5` | `3.29.2` |
| [@tiptap/extension-task-list](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-task-list) | `3.22.5` | `3.29.2` |
| [@tiptap/pm](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/pm) | `3.22.5` | `3.29.2` |
| [@tiptap/starter-kit](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/starter-kit) | `3.22.5` | `3.29.2` |
| [@tiptap/suggestion](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/suggestion) | `3.22.5` | `3.29.2` |
| [@tiptap/y-tiptap](https://github.com/ueberdosis/y-tiptap) | `3.0.3` | `3.0.8` |
| [dompurify](https://github.com/cure53/DOMPurify) | `3.4.12` | `3.4.13` |
| [svelte-dnd-action](https://github.com/isaacHagoel/svelte-dnd-action) | `0.9.74` | `0.9.77` |
| [yjs](https://github.com/yjs/yjs) | `13.6.30` | `13.6.31` |
| [@playwright/test](https://github.com/microsoft/playwright) | `1.61.1` | `1.62.1` |
| [@sveltejs/kit](https://github.com/sveltejs/kit/tree/HEAD/packages/kit) | `2.70.1` | `2.70.2` |
| [@sveltejs/vite-plugin-svelte](https://github.com/sveltejs/vite-plugin-svelte/tree/HEAD/packages/vite-plugin-svelte) | `7.1.2` | `7.2.0` |
| [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) | `5.55.8` | `5.56.8` |
| [svelte-check](https://github.com/sveltejs/language-tools) | `4.7.3` | `4.7.4` |
| [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) | `8.1.5` | `8.2.0` |



Updates `@dagrejs/dagre` from 3.0.0 to 3.1.0
- [Release notes](https://github.com/dagrejs/dagre/releases)
- [Changelog](https://github.com/dagrejs/dagre/blob/master/changelog.md)
- [Commits](https://github.com/dagrejs/dagre/compare/v3.0.0...v3.1.0)

Updates `@tiptap/core` from 3.22.5 to 3.29.2
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/core/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.29.2/packages/core)

Updates `@tiptap/extension-bubble-menu` from 3.22.5 to 3.29.2
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-bubble-menu/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.29.2/packages/extension-bubble-menu)

Updates `@tiptap/extension-code-block-lowlight` from 3.22.5 to 3.29.2
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-code-block-lowlight/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.29.2/packages/extension-code-block-lowlight)

Updates `@tiptap/extension-collaboration` from 3.22.5 to 3.29.2
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-collaboration/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.29.2/packages/extension-collaboration)

Updates `@tiptap/extension-collaboration-caret` from 3.22.5 to 3.29.2
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-collaboration-caret/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.29.2/packages/extension-collaboration-caret)

Updates `@tiptap/extension-link` from 3.22.5 to 3.29.2
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-link/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.29.2/packages/extension-link)

Updates `@tiptap/extension-placeholder` from 3.22.5 to 3.29.2
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages-deprecated/extension-placeholder/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.29.2/packages-deprecated/extension-placeholder)

Updates `@tiptap/extension-table` from 3.22.5 to 3.29.2
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-table/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.29.2/packages/extension-table)

Updates `@tiptap/extension-task-item` from 3.22.5 to 3.29.2
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.29.2/packages/extension-task-item)

Updates `@tiptap/extension-task-list` from 3.22.5 to 3.29.2
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.29.2/packages/extension-task-list)

Updates `@tiptap/pm` from 3.22.5 to 3.29.2
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/pm/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.29.2/packages/pm)

Updates `@tiptap/starter-kit` from 3.22.5 to 3.29.2
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/starter-kit/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.29.2/packages/starter-kit)

Updates `@tiptap/suggestion` from 3.22.5 to 3.29.2
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/suggestion/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.29.2/packages/suggestion)

Updates `@tiptap/y-tiptap` from 3.0.3 to 3.0.8
- [Changelog](https://github.com/ueberdosis/y-tiptap/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/y-tiptap/commits)

Updates `dompurify` from 3.4.12 to 3.4.13
- [Release notes](https://github.com/cure53/DOMPurify/releases)
- [Commits](https://github.com/cure53/DOMPurify/compare/3.4.12...3.4.13)

Updates `svelte-dnd-action` from 0.9.74 to 0.9.77
- [Changelog](https://github.com/isaacHagoel/svelte-dnd-action/blob/master/release-notes.md)
- [Commits](https://github.com/isaacHagoel/svelte-dnd-action/commits)

Updates `yjs` from 13.6.30 to 13.6.31
- [Release notes](https://github.com/yjs/yjs/releases)
- [Commits](https://github.com/yjs/yjs/compare/v13.6.30...v13.6.31)

Updates `@playwright/test` from 1.61.1 to 1.62.1
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.61.1...v1.62.1)

Updates `@sveltejs/kit` from 2.70.1 to 2.70.2
- [Release notes](https://github.com/sveltejs/kit/releases)
- [Changelog](https://github.com/sveltejs/kit/blob/version-3/packages/kit/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/kit/commits/@sveltejs/kit@2.70.2/packages/kit)

Updates `@sveltejs/vite-plugin-svelte` from 7.1.2 to 7.2.0
- [Release notes](https://github.com/sveltejs/vite-plugin-svelte/releases)
- [Changelog](https://github.com/sveltejs/vite-plugin-svelte/blob/main/packages/vite-plugin-svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/vite-plugin-svelte/commits/@sveltejs/vite-plugin-svelte@7.2.0/packages/vite-plugin-svelte)

Updates `svelte` from 5.55.8 to 5.56.8
- [Release notes](https://github.com/sveltejs/svelte/releases)
- [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.56.8/packages/svelte)

Updates `svelte-check` from 4.7.3 to 4.7.4
- [Release notes](https://github.com/sveltejs/language-tools/releases)
- [Commits](https://github.com/sveltejs/language-tools/compare/svelte-check@4.7.3...svelte-check@4.7.4)

Updates `vite` from 8.1.5 to 8.2.0
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/create-vite@8.2.0/packages/vite)

---
updated-dependencies:
- dependency-name: "@dagrejs/dagre"
  dependency-version: 3.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@playwright/test"
  dependency-version: 1.62.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@sveltejs/kit"
  dependency-version: 2.70.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: "@sveltejs/vite-plugin-svelte"
  dependency-version: 7.2.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/core"
  dependency-version: 3.29.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-bubble-menu"
  dependency-version: 3.29.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-code-block-lowlight"
  dependency-version: 3.29.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-collaboration"
  dependency-version: 3.29.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-collaboration-caret"
  dependency-version: 3.29.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-link"
  dependency-version: 3.29.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-placeholder"
  dependency-version: 3.29.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-table"
  dependency-version: 3.29.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-task-item"
  dependency-version: 3.29.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-task-list"
  dependency-version: 3.29.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/pm"
  dependency-version: 3.29.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/starter-kit"
  dependency-version: 3.29.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/suggestion"
  dependency-version: 3.29.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/y-tiptap"
  dependency-version: 3.0.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: dompurify
  dependency-version: 3.4.13
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: svelte
  dependency-version: 5.56.8
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: svelte-check
  dependency-version: 4.7.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: svelte-dnd-action
  dependency-version: 0.9.77
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: vite
  dependency-version: 8.2.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: yjs
  dependency-version: 13.6.31
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-06 23:12:41 +00:00
xarmian 96daff2e7c chore(deps): bump low-risk dev tooling + render libs (safe subset of #1002) (#1004)
Extracts the genuinely low-risk bumps from the grouped Dependabot PR #1002,
which as a whole can't be merged (it's stale — reverts the BUG-2278 advisory
overrides — and bundles a coordinated @tiptap/* 3.22.5->3.28.0 bump that needs
schema-version verification plus a svelte 5.55->5.56 runtime bump that needs
focus-suite revalidation).

Safe subset (dev tooling + rendering libs only; no collab/runtime/build-compiler
surface): @playwright/test 1.59.1->1.61.1, marked 18.0.3->18.0.7, svelte-check
4.4.7->4.7.3, mermaid 11.14.0->11.16.0, layercake 10.0.2->10.0.3,
svelte-dnd-action 0.9.69->0.9.74.

Deliberately EXCLUDED (verified unchanged): @tiptap/*, svelte,
@sveltejs/vite-plugin-svelte, yjs, and the kit/vite/rolldown toolchain — those
need their own validated PRs.

Gates: audit 0 prod vulns, check:tiptap-pins OK, npm ci in sync, build, check
(0 errors), test (464) all green.

Claude-Session: https://claude.ai/code/session_01EZ6yr6pAUFb1uffan912ra
2026-07-22 07:20:18 -04:00
xarmian add3ffabfb feat(web): adopt vite 8.1 / sveltekit 2.70 toolchain + fix pane pop focus (#1001)
Completes the deferred upgrade from BUG-2278. The advisory-fix PR #997 had
dragged this toolchain in via `npm audit fix`; #999 reverted it because it
regressed the pane-focus E2E suite. Root-caused (see BUG-2278 residual): the
trigger is @sveltejs/kit 2.66.0 (PR #15452), which blurs the active element
to <body> BEFORE the component update during navigation. On the pane's
popstate pop path (handlePaneBack -> history.go(-1), which can't carry
keepFocus), that early blur (focusout only, no focusin) makes Kit's end-of-nav
reset_focus() body.focus() a no-op emitting no focusin — starving PaneHost's
focusin-only backstop, so focus strands on <body>. (Drill path uses
goto({keepFocus:true}) and is unaffected — which is why only the 5 pop/ESC
focus tests failed. vite/rolldown/svelte are not implicated.)

Fix: re-assert focusPaneRegion() after the popstate settles (next frame, so
it runs after Kit's microtask-scheduled reset_focus), removing the dependency
on an incidental focusin(body). ~12 lines in paneHostController.ts; no-op when
the pane closed or focus already landed in-pane.

Toolchain: vite 8.0.11->8.1.5, @sveltejs/kit 2.59.1->2.70.1, rolldown
rc.18->1.1.5 (lockfile only; package.json caret ranges + advisory overrides
unchanged). Advisory deps stay at their patched versions (audit 0 prod vulns).

Verified on the bumped toolchain: the 5 previously-failing pane-focus tests
pass, full pane e2e 41 passed (the one flaky test, :160, is a PRE-EXISTING
flake on main that flakes on the reverted toolchain too and passes on retry),
web check (0 errors), test (464), build, tiptap-pins, npm ci all green.

Claude-Session: https://claude.ai/code/session_01EZ6yr6pAUFb1uffan912ra
2026-07-21 22:58:44 -04:00
xarmian dd79e837f6 fix(ci): narrow BUG-2278 web fix to advisory deps, revert toolchain slide (#999)
The broad `npm audit fix` from the previous commit (0f48bceb) fixed the
three production advisories but, as semver-compatible caret collateral,
also dragged the build toolchain forward (vite 8.0.11->8.1.5,
@sveltejs/kit 2.59.1->2.70.1, rolldown rc.18->1.1.5). That toolchain slide
regressed the timing-sensitive pane-focus E2E suite (PLAN-2154): E2E was
green on the two prior main runs and failed twice (initial + rerun) on the
broad-fix commit — a confirmed regression, not a flake. The advisory jobs
themselves (Go govulncheck, Web npm audit) went green.

This narrows the web fix to exactly the production advisories and holds the
toolchain at main's prior versions:
- dompurify (direct dep) ^3.4.2 -> ^3.4.12 (moderate XSS advisories)
- markdown-it override ^14.3.0 (moderate smartquotes DoS; transitive)
- linkify-it override ^5.0.1 -> ^5.0.2 (high, GHSA-v245-v573-v5vm; transitive)

`npm audit --audit-level=high --omit=dev` is 0 vulns (the CI Web gate). Two
dev-only advisories that the toolchain bump had incidentally cleared
(launch-editor high under vite, a moderate under @sveltejs/kit) are left
unfixed per repo policy ("dev-only advisories are informational; they don't
ship"). The Go x/text fix from 0f48bceb is retained unchanged.

Gates: web npm ci (in sync), check:tiptap-pins, audit (0 prod vulns), build,
check (0 errors), test (464) all green; toolchain verified unchanged.

Claude-Session: https://claude.ai/code/session_01EZ6yr6pAUFb1uffan912ra
2026-07-21 21:44:36 -04:00
xarmian 0f48bcebfa fix(ci): bump advisory deps to restore green CI (BUG-2278) (#997)
Two newly-published upstream advisories postdate the last green main run
and were failing the Go and Web CI jobs on every PR. Both are DoS-class
in parsing/text deps; no product code change.

Go job (govulncheck binary mode):
- GO-2026-5970: infinite loop on invalid input in golang.org/x/text.
  Bump golang.org/x/text v0.38.0 -> v0.39.0 via `go get` + `go mod tidy`.
  go mod tidy pulls the coordinated x/* release train it requires
  (crypto/term/mod/net/sys/tools). govulncheck -mode binary: 0 called.

Web job (npm audit --audit-level=high --omit=dev):
- linkify-it <=5.0.1 (high, GHSA-v245-v573-v5vm) + dompurify + markdown-it.
  `npm audit fix` (lockfile-only). Fixes the 3 advisories (audit now
  reports 0 vulns). As semver-compatible collateral within existing caret
  ranges it also refreshed the build toolchain (vite 8.0.11->8.1.5,
  @sveltejs/kit 2.59.1->2.70.1, rolldown rc.18->1.1.5). Tiptap exact-pins
  held (check:tiptap-pins green).
- Also tighten the existing linkify-it security override floor
  ^5.0.1 -> ^5.0.2 so it expresses the patched minimum for THIS advisory
  rather than relying on npm's latest-in-range resolution. Lockfile was
  already at 5.0.2, so npm ci stays in sync (verified).

Gates: go vet, go build, govulncheck -mode binary, go test ./... all green;
web npm ci, check:tiptap-pins, audit, build, check, test (464) all green.
Independent Codex review: CLEAN.

Claude-Session: https://claude.ai/code/session_01EZ6yr6pAUFb1uffan912ra
2026-07-21 21:16:48 -04:00
xarmian 652584bcfa test(web): jsdom/Svelte vitest harness + Modal & breakpoint tests (TASK-2081) (#887)
* test(web): add jsdom/Svelte vitest project + Modal & breakpoint tests (TASK-2081)

* test(web): update package-lock for jsdom test deps (TASK-2081 review)
2026-07-09 19:53:55 -04:00
xarmian 982f534b4a fix(web): exact-pin coordinated Tiptap packages + CI drift guard (BUG-2009) (#838)
CLAUDE.md mandates exact-pinning the coordinated Tiptap packages so npm
cannot silently slide the shared Y.Doc/ProseMirror schema out of sync.
@tiptap/core (^3.22.5), @tiptap/y-tiptap (^3.0.3) and @tiptap/pm (^3.20.4)
were still floating on caret ranges; only @tiptap/extension-collaboration
was exact.

- Exact-pin @tiptap/core, @tiptap/y-tiptap and @tiptap/pm to their
  currently-resolved versions (3.22.5 / 3.0.3 / 3.22.5); refresh lockfile.
- Add web/scripts/check-tiptap-pins.mjs + check:tiptap-pins npm script:
  fails if any coordinated package loses its exact pin in package.json or
  resolves to more than one version in the lockfile.
- Wire the guard into the CI web job.
2026-07-07 16:11:44 -04:00
xarmian 9f85a85427 fix(deps): clear CI vuln audits (x/image TIFF CVEs, linkify-it ReDoS) (#772)
CI on main has been red since 2026-06-25 on two dependency-audit gates,
not on tests or code:

- govulncheck: golang.org/x/image@v0.41.0 carries two TIFF-decoder CVEs
  (GO-2026-5066 out-of-bounds strip offset panic, GO-2026-5062 unbounded
  tile sizes) reached via internal/attachments/processor_purego.go ->
  image.Decode -> tiff.Decode. Both fixed in v0.43.0. The go mod tidy that
  follows pulls the usual transitive x/{text,mod,sync,tools} bumps.

- npm audit (--audit-level=high --omit=dev): linkify-it@5.0.0 has a
  high-severity quadratic-complexity ReDoS (GHSA-22p9-wv53-3rq4),
  transitive via tiptap-markdown -> markdown-it -> linkify-it. Pinned to
  ^5.0.1 via an overrides entry (5.0.1 satisfies markdown-it's ^5.0.0).

Verified locally: govulncheck clean, go vet/build/test green, npm audit
high gate exits 0, web build + svelte-check green.

Claude-Session: https://claude.ai/code/session_01HxBkAMiFBtCRJ2tKSCt3ST
2026-06-28 11:01:37 -05:00
xarmian 2b11383e13 feat(web): WebMCP browser module — register catalog tools (read dispatch) (#765)
* feat(web): WebMCP browser module — register catalog tools (read dispatch)

Add web/src/lib/webmcp/: a feature-detected, webmcp_enabled-gated module
that registers Pad's MCP catalog tools via document.modelContext, scoped to
the current-route workspace, dispatching READ actions to the api client.

Phase 3 of PLAN-1888 (TASK-1892). Builds on the Phase-1 session flag and
the Phase-2 GET /api/v1/mcp/tool-surface endpoint.

- types.d.ts: experimental ambient stub for document.modelContext.
- api.mcp.toolSurface() + ToolSurface* types; api.playbooks.list/get.
- descriptors.ts (pure, unit-tested): tool-surface JSON → ModelContextTool[],
  STRIPPING the workspace param (DR-4) and setting readOnlyHint only when
  EVERY action is read_only (DR-2).
- dispatch.ts (pure, injectable, unit-tested): (tool, action, args) → client,
  forcing the route wsSlug and REJECTING any agent-supplied workspace (DR-4).
  Write actions return a precise "wired in TASK-3b" error — never a no-op.
- register.ts: lifecycle entry; gated on webmcp_enabled + feature-detection +
  auth; AbortSignal teardown.
- +layout.svelte: register on the workspace $effect, tear down on switch /
  onDestroy, with a token guard against stale async registration.
- vitest wired (25 unit tests).

Refs TASK-1892 / PLAN-1888

Claude-Session: https://claude.ai/code/session_01KmxkPxLksjf1pmrZDpsnTJ

* fix(web): WebMCP lifecycle — invalidate in-flight reg on unmount; re-register on auth resolve

Codex review round 1 (both P1/P2):
- onDestroy now bumps webmcpToken before teardown so an in-flight
  registerWorkspaceTools() awaiting the tool-surface fetch self-discards
  (its .then sees token mismatch and unregisters) instead of leaking.
- New standalone $effect re-runs connectWebMCP() when the auth gate
  (webmcp_enabled + user) resolves after the untrack'd workspace effect
  already ran — fixes cold-load registration being permanently skipped.
  Idempotent (token guard ensures no dup registration). Kept as a separate
  effect per CONVE-606.

Refs TASK-1892 / PLAN-1888

Claude-Session: https://claude.ai/code/session_01KmxkPxLksjf1pmrZDpsnTJ
2026-06-24 23:05:34 -04:00
xarmian 771d102e89 feat(web): 2D directional dependency-graph renderer (TASK-1783) (#720)
* feat(web): 2D directional dependency-graph renderer (TASK-1783)

Add ItemGraph.svelte — a reusable, self-contained component that renders a
single item's dependency neighborhood as a 2D layered graph (parents above,
children below) via dagre layout + SVG.

- Lazy-imports @dagrejs/dagre so the layout lib stays out of the main
  bundle (confirmed split into its own chunk).
- Fetches via api.graph.getFocused; re-roots on an internal currentFocus so
  clicking a non-focus node navigates the chain, clicking the focus node (or
  Open ↗) opens it. Depth (1–5) selector + include-done toggle; breadcrumb
  back to the original root.
- Edge styling by type (hierarchy tethers, red directional blocks arrows,
  faint wiki-link dashes, muted others); collection-colored nodes with
  focus highlight + dimmed terminals; pan/zoom + fit-to-content; legend and
  a truncation notice.
- Reactivity follows CONVE-1688 (loadToken guard, no read+write of one rune
  in an effect) and CONVE-606 (data-load effect split from prop-sync).

Extract the collection palette into a shared $lib/graph/palette.ts and point
the 3D workspace graph at it too, so both views agree on collection colors.

Parent: PLAN-1780.

* fix(web): correct hierarchy layout direction + node border color-mix per Codex review (round 1)

- P1: 'parent'/'implements' edges are child→parent in the API, so feeding
  them to dagre as-is put children above parents under rankdir TB. Reverse
  those edges for layout ranking only; rendered edges keep true source→target
  so 'blocks' arrowheads still point correctly.
- P2: unfocused node border used a JS string literal with an un-interpolated
  {n.color}, yielding an invalid CSS color and a dropped stroke. Use a
  template literal so the collection tint actually applies.

Parent: PLAN-1780.
2026-06-08 18:34:50 -04:00
xarmian 0234239547 feat(web): graph node labels — always-on for hubs, distance-faded elsewhere (TASK-1743) (#708)
Camera-facing ref labels (three-spritetext, dynamic-imported alongside
the renderer so it stays out of the entry bundle) make the graph
readable without tap/hover. Anti-soup rules: hubs (child_count > 0)
always labeled; in focus mode the selected node + neighborhood +
blocker chain stay labeled while dimmed nodes hide theirs; everything
else fades in between 160 and 120 camera units. The visibility walk
is imperative (throttled orbit-controls 'change' listener + repaint
lockstep + payload commits) and only toggles sprite.visible/opacity —
nodeThreeObject is set once and never re-assigned, preserving the
BUG-1742 no-flush discipline. Label offset clears the actual sphere
radius (4·∛val + pad) so big hub labels don't render inside the node.

Parent: IDEA-1740 (graph follow-ups).
2026-06-05 22:31:47 -04:00
xarmian db3917f6d2 feat(web): /graph route MVP — lazy-loaded 3D force graph (TASK-1733) (#701)
* feat(web): /graph route MVP — lazy-loaded 3D force graph (TASK-1733)

New full-viewport graph page at /{username}/{workspace}/graph rendering
the TASK-1731 endpoint via 3d-force-graph. Three.js loads only through
a dynamic import inside onMount, landing in its own ~1.3MB chunk
referenced solely by the graph route node — entry bundle unchanged.

Node color = collection (local hex palette; the chart PALETTE's CSS
vars can't reach WebGL), node size = 1 + 2×child_count, blocks edges
red with directional arrows, structural links brighter than soft ones.
Click navigates to the item page (ResolveItem accepts refs in the slug
param). Active items by default with a "Show completed" toggle that
refetches in place; workspace switches refetch with a stale-response
guard. Teardown via _destructor + ResizeObserver disconnect.

Nav: 'graph' added to destinations.ts (NavKey, RESERVED_SLUGS, primary
destinations, getActiveKey) and a Sidebar entry after Insights — the
mobile More sheet picks it up from the shared source automatically.

Parent: PLAN-1730.

* fix: clear stale canvas on workspace switch + reserve 'graph' collection slug per Codex review (round 1)

1. The renderer-sync effect now pushes empty graphData when the
   reactive payload is null (workspace switch in flight / load error)
   instead of early-returning — the previous workspace's nodes no
   longer linger behind the loading overlay.
2. 'graph' added to reservedCollectionSlugs so a collection can't
   shadow the /{username}/{workspace}/graph route, matching the
   frontend's RESERVED_SLUGS.
2026-06-05 18:45:21 -04:00
xarmian 0c8c066dd6 feat(web): LayerCake chart component library for Reports (TASK-1632) (#639)
Add a reusable charting library under web/src/lib/components/charts/ to power
the upcoming Reports surface (TASK-1633):
- BarChart / LineChart / Sparkline (public) + Bars/Lines/AxisX/AxisY layers
- theme.ts: CSS-var palette with hex fallbacks + typed LayerCake context
- Svelte 5 runes; role=img + aria-label; "No data" empty states

Library only (not wired into a page yet). Deps: layercake@10.0.2 +
d3-scale@4.0.2 (+@types/d3-scale); npm override lets layercake accept the
repo's TypeScript 6. ~8-12KB gzipped added when the Reports bundle imports
them; Sparkline is dependency-free SVG.

Parent: PLAN-1628.
2026-05-29 09:00:16 -04:00
xarmian b801867053 chore(web): npm audit fix — svelte 5.55.8, devalue 5.8.1, mermaid 11.15.0 (#589)
* chore(web): npm audit fix — patch-bump svelte, devalue, mermaid

Resolves three advisories surfaced by the Web CI job (and visible on
recent main commits before this):

- svelte:    5.55.5 → 5.55.8 (moderate × 4: SSR XSS via spread,
             hydratable Promise XSS, DOM clobbering, ReDoS in
             <svelte:element>)
- devalue:   5.6.x → 5.8.1   (high: DoS via sparse array deser)
- mermaid:   11.14 → 11.15.0 (moderate × 4: Gantt infinite-loop DoS,
             classDef CSS/HTML injection, config CSS injection)

All three are in-range patch bumps — package.json untouched, only the
lockfile changes. No major bumps, no API churn.

Tiptap deliberately untouched: @tiptap/core, @tiptap/extension-
collaboration, @tiptap/y-tiptap stay at 3.22.5 — the CLAUDE.md
lockstep rule (coordinated bumps + Y.Doc schema-version bump) only
applies when those three move together, and nothing here does.

Verified:
- npm audit: 0 vulnerabilities
- npm run check: 0 errors (existing 6 warnings unchanged)
- make build + make install: clean, server boots and serves the new
  bundle.

* test(oauth): TestConsent_ApproveWithSpecificWorkspaces locates connection by shape

The test asserts the persisted oauth_connections row from a specific-
workspaces consent (allowed_workspaces=[alpha,beta]) has
AllCurrentWorkspaces=false and the right slug list. It indexed via
conns[0], which only worked when the approve flow was the most-recent
connection — but the test also calls runAuthCodeFlow above the
assertion to mint a bearer for the introspect call, and that helper
posts allowed_workspaces=["*"] → wildcard connection. With
ListUserOAuthConnections ordered ConnectedAt DESC, conns[0] is now the
wildcard bearer row, not the alpha+beta row this test is verifying.

We can't move the bearer-mint below the assertion (the introspect call
needs the bearer first), and the auth-code response doesn't surface the
request_id we'd need to look up the right connection directly. The
specific-workspaces flow is the only one in this test with
AllCurrentWorkspaces=false, so locate by that shape — surgical fix that
matches the test's actual intent.

Verified with `go test -count=3 -run TestConsent_ApproveWithSpecificWorkspaces`
(deflakes across orderings) and the full ./internal/server suite.
2026-05-18 17:17:42 -04:00
xarmian 4e04148f13 feat(web): localSearch MiniSearch store (TASK-1363) (#508)
* feat(web): localSearch MiniSearch store (TASK-1363)

Build the client-side full-text search foundation for Phase 3 of the
local-first read model: a per-workspace MiniSearch index that mirrors
`localIndex` and provides sub-millisecond ranked search over titles +
parsed fields. No UI changes — TASK-1364 wires the collection page and
TASK-1365 wires the global CommandPalette.

- New `web/src/lib/stores/localSearch.svelte.ts`: per-workspace
  MiniSearch index keyed by workspace slug (module-level plain Map —
  search results are derived on demand via `.search()`, not subscribed
  to). Indexed fields with boosts: title (3x), ref (2x), item_number
  (2x), tags, parent_ref, parent_title, collection_slug, and a
  flattened `fields` blob so status / priority / assignee names land
  as searchable terms. Custom tokenizer splits on whitespace + `-_./`
  so `TASK-5` indexes as both `task` and `5`. Public API: `rebuild`,
  `upsert`, `remove`, `reset`, `search(ws, q, opts?)`, `size`.
  Returns `{ id, score }[]` ranked by descending score; callers
  resolve to full rows via `localIndex` (O(1) Map lookup).
- `localIndex` mutation paths now mirror writes into the MiniSearch
  index: `applyDelta`, `upsert`, `remove`, `removeByCollection`,
  `reset` (and the 403-purge error path in `bootstrap`). The cold
  `/items-index` snapshot and the warm IDB hydrate trigger a single
  bulk `localSearch.rebuild(...)` instead of N per-row upserts —
  measurably cheaper at workspace boot.
- SSR-safe: every entry point gates on `typeof window !== 'undefined'`
  so SvelteKit prerender / SSR can't build a server-side index.
- Add `minisearch@7.2.0` (~10KB gz) as an exact-pinned dependency.

Parent: PLAN-1343 (DOC-1342 Phase 3a).

* fix(web): broaden localSearch tokenizer per Codex review (round 1)

Codex round 1 P2: the previous tokenizer only split on whitespace + a
narrow set of separators (`-_./`), so values like `foo:bar`,
`foo,bar`, and `foo(bar)` indexed as a single token. Searches for
`bar` or `foo bar` would miss valid matches.

Broaden to split on any non-letter/non-digit character (`/[^\p{L}\p{N}]+/u`).
This matches MiniSearch's default `SPACE_OR_PUNCTUATION` shape — catches
whitespace, dashes, dots, slashes, colons, commas, parens, brackets,
underscores, etc. — while preserving the `TASK-5` → ['task', '5']
behavior the original regex aimed for.
2026-05-11 22:35:40 -04:00
xarmian 13fd9bbda7 feat(web): IndexedDB persistence for localIndex (TASK-1356) (#503)
* feat(web): IndexedDB persistence for localIndex (TASK-1356)

Adds `web/src/lib/stores/localIndexPersistence.ts` and wires it into
the existing localIndex store. Cold loads still hit /items-index;
warm loads paint from IDB before any network IO.

- New `idb` (8.0.3) dependency — small wrapper around IndexedDB.
- Per-workspace database `pad-local-index-{wsSlug}` with two object
  stores (`items` keyed by id, `meta` keyed by 'key' for cursor +
  schemaVersion).
- `LOCAL_INDEX_SCHEMA_VERSION = 1` — bump it on incompatible changes
  to `ItemIndexRow` or the IDB layout; mismatches drop the store and
  force a full /items-index resync. Same pattern as the Yjs
  schemaVersion in `web/src/lib/collab/schemaVersion.ts`.
- `bootstrap` now hydrates from IDB FIRST (paints from cache, flips
  state to 'ready'), then reconciles via /items-changes?since=cursor
  in the background. Cold cache falls through to /items-index and
  persists the result.
- Every mutation path (`upsert`, `applyDelta`, `remove`, `reset`)
  writes through to IDB. Persistence failures degrade silently to
  in-memory only — the read path is never blocked.
- SSR-safe (every IDB call gated on typeof indexedDB !== 'undefined').
- Best-effort: Safari private mode / quota / eviction all surface
  as "empty cache, re-bootstrap from network".

Phase 2 acceptance: warm paint of a populated workspace should now
appear before any /api/v1 request completes.

Parent: PLAN-1343. See DOC-1342 design decision #4.

* fix(web): localIndex reconcile loop + atomic delta persist (Codex round 1)

- [P1] 403 from the warm-load reconcile is no longer swallowed.
  When /items-changes returns `forbidden`, drop the cache and
  re-throw so the registered access-revoked handler (TASK-1360)
  sees it. Other network blips remain non-fatal — cache stands
  and the next reconnect retries.

- [P2] /items-changes is paged at DefaultItemChangesLimit (5000)
  per response. The previous one-shot reconcile would only catch
  up by a single page on a long-offline cache, and bootstrapState
  would pin at 'ready' forever with no later trigger to fetch the
  rest. Loop until the cursor stops advancing; defensively cap at
  50 iterations.

- [P2] New persistDelta() writes rows + meta cursor in a SINGLE
  IDB transaction. The previous separate persistUpserts + persistCursor
  could persist the cursor without the rows that produced it
  (tx interrupt, eviction), leaving the next warm hydrate with a
  cursor that skipped rows. applyDelta + the cold-path bootstrap
  snapshot now both use persistDelta. The standalone persistCursor
  helper is no longer used by localIndex but remains for callers
  that explicitly only need a cursor write.

Parent: PLAN-1343.

* fix(web): cold-path snapshot persists post-merge rows (Codex round 2)

[P1] When the cold-path `/items-index` request is in flight, an SSE
or `applyDelta` write can overlap and stamp a newer row into the
in-RAM index. `mergeRow` correctly skips the stale response row for
that id, but the previous IDB write used `resp.items.map(toSkinny)`
— the unfiltered server response — so the cache got the stale row
under the newer cursor. On the next warm boot, /items-changes?since
would skip that row forever.

Persist the POST-merge in-memory state (state.items.values()) so
the on-disk rows match the in-RAM rows that won the seq guard, and
the cursor stays consistent with them. Uses the same atomic
persistDelta path applyDelta does.

Parent: PLAN-1343.

* fix(web): generation guard on bootstrap + user-scoped IDB cache (round 3)

- [P1] WorkspaceState now carries a `generation` counter. Each
  `reset(ws)` bumps it on the prior state object before dropping
  the workspace from the map. Any in-flight bootstrap captures the
  generation at start and re-checks after every await — if the
  generation has advanced, the bootstrap silently bails out before
  reapplying rows or writing the snapshot to IDB. Without this, a
  sign-out / 403 purge during a slow /items-index could let the
  completed snapshot resurrect just-purged rows.

- [P1] IDB databases are now keyed by (userId, workspaceSlug)
  instead of workspaceSlug alone. The cache is "what THIS user could
  see last sync" — if a different user signs into the same browser,
  their bootstrap opens a fresh per-user namespace and the previous
  user's rows never surface. Anonymous callers (pre-auth) use the
  `anon` namespace. `localIndex.bootstrap` takes an `{ userId }`
  opt the caller passes in (the workspace state captures it on
  first bootstrap and threads it through every persistence call).

Parent: PLAN-1343.

* fix(web): durable applyDelta + required userId opt (Codex round 4)

- applyDelta now ALWAYS includes the existing in-RAM row in the
  persistDelta batch when it wins the seq guard. The previous version
  advanced the IDB cursor past those rows on the assumption their
  upsert()-fired persistUpserts had already landed — but that's a
  fire-and-forget background write that can lose the race or get
  aborted. Result: a row in RAM with seq S, no copy in IDB, and a
  persisted cursor of N > S — warm boot would skip it forever. One
  extra IDB put per redundant row trades cheaply against a missing-
  row class of bug.

- localIndex.bootstrap's `opts.userId` is now REQUIRED (not optional
  with `null` default). Authenticated callers that forget to pass
  it would have silently landed their cache in the shared `anon`
  namespace — a later account on the same browser could then read
  the previous account's rows. TypeScript now enforces an explicit
  choice; pre-auth callers pass null deliberately.

Parent: PLAN-1343.

* fix(web): user-mismatch reset + transient resync retry (Codex round 5)

- [P1] bootstrap() now resets the workspace state BEFORE the
  early-return for 'ready' / pending-promise when the caller's
  opts.userId doesn't match the cached state.userId. Without this,
  a user switch in the same tab could inherit the previous user's
  in-memory map and in-flight promise.

- [P2] WorkspaceState.pendingResync tracks transient delta-sync
  failures on the warm path. When /items-changes fails (non-403)
  after warm cache hydrate, bootstrapState stays 'ready' so the
  UI keeps working off the cache, but pendingResync stays true and
  the next bootstrap() call retries the reconcile instead of
  no-opping. Cold path always finishes with pendingResync=false.

- Documented the permission-revocation-without-row-change limitation
  inline: the cache can't see grants removed without a mutation,
  per DOC-1342 design decision #3 — that's the 403-on-click purge
  flow (TASK-1360), not this layer's job.

Parent: PLAN-1343.

* fix(web): only clear pendingResync when reconcile catches up (round 6)

[P2] The /items-changes reconcile loop has a 50-page safety cap to
prevent pathological tight loops. The previous code unconditionally
cleared `pendingResync` after the loop exited, even on cap-hit, so
a cache that's 50+ pages behind (250k+ rows) would record itself as
fresh and skip retries on future bootstraps. Now `pendingResync` is
only cleared when the loop exited because the server returned no new
rows AND no cursor advance — the genuine "caught up" signal. Cap-hit
leaves `pendingResync = true` so the next bootstrap call resumes.

The permission-revocation-without-row-change concern Codex re-raised
is the explicit DOC-1342 design decision #3 (best-effort cache; 403
purge handles stale-by-permission). The server emits grant-revocation
tombstones through /items-changes per internal/store/grants.go, so the
cache reconciles to-server-truth at the next reconnect. Anything that
slips past that is the 403-on-click purge path (TASK-1360). The
limitation is now explicitly noted inline.

Parent: PLAN-1343.

* fix(web): reentry order + identity-checked inflight cleanup (round 7)

- [P2] Capture `reentry` BEFORE flipping bootstrapState to 'loading'.
  The previous version set state='loading' first, then checked
  `state.bootstrapState === 'ready'` to decide if we're in a
  pendingResync retry — that check always read false, so retries
  re-read IDB instead of just rerunning the reconcile. With
  fire-and-forget IDB writes, re-reading rows whose RAM copy was
  just removed but whose IDB delete hadn't landed would resurrect
  them. Reentry now also skips the 'loading' flip so the UI never
  blanks during a retry.

- [P2] Inflight cleanup is now identity-checked. A `reset()` during
  an in-flight bootstrap can let a fresh bootstrap call re-occupy
  the inflight slot before the stale promise's `finally` runs;
  deleting unconditionally would remove the new entry and let a
  duplicate bootstrap start. We hold the promise in `slot.p` (a
  shared object so the closure can see assignment without TDZ
  issues), and only clear `inflight.delete(ws)` if `slot.p` is
  still the registered promise.

Parent: PLAN-1343.

* fix(web): 401 + empty-cache warm-load (Codex round 8)

- [P1] 401 (unauthorized) from /items-changes reconcile is now
  treated like 403 (forbidden): drop the cache, mark state=error,
  re-throw. The api.items.changes path throws PadApiError with
  code='unauthorized' on 401 (after the redirect-to-login is fired),
  so the cache shouldn't keep showing private rows while the
  redirect is in flight. Other network failures remain transient.

- [P2] A populated IDB cache is now defined as "has rows OR cursor
  > 0", not "has rows". An empty workspace (or a guest with
  item-level grants but no items granted yet) legitimately has zero
  rows but a real meta cursor from the prior sync. The previous
  check forced those workspaces through the cold /items-index path
  on every page load, defeating the warm-load fast path.

Parent: PLAN-1343.
2026-05-11 19:30:07 -04:00
xarmian 27b048eef3 feat(collab): presence carets with deterministic user colors (TASK-1263) (#463)
Renders remote peers' carets and selection highlights in the
collab-mode Tiptap editor via @tiptap/extension-collaboration-caret
(the v3 rename of extension-collaboration-cursor — pinned at
3.22.5 to match the rest of the Tiptap suite).

- New cursorColor.ts: djb2 hash → HSL → #rrggbb (hex required
  because y-tiptap's selectionRender appends an alpha-hex byte and
  only validates hex)
- Editor.svelte: optional `awareness` + `collabUser` props; only
  registers CollaborationCaret when ydoc + awareness + user are all
  present
- +page.svelte: derives `collabUserState` from authStore.user and
  threads it + collabProvider.awareness into <Editor>
- CSS for .collaboration-carets__caret + label + selection (label
  always visible — caret is too thin to hover, so the original
  hover-reveal was unreachable)

Three rounds of Codex review.
2026-05-09 04:32:54 -04:00
xarmian 5dc42b60df feat(collab): wire Yjs WebSocket provider + Y.Doc lifecycle (TASK-1259) (#457)
* feat(collab): wire Yjs WebSocket provider + Y.Doc lifecycle (TASK-1259)

Adds a thin y-websocket-style provider speaking the binary protocol
already implemented server-side in internal/collab/room.go. The
provider lives in a Svelte 5 .svelte.ts module so connection state
(`connected`, `synced`) can be consumed reactively by upcoming UX
tasks (TASK-1264 pending-sync indicator, TASK-1265 mobile reconnect).

Wire format mirrors the server's first-byte discriminator:
  0x00 → y-protocols/sync (persisted to op-log + broadcast)
  0x01 → y-protocols/awareness (broadcast only, ephemeral)

Lifecycle is bound to the item-detail page via $effect keyed on
`${item.id}:${canEdit}` — same key the <Editor> already re-mounts on,
so the Y.Doc and provider tear down in lockstep with the editor.
View-only viewers (canEdit === false) keep the legacy non-collab
editor; their read-only y-binding is deferred to TASK-1266.

Reconnect uses 1s/2s/4s/...30s exponential backoff. Sophisticated
mobile reconnect (visibility, network state) is TASK-1265.

KNOWN TEMPORARY REGRESSION: existing items with non-empty
items.content render an empty editor on first open under collab,
because the Y.Doc starts empty and TASK-1259 doesn't seed from
markdown. TASK-1261 (next in Phase 2) adds the lazy seed-after-
initial-sync path. New items + items already round-tripped through
collab are unaffected.

Drive-by lint cleanup of dead code that escaped Phase 1's
make-install-skips-lint loophole:
- gofmt -w on internal/collab/{applier,bus,manager}.go
- removed unused test/debug helpers Room.peerCount and
  Room.applierConnCount (re-add with real callers when needed)

Parent: PLAN-1248

* fix(collab): gate Editor mount on ydoc + handle applier_request + catch-up state per Codex review (round 1)

Three findings from round 1:

1) [P1] $effect constructs ydoc AFTER Editor's onMount runs, so the
   first mount on an editable item registered StarterKit history
   instead of the Collaboration extension. The {#key} excluded ydoc,
   so the editor never re-mounted when ydoc later became truthy →
   editable users got a non-collab editor while the provider connected
   to an unused Y.Doc.

   Fix: gate the editable Editor mount on `ydoc` being ready
   (`{#if !canEdit} ... {:else if ydoc} ...`). Adds at most one
   reactive tick of delay; guarantees the first mount has the binding
   registered.

2) [P1] Provider dropped non-binary WebSocket frames, but the server
   sends `applier_request` as TextMessage. With TASK-1259 minting
   active rooms, every concurrent CLI/MCP/API content PATCH would
   sit blocked for 30s waiting for an ack, then fall back to a
   direct write — and the in-memory Y.Doc would still hold stale
   state and clobber it on the next 5s flush. Silent data loss.

   Fix: parse TextMessage frames as JSON ControlMessage. On
   `applier_request`, invoke an `onApplierRequest` callback (the
   page passes `editor.commands.setContent(markdown)`) and send
   `applier_ack` on success. The ExpiresAtMillis-driven late-apply
   guard remains TASK-1262's full scope.

3) [P2] Local Y.Doc updates were silently dropped if the socket was
   closed when handleDocUpdate fired. On reconnect the dumb-relay
   server can't reconstruct missing updates from a state vector, so
   any edits made before the first open or during a disconnect
   could be lost.

   Fix: after sending syncStep1 in onOpen, also send the current
   doc state as a single update via `Y.encodeStateAsUpdate(ydoc)`.
   CRDT idempotency makes this safe on initial open (server already
   has these ops via op-log replay → sees a no-op update). Larger
   docs incur a one-time cost on each connection; TASK-1265's
   mobile-reconnect work can replace this with a buffered queue.

* fix(collab): destroy provider during rawMode + enforce ExpiresAtMillis on applier requests per Codex review (round 2)

Two findings from round 2:

1) [P1] collabKey ignored rawMode, leaving the WS provider connected
   while the user edited via RawMarkdownEditor. Raw saves bypass the
   y-binding (PATCH writes items.content directly), but the server
   sees an active room → routes the PATCH through the applier flow
   → no editor mounted → 30s timeout fallback → direct write. The
   stale Y.Doc still in memory then overwrote the raw save on the
   next 5s flush after toggling back.

   Fix: include rawMode in the collabKey derivation so toggling raw
   destroys the provider (and the in-memory Y.Doc), and toggling back
   mints a fresh pair that re-seeds from the op-log + TASK-1261's
   lazy markdown seed.

2) [P1] Provider passed expires_at_millis to the handler but never
   gated on it. A backgrounded tab that wakes after the server
   retried or fell back could still apply setContent and overwrite
   newer peer edits.

   Fix: enforce the expiry in CollabProvider — check before
   invoking the handler AND re-check before acking (handlers are
   awaited and could span the deadline). Suppress the ack if either
   gate trips; the server interprets "no ack" as "applier
   unavailable" and falls back cleanly.

* fix(collab): prune op-log on direct-write fallback + pre-mutation expiry check per Codex review (round 3)

Two findings from round 3:

1) [P1] rawMode toggle to/from rich left a stale op-log: raw saves
   wrote items.content directly while the destroyed provider's old
   op-log persisted. Toggling back minted a fresh Y.Doc that
   replayed the old log → showed pre-raw content → silently
   overwrote the raw save on the next 5s flush.

   Fix server-side: when ApplyExternalContent returns ErrNoActiveRoom
   (no peers in memory, no in-flight Y.Doc state to corrupt), prune
   the op-log alongside the direct items.content write so future
   collab sessions start from a clean slate seeded by items.content
   (TASK-1261's lazy seed). Pruning is intentionally NOT applied to
   ErrNoApplierAvailable / ErrAllAppliersTimedOut — those paths
   may have live peers whose Y.Doc state would diverge.

2) [P2] Provider's post-handler expiry check only suppressed the
   ack, not the actual setContent mutation owned by the page
   handler. An async handler that crossed the deadline could still
   write stale markdown into the Y.Doc.

   Fix: page handler now does its own pre-mutation expiry check
   inside onApplierRequest before calling setContent. Documented
   the contract on ApplierRequestHandler — handlers MUST honour
   expiresAtMillis BEFORE mutating state.

* fix(collab): prune op-log on grace-TTL applier-unavailable + suppress autosave when collab active per Codex review (round 4)

Two findings from round 4:

1) [HIGH] op-log pruning still skipped ErrNoApplierAvailable. When
   raw-mode destroys the in-tab provider, the room remains in its
   60s grace TTL with zero conns, so the next direct-write PATCH
   returns ErrNoApplierAvailable (not ErrNoActiveRoom). Stale op-log
   rows persisted; toggling back within the grace window resurrected
   pre-raw-save Y.Doc state.

   Fix: prune op-log on ErrNoApplierAvailable too — the "no live
   conns" condition makes pruning safe (no peers to corrupt).
   ErrAllAppliersTimedOut still preserves op-log because peers may
   still be alive there.

2) [HIGH] Once the WS provider is active the legacy 1.2s content
   autosave PATCH gets intercepted by the applier path
   (handleUpdateItem branch added in TASK-1252). On applier success
   input.Content is nil'd out, so UpdateItem never writes the
   markdown snapshot. The page's autosave was the only canonical
   items.content flush in this diff — search / share-page / API
   consumers would see stale content forever.

   Fix: short-circuit handleContentUpdate when collabProvider is
   set. The Y.Doc + op-log are canonical; items.content stays at
   its pre-collab snapshot until TASK-1260 introduces the proper
   5s idle flush with applier-bypass semantics. This is a known
   Phase-2-internal regression closed by the very next task in
   this run.

* fix(collab): tighten error classification + per-item lock + raw-mode flush per Codex review (round 5)

Three findings from round 5:

1) [HIGH] applier.go could return ErrAllAppliersTimedOut even when
   no applier_request was ever successfully written (a row of write
   failures followed by no remaining candidates). The handler-side
   prune skipped that case, leaving stale op-log rows even though
   no peer received the request.

   Fix: track `anyWriteSucceeded` across the attempts and return
   ErrNoApplierAvailable (which prunes) when the loop exits without
   ever putting bytes on the wire.

2) [HIGH] Race between ApplyExternalContent's no-room classification
   and the subsequent Prune/UpdateItem: a fresh Join could mint a
   room and replay the soon-to-be-pruned op-log into a new client,
   leaving it with stale Y.Doc state that overwrites the
   freshly-written items.content on the next idle flush.

   Fix: introduce per-item setup mutex on RoomManager. Join holds
   the lock across addConn + replayTo and releases it before the
   long-lived readLoop. New PruneAndApply method wraps the
   prune+direct-write in the same per-item lock and re-verifies
   "no live peers" under it (returns ErrRoomActiveDuringPrune if a
   peer slipped in, in which case the caller falls through to a
   plain direct write without pruning). Lock order: per-item lock
   > m.mu > r.mu — Join and PruneAndApply both follow it.

3) [MEDIUM] Raw-mode 1.2s debounce timer could outlive the toggle
   to rich mode: the deferred PATCH fired post-collab-mint and got
   routed through the applier path (potentially overwriting newer
   peer state).

   Fix: track the latest pending raw markdown in
   `rawPendingMarkdown`. The Rich-mode button is now an async
   onclick that awaits a `flushRawIfPending()` synchronous PATCH
   before flipping `rawMode = false` (which is what activates the
   collab provider via the collabKey derivation).

* fix(collab): evict broken applier conn + retry on prune-race + retain raw pending on PATCH failure per Codex review (round 6)

Three findings from round 6:

1) [HIGH] When applier_request write failed, the broken roomConn
   stayed in r.conns, defeating PruneAndApply's "no live peers"
   check (which then returned ErrRoomActiveDuringPrune and the
   handler skipped pruning). Net effect: the prune-safety
   classification reverted to the round-5 hazard.

   Fix: in the applier write-failure branch, force-close the conn
   and call removeConn before continuing to the next applier. Both
   are idempotent with the readLoop's natural cleanup path
   (bus.Unsubscribe, conn map delete, conn.Close all tolerate
   double-invocation).

2) [HIGH] On ErrRoomActiveDuringPrune the handler fell through to a
   plain direct-write to items.content, bypassing the now-active
   peer's applier. The peer's stale Y.Doc could still overwrite
   items.content on the next idle flush.

   Fix: surface ErrRoomActiveDuringPrune from
   applyContentViaCollabOnce so the new applyContentViaCollab
   wrapper can retry the full ApplyExternalContent flow against
   the freshly-active room. Capped at applyContentMaxRetries=3 to
   prevent runaway loops if joins keep landing during prune
   attempts. After exhaustion, returns the same sentinel — the
   handler's existing `if err == nil { input.Content = nil }`
   gate falls through to direct write, which is the correct
   degraded-mode behavior.

3) [MEDIUM] flushRawIfPending cleared rawPendingMarkdown before
   the PATCH succeeded and the Rich-mode toggle always set
   rawMode = false regardless of flush outcome. A failed flush
   could activate collab with unsaved raw edits.

   Fix: rework flushRawIfPending to return success bool, retain
   rawPendingMarkdown on PATCH failure, and gate the Rich-button
   transition on `ok`. Added a re-entrancy guard
   (rawFlushInFlight) so a rapid double-click waits for the
   in-flight flush to settle instead of issuing a duplicate PATCH.

* fix(collab): drain-loop flushRawIfPending to handle fast-typist edge per Codex review (round 7)

[P1] flushRawIfPending snapshotted rawPendingMarkdown then awaited
the PATCH; if the user typed during the await, the equality check
preserved the newer edit but the function still returned `true` and
the Rich-mode handler flipped collab on. The newly-active provider
then raced the un-flushed pending raw save — exactly the hazard
the guard is meant to close.

Fix: rework flushRawIfPending into a bounded drain loop. Each
iteration snapshots-PATCHes-clears (with the equality check). The
loop runs up to RAW_FLUSH_DRAIN_CAP=5 iterations, returning `true`
ONLY when rawPendingMarkdown is null on exit AND no PATCH failed.
A fast typist who keeps the queue non-null across the cap returns
`false`, leaving the user in raw mode (next click retries).
PATCH failure short-circuits with `false` so the toggle stays in
raw mode and the unsaved markdown is preserved for retry.

* fix(collab): atomic prune+content-write + preserve newer raw edit on stale PATCH response per Codex review (round 8)

Two findings from round 8:

1) [P1] PruneAndApply ran the op-log prune under the per-item lock
   but the items.content write happened later in the post-loop
   UpdateItem call, OUTSIDE the lock. A fresh Join landing in that
   gap could replay the now-empty op-log, mint a peer with stale
   Y.Doc state, and then overwrite the freshly-written
   items.content on the next idle flush.

   Fix: applyContentViaCollab now takes a `directWrite` callback
   that the caller (handleUpdateItem) implements as a content-only
   UpdateItem. PruneAndApply's applyFn invokes it AFTER the prune
   so both run inside the same per-item critical section. The
   trade-off is two DB round-trips when a PATCH carries content +
   other fields together (rare): the content-only update happens
   inside the lock; the rest (title, fields, status) flows through
   the post-loop UpdateItem with input.Content nil'd to suppress
   the duplicate write.

2) [P1] In flushRawIfPending's drain loop, `item = updated`
   assigned the server-side snapshot from the just-PATCHed
   markdown even when a newer raw edit had landed in the meantime.
   RawMarkdownEditor mirrors `item.content` into its textarea
   unconditionally (line 16), so the stale assignment would reset
   the textarea mid-keystroke and lose the queued edit.

   Fix: only swap in the full updated snapshot when
   `rawPendingMarkdown === markdown` (no newer edit). Otherwise
   keep our local content and adopt only the server-side metadata
   (timestamps, version, modified_by) via spread.

* fix(collab): atomic mixed PATCH + raw autosave stale guard + rich→raw seeding per Codex review (round 9)

Three findings from round 9:

1) [P1] Toggling FROM rich+collab TO raw mode seeded
   RawMarkdownEditor from items.content, which is intentionally
   stale under collab (handleContentUpdate is suppressed while the
   provider is connected; TASK-1260 closes that gap with a 5s
   flush). Saving from raw mode would overwrite the live Y.Doc
   state with a pre-collab snapshot.

   Fix: when toggling to raw with a connected provider, capture
   the editor's current Y.Doc-derived markdown via
   `editor.storage.markdown.getMarkdown()` into a one-shot
   `rawSeedMarkdown` slot and pre-populate `rawPendingMarkdown` so
   the first auto-save persists it. RawMarkdownEditor seeds from
   `rawSeedMarkdown ?? item.content`. Cleared on rich-mode toggle.

2) [P1] The regular debounced raw autosave still assigned
   `item = updated` from a stale PATCH response. Same
   stale-snapshot hazard the Round 8 fix closed in
   flushRawIfPending.

   Fix: equality-check `rawPendingMarkdown === toSave` before
   swapping in the server snapshot. On stale, keep local content
   and adopt only the server-side metadata via spread.

3) [P2] Round 8 split the items.content write (under per-item
   lock) from the rest of UpdateItem (post-loop), losing
   atomicity for mixed PATCHes (content + title) and breaking
   Store.UpdateItem's content-versioning peek at Title.

   Fix: directWrite callback now invokes the FULL UpdateItem
   inside the per-item lock. A `fullWriteHandled` flag tells the
   handler to skip the post-loop UpdateItem entirely (otherwise
   we'd duplicate the write and create two version-history rows).
   Mixed PATCHes are atomic again under the lock.

* fix(collab): clear raw seed/pending on item navigation per Codex review (round 10)

[P1] Navigating between items left rawSeedMarkdown,
rawPendingMarkdown, and the contentDebounceTimer set from the
previous item. This caused two concrete hazards:

  (a) Item B's raw editor mounted with item A's live markdown via
      `rawSeedMarkdown ?? item.content`.
  (b) Clicking Rich on item B fired flushRawIfPending which
      PATCHed A's queued markdown INTO item B (cross-item data
      bleed).

Fix: at the top of loadData(), clear contentDebounceTimer,
rawSeedMarkdown, and rawPendingMarkdown so each navigation starts
from a clean slate. The collab provider's own lifecycle is
already keyed on item.id via $effect cleanup, so it doesn't need
the same explicit reset.

* fix(collab): item-id race guard on raw PATCH responses per Codex review (round 11)

[P1] In-flight raw PATCH responses (debounced autosave AND drain
loop) could clobber a newly navigated item. Clearing
contentDebounceTimer in loadData only cancels timers that have
not fired; an awaiting fetch keeps running and its `.then` /
`.catch` would assign back to the new page's `item` state.

Fix: mirror the existing TASK-754-style race guard pattern
(already used in the SSE / sync handlers above). Capture
`reqItemId = item.id` BEFORE the PATCH, then in the response
handler bail if `!item || item.id !== reqItemId`. Applied to
both handleRawContentUpdate's setTimeout body and
flushRawIfPending's drain loop.

* fix(collab): reset saveStatus on item navigation per Codex review (round 12)

[P2] After Round 11's race guard, a stale raw PATCH response that
matched a now-different item.id was correctly discarded — but
saveStatus had already been set to 'saving' before the await. With
loadData not resetting it, the next item could mount with
saveStatus pinned at 'saving' indefinitely, which then suppressed
all SSE/sync refreshes via the `if (saveStatus === 'saving')`
guards above.

Fix: in loadData's per-item state reset, clear saveStatusTimer
and reset saveStatus to 'idle' alongside the other transient
state. Cheap, scoped, no impact on the in-flight save's eventual
discard path.
2026-05-08 20:48:57 -04:00
xarmian b4904e0db9 feat(editor): wire optional ydoc + Collaboration extension (TASK-1258) (#456)
* feat(editor): wire optional ydoc + Collaboration extension into Editor.svelte (TASK-1258)

Productionizes the Yjs binding pattern verified in the
feat/yjs-tiptap-spike branch (TASK-1245 sandbox). Editor.svelte now
accepts an optional `ydoc?: Y.Doc` prop; when set, it registers the
Tiptap Collaboration extension and disables StarterKit's undoRedo
so the y-tiptap binding takes over document state + history.

Editor.svelte changes:

- Imports: `Collaboration` from `@tiptap/extension-collaboration`,
  type-only `Y.Doc` from `yjs` (the actual constructor lives in the
  caller route — TASK-1260 wires the WS provider).
- Props: new optional `ydoc?: Y.Doc`. When undefined the editor's
  extension list and behaviour are byte-identical to today;
  every existing call site stays backward-compatible.
- Extensions:
    · StarterKit.configure now spreads `{ undoRedo: false }` IFF
      ydoc is set (Yjs owns history when collab is active; v3's
      option is `undoRedo`, the v2 `history` key was renamed).
    · `...(ydoc ? [Collaboration.configure({ document: ydoc,
      field: 'default' })] : [])` slot at the end of the extension
      array — empty when ydoc is undefined, so existing single-Doc
      usage is unaffected.

Dependencies added to web/package.json:

- `yjs@^13.6.30`
- `@tiptap/extension-collaboration@3.22.5` — EXACT pin (no caret)
  because @tiptap/core is on 3.22.5 and the latest published
  3.23.x of extension-collaboration requires a matching core. The
  CLAUDE.md note in TASK-1269 codifies the multi-package coordinated
  bump rule.
- `@tiptap/y-tiptap@^3.0.3`

Out of scope (per task spec): WS provider plumbing (TASK-1260),
lifecycle binding to item-detail page (TASK-1260), first-edit seed
from markdown (TASK-1262), CollaborationCursor extension
(TASK-1264).

Parent: PLAN-1248. Phase 1 — Backend foundation complete; Phase 2
is the next logical block to start.

* fix(editor): gate content-prop sync $effect when ydoc is active per Codex review (round 1)

P1: Editor.svelte's existing $effect that calls
editor.commands.setContent on prop-content changes (item switch,
external REST update) would route through the y-tiptap binding as a
LOCAL ProseMirror change when Collaboration is registered —
overwriting peers' Y.Doc state with stale REST markdown on every
content prop refresh.

Add an early return in the $effect when ydoc is set. Y.Doc is the
authoritative state under collab; markdown→Y.Doc seeding for the
first-edit-on-empty case lives in TASK-1262 and uses Y.Doc's own
primitives, NOT setContent.

The early return also captures `tracker.prev = content` so a future
host route that swapped ydoc on/off mid-editor wouldn't see the
non-collab branch's `prev === undefined` and accidentally skip its
first sync. In practice ydoc is set once per editor mount today,
but the cheap capture keeps the contract honest.
2026-05-08 16:46:32 -04:00
xarmian 1dabfd02ae chore(web)(deps): bump vite-plugin-svelte 6 → 7 + vite 7 → 8 (TASK-1238) (#444)
Coordinated bump of the Vite/Svelte build-tool stack:
  • @sveltejs/vite-plugin-svelte  6.2.4 → 7.1.2
  • vite                          7.3.1 → 8.0.11
  • @sveltejs/kit                 2.59.0 → 2.59.1 (patch, free)
  • @sveltejs/adapter-auto        7.0.0  → 7.0.1  (patch, free)

Smaller cascade than the deferred-bumps task body anticipated:
SvelteKit 2.59 already declared `^8.0.0` in its vite peer-dep range, so
no Kit major bump was needed. adapter-static is unaffected. svelte
itself (5.55.5) already meets the new vite-plugin-svelte v7 peer
constraint of ^5.46.4.

vite-plugin-svelte v7 integrated the inspector into the main package,
so the @sveltejs/vite-plugin-svelte-inspector subdep is gone — net 5
fewer packages in node_modules and a ~7KB smaller package-lock.json.

Vite 8 highlights:
  • Rolldown replaces Rollup as the bundler — production build dropped
    from ~12-16s to ~5.9s on this codebase
  • Internally compiled with TypeScript 6 (matches our own TS 6 bump
    from TASK-1236)
  • npm audit moderate vulnerability count: 1 → 0 (the uuid advisory
    was in a transitive that's no longer needed)

Configs reviewed:
  • vite.config.ts is minimal (just sveltekit() plugin + dev proxy);
    none of v7's removed options (vitePlugin.hot,
    vitePlugin.ignorePluginPreprocessors, api.idFilter,
    plugin.api.sveltePreprocess) are in use.
  • svelte.config.js uses vitePlugin.dynamicCompileOptions (still
    supported in v7).

Verified:
  • Clean reinstall (rm -rf node_modules package-lock.json && npm i):
    290 packages, 0 vulnerabilities
  • npm run build: succeeds in 5.91s, output passes through
    adapter-static
  • make check (golangci-lint + go test + npm run build + svelte-check):
    0 errors, same 6 pre-existing warnings
  • Manual UI verify: dashboard, item list, item detail, editor (block
    drag-handle, content edit/save), role board, share page all render
    and function correctly

Rolldown surfaced one informational warning that's PRE-EXISTING in our
code, not introduced by the bump:
  [INEFFECTIVE_DYNAMIC_IMPORT] sync.svelte.ts is both static- and
  dynamic-imported. Filed as a follow-up task — fix is out of scope
  for this PR.

Closes dependabot/npm_and_yarn/web/sveltejs/vite-plugin-svelte-7.0.0
(PR #215). Closes PLAN-1240 Tier 3 (5/5 ships).
2026-05-08 07:52:15 -04:00
xarmian 1ac3f76478 chore(web)(deps): bump typescript 5.9.3 → 6.0.3 (TASK-1236) (#442)
The compiler bump itself is clean — svelte-check (TS 5.9 baseline) and
svelte-check (TS 6.0.3) both report 0 errors against the same 714-file
codebase. Confirms TypeScript 6's stricter inference doesn't surface
new errors in our Svelte 5 / SvelteKit / TipTap stack.

`tsc --noEmit` directly (which traverses standalone .ts files differently
than svelte-check) flagged 9 errors in one file — block-drag-handle.ts
calls TipTap chained commands (setParagraph, setHeading, toggleBulletList,
toggleOrderedList, toggleTaskList, toggleCodeBlock, toggleBlockquote)
that are added to `ChainedCommands` via TypeScript module augmentation
in the respective extension packages. TS 6 stopped propagating those
augmentations to files that don't import the augmenting modules
themselves, so each consumer must opt in.

Fix: add side-effect imports of `@tiptap/starter-kit` and
`@tiptap/extension-task-list` at the top of block-drag-handle.ts. The
modules are already loaded at runtime by Editor.svelte, so this adds
nothing to the bundle — it just re-registers the type augmentations
in this file's compilation context.

Verified:
  • Manual: hover the drag handle, open the block context menu,
    "Turn into" each block type (paragraph / H1-H3 / bullet / ordered /
    task / code / quote), drag-reorder. All commands fire correctly.
  • `make check` clean (golangci-lint + go test + npm run build +
    svelte-check, 0 errors).
  • Both `tsc --noEmit` and `svelte-check` return 0 errors after fix.

Closes dependabot/npm_and_yarn/web/typescript-6.0.3 (PR #213).
2026-05-08 06:41:04 -04:00
xarmian 3ec3d6ec17 chore(web)(deps): bump marked 17.0.5 → 18.0.3 + drop @types/marked (TASK-1237) (#441)
v18.0.0 breaking changes are limited to (1) trimming trailing blank
lines from block tokens and (2) bumping the bundled-types compiler to
TypeScript 6. Our app calls `marked(content)` and overrides
`renderer.link` / `renderer.image` — neither path introspects the
intermediate token tree, and svelte-check (TS 5.9) consumes the
TS 6-emitted .d.ts cleanly.

Verified:
  • Render snapshot of representative content (headings, code blocks,
    tables, lists, task lists, blockquotes, links, images, autolinks,
    strikethrough, multi-paragraph, escaped HTML) is byte-identical
    between v17.0.5 and v18.0.3 — same 1847 bytes, zero diff lines.
  • `make check` (golangci-lint + go test + npm run build + svelte-check)
    passes with 0 errors.
  • Manual UI spot-check: item body, wiki-links, code blocks, tables,
    attachments, and the share-page route all render correctly.

Also drops @types/marked@5.0.2 — marked has shipped its own bundled
.d.ts since v9, so this devDep has been redundant and 12 majors stale.
Removing it eliminates type-resolution drift against the bundled types.

Mermaid retains its nested marked@16.4.2 — no transitive ripple. Closes
dependabot/npm_and_yarn/web/marked-18.0.2 (PR #212).
2026-05-08 06:14:52 -04:00
xarmian a1e7378d8e chore(web)(deps): bump diff 8.0.3 → 9.0.0 (TASK-1239) (#440)
v9.0.0 breaking changes are confined to patch parse/format functions
(parsePatch, formatPatch, reversePatch, StructuredPatch). The only
in-tree consumer is web/src/lib/components/versions/DiffView.svelte,
which uses diffLines + the Change type — both unchanged in v9.

ES5 support is dropped, which is irrelevant for our Vite/SvelteKit
target. Verified via runtime smoke-test that diffLines() in v9 returns
the same Change shape (value: string, added/removed: boolean) the
DiffView consumer expects.

Closes dependabot/npm_and_yarn/web/diff-9.0.0 (PR #214).
2026-05-08 05:32:56 -04:00
dependabot[bot] 06f8487d6b chore(deps)(deps): bump the npm-minor-and-patch group across 1 directory with 16 updates (#412)
Bumps the npm-minor-and-patch group with 15 updates in the /web directory:

| Package | From | To |
| --- | --- | --- |
| [@tiptap/core](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/core) | `3.20.4` | `3.22.5` |
| [@tiptap/extension-bubble-menu](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-bubble-menu) | `3.20.4` | `3.22.5` |
| [@tiptap/extension-code-block-lowlight](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-code-block-lowlight) | `3.20.4` | `3.22.5` |
| [@tiptap/extension-link](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-link) | `3.20.4` | `3.22.5` |
| [@tiptap/extension-placeholder](https://github.com/ueberdosis/tiptap/tree/HEAD/packages-deprecated/extension-placeholder) | `3.20.4` | `3.22.5` |
| [@tiptap/extension-table](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-table) | `3.20.4` | `3.22.5` |
| [@tiptap/extension-task-item](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-task-item) | `3.20.4` | `3.22.5` |
| [@tiptap/extension-task-list](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-task-list) | `3.20.4` | `3.22.5` |
| [@tiptap/starter-kit](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/starter-kit) | `3.20.4` | `3.22.5` |
| [@tiptap/suggestion](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/suggestion) | `3.20.4` | `3.22.5` |
| [dompurify](https://github.com/cure53/DOMPurify) | `3.4.1` | `3.4.2` |
| [mermaid](https://github.com/mermaid-js/mermaid) | `11.13.0` | `11.14.0` |
| [@sveltejs/kit](https://github.com/sveltejs/kit/tree/HEAD/packages/kit) | `2.57.1` | `2.59.0` |
| [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) | `5.54.1` | `5.55.5` |
| [svelte-check](https://github.com/sveltejs/language-tools) | `4.4.5` | `4.4.7` |



Updates `@tiptap/core` from 3.20.4 to 3.22.5
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/core/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.22.5/packages/core)

Updates `@tiptap/extension-bubble-menu` from 3.20.4 to 3.22.5
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-bubble-menu/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.22.5/packages/extension-bubble-menu)

Updates `@tiptap/extension-code-block-lowlight` from 3.20.4 to 3.22.5
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-code-block-lowlight/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.22.5/packages/extension-code-block-lowlight)

Updates `@tiptap/extension-link` from 3.20.4 to 3.22.5
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-link/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.22.5/packages/extension-link)

Updates `@tiptap/extension-placeholder` from 3.20.4 to 3.22.5
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages-deprecated/extension-placeholder/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.22.5/packages-deprecated/extension-placeholder)

Updates `@tiptap/extension-table` from 3.20.4 to 3.22.5
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-table/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.22.5/packages/extension-table)

Updates `@tiptap/extension-task-item` from 3.20.4 to 3.22.5
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.22.5/packages/extension-task-item)

Updates `@tiptap/extension-task-list` from 3.20.4 to 3.22.5
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.22.5/packages/extension-task-list)

Updates `@tiptap/pm` from 3.20.4 to 3.22.5
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/pm/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.22.5/packages/pm)

Updates `@tiptap/starter-kit` from 3.20.4 to 3.22.5
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/starter-kit/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.22.5/packages/starter-kit)

Updates `@tiptap/suggestion` from 3.20.4 to 3.22.5
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/main/packages/suggestion/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.22.5/packages/suggestion)

Updates `dompurify` from 3.4.1 to 3.4.2
- [Release notes](https://github.com/cure53/DOMPurify/releases)
- [Commits](https://github.com/cure53/DOMPurify/compare/3.4.1...3.4.2)

Updates `mermaid` from 11.13.0 to 11.14.0
- [Release notes](https://github.com/mermaid-js/mermaid/releases)
- [Commits](https://github.com/mermaid-js/mermaid/compare/mermaid@11.13.0...mermaid@11.14.0)

Updates `@sveltejs/kit` from 2.57.1 to 2.59.0
- [Release notes](https://github.com/sveltejs/kit/releases)
- [Changelog](https://github.com/sveltejs/kit/blob/main/packages/kit/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/kit/commits/@sveltejs/kit@2.59.0/packages/kit)

Updates `svelte` from 5.54.1 to 5.55.5
- [Release notes](https://github.com/sveltejs/svelte/releases)
- [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.55.5/packages/svelte)

Updates `svelte-check` from 4.4.5 to 4.4.7
- [Release notes](https://github.com/sveltejs/language-tools/releases)
- [Commits](https://github.com/sveltejs/language-tools/compare/svelte-check@4.4.5...svelte-check@4.4.7)

---
updated-dependencies:
- dependency-name: "@tiptap/core"
  dependency-version: 3.22.5
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-bubble-menu"
  dependency-version: 3.22.5
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-code-block-lowlight"
  dependency-version: 3.22.5
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-link"
  dependency-version: 3.22.5
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-placeholder"
  dependency-version: 3.22.5
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-table"
  dependency-version: 3.22.5
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-task-item"
  dependency-version: 3.22.5
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/extension-task-list"
  dependency-version: 3.22.5
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/pm"
  dependency-version: 3.22.5
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/starter-kit"
  dependency-version: 3.22.5
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@tiptap/suggestion"
  dependency-version: 3.22.5
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: dompurify
  dependency-version: 3.4.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
- dependency-name: mermaid
  dependency-version: 11.14.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: "@sveltejs/kit"
  dependency-version: 2.59.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: svelte
  dependency-version: 5.55.5
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor-and-patch
- dependency-name: svelte-check
  dependency-version: 4.4.7
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor-and-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-07 23:44:48 -04:00
xarmian c8601a2031 test(e2e): Playwright smoke test infrastructure + 2 dashboard tests (TASK-689) (#225)
* test(e2e): Playwright smoke test infrastructure + 2 dashboard tests (TASK-689)

Option A of TASK-689: land the test infrastructure and a minimal smoke
test on both mobile and desktop viewports. Broader flow coverage (board
view drag, item detail, comments, mobile hamburger, BottomSheet
regression guard) is tracked as TASK-733.

Infrastructure
--------------
- web/playwright.config.ts: two projects (desktop-chromium, mobile-
  chromium via Pixel 7), reporter list+html, trace/video/screenshot
  retained on failure, webServer that wipes + recreates the data dir
  then runs the pad binary. Paths anchored to the config file's
  directory so runs are cwd-invariant.
- web/e2e/global-setup.ts: bootstraps admin via POST /auth/bootstrap,
  logs in, creates the e2e workspace, mints a user-scoped API token,
  and persists the token + resolved admin username to fixture.json.
- web/e2e/fixtures.ts: extends base test so every BrowserContext
  automatically gets Authorization: Bearer <token>. Uses a token
  rather than a session cookie because sessions are User-Agent bound
  in middleware_auth.go and a node-minted session would be rejected
  by a Chromium UA.

Tests
-----
- web/e2e/dashboard.spec.ts: a logged-in user lands on the seeded
  workspace, no login form is rendered, and the workspace name
  appears on the page. Runs in both project viewports.

CI
--
- New `e2e` job in .github/workflows/ci.yml: builds web UI + binary,
  installs Playwright chromium with OS deps, runs the suite, and
  uploads the HTML report as an artifact on failure. Timeout capped
  at 10 minutes (suite itself runs in ~4s today).

Local run (in mcr.microsoft.com/playwright:v1.59.1-noble): 2 passed
in 4.1s.

Parent: PLAN-644.
Follow-up: TASK-733 for broader flow coverage (Option B in the
original ship plan).

* fix(e2e): persist server-returned workspace slug instead of the constant (TASK-689)

Addresses Codex P2 on PR #225. When Playwright's `reuseExistingServer:
true` (local dev), a re-run of globalSetup hits `POST /api/v1/workspaces`
against a DB that already has `e2e`. The server uniquifies the slug
(`e2e` → `e2e-2` → …) and returns the uniquified value, but the old
code wrote `WORKSPACE_SLUG` (the constant) to fixture.json. Tests
then navigated to /e2e-admin/e2e — which might still exist from a
previous run with stale state — instead of /e2e-admin/e2e-2, missing
regressions in freshly-seeded content.

Fix: read `slug` back from the workspace-create response and use that
when writing fixture.json. Local re-runs now always point at the
workspace this run actually created.

Parent: PLAN-644.

* fix(e2e): cross-platform webServer bootstrap via Node wrapper (TASK-689)

Addresses Codex P2 on PR #225: `rm -rf && mkdir -p && pad server start`
in webServer.command is POSIX-only. Windows contributors on cmd.exe or
PowerShell can't run `npm run test:e2e` at all — the e2e suite becomes
Linux/macOS-only, defeating the "CI parity" goal.

Fix: extract the wipe-and-exec logic into web/e2e/run-pad.mjs. Node's
fs.rmSync / mkdirSync / child_process.spawn are uniform across
platforms, and the wrapper forwards SIGTERM/SIGINT so Playwright's
teardown still cleanly kills the child on suite exit.

Local re-run in mcr.microsoft.com/playwright:v1.59.1-noble: 2 passed.

Parent: PLAN-644.
2026-04-22 22:35:08 -04:00
xarmian 9909d7b7c6 fix(web): resolve 9 svelte-check errors blocking CI (TASK-674) (#200)
svelte-check was reporting 9 errors on main, blocking the CI gate.
All fixed:

 1. EditCollectionModal: make `open` prop bindable ($bindable()). This
    unblocks `bind:open={editCollectionOpen}` in two call sites:
    - routes/[username]/[workspace]/[collection]/+page.svelte:1153
    - routes/[username]/[workspace]/[collection]/[slug]/+page.svelte:1101

 2. [slug]/+page.svelte: narrow `item` inside callback-bound expressions:
    - Line 684 (.find closure) now uses a local @const for the slug
      rather than re-reading item.parent_collection_slug inside the
      callback (TS cannot narrow across the closure).
    - Line 744 star toggle handler now short-circuits on item presence,
      so both `item.slug` and `item.id` are safe.

 3. auth/cli/[code]/+page.svelte: guard against `$page.params.code`
    being `undefined` in both onMount and handleApprove.

 4. console/settings/+page.svelte: add @types/qrcode dev dependency
    so the dynamic `import('qrcode')` calls have proper typings.

`cd web && npx svelte-check` now reports 0 errors (warnings were
out of scope — addressed separately in TASK-685). `go build/vet/test`
and `cd web && npm run build` are green.

Parent: PLAN-644.
2026-04-22 14:59:21 -04:00
xarmian 3544e42de1 chore(web): npm audit fix + CI audit gate (TASK-654) (#181)
web/package-lock.json had 11 advisories (1 low, 1 moderate, 9 high)
before this PR: @sveltejs/kit (redirect/body-size), cookie<0.7.0,
dompurify<=3.3.3, vite 7.0.0-7.3.1, lodash-es, picomatch, chevrotain,
etc. All required a mix of `npm audit fix` and targeted upgrades.

Changes:
- web/package.json: upgrade @sveltejs/kit to ^2.57.1. Add `overrides`
  map pinning cookie to ^0.7.2 (upstream @sveltejs/kit@2.57.1 still
  ships cookie@0.6.0 which is LOW severity but trivially fixable).
- web/package-lock.json: regenerated via `npm install` + `npm audit fix`.
- .github/workflows/ci.yml: add `npm audit --audit-level=high --omit=dev`
  step after `npm ci`. Fails the build on any HIGH+ advisory in
  production deps; dev-only issues stay informational so CI isn't held
  hostage by unfixable upstream chevrotain/vite dev-server advisories.

`npm audit --audit-level=high --omit=dev` now reports 0 vulnerabilities
locally. `npm run build` and `go test ./...` remain green.

Parent: PLAN-643 (OSS Security Hardening).
2026-04-21 21:37:16 -04:00
xarmian ba01d95111 feat: add web UI for TOTP 2FA setup in user settings (#109)
* feat: add web UI for TOTP 2FA setup in user settings

Add a Two-Factor Authentication section to the console settings page
so users can enable/disable TOTP 2FA from the browser. The backend
API already existed (PR #77); this wires up the frontend.

- Add 2FA section to console settings with enable/disable flows
- Enable flow: QR code + manual secret + verification code input
- Recovery codes displayed with copy/download after setup
- Disable flow: password confirmation modal
- Add totp.setup/verify/disable methods to API client
- Add TOTP types (TOTPSetupResponse, TOTPVerifyResponse, etc.)
- Add totp_enabled to User type and /auth/me response
- Add qrcode npm dependency for rendering otpauth:// URIs

Closes TASK-402

* fix: address codex review — separate QR rendering from setup, use clipboard util

- Separate QR code rendering from TOTP setup API call so a QR failure
  doesn't abort setup when manual entry is still available
- Use existing copyToClipboard utility with legacy fallback instead of
  raw navigator.clipboard.writeText
2026-04-14 09:04:37 -04:00
xarmian 94d35509a4 feat: share links with hardened security, anonymous access, and analytics (#88)
* feat: share links with hashed tokens and /s/{token} route

Add share_links and share_link_views tables with CRUD API and
anonymous resolution route (TASK-421).

Data model:
- share_links: token_hash (SHA-256), target_type/id, permission,
  password_hash, expires_at, max_views, require_auth, view tracking
- share_link_views: per-view records with fingerprint/user tracking

Token security:
- 192-bit entropy (crypto/rand), URL-safe base64 encoding
- SHA-256 hashed at rest, raw token returned only once on creation
- Generic 404 for invalid tokens (no info leakage)
- /api/v1/s/ exempt from auth middleware for anonymous access

API endpoints:
- POST /items/{slug}/share-links — create item share link
- POST /collections/{coll}/share-links — create collection share link
- GET /items/{slug}/share-links — list share links for item
- GET /collections/{coll}/share-links — list for collection
- DELETE /share-links/{id} — revoke share link
- GET /s/{token} — resolve share link, return shared content

D8: Anonymous users are ALWAYS read-only. View count and unique
viewers tracked on each resolution.

* feat: anonymous share page + share link management UI

Add minimal-chrome share link viewer page and share link CRUD in
the share dialog (TASK-422 + TASK-425).

Share page (/s/{token}):
- New SvelteKit route at /s/[token] for anonymous viewing
- Renders item (title, fields, markdown content) or collection
  (name, item list) with no app chrome (no sidebar/topbar)
- Handles require_auth links with "Sign in to view" prompt
- Root layout bypasses auth checks for /s/ routes
- "Powered by Pad" footer

Share dialog updates:
- "Share links" section below existing grants
- Create/list/revoke share links for items and collections
- Copy-to-clipboard for share URLs
- Newly created links highlighted with "only shown once" notice
- View count and auth-required badges

API client:
- ShareLink type added
- shareLinks.* methods for CRUD
- share.get(token) for anonymous resolution

* feat: share link constraints + view analytics

Add password protection, expiry, max views, and view history
endpoints for share links (TASK-423 + TASK-424).

Constraints (TASK-423):
- CreateShareLink accepts ShareLinkOptions: password, expires_at,
  max_views, require_auth, restrict_to_email
- Password hashed with bcrypt, verified on /s/{token} resolution
- Password-protected links return {require_password: true} prompt
- Expiry and max_views already validated by ValidateShareLink

Analytics (TASK-424):
- GET /share-links/{id}/views returns view history with fingerprint,
  user ID, and timestamp
- Response includes total_views, unique_viewers, last_viewed_at
- View history stored per-view in share_link_views table

* fix: harden share links — XSS, access control, data leakage, and UX gaps

- Sanitize rendered markdown with DOMPurify before {@html} injection (XSS)
- Force require_auth=true when restrict_to_email is set (access bypass)
- Reject malformed non-empty JSON bodies with 400 instead of failing open
- Return public DTOs on share endpoints to prevent leaking internal IDs,
  creator info, assignees, schemas, and other sensitive fields
- Enforce max_views atomically via conditional UPDATE to prevent races
- Fix collection share rendering: read items from top-level response key
  and map ref/status fields correctly
- Add password prompt UI and X-Share-Password header support so
  password-protected links can actually be unlocked by the frontend

* fix: follow-up hardening for share links

- Sanitize catch fallback in rendered markdown (XSS edge case if marked throws)
- Remove query-string password fallback; accept only X-Share-Password header
  to avoid leaking passwords in logs, browser history, and referrers
- Return 500 on ListItems DB failure instead of swallowing as empty collection
- Normalize restrict_to_email with ToLower/TrimSpace on create and compare
- Fix malformed JSON check for chunked bodies (ContentLength == -1)
  by checking for io.EOF instead of ContentLength > 0
- Remove internal share_link.id from public DTO responses
- Use clientIP(r) helper for consistent fingerprinting instead of raw
  X-Forwarded-For which is spoofable and includes port in RemoteAddr
- Distinguish DB errors from not-found in share link delete handler

* fix: final hardening pass for share links

- Move auth/email gate before password check to prevent unauthenticated
  callers from probing passwords and burning bcrypt CPU
- Wrap view recording (counter increment, unique-viewer accounting, view
  insert) in a single transaction so a failed insert rolls back the
  consumed view count instead of silently losing it
- Add X-Share-Password to CORS AllowedHeaders so cross-origin
  deployments can send the custom header without preflight rejection
- Validate expires_at (RFC3339) and max_views (> 0) on share link
  creation; return 400 for invalid constraints instead of creating
  immediately-unusable links
- Cap view-history endpoint limit to 1000 to prevent unbounded queries
2026-04-11 16:40:18 -04:00
xarmian a8059c5a0f Implement 8 ideas from the idea board
Quick wins:
- IDEA-31: URL autolink + link popover in editor (SafeLink with data-href
  prevents mobile navigation, popover shows open/edit/remove actions)
- IDEA-36: Focus title on new item creation, Enter moves to editor
- IDEA-38: Add `pad link` CLI command to link directory to existing workspace
- IDEA-34: Show checklist progress bar on item cards (parses markdown checkboxes)
- IDEA-28: Workspace rename (already existed in settings)

Medium effort:
- IDEA-33: Drag-and-drop task reordering in Phase documents via svelte-dnd-action
- IDEA-26: Archive collections (frontend wiring — backend already supported soft delete)
- IDEA-29: Archive workspaces with danger zone confirmation in settings
- IDEA-37: Raw markdown editor toggle + inline Mermaid diagram rendering
  (NodeView with ignoreMutation to prevent ProseMirror re-parse loops)
2026-03-27 23:41:20 +00:00
xarmian 81579847c6 Initial release
Pad — project management for developers and AI agents.
Single Go binary with embedded SvelteKit web UI, SQLite storage,
CLI, and Claude Code /pad skill integration.

https://getpad.dev
2026-03-26 01:52:36 +00:00