Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Historical release entries from **0.1.0 through 0.5.27** are preserved verbatim
- Restored a visible `:focus-visible` indicator on the editable textbox, mapped it to `CanvasText` in forced-colors mode, and suppresses that interactive focus chrome under `@media print`; dependency-locked Chromium, Firefox, and WebKit acceptance exercises the packed stylesheet on the real `role="textbox"` surface.

### Security
- Patched an editor dependency vulnerability while preserving the existing formatting, collaboration-presence, safe-link, and exact document-restore behavior.
- Raised workspace-wide transitive development-tool overrides for `fast-uri`, `nanoid`, and `postcss` to patched minimums, keeping the lockfile audit clean without changing runtime package authority.
- Normalized isolated package-verifier temporary roots before containment checks on macOS.

Expand Down
4 changes: 2 additions & 2 deletions docs/atomic-envelope-restore.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Restore performs these operations in order:
4. validate the Inkspan envelope schema identifier and version;
5. detach and deeply freeze the document JSON;
6. reconstruct and recursively check the complete document against the active ProseMirror schema;
7. dispatch one TipTap `setContent(..., false)` replacement;
7. dispatch one TipTap `setContent(..., { emitUpdate: false })` replacement;
8. verify that the resulting active document is structurally equal to the prepared document.

Any failure before step 7 leaves the current document unchanged. A ProseMirror transaction filter may reject a schema-valid replacement at step 7; Inkspan then throws `DocumentEnvelopeRestoreError` and never reports the operation as successful. Inkspan's built-in safe-link and inline-image policies reject unsafe replacements without changing the document.
Expand Down Expand Up @@ -87,7 +87,7 @@ The helper adds no database, credential, environment-variable, transport, provid

## Primary references

- [TipTap v2 `setContent` command](https://v2.tiptap.dev/docs/editor/api/commands/content/set-content)
- [TipTap `setContent` command](https://tiptap.dev/docs/editor/api/commands/content/set-content)
- [ProseMirror `PluginSpec.filterTransaction`](https://prosemirror.net/docs/ref/#state.PluginSpec.filterTransaction)
- [ProseMirror `Schema.nodeFromJSON()` and `Node.check()`](https://prosemirror.net/docs/ref/#model.Schema.nodeFromJSON)
- [RFC 8259: The JavaScript Object Notation (JSON) Data Interchange Format](https://www.rfc-editor.org/rfc/rfc8259)
Expand Down
4 changes: 2 additions & 2 deletions docs/collaboration.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,8 @@ editor or coupling its core package to organization infrastructure.

## Primary references

- [TipTap v2 Collaboration](https://v2.tiptap.dev/docs/editor/extensions/functionality/collaboration)
- [TipTap v2 Collaboration Cursor](https://v2.tiptap.dev/docs/editor/extensions/functionality/collaboration-cursor)
- [TipTap Collaboration](https://tiptap.dev/docs/editor/extensions/functionality/collaboration)
- [TipTap Collaboration Caret](https://tiptap.dev/docs/editor/extensions/functionality/collaboration-caret)
- [Yjs Awareness and Presence](https://docs.yjs.dev/getting-started/adding-awareness)
- [WAI-ARIA `status` role](https://www.w3.org/TR/wai-aria-1.2/#status)
- [WCAG relative luminance definition](https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html)
22 changes: 14 additions & 8 deletions docs/doctoring/tiptap-v2-prosemirror-paste-adapter.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# TipTap v2 ProseMirror paste adapter
# TipTap ProseMirror paste adapter

## Decision

Inkspan registers SafeClipboard through a TipTap v2 extension whose
Inkspan registers SafeClipboard through a TipTap v3 extension whose
`addProseMirrorPlugins` hook returns a ProseMirror `Plugin` with a
`transformPastedHTML` editor property. That property receives rich clipboard
HTML before ProseMirror parses it into an editor document.
Expand All @@ -16,7 +16,7 @@ retention, migration, and model-use policy.

## Root cause

Inkspan locks `@tiptap/core 2.27.2`. The first implementation placed a
Inkspan previously locked `@tiptap/core 2.27.2`. The first implementation placed a
`transformPastedHTML` field directly on a TipTap extension configuration and its
unit tests invoked that field manually. That appeared consistent with current
TipTap documentation, but it did not prove registration in the installed v2
Expand All @@ -28,7 +28,13 @@ plugins. It does not collect an arbitrary direct extension
`transformPastedHTML` field into editor props. The locked source is authoritative
for the installed runtime, while current mutable documentation is useful design
context but not evidence that an API existed in this historical dependency
version.
version. Inkspan now locks the coherent TipTap 3.30.4 package family; the same
real-pipeline tests prove that the adapter remains registered after migration.
The published `@tiptap/react` 3.30.4 ESM and CommonJS declarations qualify four
`Editor` references through an internal namespace that does not export that
type. The lockfile applies one bounded patch to those declarations, and the
packed strict-TypeScript consumer check prevents the broken public types from
shipping.

The practical result was a false assurance gap: direct sanitizer unit tests were
green, but the real `editor.view` paste pipeline had no SafeClipboard
Expand Down Expand Up @@ -57,7 +63,7 @@ result is not completion evidence for a later exact head.

## Ordering and residual host boundary

TipTap v2.27.2 sorts extension priorities from higher to lower when resolving
TipTap v3.30.4 sorts extension priorities from higher to lower when resolving
extensions and again when assembling ProseMirror plugins. ProseMirror checks
plugin-provided editor properties in plugin order. SafeClipboard therefore uses
a deliberately low priority so ordinary host transforms run first and the
Expand Down Expand Up @@ -107,6 +113,6 @@ TipTap GmbH. (n.d.). *Extension API*. TipTap Editor Docs. Retrieved August 7,
2026, from
https://tiptap.dev/docs/editor/extensions/custom-extensions/create-new/extension

TipTap GmbH. (2025). *ExtensionManager.ts (Version 2.27.2)* [Source code].
GitHub. Retrieved August 7, 2026, from
https://github.com/ueberdosis/tiptap/blob/%40tiptap/core%402.27.2/packages/core/src/ExtensionManager.ts
TipTap GmbH. (2026). *ExtensionManager.ts (Version 3.30.4)* [Source code].
GitHub. Retrieved September 4, 2026, from
https://github.com/ueberdosis/tiptap/blob/%40tiptap/core%403.30.4/packages/core/src/ExtensionManager.ts
4 changes: 2 additions & 2 deletions docs/imperative-envelope-persistence.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ Object and JSON-text inputs use `validateDocumentEnvelope()` and
`...Bytes` methods. Validation is non-mutating. Restore completes duplicate
object-name detection, resource checks, schema/version routing, hostile-value
detachment, and full active ProseMirror schema reconstruction before one
`setContent(..., false)` mutation. A failure leaves the current document
`setContent(..., { emitUpdate: false })` mutation. A failure leaves the current document
unchanged.

Successful restore suppresses normal change callbacks because loading an
Expand Down Expand Up @@ -216,5 +216,5 @@ metadata rather than extending the strict envelope with ad hoc fields.
- [W3C Web Cryptography API Recommendation](https://www.w3.org/TR/2017/REC-WebCryptoAPI-20170126/)
- [WHATWG Encoding Standard: UTF-8](https://encoding.spec.whatwg.org/#utf-8)
- [TipTap persistence guidance](https://tiptap.dev/docs/editor/core-concepts/persistence)
- [TipTap v2 `setContent`](https://v2.tiptap.dev/docs/editor/api/commands/content/set-content)
- [TipTap `setContent`](https://tiptap.dev/docs/editor/api/commands/content/set-content)
- [ProseMirror `Node.fromJSON`](https://prosemirror.net/docs/ref/#model.Node^fromJSON)
2 changes: 1 addition & 1 deletion docs/papers/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,4 @@ GFM table and strikethrough extensions.
as base64, which this module relies on so figures travel with the document and
remain readable by a downstream LLM. <https://datatracker.ietf.org/doc/html/rfc2397>
- **ProseMirror** (Marijn Haverbeke) — the MIT-licensed document model and
editing toolkit that TipTap v2 is built on. <https://prosemirror.net/>
editing toolkit that TipTap is built on. <https://prosemirror.net/>
23 changes: 10 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -125,19 +125,16 @@
}
},
"dependencies": {
"@tiptap/core": "^2.27.2",
"@tiptap/extension-collaboration": "^2.27.2",
"@tiptap/extension-collaboration-cursor": "^2.27.2",
"@tiptap/extension-image": "^2.11.5",
"@tiptap/extension-link": "^2.11.5",
"@tiptap/extension-placeholder": "^2.11.5",
"@tiptap/extension-table": "^2.11.5",
"@tiptap/extension-table-cell": "^2.11.5",
"@tiptap/extension-table-header": "^2.11.5",
"@tiptap/extension-table-row": "^2.11.5",
"@tiptap/pm": "^2.11.5",
"@tiptap/react": "^2.11.5",
"@tiptap/starter-kit": "^2.11.5",
"@tiptap/core": "3.30.4",
"@tiptap/extension-collaboration": "3.30.4",
"@tiptap/extension-collaboration-caret": "3.30.4",
"@tiptap/extension-image": "3.30.4",
"@tiptap/extension-link": "3.30.4",
"@tiptap/extension-table": "3.30.4",
"@tiptap/extensions": "3.30.4",
"@tiptap/pm": "3.30.4",
"@tiptap/react": "3.30.4",
"@tiptap/starter-kit": "3.30.4",
"marked": "^15.0.6",
"turndown": "^7.2.0",
"turndown-plugin-gfm": "^1.0.2",
Expand Down
70 changes: 70 additions & 0 deletions patches/@tiptap__react@3.30.4.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
diff --git a/dist/index.d.cts b/dist/index.d.cts
index 331a7a57ec202e68d67e2c692c4b1bedb928c6cc..5fc696b31890d469d3b1cd07bc2f860f5cfc782e 100644
--- a/dist/index.d.cts
+++ b/dist/index.d.cts
@@ -402,7 +402,7 @@ declare function ReactWidgetRenderer<P extends Record<string, any> = object>(com
*/
type TiptapContextType = {
/** The Tiptap editor instance. */
- editor: index_d_exports.Editor;
+ editor: Editor;
};
/**
* React context that stores the current editor instance.
@@ -459,18 +459,18 @@ declare const useTiptap: () => TiptapContextType;
* }
* ```
*/
-declare function useTiptapState<TSelectorResult>(selector: (context: EditorStateSnapshot<index_d_exports.Editor>) => TSelectorResult, equalityFn?: (a: TSelectorResult, b: TSelectorResult | null) => boolean): TSelectorResult;
+declare function useTiptapState<TSelectorResult>(selector: (context: EditorStateSnapshot<Editor>) => TSelectorResult, equalityFn?: (a: TSelectorResult, b: TSelectorResult | null) => boolean): TSelectorResult;
type TiptapWrapperEditorInstanceProps = {
/**
* The editor instance to provide to child components.
* Use `useEditor()` to create this instance.
*/
- editor: index_d_exports.Editor;
+ editor: Editor;
} | {
/**
* @deprecated Use `editor` instead. Will be removed in the next major version.
*/
- instance: index_d_exports.Editor;
+ instance: Editor;
};
/**
* Props for the `Tiptap` root/provider component.
diff --git a/dist/index.d.ts b/dist/index.d.ts
index 1ea46992a97b34957359edd7e70186dfa060fe87..1aeb997fa11b4dfb57604596dded3f54f2c3e270 100644
--- a/dist/index.d.ts
+++ b/dist/index.d.ts
@@ -402,7 +402,7 @@ declare function ReactWidgetRenderer<P extends Record<string, any> = object>(com
*/
type TiptapContextType = {
/** The Tiptap editor instance. */
- editor: index_d_exports.Editor;
+ editor: Editor;
};
/**
* React context that stores the current editor instance.
@@ -459,18 +459,18 @@ declare const useTiptap: () => TiptapContextType;
* }
* ```
*/
-declare function useTiptapState<TSelectorResult>(selector: (context: EditorStateSnapshot<index_d_exports.Editor>) => TSelectorResult, equalityFn?: (a: TSelectorResult, b: TSelectorResult | null) => boolean): TSelectorResult;
+declare function useTiptapState<TSelectorResult>(selector: (context: EditorStateSnapshot<Editor>) => TSelectorResult, equalityFn?: (a: TSelectorResult, b: TSelectorResult | null) => boolean): TSelectorResult;
type TiptapWrapperEditorInstanceProps = {
/**
* The editor instance to provide to child components.
* Use `useEditor()` to create this instance.
*/
- editor: index_d_exports.Editor;
+ editor: Editor;
} | {
/**
* @deprecated Use `editor` instead. Will be removed in the next major version.
*/
- instance: index_d_exports.Editor;
+ instance: Editor;
};
/**
* Props for the `Tiptap` root/provider component.
Loading
Loading