snapshot of https://github.com/ueberdosis/tiptap.git (main, 2026-09-15; blobs > 131072B omitted)
Some checks failed
Verify / Install node dependencies (push) Failing after 2s
Verify / Build packages (push) Has been skipped
Verify / Check linting & formatting (push) Has been skipped
Verify / Run unit tests (push) Has been skipped
Verify / Run e2e tests (shard 1/4) (push) Has been skipped
Verify / Run e2e tests (shard 2/4) (push) Has been skipped
Verify / Run e2e tests (shard 3/4) (push) Has been skipped
Verify / Run e2e tests (shard 4/4) (push) Has been skipped
Publish / Resolve publish config (push) Failing after 2s
Publish / Send notification to Slack (push) Has been skipped
Publish / Send failure notification to Slack (push) Failing after 8s
Verify / Merge e2e reports (push) Has been skipped
Publish / Build (push) Has been skipped
Publish / Bump Versions OR Release to Registry (push) Has been skipped
Some checks failed
Verify / Install node dependencies (push) Failing after 2s
Verify / Build packages (push) Has been skipped
Verify / Check linting & formatting (push) Has been skipped
Verify / Run unit tests (push) Has been skipped
Verify / Run e2e tests (shard 1/4) (push) Has been skipped
Verify / Run e2e tests (shard 2/4) (push) Has been skipped
Verify / Run e2e tests (shard 3/4) (push) Has been skipped
Verify / Run e2e tests (shard 4/4) (push) Has been skipped
Publish / Resolve publish config (push) Failing after 2s
Publish / Send notification to Slack (push) Has been skipped
Publish / Send failure notification to Slack (push) Failing after 8s
Verify / Merge e2e reports (push) Has been skipped
Publish / Build (push) Has been skipped
Publish / Bump Versions OR Release to Registry (push) Has been skipped
This commit is contained in:
6
.changeset/2026-08-20-ordered-list-markdown-indent.md
Normal file
6
.changeset/2026-08-20-ordered-list-markdown-indent.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@tiptap/core': patch
|
||||
'@tiptap/extension-list': patch
|
||||
---
|
||||
|
||||
Nested lists exported to Markdown now keep their hierarchy when the file is read back by other Markdown tools.
|
||||
5
.changeset/2026-08-23-youtube-missing-src.md
Normal file
5
.changeset/2026-08-23-youtube-missing-src.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@tiptap/extension-youtube': patch
|
||||
---
|
||||
|
||||
Pasting a YouTube iframe without a `src` attribute no longer crashes the editor. The embed is kept without a source.
|
||||
7
.changeset/2026-08-25-remove-deprecated-packages.md
Normal file
7
.changeset/2026-08-25-remove-deprecated-packages.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'@tiptap/starter-kit': major
|
||||
---
|
||||
|
||||
StarterKit no longer installs the deprecated `@tiptap/extension-dropcursor`, `@tiptap/extension-gapcursor`, `@tiptap/extension-list-item`, and `@tiptap/extension-list-keymap` packages. We will not publish further updates to these packages. StarterKit still includes their extensions, so you do not need to change your configuration.
|
||||
|
||||
If you import these extensions directly, switch to the new packages: `Dropcursor` and `Gapcursor` from `@tiptap/extensions`, and `ListItem` and `ListKeymap` from `@tiptap/extension-list`.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@tiptap/markdown': patch
|
||||
---
|
||||
|
||||
Fix Markdown serialization of whitespace-only marked text.
|
||||
6
.changeset/2026-08-26-markdown-attribute-redos.md
Normal file
6
.changeset/2026-08-26-markdown-attribute-redos.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@tiptap/core': patch
|
||||
'@tiptap/extension-mention': patch
|
||||
---
|
||||
|
||||
Fix a denial-of-service risk where crafted block or inline Markdown attributes could consume excessive CPU and block the browser or server event loop.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@tiptap/extension-collaboration-caret': patch
|
||||
---
|
||||
|
||||
Fixed a bug which allowed potentially unsafe color values being sent by other clients. Those unsafe colors received from collaboration users are now ignored.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@tiptap/react': patch
|
||||
---
|
||||
|
||||
Reduce overhead in React node views on documents with many nodes.
|
||||
5
.changeset/2026-08-31-bump-prosemirror-view.md
Normal file
5
.changeset/2026-08-31-bump-prosemirror-view.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@tiptap/pm': patch
|
||||
---
|
||||
|
||||
Bump `prosemirror-view` to `^1.42.3`, which fixes an XSS vulnerability where pasting crafted HTML could run arbitrary JavaScript (GHSA-c8x8-7fp4-3x9w).
|
||||
5
.changeset/2026-08-31-react-nodeview-selection-state.md
Normal file
5
.changeset/2026-08-31-react-nodeview-selection-state.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@tiptap/react': minor
|
||||
---
|
||||
|
||||
Align `selected` with ProseMirror node selections by default, expose text selections through `selectionInside`, and keep `selectedOnTextSelection` compatible.
|
||||
5
.changeset/2026-09-04-react-editor-namespace-ts2694.md
Normal file
5
.changeset/2026-09-04-react-editor-namespace-ts2694.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@tiptap/react': patch
|
||||
---
|
||||
|
||||
Fix a TypeScript error (TS2694) in the shipped type declarations when `skipLibCheck` is turned off.
|
||||
8
.changeset/README.md
Normal file
8
.changeset/README.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# Changesets
|
||||
|
||||
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
|
||||
with multi-package repos, or single-package repos to help you version and publish your code. You can
|
||||
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
|
||||
|
||||
We have a quick list of common questions to get you started engaging with this project in
|
||||
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
|
||||
14
.changeset/config.json
Normal file
14
.changeset/config.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"$schema": "https://unpkg.com/@changesets/config@3.0.1/schema.json",
|
||||
"changelog": "@changesets/cli/changelog",
|
||||
"commit": false,
|
||||
"fixed": [["@tiptap/*", "!@tiptap/ai-toolkit"]],
|
||||
"linked": [],
|
||||
"access": "public",
|
||||
"baseBranch": "main",
|
||||
"updateInternalDependencies": "patch",
|
||||
"ignore": [],
|
||||
"___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": {
|
||||
"onlyUpdatePeerDependentsWhenOutOfRange": true
|
||||
}
|
||||
}
|
||||
5
.changeset/fix-ios-ignore-mutation-contentdom.md
Normal file
5
.changeset/fix-ios-ignore-mutation-contentdom.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@tiptap/core': patch
|
||||
---
|
||||
|
||||
Fix freezes in framework-based node views on iOS and Android
|
||||
5
.changeset/fix-merge-attributes-prototype.md
Normal file
5
.changeset/fix-merge-attributes-prototype.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@tiptap/core': patch
|
||||
---
|
||||
|
||||
Prevent untrusted HTML attributes from changing an object's prototype when merged with `mergeAttributes`.
|
||||
68
.changeset/pre.json
Normal file
68
.changeset/pre.json
Normal file
@@ -0,0 +1,68 @@
|
||||
{
|
||||
"mode": "pre",
|
||||
"tag": "next",
|
||||
"initialVersions": {
|
||||
"tiptap-demos": "3.0.3",
|
||||
"@tiptap/ai-toolkit": "0.4.0",
|
||||
"@tiptap/core": "3.30.3",
|
||||
"@tiptap/extension-audio": "3.30.3",
|
||||
"@tiptap/extension-blockquote": "3.30.3",
|
||||
"@tiptap/extension-bold": "3.30.3",
|
||||
"@tiptap/extension-bubble-menu": "3.30.3",
|
||||
"@tiptap/extension-bullet-list": "3.30.3",
|
||||
"@tiptap/extension-code": "3.30.3",
|
||||
"@tiptap/extension-code-block": "3.30.3",
|
||||
"@tiptap/extension-code-block-lowlight": "3.30.3",
|
||||
"@tiptap/extension-collaboration": "3.30.3",
|
||||
"@tiptap/extension-collaboration-caret": "3.30.3",
|
||||
"@tiptap/extension-color": "3.30.3",
|
||||
"@tiptap/extension-details": "3.30.3",
|
||||
"@tiptap/extension-document": "3.30.3",
|
||||
"@tiptap/extension-drag-handle": "3.30.3",
|
||||
"@tiptap/extension-drag-handle-react": "3.30.3",
|
||||
"@tiptap/extension-drag-handle-vue": "3.30.3",
|
||||
"@tiptap/extension-emoji": "3.30.3",
|
||||
"@tiptap/extension-file-handler": "3.30.3",
|
||||
"@tiptap/extension-find-and-replace": "3.30.3",
|
||||
"@tiptap/extension-floating-menu": "3.30.3",
|
||||
"@tiptap/extension-font-family": "3.30.3",
|
||||
"@tiptap/extension-hard-break": "3.30.3",
|
||||
"@tiptap/extension-heading": "3.30.3",
|
||||
"@tiptap/extension-highlight": "3.30.3",
|
||||
"@tiptap/extension-horizontal-rule": "3.30.3",
|
||||
"@tiptap/extension-image": "3.30.3",
|
||||
"@tiptap/extension-invisible-characters": "3.30.3",
|
||||
"@tiptap/extension-italic": "3.30.3",
|
||||
"@tiptap/extension-link": "3.30.3",
|
||||
"@tiptap/extension-list": "3.30.3",
|
||||
"@tiptap/extension-mathematics": "3.30.3",
|
||||
"@tiptap/extension-mention": "3.30.3",
|
||||
"@tiptap/extension-node-range": "3.30.3",
|
||||
"@tiptap/extension-ordered-list": "3.30.3",
|
||||
"@tiptap/extension-paragraph": "3.30.3",
|
||||
"@tiptap/extension-ruby-text": "3.30.3",
|
||||
"@tiptap/extension-strike": "3.30.3",
|
||||
"@tiptap/extension-subscript": "3.30.3",
|
||||
"@tiptap/extension-superscript": "3.30.3",
|
||||
"@tiptap/extension-table": "3.30.3",
|
||||
"@tiptap/extension-table-of-contents": "3.30.3",
|
||||
"@tiptap/extension-text": "3.30.3",
|
||||
"@tiptap/extension-text-align": "3.30.3",
|
||||
"@tiptap/extension-text-style": "3.30.3",
|
||||
"@tiptap/extension-twitch": "3.30.3",
|
||||
"@tiptap/extension-typography": "3.30.3",
|
||||
"@tiptap/extension-underline": "3.30.3",
|
||||
"@tiptap/extension-unique-id": "3.30.3",
|
||||
"@tiptap/extension-youtube": "3.30.3",
|
||||
"@tiptap/extensions": "3.30.3",
|
||||
"@tiptap/html": "3.30.3",
|
||||
"@tiptap/markdown": "3.30.3",
|
||||
"@tiptap/pm": "3.30.3",
|
||||
"@tiptap/react": "3.30.3",
|
||||
"@tiptap/starter-kit": "3.30.3",
|
||||
"@tiptap/static-renderer": "3.30.3",
|
||||
"@tiptap/suggestion": "3.30.3",
|
||||
"@tiptap/vue": "3.30.3"
|
||||
},
|
||||
"changesets": []
|
||||
}
|
||||
18
.changeset/remove-vue-2.md
Normal file
18
.changeset/remove-vue-2.md
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
'@tiptap/vue': major
|
||||
'@tiptap/extension-drag-handle-vue': major
|
||||
---
|
||||
|
||||
Vue 2 support is removed. `@tiptap/vue-3` is now `@tiptap/vue`, and `@tiptap/extension-drag-handle-vue-3` is now `@tiptap/extension-drag-handle-vue`.
|
||||
|
||||
Update imports and dependencies:
|
||||
|
||||
```diff
|
||||
- import { EditorContent } from '@tiptap/vue-3'
|
||||
+ import { EditorContent } from '@tiptap/vue'
|
||||
|
||||
- import DragHandle from '@tiptap/extension-drag-handle-vue-3'
|
||||
+ import DragHandle from '@tiptap/extension-drag-handle-vue'
|
||||
```
|
||||
|
||||
Vue 2 users must migrate to Vue 3 before using the new packages. The planned Codemod Registry package `@tiptap/codemod-v3-to-v4` updates package imports and dependencies.
|
||||
123
.coderabbit.yaml
Normal file
123
.coderabbit.yaml
Normal file
@@ -0,0 +1,123 @@
|
||||
inheritance: true
|
||||
|
||||
reviews:
|
||||
request_changes_workflow: true
|
||||
high_level_summary_instructions:
|
||||
Short and simple, keep it cohesive, no complex wording, assume everyone including the
|
||||
PR reviewer is new to the project or to code in general - simple english so even non-english native speakers
|
||||
understand everything just fine.
|
||||
high_level_summary_in_walkthrough: true
|
||||
in_progress_fortune: false
|
||||
slop_detection:
|
||||
label: slop
|
||||
suggested_labels: true
|
||||
auto_apply_labels: true
|
||||
labeling_instructions:
|
||||
- label: 'area: ai'
|
||||
instructions: Apply this label if the PR changes AI features, AI integrations, prompts, or AI-related dependencies.
|
||||
- label: 'area: ci'
|
||||
instructions: Apply this label if the PR changes CI workflows, build pipelines, checks, or release automation.
|
||||
- label: 'area: cloud'
|
||||
instructions: Apply this label if the PR changes Tiptap Cloud features, integrations, or configuration.
|
||||
- label: 'area: convert'
|
||||
instructions: Apply this label if the PR changes Tiptap Convert or document conversion behavior.
|
||||
- label: 'area: core'
|
||||
instructions: Apply this label if the PR changes Tiptap Core APIs, commands, extensions, or editor behavior.
|
||||
- label: 'area: docs'
|
||||
instructions: Apply this label if the PR changes documentation, examples, guides, or API reference content.
|
||||
- label: 'area: editor'
|
||||
instructions: Apply this label if the PR changes the Tiptap Editor or its user-facing editing behavior.
|
||||
- label: 'area: markdown'
|
||||
instructions: Apply this label if the PR changes Markdown parsing, serialization, or Markdown-related extensions.
|
||||
- label: 'area: pages'
|
||||
instructions: Apply this label if the PR changes Tiptap Pages, pagination, or Word-like UI behavior.
|
||||
- label: 'area: react'
|
||||
instructions: Apply this label if the PR changes the Tiptap React implementation, hooks, or React examples.
|
||||
- label: 'area: ui-components'
|
||||
instructions: Apply this label if the PR changes Tiptap UI Components, their styles, or component behavior.
|
||||
- label: 'area: vue'
|
||||
instructions: Apply this label if the PR changes the Tiptap Vue implementation, composables, or Vue examples.
|
||||
- label: 'complexity: easy'
|
||||
instructions: Apply this label if the PR makes a clearly scoped change in one isolated component or package.
|
||||
- label: 'complexity: expert'
|
||||
instructions: Apply this label if the PR changes fundamental architecture, core APIs across extensions, or collaboration behavior.
|
||||
- label: 'complexity: hard'
|
||||
instructions: Apply this label if the PR changes ProseMirror internals, transaction handling, or node view rendering.
|
||||
- label: 'complexity: medium'
|
||||
instructions: Apply this label if the PR changes multiple components or the ProseMirror/Tiptap integration with a clear implementation path.
|
||||
- label: 'complexity: trivial'
|
||||
instructions: Apply this label if the PR is an obvious change under 10 lines, such as a documentation typo or missing type check.
|
||||
- label: good first issue
|
||||
instructions: Apply this label if the PR is a small, self-contained change that is easy for a first-time contributor to understand.
|
||||
- label: 'impact: critical'
|
||||
instructions: Apply this label if the PR fixes data loss, a security vulnerability, broken core functionality, or a problem affecting nearly all users of a feature.
|
||||
- label: 'impact: high'
|
||||
instructions: Apply this label if the PR fixes behavior affecting a large portion of users or a common workflow.
|
||||
- label: 'impact: low'
|
||||
instructions: Apply this label if the PR fixes an obscure edge case, a specific browser condition, or a rarely used feature.
|
||||
- label: 'impact: medium'
|
||||
instructions: Apply this label if the PR fixes a common feature usage or a workflow shared by a group of users.
|
||||
- label: 'status: needs-info'
|
||||
instructions: Apply this label if the PR lacks the context needed to understand or validate the changes.
|
||||
- label: 'status: needs-reproduction'
|
||||
instructions: Apply this label if the PR fixes a bug without a reproduction or regression test for the reported behavior.
|
||||
auto_review:
|
||||
ignore_usernames:
|
||||
- github-actions[bot]
|
||||
|
||||
issue_enrichment:
|
||||
auto_enrich:
|
||||
enabled: true
|
||||
labeling:
|
||||
auto_apply_labels: true
|
||||
labeling_instructions:
|
||||
- label: 'area: ai'
|
||||
instructions: Add this label if an item is related to AI features
|
||||
- label: 'area: ci'
|
||||
instructions: Add this label if an item is related to the CI
|
||||
- label: 'area: cloud'
|
||||
instructions: Add this label if an item is related to Tiptap Cloud
|
||||
- label: 'area: convert'
|
||||
instructions: Apply this label if the item is related to Tiptap Convert / Conversion
|
||||
- label: 'area: core'
|
||||
instructions: Apply this label if an item is related to the Tiptap Core
|
||||
- label: 'area: docs'
|
||||
instructions: Apply this label if an item is related to documentation
|
||||
- label: 'area: editor'
|
||||
instructions: Add this label if an item is related to the Tiptap Editor
|
||||
- label: 'area: markdown'
|
||||
instructions: Apply this if the item relates to Markdown Parsing or Serialization
|
||||
- label: 'area: pages'
|
||||
instructions: Apply this label if an issue is related to Tiptap Pages / Pagination (Word-like UI)
|
||||
- label: 'area: react'
|
||||
instructions: Apply this label if the item is related to the Tiptap React implementation
|
||||
- label: 'area: ui-components'
|
||||
instructions: Apply this label if the item is related to Tiptap UI Components
|
||||
- label: 'area: vue'
|
||||
instructions: Apply this label if an item is related to the Tiptap Vue implementation
|
||||
- label: 'complexity: easy'
|
||||
instructions: 'clearly described, affects one isolated component, fix localized to a single package'
|
||||
- label: 'complexity: expert'
|
||||
instructions: 'fundamental architecture conflict, core API changes affecting all extensions, or concurrent/collaboration behavior'
|
||||
- label: 'complexity: hard'
|
||||
instructions: 'requires deep understanding of ProseMirror internals, transaction handling, or node view rendering; no obvious solution'
|
||||
- label: 'complexity: medium'
|
||||
instructions: 'touches multiple components or requires understanding ProseMirror/Tiptap interaction, but solution path is clear'
|
||||
- label: 'complexity: trivial'
|
||||
instructions: 'typo in docs, missing type check, obvious fix in <10 lines'
|
||||
- label: good first issue
|
||||
instructions:
|
||||
Apply this label if the issue is a good ticket to pick up as a first-time contributor or beginner at
|
||||
ProseMirror or Tiptap.
|
||||
- label: 'impact: critical'
|
||||
instructions: 'data loss, security vulnerability, completely broken core functionality, or affects nearly everyone using a specific feature'
|
||||
- label: 'impact: high'
|
||||
instructions: 'affects a large portion of users, blocks a common use case, or causes noticeable UX problem many will run into'
|
||||
- label: 'impact: low'
|
||||
instructions: 'obscure edge case, specific browser under specific conditions, or feature request very few would use'
|
||||
- label: 'impact: medium'
|
||||
instructions: 'affects a common usage of a feature (e.g. specific extension combination) or handful of users with similar workflows'
|
||||
- label: 'status: needs-info'
|
||||
instructions: Apply this label if the item needs more info to become actionable.
|
||||
- label: 'status: needs-reproduction'
|
||||
instructions: Add this label to an item if it needs more reproduction to become actionable.
|
||||
6
.codex/environments/environment.toml
Normal file
6
.codex/environments/environment.toml
Normal file
@@ -0,0 +1,6 @@
|
||||
# THIS IS AUTOGENERATED. DO NOT EDIT MANUALLY
|
||||
version = 1
|
||||
name = "tiptap"
|
||||
|
||||
[setup]
|
||||
script = "pnpm install --frozen-lockfile"
|
||||
12
.editorconfig
Normal file
12
.editorconfig
Normal file
@@ -0,0 +1,12 @@
|
||||
# EditorConfig is awesome: http://EditorConfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
insert_final_newline = true
|
||||
max_line_length = 100
|
||||
trim_trailing_whitespace = true
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
6
.fallowrc.json
Normal file
6
.fallowrc.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/fallow-rs/fallow/main/schema.json",
|
||||
"ignorePatterns": ["**/*.spec.ts"],
|
||||
"ignoreExports": [{ "file": "**/*", "exports": ["default"] }],
|
||||
"dynamicallyLoaded": ["demos/src/**/index.*"]
|
||||
}
|
||||
5
.github/CODEOWNERS
vendored
Normal file
5
.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# Repository-wide ownership
|
||||
* @ueberdosis/editor-maintainers
|
||||
|
||||
# LICENSE
|
||||
LICENSE.md @philipisik @ueberdosis/editor-maintainers
|
||||
51
.github/DISCUSSION_TEMPLATE/community-extensions.yml
vendored
Normal file
51
.github/DISCUSSION_TEMPLATE/community-extensions.yml
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
title: 'Community Extension: '
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Hey! Thanks for your time and effort to create a new community extension! Please make sure to fill out the form below.
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Description
|
||||
description: Please describe how your extension works and what it does.
|
||||
placeholder: 'My extension does …'
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: installation
|
||||
attributes:
|
||||
label: Installation
|
||||
description: Please describe how users can install your extension.
|
||||
placeholder: 'npm install …'
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: usage
|
||||
attributes:
|
||||
label: Usage
|
||||
description: Please describe how users can use your extension in their editor.
|
||||
placeholder: 'To use my extension you have to …'
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: type
|
||||
attributes:
|
||||
label: Type
|
||||
description: Please select the type of this extension.
|
||||
options:
|
||||
- 'Node'
|
||||
- 'Mark'
|
||||
- 'Prosemirror plugin'
|
||||
- 'Package or Kit'
|
||||
- 'Other'
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: other
|
||||
attributes:
|
||||
label: Other
|
||||
description: Feel free to add any other information about your extension.
|
||||
placeholder: 'I hope you like …'
|
||||
validations:
|
||||
required: false
|
||||
36
.github/DISCUSSION_TEMPLATE/feature-requests.yml
vendored
Normal file
36
.github/DISCUSSION_TEMPLATE/feature-requests.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
title: 'Feature Request: '
|
||||
labels:
|
||||
- 'Type: Feature Request'
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for your time to create a new feature request! Please make sure to fill out the form below.
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Description
|
||||
description: Please describe the feature you would like to see in Tiptap.
|
||||
placeholder: 'I wish there was an extension for …'
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: use-case
|
||||
attributes:
|
||||
label: Use Case
|
||||
description: Please describe the use case for this feature.
|
||||
placeholder: 'I want to use this feature for …'
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: type
|
||||
attributes:
|
||||
label: Type
|
||||
description: Please select the type of this feature.
|
||||
options:
|
||||
- 'New extension'
|
||||
- 'New feature'
|
||||
- 'New Tiptap API'
|
||||
- 'Other'
|
||||
validations:
|
||||
required: true
|
||||
52
.github/DISCUSSION_TEMPLATE/showcase.yml
vendored
Normal file
52
.github/DISCUSSION_TEMPLATE/showcase.yml
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
title: 'Community Extension: '
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Hey! Thanks for using Tiptap in your project. We hope you had a great experience. Please take a moment to share your project with us. We would love to see what you built with Tiptap.
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Description
|
||||
description: Please describe what your project is about
|
||||
placeholder: 'My project is about …'
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: url
|
||||
attributes:
|
||||
label: URL
|
||||
description: If possible share the URL of your project.
|
||||
placeholder: 'https://example.com'
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: about
|
||||
attributes:
|
||||
label: About
|
||||
description: Feel free to talk about how you used Tiptap in your project, what you liked about it, what you didn't like about it, and what you would like to see in the future.
|
||||
placeholder: 'If used Tiptap to …'
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: type
|
||||
attributes:
|
||||
label: Type
|
||||
description: Please select the type of your project.
|
||||
options:
|
||||
- 'Chat Application'
|
||||
- 'Commenting Application'
|
||||
- 'Content Management System'
|
||||
- 'Document Editor'
|
||||
- 'Document Editor with Collaboration'
|
||||
- 'Other'
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: other
|
||||
attributes:
|
||||
label: Other
|
||||
description: Feel free to add any other information about your project.
|
||||
placeholder: 'I hope you like …'
|
||||
validations:
|
||||
required: false
|
||||
81
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
81
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
name: Bug Report
|
||||
description: Found a bug in the editor core or one of the extensions? Report it here to help us improve.
|
||||
|
||||
labels: ['status: triage']
|
||||
type: 'Bug'
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Please use simple, human-written English. Keep your answers short and clear.
|
||||
Small code examples that show the problem are welcome. Do not include a code fix or a complete implementation in an issue. If you already have a fix, please open a pull request instead.
|
||||
- type: input
|
||||
id: packages
|
||||
attributes:
|
||||
label: Affected Packages
|
||||
description: List the Tiptap packages involved in the problem.
|
||||
placeholder: core, extension-mention, react
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: Tiptap Version
|
||||
description: Which Tiptap version or package versions are affected?
|
||||
placeholder: 3.0.0
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: browser
|
||||
attributes:
|
||||
label: Browser Used
|
||||
description: Select the browser where you saw the problem.
|
||||
options:
|
||||
- Chrome
|
||||
- Firefox
|
||||
- Safari
|
||||
- Edge
|
||||
- Other
|
||||
- Not browser-specific
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: body
|
||||
attributes:
|
||||
label: What happened?
|
||||
description: Describe what happens, when it happens, and why it is a problem. Use short sentences and simple English. Do not write a wall of text, use complicated language, or include a complete code fix.
|
||||
placeholder: The issue happens when...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: expectation
|
||||
attributes:
|
||||
label: Expected Behavior
|
||||
description: Describe what you expected to happen.
|
||||
placeholder: I expected...
|
||||
validations:
|
||||
required: true
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
A small reproducible example is the best way to help us investigate the issue. Please add one if you can.
|
||||
|
||||
CodeSandbox templates:
|
||||
* JavaScript: [JS Template](https://codesandbox.io/s/tiptap-js-fv1lyo)
|
||||
* React: [React Template](https://codesandbox.io/s/tiptap-react-qidlsv)
|
||||
* Vue 3: [Vue 3 Template](https://codesandbox.io/p/sandbox/tiptap-vue-3-ci7q9h)
|
||||
- type: input
|
||||
id: sandbox
|
||||
attributes:
|
||||
label: Reproducible Example URL (Optional)
|
||||
description: Link a CodeSandbox, StackBlitz, GitHub repository, or similar example if you have one.
|
||||
placeholder: https://codesandbox.io/s/example
|
||||
- type: textarea
|
||||
id: context
|
||||
attributes:
|
||||
label: Additional Context (Optional)
|
||||
description: Add useful screenshots, videos, or other short details. Avoid long explanations.
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: 'Thank you for helping us improve our open-source projects by reporting this issue!'
|
||||
81
.github/ISSUE_TEMPLATE/bug_report_pro.yml
vendored
Normal file
81
.github/ISSUE_TEMPLATE/bug_report_pro.yml
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
name: Bug Report (Tiptap Pro)
|
||||
description: If you've encountered a bug with Tiptap Pro features, please report it here.
|
||||
|
||||
labels: ['tier: pro', 'status: triage']
|
||||
type: 'Bug'
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Please use simple, human-written English. Keep your answers short and clear.
|
||||
Small code examples that show the problem are welcome. Do not include a code fix or a complete implementation in an issue. If you already have a fix, please open a pull request instead.
|
||||
- type: input
|
||||
id: packages
|
||||
attributes:
|
||||
label: Affected Packages
|
||||
description: List the Tiptap Pro packages involved in the problem.
|
||||
placeholder: pro-extension, pro-collaboration
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: Tiptap Version
|
||||
description: Which Tiptap or Tiptap Pro version or package versions are affected?
|
||||
placeholder: 3.0.0
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: browser
|
||||
attributes:
|
||||
label: Browser Used
|
||||
description: Select the browser where you saw the problem.
|
||||
options:
|
||||
- Chrome
|
||||
- Firefox
|
||||
- Safari
|
||||
- Edge
|
||||
- Other
|
||||
- Not browser-specific
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: body
|
||||
attributes:
|
||||
label: What happened?
|
||||
description: Describe what happens, when it happens, and why it is a problem. Use short sentences and simple English. Do not write a wall of text, use complicated language, or include a complete code fix.
|
||||
placeholder: The issue happens when...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: expectation
|
||||
attributes:
|
||||
label: Expected Behavior
|
||||
description: Describe what you expected to happen.
|
||||
placeholder: I expected...
|
||||
validations:
|
||||
required: true
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
A small reproducible example is the best way to help us investigate the issue. Please add one if you can.
|
||||
|
||||
CodeSandbox templates:
|
||||
- JavaScript: [Template](https://codesandbox.io/s/tiptap-js-fv1lyo)
|
||||
- React: [Template](https://codesandbox.io/s/tiptap-react-qidlsv)
|
||||
- Vue 3: [Template](https://codesandbox.io/p/sandbox/tiptap-vue-3-ci7q9h)
|
||||
- type: input
|
||||
id: sandbox
|
||||
attributes:
|
||||
label: Reproducible Example URL (Optional)
|
||||
description: Link a CodeSandbox, StackBlitz, GitHub repository, or similar example if you have one.
|
||||
placeholder: https://codesandbox.io/s/example
|
||||
- type: textarea
|
||||
id: context
|
||||
attributes:
|
||||
label: Additional Context (Optional)
|
||||
description: Add useful screenshots, videos, or other short details. Avoid long explanations.
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: 'Thank you for contributing to Tiptap Pro by reporting this issue!'
|
||||
17
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
17
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: New Feature Request
|
||||
url: https://github.com/ueberdosis/tiptap/discussions/new?category=feature-requests
|
||||
about: Interested in proposing a new feature for Tiptap? Submit your feature request here.
|
||||
- name: Help & Support
|
||||
url: https://github.com/ueberdosis/tiptap/discussions/new?category=questions-help
|
||||
about: Require assistance or have inquiries about using Tiptap? Ask your questions here.
|
||||
- name: Join our Discord
|
||||
url: https://discord.gg/WtJ49jGshW
|
||||
about: Interested in engaging with the Tiptap community? Join our Discord server.
|
||||
- name: Present your project
|
||||
url: https://github.com/ueberdosis/tiptap/discussions/new?category=showcase
|
||||
about: Developed something impressive using Tiptap? Share your project with the community here.
|
||||
- name: Present your Tiptap extensions
|
||||
url: https://github.com/ueberdosis/tiptap/discussions/new?category=community-extensions
|
||||
about: Created a Tiptap extension? Showcase your work to the community here.
|
||||
45
.github/ISSUE_TEMPLATE/documentation.yml
vendored
Normal file
45
.github/ISSUE_TEMPLATE/documentation.yml
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
name: Documentation feedback
|
||||
description: Share what we need to explain better.
|
||||
title: '[Documentation]: '
|
||||
labels:
|
||||
- 'Type: Documentation'
|
||||
- 'Category: Open Source'
|
||||
- 'Status: New'
|
||||
body:
|
||||
- type: input
|
||||
id: url
|
||||
attributes:
|
||||
label: What’s the URL to the page you’re sending feedback for?
|
||||
placeholder: https://tiptap.dev/example
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: part-of-the-documentation
|
||||
attributes:
|
||||
label: What part of the documentation needs improvement?
|
||||
placeholder: 'I’ve read the following page of the documentation …'
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: good-parts
|
||||
attributes:
|
||||
label: What is helpful about that part?
|
||||
placeholder: 'I think this part is really good: …'
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: bad-parts
|
||||
attributes:
|
||||
label: What is hard to understand, missing or misleading?
|
||||
placeholder: 'But you really need to improve …'
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: context
|
||||
attributes:
|
||||
label: Anything to add? (optional)
|
||||
description: 'Add any other context or screenshots here.'
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to send us feedback!
|
||||
9
.github/actions/restore-dependencies/action.yml
vendored
Normal file
9
.github/actions/restore-dependencies/action.yml
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
name: Restore dependencies
|
||||
description: Install dependencies from the cached package manager data
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Restore dependencies
|
||||
shell: bash
|
||||
run: vp install --offline --frozen-lockfile -- --strict-peer-dependencies
|
||||
20
.github/actions/setup-env/action.yml
vendored
Normal file
20
.github/actions/setup-env/action.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
name: Setup Vite+ environment
|
||||
description: Install Vite+ and set up Node.js and dependency caching
|
||||
|
||||
inputs:
|
||||
node-version:
|
||||
description: Node.js version
|
||||
required: true
|
||||
cache:
|
||||
description: Whether to cache package manager data
|
||||
default: 'false'
|
||||
required: false
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- uses: voidzero-dev/setup-vp@v1.18.0
|
||||
with:
|
||||
node-version: ${{ inputs.node-version }}
|
||||
cache: ${{ inputs.cache == 'true' && 'true' || 'false' }}
|
||||
run-install: false
|
||||
27
.github/actions/setup-playwright/action.yml
vendored
Normal file
27
.github/actions/setup-playwright/action.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
name: Setup Playwright
|
||||
description: Cache and install Playwright browsers with system dependencies
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Get Playwright version
|
||||
id: playwright-version
|
||||
shell: bash
|
||||
run: echo "version=$(node -p 'require("@playwright/test/package.json").version')" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Cache Playwright browsers
|
||||
id: playwright-cache
|
||||
uses: actions/cache@v6
|
||||
with:
|
||||
path: ~/.cache/ms-playwright
|
||||
key: playwright-${{ runner.os }}-${{ steps.playwright-version.outputs.version }}
|
||||
|
||||
- name: Install Playwright browsers
|
||||
if: steps.playwright-cache.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
run: vp exec playwright install --with-deps chromium
|
||||
|
||||
- name: Install Playwright system deps
|
||||
if: steps.playwright-cache.outputs.cache-hit == 'true'
|
||||
shell: bash
|
||||
run: vp exec playwright install-deps chromium
|
||||
BIN
.github/assets/cover.png
vendored
Normal file
BIN
.github/assets/cover.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.9 KiB |
13
.github/dependabot.yml
vendored
Normal file
13
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
# Dependabot creates pull requests to keep your dependencies secure and up-to-date.
|
||||
# Documentation: https://docs.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: 'github-actions'
|
||||
directory: '/'
|
||||
open-pull-requests-limit: 10
|
||||
schedule:
|
||||
interval: 'weekly'
|
||||
day: 'monday'
|
||||
reviewers:
|
||||
- 'bdbch'
|
||||
43
.github/instructions/PR.instructions.md
vendored
Normal file
43
.github/instructions/PR.instructions.md
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
---
|
||||
applyTo: '**'
|
||||
---
|
||||
|
||||
When asked to write a pull request description, use the following template:
|
||||
|
||||
```
|
||||
## Changes Overview
|
||||
|
||||
<!-- Briefly describe your changes. -->
|
||||
|
||||
## Implementation Approach
|
||||
|
||||
<!-- Describe your approach to implementing these changes. Keep it concise. -->
|
||||
|
||||
## Testing Done
|
||||
|
||||
<!-- Explain how you tested these changes. Link to test scenarios or specs if relevant. -->
|
||||
|
||||
## Verification Steps
|
||||
|
||||
<!-- Describe steps reviewers can take to verify the functionality of your changes. -->
|
||||
|
||||
## Additional Notes
|
||||
|
||||
<!-- Add any other notes or screenshots about the PR here. -->
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] I have created a [changeset](https://github.com/changesets/changesets) for this PR if necessary.
|
||||
- [ ] My changes do not break the library.
|
||||
- [ ] I have added tests where applicable.
|
||||
- [ ] I have followed the project guidelines.
|
||||
- [ ] I have fixed any lint issues.
|
||||
|
||||
## Related Issues
|
||||
|
||||
<!-- Link any related issues here -->
|
||||
```
|
||||
|
||||
When generating the pull request description, ensure it is clear, concise, and follows the provided template. Focus on the key aspects of the changes made, how they were implemented, and how they can be verified.
|
||||
|
||||
Make it as minimal as possible to convey the necessary information effectively without causing to much noise and making it hard to read.
|
||||
9
.github/instructions/changeset.instructions.md
vendored
Normal file
9
.github/instructions/changeset.instructions.md
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
applyTo: '**'
|
||||
---
|
||||
|
||||
When a user asks for a changeset to be generated, follow the following rules:
|
||||
|
||||
Create a good changeset file for the changes in the diff. Don't include non-frontfacing changes, as the changeset file will be used for the changelog. Our users don't care about deep logic that they'll not interact with so we only want to generate changelog entries for front-facing/user-facing changes and API changes our users will need to know about.
|
||||
|
||||
Make sure that the changeset file stays minimal and short but includes important information that may be important for our users to understand what actually changed.
|
||||
261
.github/instructions/tiptap.instructions.md
vendored
Normal file
261
.github/instructions/tiptap.instructions.md
vendored
Normal file
@@ -0,0 +1,261 @@
|
||||
---
|
||||
applyTo: '**'
|
||||
---
|
||||
|
||||
# Tiptap
|
||||
|
||||
This document explains how to work on the Tiptap monorepo in VS Code. It covers repo layout, local dev, linting and formatting, tests, docs, and release workflow. It is written to be friendly for both humans and AI coding assistants.
|
||||
|
||||
---
|
||||
|
||||
## What is Tiptap
|
||||
|
||||
Tiptap is a headless rich text editor toolkit built on ProseMirror. It ships a small Core and many opt-in Extensions so you can compose exactly the editor you need for React, Vue, or vanilla apps. The project is optimized for user experience and developer experience. APIs are predictable, behavior is testable, and everything should be documented with JSDoc and runnable examples so we can generate API docs automatically.
|
||||
|
||||
Key points for AI assistants:
|
||||
|
||||
- Treat Tiptap as a collection of focused packages that together form an editor system.
|
||||
- Do not assume a single framework. Many packages are framework agnostic, with separate bindings for React and Vue.
|
||||
- Favor small pure utilities and deterministic code. Side effects should be explicit.
|
||||
|
||||
---
|
||||
|
||||
## Repository layout
|
||||
|
||||
```
|
||||
.
|
||||
├─ packages/ # Core and all first-party extensions
|
||||
│ ├─ core/ # Editor core (@tiptap/core)
|
||||
│ ├─ extension-*/ # Individual extensions
|
||||
│ ├─ pm/ # ProseMirror related internals and helpers
|
||||
│ └─ ... # Shared utilities, framework bindings, etc.
|
||||
├─ demos/ # Vite app for live examples and colocated e2e specs
|
||||
│ ├─ src/
|
||||
│ │ ├─ react/ # React demos
|
||||
│ │ └─ vue/ # Vue demos
|
||||
│ └─ test/ # Playwright helpers (getEditor, setEditorContent, ...)
|
||||
├─ .changeset/ # Changesets for versioning and changelogs
|
||||
└─ .github/ # Workflows and docs like this file
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- All packages we publish or use live under `packages/*`.
|
||||
- The `demos/` folder contains a Vite app. It automatically discovers and parses React and Vue demos so they appear in the UI without manual wiring.
|
||||
- Playwright e2e specs live alongside their demos as `demos/src/**/index.spec.ts`. `playwright.config.ts` auto-starts the Vite dev server on `http://127.0.0.1:4080` — no need to launch it manually.
|
||||
|
||||
## NPM scripts
|
||||
|
||||
Scripts defined at the repo root. Run them with `vp run <script>` (or `pnpm run <script>`):
|
||||
|
||||
- `vp run dev` - start the demos on port 3000
|
||||
- `vp run build` - build all packages via Vite+ (`vp run -r build`)
|
||||
- `vp run check` - run format check + lint
|
||||
- `vp run check:fix` - run format:fix + lint:fix
|
||||
- `vp run format` - run oxfmt formatter check
|
||||
- `vp run format:fix` - run oxfmt formatter
|
||||
- `vp run lint` - run oxlint checks
|
||||
- `vp run lint:fix` - run oxlint with auto-fix
|
||||
- `vp run lint:staged` - run Vite+ staged checks on staged files
|
||||
- `vp run test:e2e` - run Playwright e2e tests headlessly in Chromium
|
||||
- `vp run test:e2e:firefox` - same, in Firefox
|
||||
- `vp run test:e2e:all` - same, in both browsers
|
||||
- `vp run test:e2e:open` - run Playwright in UI mode (Chromium tests)
|
||||
- `vp run test:e2e:open:firefox` - UI mode, Firefox tests
|
||||
- `vp run test:e2e:open:all` - UI mode, both browsers selectable
|
||||
- `vp run test:e2e:report` - open the HTML report from the last run
|
||||
- `vp run test:unit` - run Vitest unit tests
|
||||
- `vp run test` - build then run all tests
|
||||
- `vp run serve` - build and serve the demos on port 3000
|
||||
- `vp run publish` - build and publish with Changesets
|
||||
- `vp run reset` - remove caches, build artifacts, and reinstall deps
|
||||
|
||||
Scripts are cached by Vite Task (`run.cache.scripts` is enabled in `vite.config.mts`). Re-running an unchanged script replays its output instead of executing. Persistent scripts like `dev` never exit, so they are never cached. Clear the cache with `vp cache clean`.
|
||||
|
||||
---
|
||||
|
||||
## Linting & formatting
|
||||
|
||||
- Lint and format settings are in `vite.config.mts`.
|
||||
- Vite+ hooks (`vp staged`) run automatically on commits.
|
||||
|
||||
Use `vp run lint` / `vp run format`, or the equivalent `pnpm run` commands.
|
||||
|
||||
Run manually:
|
||||
|
||||
```bash
|
||||
vp run lint
|
||||
vp run lint:fix
|
||||
vp run format
|
||||
vp run format:fix
|
||||
vp run check
|
||||
vp run check:fix
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Demos
|
||||
|
||||
- Demos are a Vite app in `demos/`.
|
||||
- React and Vue examples live in `demos/react` and `demos/vue`. They are automatically parsed into the app.
|
||||
- Start in dev mode:
|
||||
|
||||
```bash
|
||||
vp run dev
|
||||
```
|
||||
|
||||
- Build static output and serve locally:
|
||||
|
||||
```bash
|
||||
vp run serve
|
||||
```
|
||||
|
||||
When adding a demo, keep it small and self-contained, with imports from published package names (`@tiptap/...`).
|
||||
|
||||
---
|
||||
|
||||
## Testing
|
||||
|
||||
Two layers:
|
||||
|
||||
- **Unit tests** with Vitest (happy-dom). Place file-specific tests next to the file they cover and name them `<basename>.spec.ts` (for example, `getAttributes.ts` uses `getAttributes.spec.ts`). Each package keeps a `tests/` folder next to `src/` for package-wide test support. Put reusable package test utilities in `tests/utils/`, scenario or reproduction data in `tests/fixtures/`, and file-based fixtures in `tests/fixtures/files/`. Add shared package setup in `tests/setup/` only when multiple specs need it. Keep one-off mocks and helpers next to the spec that uses them.
|
||||
- **E2E tests** with Playwright, colocated next to their demos as `demos/src/**/index.spec.ts`. They drive the real Vite-served demo pages in Chromium.
|
||||
|
||||
Run them:
|
||||
|
||||
```bash
|
||||
vp run test:unit # Vitest
|
||||
vp run test:e2e # Playwright headless (Chromium)
|
||||
vp run test:e2e:firefox # Playwright headless (Firefox)
|
||||
vp run test:e2e:all # both browsers — every test twice
|
||||
vp run test:e2e:open # UI mode (Chromium tests)
|
||||
vp run test:e2e:open:firefox # UI mode (Firefox tests)
|
||||
vp run test:e2e:open:all # UI mode, switch between browsers in the project picker
|
||||
vp run test:e2e:report # open the HTML report from the last run
|
||||
```
|
||||
|
||||
Playwright auto-starts the demo dev server (`pnpm -C demos run start:e2e` on port 4080) via `playwright.config.ts` — no separate terminal needed. Shared helpers live in `demos/test/helpers.ts`: `getEditor`, `setEditorContent`, `clickButton`. Use `demos/src/Commands/Cut/index.spec.ts` as a canonical template when adding new specs.
|
||||
|
||||
Browser setup:
|
||||
|
||||
- CI installs Chromium only (cached between runs) and only runs the Chromium project.
|
||||
- For local Firefox testing, install it once with `vp exec playwright install firefox` (~80MB).
|
||||
- UI mode (`--ui`) always opens its host window in Chromium — that's the Playwright UI app itself, not the browser running your tests. Tests still execute in the project you selected (check the trace metadata or `browserName` fixture if you need to confirm).
|
||||
|
||||
---
|
||||
|
||||
## Documentation style
|
||||
|
||||
We focus heavily on **User Experience** and **Developer Experience**. Every public API must be documented with JSDoc, including:
|
||||
|
||||
- `@param` and `@returns` annotations
|
||||
- Argument descriptions
|
||||
- At least one runnable example
|
||||
|
||||
This ensures our automated API docs are complete and examples are usable without extra context.
|
||||
|
||||
Example:
|
||||
|
||||
````ts
|
||||
/**
|
||||
* Toggle bold mark on the current selection.
|
||||
*
|
||||
* Example
|
||||
* ```ts
|
||||
* editor.chain().focus().toggleBold().run()
|
||||
* ```
|
||||
*
|
||||
* @param editor - The editor instance
|
||||
* @returns true if the command was applied
|
||||
*/
|
||||
export function toggleBold(editor: Editor): boolean {
|
||||
// ...
|
||||
}
|
||||
````
|
||||
|
||||
---
|
||||
|
||||
## Versioning and releases with Changesets
|
||||
|
||||
- Run `pnpm changeset` to create a new changeset (choose packages + bump type).
|
||||
- Run `vp run version` to update versions and changelogs.
|
||||
- Publishing happens automatically via the Publish CI workflow on configured branches. See `agents/VERSIONING.md` for details.
|
||||
|
||||
Changelogs must describe **user-facing changes**. Avoid internal noise.
|
||||
|
||||
---
|
||||
|
||||
## Cleaning and resetting
|
||||
|
||||
- `vp run clean:packages` - remove build artifacts
|
||||
- `vp run clean:packs` - remove generated tarballs
|
||||
- `vp run reset` - full reset of caches, node_modules, and lockfiles
|
||||
|
||||
---
|
||||
|
||||
## Principles
|
||||
|
||||
- Keep packages modular and framework-agnostic where possible.
|
||||
- Breaking changes require a major bump and a clear migration path.
|
||||
- Always add or update demos and tests when introducing a feature.
|
||||
- Code should be deterministic, documented, and tested.
|
||||
|
||||
---
|
||||
|
||||
## Extra guidance (short additions)
|
||||
|
||||
To make these instructions easier for automated agents and new contributors, the sections below add a few operational details and guardrails that speed up safe, repeatable changes.
|
||||
|
||||
### Environment
|
||||
|
||||
- Recommended Node version: >=18.x. Use a node version manager (nvm, fnm) or Corepack to pin a runtime.
|
||||
- Recommended package manager: pnpm (use the repo's lockfile). If you see unexpected errors, run `vp run reset`.
|
||||
|
||||
### Where to edit packages
|
||||
|
||||
Packages live under `packages/*`. Public entry points are typically `packages/<name>/src/index.ts` and are referenced by the package's `package.json` (`main`/`module`/`exports`). Prefer editing `src/` files and keep package diffs focused. For framework bindings check `packages/react/` or `packages/vue/`.
|
||||
|
||||
### Demos auto-discovery rules
|
||||
|
||||
The demos app discovers examples automatically. When adding a demo:
|
||||
|
||||
- Keep demo files small and self-contained. Import from published package names (for example `@tiptap/extension-foo`).
|
||||
- Name demo files clearly; follow existing naming conventions in `demos/`.
|
||||
|
||||
### Validation checklist (run locally before opening a PR)
|
||||
|
||||
Run the following to validate changes quickly:
|
||||
|
||||
```bash
|
||||
vp run lint
|
||||
vp run build
|
||||
vp run test:unit # Vitest
|
||||
vp run test:e2e # Playwright (auto-starts the demo server)
|
||||
vp run dev # optionally run the demos and open http://localhost:3000
|
||||
```
|
||||
|
||||
If a single package is failing types, run a targeted build for that package (e.g. `vp run -F @tiptap/core build`), or run `vp run build` at the repo root.
|
||||
|
||||
### PR checklist
|
||||
|
||||
- All checks pass (lint/build/tests).
|
||||
- Changeset added for user-facing changes (`pnpm changeset`).
|
||||
- Demo added/updated for UI-visible changes.
|
||||
- Short, clear PR description and changelog entry that explains why the change is needed.
|
||||
|
||||
### Guidance for automated agents and AI assistants
|
||||
|
||||
- Keep PR descriptions short, clear, and easy to read. Use simple English and explain why the change is needed. Avoid large text blocks and complicated language.
|
||||
- Make single-purpose, small diffs. Avoid sweeping changes in one PR.
|
||||
- Always run the validation checklist above after edits.
|
||||
- Add or update a demo and tests for user-visible behavior. For deterministic behaviour, favour unit tests over fragile e2e tests where possible.
|
||||
- Add a Changeset for any user-facing change. Do not change public APIs without a major bump; document migration steps in the PR description.
|
||||
|
||||
### Troubleshooting notes
|
||||
|
||||
- If CI fails with dependency or lockfile errors, run `vp run reset` locally and re-run the build.
|
||||
- For flaky Playwright tests, reproduce locally with `vp run test:e2e:open` (UI mode) or rerun with `--trace on` and inspect via `vp run test:e2e:report`.
|
||||
|
||||
---
|
||||
|
||||
These additions are intentionally short so they are easy to follow and scriptable by tools and agents. If you'd like, I can apply a slightly different tone or expand any section into more detail (for example, exact node/pnpm version pinning or demo naming patterns).
|
||||
10
.github/publish-config.json
vendored
Normal file
10
.github/publish-config.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"branches": {
|
||||
"main": {
|
||||
"distTag": "next",
|
||||
"label": "next",
|
||||
"title": "Release new pre release",
|
||||
"commit": "chore(release): release new pre release"
|
||||
}
|
||||
}
|
||||
}
|
||||
21
.github/pull_request_template.md
vendored
Normal file
21
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
## Fixes
|
||||
|
||||
<!-- Use GitHub keywords to link the issue or issues fixed by this PR, for example: Fixes #123 or Closes #456. -->
|
||||
|
||||
## Changes and Review
|
||||
|
||||
<!-- Explain what changed, why it changed, and how reviewers can verify it. -->
|
||||
<!-- Keep this section short: use 2–4 short sentences or a few bullets. Do not write a technical deep dive, a full implementation explanation, or a large wall of text. -->
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] I have added a [changeset](https://github.com/changesets/changesets) if necessary.
|
||||
- [ ] I have added tests if possible.
|
||||
- [ ] I have made sure to test my changes myself.
|
||||
- [ ] This is a critical bug or security fix that also needs to land on the current stable release (see [Branching](../CONTRIBUTING.md#branching) in CONTRIBUTING.md).
|
||||
|
||||
### Responsibility
|
||||
|
||||
<!-- This checkbox is checked by default on purpose. The PR author is responsible for reviewing and understanding the changes, even when an AI agent created them. -->
|
||||
|
||||
- [x] I have reviewed and understand these changes, and I take responsibility for this PR, even if an AI agent created it.
|
||||
128
.github/scripts/__tests__/resolve-publish-config.test.mjs
vendored
Normal file
128
.github/scripts/__tests__/resolve-publish-config.test.mjs
vendored
Normal file
@@ -0,0 +1,128 @@
|
||||
import { describe, it, before, after } from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { readFileSync, writeFileSync, mkdtempSync, rmSync } from 'node:fs'
|
||||
import { join, dirname } from 'node:path'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { resolveConfig } from '../resolve-publish-config.mjs'
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url))
|
||||
const ACTUAL_CONFIG_PATH = join(__dirname, '..', '..', 'publish-config.json')
|
||||
|
||||
let tmpDir
|
||||
let configPath
|
||||
|
||||
before(() => {
|
||||
tmpDir = mkdtempSync(join(tmpdir(), 'publish-config-test-'))
|
||||
configPath = join(tmpDir, 'publish-config.json')
|
||||
})
|
||||
|
||||
after(() => {
|
||||
rmSync(tmpDir, { recursive: true, force: true })
|
||||
})
|
||||
|
||||
function writeConfig(data) {
|
||||
writeFileSync(configPath, JSON.stringify(data, null, 2))
|
||||
}
|
||||
|
||||
describe('real publish-config.json', () => {
|
||||
let actualConfig
|
||||
|
||||
before(() => {
|
||||
const raw = readFileSync(ACTUAL_CONFIG_PATH, 'utf8')
|
||||
actualConfig = JSON.parse(raw)
|
||||
})
|
||||
|
||||
it('is valid JSON and contains a branches object', () => {
|
||||
assert.ok(actualConfig.branches, 'missing branches object')
|
||||
assert.ok(typeof actualConfig.branches === 'object', 'branches must be an object')
|
||||
})
|
||||
|
||||
it('has at least one configured branch', () => {
|
||||
const names = Object.keys(actualConfig.branches)
|
||||
assert.ok(names.length > 0, 'publish-config.json has no branches configured')
|
||||
})
|
||||
|
||||
it('every branch entry has all required string fields', () => {
|
||||
const required = ['distTag', 'label', 'title', 'commit']
|
||||
for (const [name, entry] of Object.entries(actualConfig.branches)) {
|
||||
for (const field of required) {
|
||||
assert.ok(
|
||||
entry[field] && typeof entry[field] === 'string',
|
||||
`Branch "${name}" is missing required string field "${field}"`,
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
it('resolves each configured branch through the resolver', () => {
|
||||
for (const name of Object.keys(actualConfig.branches)) {
|
||||
const result = resolveConfig(name, ACTUAL_CONFIG_PATH)
|
||||
assert.equal(result.configured, true, `Branch "${name}" should resolve as configured`)
|
||||
assert.equal(result.distTag, actualConfig.branches[name].distTag)
|
||||
assert.equal(result.label, actualConfig.branches[name].label)
|
||||
assert.equal(result.title, actualConfig.branches[name].title)
|
||||
assert.equal(result.commit, actualConfig.branches[name].commit)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('unknown branches', () => {
|
||||
it('returns configured=false for a branch with no entry', () => {
|
||||
writeConfig({
|
||||
branches: { known: { distTag: 'x', label: 'stable', title: 'T', commit: 'c' } },
|
||||
})
|
||||
const result = resolveConfig('unknown', configPath)
|
||||
assert.equal(result.configured, false)
|
||||
})
|
||||
|
||||
it('does not match partial branch names', () => {
|
||||
writeConfig({
|
||||
branches: { main: { distTag: 'x', label: 'stable', title: 'T', commit: 'c' } },
|
||||
})
|
||||
const result = resolveConfig('main-foo', configPath)
|
||||
assert.equal(result.configured, false)
|
||||
})
|
||||
|
||||
it('returns only configured=false for an unknown branch', () => {
|
||||
writeConfig({ branches: {} })
|
||||
const result = resolveConfig('anything', configPath)
|
||||
assert.deepEqual(result, { configured: false })
|
||||
})
|
||||
})
|
||||
|
||||
describe('validation errors', () => {
|
||||
it('fails when a required field is missing', () => {
|
||||
writeConfig({ branches: { main: { distTag: 'latest', label: 'stable' } } })
|
||||
assert.throws(() => resolveConfig('main', configPath), /missing required/i)
|
||||
})
|
||||
|
||||
it('fails when a required field is not a string', () => {
|
||||
writeConfig({
|
||||
branches: { main: { distTag: 'latest', label: 123, title: null, commit: 'ok' } },
|
||||
})
|
||||
assert.throws(() => resolveConfig('main', configPath), /missing required/i)
|
||||
})
|
||||
|
||||
it('fails when config has no branches object', () => {
|
||||
writeConfig({})
|
||||
assert.throws(() => resolveConfig('main', configPath), /branches/)
|
||||
})
|
||||
|
||||
it('fails when branches is an array instead of an object', () => {
|
||||
writeConfig({ branches: [] })
|
||||
assert.throws(() => resolveConfig('any', configPath), /branches/)
|
||||
})
|
||||
|
||||
it('fails when config file is malformed JSON', () => {
|
||||
writeFileSync(configPath, 'not-json')
|
||||
assert.throws(() => resolveConfig('main', configPath), /Failed to read or parse/)
|
||||
})
|
||||
|
||||
it('fails when config file does not exist', () => {
|
||||
assert.throws(
|
||||
() => resolveConfig('main', join(tmpDir, 'nonexistent.json')),
|
||||
/Failed to read or parse/,
|
||||
)
|
||||
})
|
||||
})
|
||||
103
.github/scripts/resolve-publish-config.mjs
vendored
Normal file
103
.github/scripts/resolve-publish-config.mjs
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { join, dirname, resolve } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url))
|
||||
const DEFAULT_CONFIG_PATH = join(__dirname, '..', 'publish-config.json')
|
||||
const REQUIRED_FIELDS = ['distTag', 'label', 'title', 'commit']
|
||||
|
||||
/** @typedef {{ distTag: string, label: string, title: string, commit: string }} BranchEntry */
|
||||
|
||||
/**
|
||||
* Reads and parses the publish config JSON file.
|
||||
* @param {string} configPath
|
||||
* @returns {object}
|
||||
*/
|
||||
function readConfig(configPath) {
|
||||
try {
|
||||
return JSON.parse(readFileSync(configPath, 'utf8'))
|
||||
} catch (err) {
|
||||
throw new Error(`Failed to read or parse ${configPath}: ${err.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that the config has a "branches" object.
|
||||
* @param {object} config
|
||||
*/
|
||||
function requireBranchesObject(config) {
|
||||
if (!config.branches || typeof config.branches !== 'object' || Array.isArray(config.branches)) {
|
||||
throw new Error('publish-config.json must contain a "branches" object')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that a branch entry has all required string fields.
|
||||
* @param {string} branch
|
||||
* @param {BranchEntry} entry
|
||||
*/
|
||||
function requireValidEntry(branch, entry) {
|
||||
for (const field of REQUIRED_FIELDS) {
|
||||
if (!entry[field] || typeof entry[field] !== 'string') {
|
||||
throw new Error(
|
||||
`Branch "${branch}" is missing required string field "${field}" in publish-config.json`,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up a branch in the publish config and returns its release settings.
|
||||
*
|
||||
* @param {string} branch - Branch name to look up (exact match).
|
||||
* @param {string} [configPath] - Path to publish-config.json. Defaults to the
|
||||
* canonical location next to the scripts directory.
|
||||
* @returns {{ configured: false } | { configured: true, distTag: string, label: string, title: string, commit: string }}
|
||||
* @throws {Error} When the config cannot be read/parsed or a required field is missing.
|
||||
*/
|
||||
export function resolveConfig(branch, configPath = DEFAULT_CONFIG_PATH) {
|
||||
const config = readConfig(configPath)
|
||||
requireBranchesObject(config)
|
||||
|
||||
const entry = config.branches[branch]
|
||||
if (!entry) {
|
||||
return { configured: false }
|
||||
}
|
||||
|
||||
requireValidEntry(branch, entry)
|
||||
|
||||
return {
|
||||
configured: true,
|
||||
distTag: entry.distTag,
|
||||
label: entry.label,
|
||||
title: entry.title,
|
||||
commit: entry.commit,
|
||||
}
|
||||
}
|
||||
|
||||
// ── CLI entry point ───────────────────────────────────────────
|
||||
// When run as `node resolve-publish-config.mjs <branch>`, emits
|
||||
// key=value lines suitable for GitHub Actions step outputs.
|
||||
if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
||||
const branch = process.argv[2]
|
||||
if (!branch) {
|
||||
console.error('Usage: node resolve-publish-config.mjs <branch-name> [config-path]')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const configPath = process.argv[3] || DEFAULT_CONFIG_PATH
|
||||
|
||||
try {
|
||||
const result = resolveConfig(branch, configPath)
|
||||
for (const [key, value] of Object.entries(result)) {
|
||||
// Convert camelCase to snake_case for GitHub Actions output keys
|
||||
const actionsKey = key.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`)
|
||||
console.log(`${actionsKey}=${value}`)
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err.message)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
167
.github/workflows/build.yml
vendored
Normal file
167
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,167 @@
|
||||
name: Verify
|
||||
|
||||
env:
|
||||
NODE_VERSION: 24
|
||||
VP_GIT_HOOKS: 0
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- next
|
||||
- release/*
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- next
|
||||
- release/*
|
||||
|
||||
jobs:
|
||||
install-node-dependencies:
|
||||
name: Install node dependencies
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- uses: ./.github/actions/setup-env
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: 'true'
|
||||
|
||||
- name: Install dependencies
|
||||
run: vp install --frozen-lockfile -- --strict-peer-dependencies
|
||||
|
||||
build-packages:
|
||||
name: Build packages
|
||||
runs-on: ubuntu-latest
|
||||
needs: install-node-dependencies
|
||||
timeout-minutes: 20
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- uses: ./.github/actions/setup-env
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: 'true'
|
||||
|
||||
- uses: ./.github/actions/restore-dependencies
|
||||
|
||||
- name: Build packages
|
||||
run: vp run build && vp run build:demos
|
||||
|
||||
- name: Check package exports
|
||||
run: vp run check:package-exports
|
||||
|
||||
check-linting-formatting:
|
||||
name: Check linting & formatting
|
||||
runs-on: ubuntu-latest
|
||||
needs: install-node-dependencies
|
||||
timeout-minutes: 15
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- uses: ./.github/actions/setup-env
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: 'true'
|
||||
|
||||
- uses: ./.github/actions/restore-dependencies
|
||||
|
||||
- name: Run linting and formatting checks
|
||||
run: vp check
|
||||
|
||||
run-unit-tests:
|
||||
name: Run unit tests
|
||||
runs-on: ubuntu-latest
|
||||
needs: install-node-dependencies
|
||||
timeout-minutes: 20
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- uses: ./.github/actions/setup-env
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: 'true'
|
||||
|
||||
- uses: ./.github/actions/restore-dependencies
|
||||
|
||||
- name: Run unit tests
|
||||
run: vp test
|
||||
|
||||
run-e2e-tests:
|
||||
name: Run e2e tests (shard ${{ matrix.shard }}/${{ matrix.total }})
|
||||
runs-on: ubuntu-latest
|
||||
needs: build-packages
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
shard: [1, 2, 3, 4]
|
||||
total: [4]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- uses: ./.github/actions/setup-env
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: 'true'
|
||||
|
||||
- uses: ./.github/actions/restore-dependencies
|
||||
|
||||
- uses: ./.github/actions/setup-playwright
|
||||
|
||||
- name: Run Playwright tests
|
||||
run: vp exec playwright test --project=chromium --shard=${{ matrix.shard }}/${{ matrix.total }}
|
||||
|
||||
- name: Upload blob report
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: e2e-blob-report-${{ matrix.shard }}
|
||||
path: blob-report
|
||||
retention-days: 7
|
||||
|
||||
merge-e2e-reports:
|
||||
name: Merge e2e reports
|
||||
runs-on: ubuntu-latest
|
||||
needs: [install-node-dependencies, run-e2e-tests]
|
||||
if: always() && needs.install-node-dependencies.result == 'success'
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- uses: ./.github/actions/setup-env
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: 'true'
|
||||
|
||||
- uses: ./.github/actions/restore-dependencies
|
||||
|
||||
- name: Download blob reports
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
path: all-blob-reports
|
||||
pattern: e2e-blob-report-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: Merge into HTML report
|
||||
if: ${{ hashFiles('all-blob-reports/**') != '' }}
|
||||
run: vp exec playwright merge-reports --reporter html ./all-blob-reports
|
||||
|
||||
- name: Upload merged HTML report
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: playwright-html-report
|
||||
path: playwright-report
|
||||
retention-days: 14
|
||||
40
.github/workflows/notify.yml
vendored
Normal file
40
.github/workflows/notify.yml
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
name: Notify
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
discord_notify:
|
||||
name: Send release to Discord
|
||||
if: github.event_name == 'release'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Post release to Discord (tsickert/discord-webhook)
|
||||
uses: tsickert/discord-webhook@v7.0.0
|
||||
with:
|
||||
webhook-url: ${{ secrets.DISCORD_RELEASES_STABLE_WEBHOOK }}
|
||||
embed-title: ${{ github.event.release.name || github.event.release.tag_name }}
|
||||
embed-description: ${{ github.event.release.body }}
|
||||
embed-url: ${{ github.event.release.html_url }}
|
||||
wait: true
|
||||
|
||||
slack_release_notify:
|
||||
name: Send release to Slack
|
||||
if: github.event_name == 'release'
|
||||
runs-on: ubuntu-latest
|
||||
permissions: {}
|
||||
steps:
|
||||
- name: Send Slack release notification
|
||||
uses: slackapi/slack-github-action@v4.0.0
|
||||
with:
|
||||
webhook: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
webhook-type: webhook-trigger
|
||||
payload: |
|
||||
{
|
||||
"message": ${{ toJSON(format('[Tiptap Editor {0}]: {1}\nRelease title: {2}\n{3}', github.event.release.prerelease && 'Pre-release' || 'Release', github.event.release.name || github.event.release.tag_name, github.event.release.html_url, github.event.release.body || 'No release notes provided.')) }}
|
||||
}
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
165
.github/workflows/publish.yml
vendored
Normal file
165
.github/workflows/publish.yml
vendored
Normal file
@@ -0,0 +1,165 @@
|
||||
name: Publish
|
||||
|
||||
env:
|
||||
NODE_VERSION: 24
|
||||
VP_GIT_HOOKS: 0
|
||||
|
||||
# this branch only releases from main
|
||||
# other versions are released from their respective release/* branches
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref_name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
resolve-config:
|
||||
name: Resolve publish config
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
outputs:
|
||||
configured: ${{ steps.resolve.outputs.configured }}
|
||||
dist_tag: ${{ steps.resolve.outputs.dist_tag }}
|
||||
label: ${{ steps.resolve.outputs.label }}
|
||||
title: ${{ steps.resolve.outputs.title }}
|
||||
commit: ${{ steps.resolve.outputs.commit }}
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v7
|
||||
- name: Validate publish config
|
||||
run: node --test .github/scripts/__tests__/*.test.mjs
|
||||
- name: Look up branch in publish config
|
||||
id: resolve
|
||||
run: node .github/scripts/resolve-publish-config.mjs "${{ github.ref_name }}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
needs: resolve-config
|
||||
if: needs.resolve-config.outputs.configured == 'true'
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- uses: ./.github/actions/setup-env
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: 'true'
|
||||
|
||||
- name: Install dependencies
|
||||
run: vp install --frozen-lockfile -- --strict-peer-dependencies
|
||||
|
||||
- name: Run build
|
||||
run: vp run build && vp run build:demos
|
||||
|
||||
- name: Check package exports
|
||||
run: vp run check:package-exports
|
||||
|
||||
- name: Archive package dist artifacts
|
||||
run: tar -cf package-dist-artifacts.tar packages/*/dist
|
||||
|
||||
- name: Upload package dist artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: package-dist-artifacts
|
||||
if-no-files-found: error
|
||||
path: package-dist-artifacts.tar
|
||||
|
||||
release:
|
||||
name: Bump Versions OR Release to Registry
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
needs: [resolve-config, build]
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: write
|
||||
pull-requests: write
|
||||
outputs:
|
||||
dist_tag: ${{ needs.resolve-config.outputs.dist_tag }}
|
||||
label: ${{ needs.resolve-config.outputs.label }}
|
||||
published: ${{ steps.changesets.outputs.published }}
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- uses: ./.github/actions/setup-env
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: 'true'
|
||||
|
||||
- name: Update npm for trusted publishing
|
||||
run: npm install -g npm@11.6.2
|
||||
|
||||
- name: Install dependencies
|
||||
run: vp install --frozen-lockfile -- --strict-peer-dependencies
|
||||
|
||||
- name: Download package dist artifacts
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: package-dist-artifacts
|
||||
path: .
|
||||
|
||||
- name: Restore package dist artifacts
|
||||
run: tar -xf package-dist-artifacts.tar
|
||||
|
||||
- name: Verify package dist artifacts
|
||||
run: bash ./scripts/check-package-dists.sh && vp run check:package-exports
|
||||
|
||||
- name: Run changesets release flow
|
||||
id: changesets
|
||||
uses: changesets/action@v1
|
||||
with:
|
||||
createGithubReleases: false
|
||||
version: vp run version
|
||||
publish: vp exec changeset publish
|
||||
title: ${{ needs.resolve-config.outputs.title }}
|
||||
commit: ${{ needs.resolve-config.outputs.commit }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
NPM_CONFIG_PROVENANCE: true
|
||||
NPM_CONFIG_ACCESS: public
|
||||
|
||||
notify-slack:
|
||||
name: Send notification to Slack
|
||||
runs-on: ubuntu-latest
|
||||
needs: release
|
||||
if: needs.release.outputs.published == 'true'
|
||||
timeout-minutes: 5
|
||||
permissions: {}
|
||||
steps:
|
||||
- name: Send Slack notification
|
||||
uses: slackapi/slack-github-action@v4.0.0
|
||||
with:
|
||||
webhook: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
webhook-type: webhook-trigger
|
||||
payload: |
|
||||
{
|
||||
"message": "[Tiptap Editor ${{ needs.release.outputs.label }}]: Published packages from branch ${{ github.ref_name }} with npm tag ${{ needs.release.outputs.dist_tag }}."
|
||||
}
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
|
||||
notify-slack-failure:
|
||||
name: Send failure notification to Slack
|
||||
runs-on: ubuntu-latest
|
||||
needs: [resolve-config, release]
|
||||
if: always() && (needs.resolve-config.result == 'failure' || needs.release.result == 'failure')
|
||||
timeout-minutes: 5
|
||||
permissions: {}
|
||||
steps:
|
||||
- name: Send Slack failure notification
|
||||
uses: slackapi/slack-github-action@v4.0.0
|
||||
with:
|
||||
webhook: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
webhook-type: webhook-trigger
|
||||
payload: |
|
||||
{
|
||||
"message": "[Tiptap Editor Release]: There was an issue publishing packages from branch ${{ github.ref_name }}. Logs: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
|
||||
}
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
59
.gitignore
vendored
Normal file
59
.gitignore
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
*.log
|
||||
.cache
|
||||
.DS_Store
|
||||
.temp
|
||||
node_modules
|
||||
dist
|
||||
.env
|
||||
.env.*
|
||||
|
||||
.instructions
|
||||
.agent
|
||||
|
||||
docs
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
|
||||
# Turbo cache
|
||||
.turbo
|
||||
|
||||
# Linter caches
|
||||
.eslintcache
|
||||
|
||||
.rpt2_cache
|
||||
.rts2_cache
|
||||
.rts2_cache_cjs
|
||||
.rts2_cache_es
|
||||
.rts2_cache_umd
|
||||
|
||||
# Playwright
|
||||
/playwright-report
|
||||
/test-results
|
||||
/playwright/.cache
|
||||
/blob-report
|
||||
# Ignore intellij project files
|
||||
.idea
|
||||
|
||||
# packaged files
|
||||
/package-dist-artifacts.tar
|
||||
packages/**/*.tgz
|
||||
demos/*.tgz
|
||||
|
||||
# pnpm
|
||||
.pnpm-store
|
||||
|
||||
# demo directories
|
||||
demos/src/Dev/**
|
||||
!demos/src/Dev/.gitkeep
|
||||
|
||||
# instructions
|
||||
.github/instructions/*
|
||||
!.github/instructions/tiptap.instructions.md
|
||||
!.github/instructions/changeset.instructions.md
|
||||
!.github/instructions/PR.instructions.md
|
||||
2
.npmrc
Normal file
2
.npmrc
Normal file
@@ -0,0 +1,2 @@
|
||||
link-workspace-packages=deep
|
||||
prefer-workspace-packages=true
|
||||
1
.vite-hooks/pre-commit
Executable file
1
.vite-hooks/pre-commit
Executable file
@@ -0,0 +1 @@
|
||||
vp staged
|
||||
11
.vscode/launch.json
vendored
Normal file
11
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch Tiptap demos in Google Chrome",
|
||||
"request": "launch",
|
||||
"type": "chrome",
|
||||
"url": "http://localhost:3000",
|
||||
"webRoot": "${workspaceFolder}"
|
||||
}
|
||||
]
|
||||
}
|
||||
16
.vscode/settings.json
vendored
Normal file
16
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"js/ts.tsdk.path": "node_modules/typescript/lib",
|
||||
"editor.defaultFormatter": "oxc.oxc-vscode",
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "oxc.oxc-vscode"
|
||||
},
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "oxc.oxc-vscode"
|
||||
},
|
||||
"[typescriptreact]": {
|
||||
"editor.defaultFormatter": "oxc.oxc-vscode"
|
||||
},
|
||||
"[javascriptreact]": {
|
||||
"editor.defaultFormatter": "oxc.oxc-vscode"
|
||||
}
|
||||
}
|
||||
240
.zed/settings.json
Normal file
240
.zed/settings.json
Normal file
@@ -0,0 +1,240 @@
|
||||
// Folder-specific settings
|
||||
//
|
||||
// For a full list of overridable settings, and general information on folder-specific settings,
|
||||
// see the documentation: https://zed.dev/docs/configuring-zed#settings-files
|
||||
{
|
||||
"lsp": {
|
||||
"oxlint": {
|
||||
"initialization_options": {
|
||||
"settings": {
|
||||
"configPath": null,
|
||||
"run": "onType",
|
||||
"disableNestedConfig": false,
|
||||
"fixKind": "safe_fix",
|
||||
"unusedDisableDirectives": "deny"
|
||||
}
|
||||
}
|
||||
},
|
||||
"oxfmt": {
|
||||
"initialization_options": {
|
||||
"settings": {
|
||||
"fmt.configPath": null,
|
||||
"run": "onSave"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"languages": {
|
||||
"CSS": {
|
||||
"format_on_save": "on",
|
||||
"prettier": {
|
||||
"allowed": false
|
||||
},
|
||||
"formatter": [
|
||||
{
|
||||
"language_server": {
|
||||
"name": "oxfmt"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"GraphQL": {
|
||||
"format_on_save": "on",
|
||||
"prettier": {
|
||||
"allowed": false
|
||||
},
|
||||
"formatter": [
|
||||
{
|
||||
"language_server": {
|
||||
"name": "oxfmt"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Handlebars": {
|
||||
"format_on_save": "on",
|
||||
"prettier": {
|
||||
"allowed": false
|
||||
},
|
||||
"formatter": [
|
||||
{
|
||||
"language_server": {
|
||||
"name": "oxfmt"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"HTML": {
|
||||
"format_on_save": "on",
|
||||
"prettier": {
|
||||
"allowed": false
|
||||
},
|
||||
"formatter": [
|
||||
{
|
||||
"language_server": {
|
||||
"name": "oxfmt"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"JavaScript": {
|
||||
"format_on_save": "on",
|
||||
"prettier": {
|
||||
"allowed": false
|
||||
},
|
||||
"formatter": [
|
||||
{
|
||||
"language_server": {
|
||||
"name": "oxfmt"
|
||||
}
|
||||
},
|
||||
{
|
||||
"code_action": "source.fixAll.oxc"
|
||||
}
|
||||
]
|
||||
},
|
||||
"JSON": {
|
||||
"format_on_save": "on",
|
||||
"prettier": {
|
||||
"allowed": false
|
||||
},
|
||||
"formatter": [
|
||||
{
|
||||
"language_server": {
|
||||
"name": "oxfmt"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"JSON5": {
|
||||
"format_on_save": "on",
|
||||
"prettier": {
|
||||
"allowed": false
|
||||
},
|
||||
"formatter": [
|
||||
{
|
||||
"language_server": {
|
||||
"name": "oxfmt"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"JSONC": {
|
||||
"format_on_save": "on",
|
||||
"prettier": {
|
||||
"allowed": false
|
||||
},
|
||||
"formatter": [
|
||||
{
|
||||
"language_server": {
|
||||
"name": "oxfmt"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Less": {
|
||||
"format_on_save": "on",
|
||||
"prettier": {
|
||||
"allowed": false
|
||||
},
|
||||
"formatter": [
|
||||
{
|
||||
"language_server": {
|
||||
"name": "oxfmt"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Markdown": {
|
||||
"format_on_save": "on",
|
||||
"prettier": {
|
||||
"allowed": false
|
||||
},
|
||||
"formatter": [
|
||||
{
|
||||
"language_server": {
|
||||
"name": "oxfmt"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"MDX": {
|
||||
"format_on_save": "on",
|
||||
"prettier": {
|
||||
"allowed": false
|
||||
},
|
||||
"formatter": [
|
||||
{
|
||||
"language_server": {
|
||||
"name": "oxfmt"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"SCSS": {
|
||||
"format_on_save": "on",
|
||||
"prettier": {
|
||||
"allowed": false
|
||||
},
|
||||
"formatter": [
|
||||
{
|
||||
"language_server": {
|
||||
"name": "oxfmt"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"TypeScript": {
|
||||
"format_on_save": "on",
|
||||
"prettier": {
|
||||
"allowed": false
|
||||
},
|
||||
"formatter": [
|
||||
{
|
||||
"language_server": {
|
||||
"name": "oxfmt"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"TSX": {
|
||||
"format_on_save": "on",
|
||||
"prettier": {
|
||||
"allowed": false
|
||||
},
|
||||
"formatter": [
|
||||
{
|
||||
"language_server": {
|
||||
"name": "oxfmt"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Vue.js": {
|
||||
"format_on_save": "on",
|
||||
"prettier": {
|
||||
"allowed": false
|
||||
},
|
||||
"formatter": [
|
||||
{
|
||||
"language_server": {
|
||||
"name": "oxfmt"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"YAML": {
|
||||
"format_on_save": "on",
|
||||
"prettier": {
|
||||
"allowed": false
|
||||
},
|
||||
"formatter": [
|
||||
{
|
||||
"language_server": {
|
||||
"name": "oxfmt"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
161
AGENTS.md
Normal file
161
AGENTS.md
Normal file
@@ -0,0 +1,161 @@
|
||||
# Tiptap
|
||||
|
||||
Headless rich text editor toolkit built on ProseMirror. A monorepo of small packages: a framework-agnostic core and extensions, plus React and Vue bindings.
|
||||
|
||||
Published packages live in `packages/*`. `demos/` is a Vite app used as playground and as the target for e2e tests. All scripts are in the root `package.json`.
|
||||
|
||||
## Rules
|
||||
|
||||
- Small, single-purpose diffs. Ask the user to review. Never autocommit.
|
||||
- Add a changeset for user-facing changes. Public API breaks need a major bump and migration notes.
|
||||
- Add or update a demo and tests for user-visible behavior. Prefer unit tests over e2e when deterministic.
|
||||
- Fix fallow findings your change introduced. Don't suppress them.
|
||||
- Comments should be clear, concise and use plain, simple english.
|
||||
- Rather use a oneliner
|
||||
- Try to always be intent-focused
|
||||
- stay local with comments
|
||||
- scannable, no long or complex wording
|
||||
- assume the reader doesn't know what you're talking about
|
||||
|
||||
## Before opening a PR
|
||||
|
||||
```bash
|
||||
vp run lint
|
||||
vp run build
|
||||
vp run test:unit
|
||||
vp run test:e2e
|
||||
vp run fallow:audit # verdict must be pass or warn, never fail
|
||||
```
|
||||
|
||||
Single package failing types: `vp run -F @tiptap/core build`.
|
||||
Dependency or lockfile errors: `vp run reset`, then rebuild.
|
||||
|
||||
## Code style
|
||||
|
||||
oxlint lints, oxfmt formats. Vite+ hooks (`vp staged`) run both on commit.
|
||||
|
||||
Prefer simple, readable code over clever code. Use early returns. Avoid deep nesting, nested ternaries, and abstractions you don't need yet. Keep functions focused. Apply DRY and SOLID pragmatically, not blindly.
|
||||
|
||||
### Files
|
||||
|
||||
- Keep files small and focused. Split unrelated utilities, types, constants and logic apart.
|
||||
- One reusable utility per file, named after its export: `findDecorations.ts`, `addDecoration.ts`. Not `utils.ts`, `helpers.ts`, `decorations.ts`.
|
||||
- A helper used in one file only can stay local.
|
||||
|
||||
### Naming
|
||||
|
||||
- Short, clear, recognizable. Never shorten just to save characters. No unclear abbreviations.
|
||||
- `index` for numeric indexes, the item's real name for collection values. Single letters only in small math contexts like `x` and `y`.
|
||||
- Use existing project terminology.
|
||||
|
||||
```ts
|
||||
// good
|
||||
items.map((item, index) => createNode(item, index))
|
||||
decorations.filter(decoration => decoration.visible)
|
||||
|
||||
// bad
|
||||
items.map((i, idx) => createNode(i, idx))
|
||||
decorations.filter(d => d.visible)
|
||||
```
|
||||
|
||||
### Comments
|
||||
|
||||
Comments are a last resort. Prefer code that explains itself through clear names, structure, and small functions.
|
||||
|
||||
- Default to **no comment**.
|
||||
- Comment only when the **reason for a decision is not apparent from the code**.
|
||||
- Keep comments **short, local, and intent-focused**.
|
||||
- Prefer a short one-line fragment or sentence.
|
||||
- Never restate what the code does.
|
||||
- Never narrate control flow.
|
||||
- Never explain surrounding architecture, history, edge cases, or implementation details unless they are essential to understanding the decision.
|
||||
- Never use comments as a substitute for clearer code, naming, or structure.
|
||||
- Do not write prose paragraphs, mini-documentation, or essay-style explanations in implementation code.
|
||||
- Do not add examples, scenarios, or parenthetical explanations to comments.
|
||||
- Do not use multi-line comments just because an explanation can be written. If it cannot be expressed concisely, reconsider whether the comment belongs in the code at all.
|
||||
- Existing verbose comments are not a style precedent. Do not imitate them.
|
||||
- When modifying code, remove comments that merely describe code made obvious by the change.
|
||||
|
||||
Prefer:
|
||||
|
||||
```ts
|
||||
// Skip empty text nodes
|
||||
// Preserve the original selection
|
||||
// Stop after the first match
|
||||
// Avoid dispatching during composition
|
||||
// Keep inactive editors measurable
|
||||
// Prevent collisions with imported IDs
|
||||
```
|
||||
|
||||
Avoid:
|
||||
|
||||
```ts
|
||||
// While composition is running, the update handler exits early because the
|
||||
// view is still composing. The final update may also contain no document or
|
||||
// selection changes, which means the menu would otherwise never update.
|
||||
```
|
||||
|
||||
```ts
|
||||
/**
|
||||
* Page content width used for the off-screen host. This is necessary because
|
||||
* inactive editors need to remain measurable for ResizeObserver to detect
|
||||
* changes while no overlay is currently open.
|
||||
*/
|
||||
```
|
||||
|
||||
```ts
|
||||
/**
|
||||
* Generates a unique endnote ID. Imported documents use numeric DOCX IDs,
|
||||
* while client-created endnotes use this prefix to ensure that IDs cannot
|
||||
* collide with imported endnotes or footnotes.
|
||||
*/
|
||||
```
|
||||
|
||||
If a short comment loses useful detail, that detail usually belongs in the code structure, a test, commit/PR description, or documentation instead.
|
||||
|
||||
JSDoc is exempt **only when it documents a public API**. Public API JSDoc should still be concise and include `@param`, `@returns`, and a runnable example where appropriate.
|
||||
|
||||
### Writing
|
||||
|
||||
Short, simple English in comments, docs, changesets and PRs. Most important information first. Assume the reader is new to the project or not a native speaker. No filler.
|
||||
|
||||
### Before you finish
|
||||
|
||||
Simplify what is hard to follow. Remove needless nesting and abstractions. Split large or unfocused files. Move reusable utilities into their own files. Drop redundant comments.
|
||||
|
||||
Then run `pnpm fallow` for complexity and dead code, `pnpm fallow:health` for refactor targets, and `pnpm fallow:audit` on your changes.
|
||||
|
||||
## Tests
|
||||
|
||||
- Unit: Vitest, running on happy-dom. Place file-specific tests next to the file they cover and name them `<basename>.test.ts` (for example, `getAttributes.ts` uses `getAttributes.test.ts`).
|
||||
- Every package keeps a `tests/` folder next to `src/` for package-wide tests and support. Put integration tests that exercise multiple source files or runtime wiring in `tests/integration/`.
|
||||
- Put reusable package test utilities in `tests/utils/`. Put shared package test setup in `tests/setup/` only when multiple tests use it.
|
||||
- Fixtures are passive inputs loaded by tests, not executable tests. Put scenario or reproduction data in `tests/fixtures/`.
|
||||
- Put file-based fixtures in `tests/fixtures/files/`. Use a more specific subfolder when the fixture type needs it, such as `tests/fixtures/markdown/`.
|
||||
- Keep one-off mocks and helpers next to the test that uses them.
|
||||
- E2E: Playwright, next to the demo it drives as `demos/src/**/index.spec.ts`. Playwright starts the demo server itself on port 4080, no separate terminal. Helpers live in `demos/test/helpers.ts`. Copy `demos/src/Commands/Cut/index.spec.ts` as a template.
|
||||
|
||||
## Demos
|
||||
|
||||
- Path pattern is `demos/src/<Category>/<DemoName>/<Variant>`, for example `demos/src/Marks/Bold/React`. Scaffold with `pnpm make:demo`.
|
||||
- Every demo needs an empty `index.html`. Vite routes by filesystem and ignores folders without it.
|
||||
- Import from `@tiptap/core` and friends, never relative paths. The demos `tsconfig.json` aliases those to the local package sources.
|
||||
- Reuse the global styles in `demos/setup/style.scss`: `.button-group` for rows of buttons, `.control-group` to wrap a toolbar, `.output-group` for demo output. Don't style `.tiptap`, that is the editor content itself.
|
||||
|
||||
## Changesets
|
||||
|
||||
Run `pnpm changeset`, or write the file yourself as `.changeset/YYYY-MM-DD-short-description.md`:
|
||||
|
||||
```markdown
|
||||
---
|
||||
'@tiptap/core': patch
|
||||
---
|
||||
|
||||
One short sentence on what changed for the user.
|
||||
```
|
||||
|
||||
Describe behavior users notice. No internals, no root-cause detail. `.github/publish-config.json` decides which branches publish and under which npm tag.
|
||||
|
||||
## Docs
|
||||
|
||||
User-facing documentation lives in the separate `ueberdosis/tiptap-docs` repo. Ask the user for the local path when you need to change it.
|
||||
194
CONTRIBUTING.md
Normal file
194
CONTRIBUTING.md
Normal file
@@ -0,0 +1,194 @@
|
||||
# Contributing
|
||||
|
||||
Contributions are **welcome** and will be fully **credited**.
|
||||
|
||||
Please read and understand the [contribution guide](https://tiptap.dev/docs/resources/contributing) before creating an issue or pull request.
|
||||
|
||||
## Etiquette
|
||||
|
||||
This project is open source, and as such, the maintainers give their free time to build and maintain the source code
|
||||
held within. They make the code freely available in the hope that it will be of use to other developers. It would be
|
||||
extremely unfair for them to suffer abuse or anger for their hard work.
|
||||
|
||||
Please be considerate towards maintainers when raising issues or presenting pull requests. Let's show the
|
||||
world that developers are civilized and selfless people.
|
||||
|
||||
It's the duty of the maintainer to ensure that all submissions to the project are of sufficient
|
||||
quality to benefit the project. Many developers have different skillsets, strengths, and weaknesses. Respect the maintainer's decision, and do not be upset or abusive if your submission is not used.
|
||||
|
||||
## Security
|
||||
|
||||
If you discover a security vulnerability, please refer to our [Security Policy](SECURITY.md) for reporting instructions.
|
||||
|
||||
## Branching
|
||||
|
||||
`main` is the default branch and always holds the newest code. That's not the same as "stable".
|
||||
While a new major version is being developed, `main` can be a pre-release (`next`, `alpha`,
|
||||
`beta`) for months before it ships. Check the npm dist-tag (`latest`, `next`, `alpha`, ...), not
|
||||
the branch, to know what is currently stable.
|
||||
|
||||
Older major versions live on `release/v*` branches (e.g. `release/v3`). These only
|
||||
receive critical bug fixes and security patches, no new features.
|
||||
|
||||
| Branch | Purpose | Publishes |
|
||||
| ------------ | --------------------------------------- | ------------------------------------------------------------------------------ |
|
||||
| `main` | Active development, default branch | `next` / `alpha` / `beta`, or `latest` once main is the current stable version |
|
||||
| `release/v*` | Frozen stable line, critical fixes only | `latest` (while current) or `latest-v*` (once superseded) |
|
||||
|
||||
Once a `release/v*` branch is cut, it never merges back into `main`, and `main` never
|
||||
merges into it. Merging two branches with that much diverged history just produces huge
|
||||
conflicts. Individual fixes still travel between them, one commit at a time, by cherry-pick.
|
||||
|
||||
### Where to open your pull request
|
||||
|
||||
- Open your PR against `main`. That's the default target and where all new development happens.
|
||||
- Only target a `release/v*` branch directly if your fix applies exclusively to that old
|
||||
version and not to `main` (see "Change only relevant for a maintained version" below).
|
||||
|
||||
### Does your fix need to reach the current stable release too?
|
||||
|
||||
A fix merged into `main` during a pre-release cycle does not reach users on the current stable
|
||||
release by itself, because `main` and the maintenance branch never merge. If your fix addresses
|
||||
a critical bug or security issue that also affects the current stable release, it needs a
|
||||
second PR that cherry-picks your commit onto the relevant `release/v*` branch.
|
||||
|
||||
- Check the box in the pull request template if this applies to your change.
|
||||
- Open the backport PR yourself if you can. You know the fix best.
|
||||
- If you can't, a maintainer may do it as a last resort, but that's not guaranteed, so try first.
|
||||
|
||||
### The three cases
|
||||
|
||||
#### Default workflow
|
||||
|
||||
Most changes. Lands on `main`, ships under whatever tag `main` is currently publishing, no
|
||||
backport needed.
|
||||
|
||||

|
||||
|
||||
#### Change only relevant for `main`
|
||||
|
||||
A change that does not apply to any maintained stable version, for example a new-major-only
|
||||
feature. Lands on `main` only. `release/v*` never sees it.
|
||||
|
||||

|
||||
|
||||
#### Change only relevant for a maintained version
|
||||
|
||||
A fix specific to an already-stable release. Open the PR directly against `release/v*`.
|
||||
It never touches `main`.
|
||||
|
||||

|
||||
|
||||
## Viability
|
||||
|
||||
When requesting or submitting new features, first consider whether it might be useful to others. Open
|
||||
source projects are used by many developers, who may have entirely different needs to your own. Think about
|
||||
whether or not your feature is likely to be used by other users of the project.
|
||||
|
||||
## Procedure
|
||||
|
||||
Before filing an issue:
|
||||
|
||||
- Attempt to replicate the problem, to ensure that it wasn't a coincidental incident. Create a CodeSandbox to reproduce the issue. Use one of these templates to get started:
|
||||
- [JavaScript template](https://codesandbox.io/s/tiptap-js-fv1lyo)
|
||||
- [React template](https://codesandbox.io/s/tiptap-react-qidlsv)
|
||||
- [Vue 3 template](https://codesandbox.io/p/sandbox/tiptap-vue-3-ci7q9h)
|
||||
- Check to make sure your feature suggestion isn't already present within the project.
|
||||
- Check the pull requests tab to ensure that the bug doesn't have a fix in progress.
|
||||
- Check the pull requests tab to ensure that the feature isn't already in progress.
|
||||
|
||||
Before submitting a pull request:
|
||||
|
||||
- Check the codebase to ensure that your feature doesn't already exist.
|
||||
- Check the pull requests to ensure that another person hasn't already submitted the feature or fix.
|
||||
- Check which branch to target. See [Branching](#branching) above.
|
||||
|
||||
Before committing:
|
||||
|
||||
- Make sure to run the tests and linter before committing your changes.
|
||||
- If you are making changes to one of the packages, make sure to **always** include a [changeset](https://github.com/changesets/changesets) in your PR describing **what changed** with a **description** of the change. Those are responsible for changelog creation
|
||||
|
||||
## Testing
|
||||
|
||||
- Place file-specific unit tests next to the file they cover and name them `<basename>.test.ts`.
|
||||
- Keep a `tests/` folder next to `src/` in each package for package-wide tests and support.
|
||||
- Put integration tests that exercise multiple source files or runtime wiring in `tests/integration/`.
|
||||
- Put shared test utilities in `tests/utils/`.
|
||||
- Fixtures are passive inputs loaded by tests, not executable tests. Put scenario or reproduction data in `tests/fixtures/`.
|
||||
- Put file-based fixtures in `tests/fixtures/files/`, with more specific subfolders when needed.
|
||||
- Put shared package test setup in `tests/setup/`. Keep one-off mocks and helpers next to the test that uses them.
|
||||
|
||||
## Create a new demo
|
||||
|
||||
To make it easier to add new demos to the demos app we provide a small helper script via `pnpm run make:demo` that scaffolds a new demo directory from our default template.
|
||||
|
||||
**What it does**
|
||||
|
||||
- Prompts for a demo name and category.
|
||||
- Validates the category is one of: `Dev`, `Examples`, `Extensions`, `Experiments`, `Marks`, `Nodes`.
|
||||
- Copies the template `demos/src/Examples/Default` to `demos/src/<Category>/<Demo_Name>`.
|
||||
|
||||
**How to use**
|
||||
|
||||
- From the repository root run:
|
||||
- If the script is executable:
|
||||
- Or with bash directly:
|
||||
- Follow the interactive prompts for the demo name and category.
|
||||
|
||||
**Notes and follow-up steps**
|
||||
|
||||
- The script only copies the template. After the scaffold is created, update the demo's files (title, description, imports) to reflect your example.
|
||||
- Make sure to review the generated demo in `demos/` and run the demos app (`pnpm dev`) to verify it appears and works as expected.
|
||||
- If your demo changes package behaviour or exposes user-facing changes, follow the normal rule and add a changeset and tests as needed.
|
||||
- If you don't want your demo to be included in the Git repository, use the `Dev` category. Demos in this category are ignored by git via `.gitignore`.
|
||||
|
||||
## Publishing New Packages
|
||||
|
||||
When adding a new package to the repository that does not yet exist on NPM, additional setup is required before the automated publish CI can release it:
|
||||
|
||||
1. **Manual initial publish** - The package must be published manually to NPM for the first time using normal user authentication. This is required because trusted publishing can only be configured for packages that already exist on the registry.
|
||||
- For a single package, run `pnpm run build && pnpm publish` from the package directory (e.g., `packages/extension-audio/`).
|
||||
- Alternatively, run `pnpm run publish` from the root directory to publish all packages.
|
||||
2. **Configure trusted publishing** - After the initial publish, set up [NPM trusted publishing](https://docs.npmjs.com/trusted-publishers) (also known as provenance) for the package on NPM. This allows the GitHub Actions workflow to publish subsequent versions automatically.
|
||||
|
||||
Without this setup, the publish CI will fail when attempting to release a new package.
|
||||
|
||||
### Adding a new release branch
|
||||
|
||||
When work on a new major version starts on `main`, cut the current stable line into its own
|
||||
`release/v*` branch (e.g., `release/v2`) before the first breaking change merges. See
|
||||
[Branching](#branching) for the full model. Then update two places:
|
||||
|
||||
1. **Workflow trigger** — Add the branch name to the `on.push.branches` list in `.github/workflows/publish.yml`.
|
||||
2. **Publish configuration** — Add a matching entry in `.github/publish-config.json` with the desired dist-tag and release messages.
|
||||
|
||||
Both lists must stay in sync. A branch present in one but not the other will either never trigger the workflow or produce a harmless no-op.
|
||||
|
||||
Each entry in `.github/publish-config.json` takes four fields:
|
||||
|
||||
| Field | Description |
|
||||
| --------- | ------------------------------------------------------------------------------------------ |
|
||||
| `distTag` | npm dist-tag passed to `pnpm changeset publish --tag`, e.g. `latest`, `next`, `latest-v2`. |
|
||||
| `label` | Label used in the Slack release announcement, e.g. `stable` or `prerelease`. |
|
||||
| `title` | Title of the Changesets version PR created by CI. |
|
||||
| `commit` | Commit message of that version PR. |
|
||||
|
||||
The resolver job looks up the current branch by exact name. If it is missing, the workflow exits cleanly without building, publishing, or notifying. Make sure the dist-tag exists on npm (`npm dist-tag add <package>@<version> <tag>`) before the first release from a new branch.
|
||||
|
||||
## Requirements
|
||||
|
||||
If the project maintainer has any additional requirements, you will find them listed here.
|
||||
|
||||
- **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date.
|
||||
|
||||
- **One pull request per feature** - If you want to do more than one thing, send multiple pull requests.
|
||||
|
||||
- **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](https://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting.
|
||||
|
||||
- **Disclose AI usage** — If you used AI tools (e.g., ChatGPT, Claude, GitHub Copilot) to generate any part of your contribution, you must clearly disclose this in your pull request description.
|
||||
|
||||
- **Link your pull request to an issue** — Pull requests must be linked to an existing issue that has been assigned to you. Before opening a PR, ensure there is an issue describing the bug or feature you're addressing. Trivial fixes (e.g., typos, broken links) are exempt.
|
||||
|
||||
- **Respond to feedback** — Maintainers may ask follow-up questions or request changes on your pull request. If you do not respond within 30 days, your PR may be closed. You are welcome to reopen it or submit a new PR once you're able to address the feedback.
|
||||
|
||||
**Happy coding**!
|
||||
21
LICENSE.md
Normal file
21
LICENSE.md
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025, Tiptap GmbH
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
133
README.md
Normal file
133
README.md
Normal file
@@ -0,0 +1,133 @@
|
||||

|
||||
|
||||
[](https://insights.linuxfoundation.org/project/tiptap)
|
||||
[](https://github.com/ueberdosis/tiptap/actions/workflows/build.yml)
|
||||
[](https://www.npmjs.com/package/@tiptap/core)
|
||||
[](https://npmcharts.com/compare/@tiptap/core?minimal=true)
|
||||
[](https://www.npmjs.com/package/@tiptap/core)
|
||||
[](https://discord.gg/WtJ49jGshW)
|
||||
[](https://github.com/sponsors/ueberdosis)
|
||||
[](https://insights.linuxfoundation.org/project/tiptap)
|
||||
|
||||
# Tiptap Editor
|
||||
|
||||
The Tiptap Editor is a headless, framework-agnostic rich text editor that's customizable and extendable through extensions. Its headless nature means it comes without a set user interface, offering full design freedom (for a jumpstart, see linked [UI templates](#examples-codesandbox-and-ui-templates) below). Tiptap is based on the highly reliable [ProseMirror](https://prosemirror.net/) library.
|
||||
|
||||
Tiptap Editor is complemented by the collaboration open-source backend [Hocuspocus](https://github.com/ueberdosis/hocuspocus). Both the Editor and Hocuspocus form the foundation of the [Tiptap Suite](https://tiptap.dev/).
|
||||
|
||||
### How does the Tiptap Editor work?
|
||||
|
||||
- **Headless Framework:** Tiptap does not rely on a user interface. So there is no need for class overrides or code hacks. If you do need an example UI feel free to browse our [UI templates](#examples-codesandbox-and-ui-templates) linked below.
|
||||
- **Framework-agnostic:** The Tiptap Editor is designed to work across different frontend frameworks. This means whether you're using Vue, React, or plain JavaScript, Tiptap integrates without compatibility issues.
|
||||
- **Extension based:** Extensions in Tiptap allow for a tailored editing experience, from simple text styling to advanced features like drag-and-drop block editing. You have the option to choose from over 100 extensions available in the [documentation](https://tiptap.dev/docs/editor/extensions) and [community](https://github.com/ueberdosis/awesome-tiptap/#community-extensions) to enhance your editor's functionality.
|
||||
- **Customize your UX:** The editor was built to give you control to define your own [extensions](https://tiptap.dev/docs/editor/guide/custom-extensions) and [nodes](https://tiptap.dev/docs/editor/api/nodes).
|
||||
|
||||
### Editor Pro Extensions
|
||||
|
||||
The **Pro Extensions** are a set of advanced functionalities that enhance the capabilities of the Tiptap Editor. They are additional features that can be integrated into the base editor to provide more sophisticated editing options.
|
||||
|
||||
Key functionalities include collaborative editing, commenting, versioning, document conversion and AI related features.
|
||||
Review the docs right [here](https://tiptap.dev/docs/editor/extensions).
|
||||
|
||||
Pro Extensions need a valid subscription.
|
||||
|
||||
### Make your editor collaborative
|
||||
|
||||
Interested in collaborative editing? Check out our open-source package [Hocuspocus](https://github.com/ueberdosis/hocuspocus) - a collaboration backend built around the CRDT power of [Yjs](https://github.com/yjs/yjs). Hocuspocus serves as the backbone for the [Tiptap Suite](https://tiptap.dev/).
|
||||
|
||||
## Documentation
|
||||
|
||||
For more detailed information, make sure to check out our [documentation](https://tiptap.dev/docs/editor/installation). If you encounter any problems or have suggestions for our system, please open an issue.
|
||||
|
||||
### Examples, CodeSandbox and UI Templates
|
||||
|
||||
Have a look at the [examples to see Tiptap in action](https://tiptap.dev/examples) or review and fork our codesandboxes.
|
||||
|
||||
- [Basic example of the Tiptap editor.](https://codesandbox.io/p/devbox/editor-9x9dkd?embed=1&file=%2Fsrc%2FApp.js)
|
||||
- [Collaboration ready Tiptap CodeSandbox](https://codesandbox.io/p/devbox/collaboration-4stk94)
|
||||
- React notion-like block editor template: [Demo](https://templates.tiptap.dev/)
|
||||
|
||||
## About Tiptap
|
||||
|
||||
Tiptap is a collection of developer components based on open-source technology, forming the basis of our advanced, paid features. It includes the open-source editor component, collaboration features, Content AI, and Tiptap Cloud. We are developing open-source products that also shape our paid features. We're committed to improving both, ensuring quality and reliability in every update.
|
||||
|
||||
For more details, visit the Tiptap [documentation](https://tiptap.dev/docs/editor/introduction) or [website](https://tiptap.dev/).
|
||||
|
||||
### Community
|
||||
|
||||
For help, discussion about best practices, or any other conversation that would benefit from being searchable:
|
||||
|
||||
[Discuss Tiptap on GitHub](https://github.com/ueberdosis/tiptap/discussions)
|
||||
|
||||
### Sponsors 💖
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://www.complish.app/">
|
||||
<img src="https://uploads-ssl.webflow.com/5fa93d27380666789a1cbbd3/5fae50824b4d2d06f3d2898f_Frame%20374.png" width="25"><br>
|
||||
<strong>Complish</strong>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://www.storyblok.com/">
|
||||
<img src="https://unavatar.io/github/storyblok" width="25"><br>
|
||||
<strong>Storyblok</strong>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://posthog.com/">
|
||||
<img src="https://unavatar.io/github/posthog" width="25"><br>
|
||||
<strong>PostHog</strong>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" width="100">
|
||||
<a href="https://reflect.app/">
|
||||
<img src="https://unavatar.io/reflect.app" width="25"><br>
|
||||
<strong>Reflect</strong>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" width="100">
|
||||
<a href="https://ziffmedia.com/">
|
||||
<img src="https://unavatar.io/github/ziffmedia" width="25"><br>
|
||||
<strong>Ziff Media</strong>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" width="100">
|
||||
<a href="https://www.basewell.com/">
|
||||
<img src="https://unavatar.io/github/Basewell" width="25"><br>
|
||||
<strong>Basewell</strong>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" width="100">
|
||||
<a href="https://poggio.io">
|
||||
<img src="https://unavatar.io/github/poggiolabs" width="25"><br>
|
||||
<strong>Poggio</strong>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
[iFixit](https://www.ifixit.com/), [ApostropheCMS](https://apostrophecms.com/), [Novadiscovery](http://www.novadiscovery.com/), [Omics Data Automation](https://www.omicsautomation.com), [Flow Mobile](https://www.flowmobile.app/), [DocIQ](https://www.dociq.io/) and [hundreds of awesome individuals](https://github.com/sponsors/ueberdosis).
|
||||
|
||||
### Contributing
|
||||
|
||||
Feel like adding some magic of your own to Tiptap Editor Core? We welcome contributions! Please see our [CONTRIBUTING](CONTRIBUTING.md) guidelines for how to get started.
|
||||
|
||||
### Contributors
|
||||
|
||||
[Sam Willis](https://github.com/samwillis),
|
||||
[Brian Hung](https://github.com/BrianHung),
|
||||
[Dirk Holtwick](https://github.com/holtwick),
|
||||
[Sam Duvall](https://github.com/SamDuvall),
|
||||
[Christoph Flathmann](https://github.com/Chrissi2812),
|
||||
[Erick Wilder](https://github.com/erickwilder),
|
||||
[Marius Tolzmann](https://github.com/mariux),
|
||||
[jjangga0214](https://github.com/jjangga0214),
|
||||
[Maya Nedeljkovich](https://github.com/mayacoda),
|
||||
[Ryan Bliss](https://github.com/ryanbliss),
|
||||
[Gregor](https://github.com/gambolputty) and [many more](../../contributors).
|
||||
|
||||
## License
|
||||
|
||||
The MIT License (MIT). Please see [License File](LICENSE.md) for more information.
|
||||
78
SCOPES.md
Normal file
78
SCOPES.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# Scopes
|
||||
|
||||
## Primary scopes (monorepo packages)
|
||||
|
||||
### Core, framework bindings, and shared utilities
|
||||
|
||||
- core
|
||||
- ai-toolkit
|
||||
- extensions
|
||||
- html
|
||||
- markdown
|
||||
- pm
|
||||
- react
|
||||
- server-ai-toolkit
|
||||
- starter-kit
|
||||
- static-renderer
|
||||
- suggestion
|
||||
- vue
|
||||
|
||||
### Extensions
|
||||
|
||||
- extension-audio
|
||||
- extension-blockquote
|
||||
- extension-bold
|
||||
- extension-bubble-menu
|
||||
- extension-bullet-list
|
||||
- extension-code
|
||||
- extension-code-block
|
||||
- extension-code-block-lowlight
|
||||
- extension-collaboration
|
||||
- extension-collaboration-caret
|
||||
- extension-color
|
||||
- extension-details
|
||||
- extension-document
|
||||
- extension-drag-handle
|
||||
- extension-drag-handle-react
|
||||
- extension-drag-handle-vue
|
||||
- extension-emoji
|
||||
- extension-file-handler
|
||||
- extension-floating-menu
|
||||
- extension-font-family
|
||||
- extension-hard-break
|
||||
- extension-heading
|
||||
- extension-highlight
|
||||
- extension-horizontal-rule
|
||||
- extension-image
|
||||
- extension-invisible-characters
|
||||
- extension-italic
|
||||
- extension-link
|
||||
- extension-list
|
||||
- extension-mathematics
|
||||
- extension-mention
|
||||
- extension-node-range
|
||||
- extension-ordered-list
|
||||
- extension-paragraph
|
||||
- extension-strike
|
||||
- extension-subscript
|
||||
- extension-superscript
|
||||
- extension-ruby-text
|
||||
- extension-table
|
||||
- extension-table-of-contents
|
||||
- extension-text
|
||||
- extension-text-align
|
||||
- extension-text-style
|
||||
- extension-twitch
|
||||
- extension-typography
|
||||
- extension-underline
|
||||
- extension-unique-id
|
||||
- extension-youtube
|
||||
|
||||
## Additional scopes
|
||||
|
||||
- demos
|
||||
- tests
|
||||
- ci
|
||||
- config
|
||||
- docs
|
||||
- changeset
|
||||
32
SECURITY.md
Normal file
32
SECURITY.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# Security Policy
|
||||
|
||||
## Reporting Vulnerabilities
|
||||
|
||||
If you discover a security vulnerability in Tiptap, please report it responsibly. We appreciate your help in keeping our users safe.
|
||||
|
||||
### How to Report
|
||||
|
||||
**Please do not report security vulnerabilities through public GitHub issues.**
|
||||
|
||||
Send details to security@tiptap.dev. Include a description of the vulnerability, steps to reproduce, and any potential impact.
|
||||
|
||||
We will acknowledge receipt within 48 hours and provide updates as we investigate.
|
||||
|
||||
## Response Process
|
||||
|
||||
Vulnerabilities are assessed and prioritized based on severity, exploitability, and business impact. Fixes are deployed according to the following timelines:
|
||||
|
||||
- **Critical** (e.g., remote code execution, data breaches): Fixed and released within 24 hours.
|
||||
- **High** (e.g., significant data exposure): Fixed and released within 72 hours.
|
||||
- **Medium** (e.g., moderate leaks): Fixed and released within 14 days.
|
||||
- **Low** (e.g., minor issues): Addressed in the next scheduled update.
|
||||
|
||||
We follow responsible disclosure practices and will credit reporters unless requested otherwise.
|
||||
|
||||
## Scope
|
||||
|
||||
This policy applies to all Tiptap packages and related infrastructure. Reports are handled through our established vulnerability management process.
|
||||
|
||||
## Contact
|
||||
|
||||
For questions, email security@tiptap.dev or humans@tiptap.dev.
|
||||
7
babel.config.js
Normal file
7
babel.config.js
Normal file
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
presets: ['@babel/preset-env', '@babel/preset-react'],
|
||||
plugins: [
|
||||
'@babel/plugin-proposal-nullish-coalescing-operator',
|
||||
'@babel/plugin-proposal-optional-chaining',
|
||||
],
|
||||
}
|
||||
33
codemods/tiptap-v4-migration/.gitignore
vendored
Normal file
33
codemods/tiptap-v4-migration/.gitignore
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
# Dependencies
|
||||
node_modules/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Build artifacts
|
||||
target/
|
||||
dist/
|
||||
build/
|
||||
|
||||
# Temporary files
|
||||
*.tmp
|
||||
*.temp
|
||||
.cache/
|
||||
|
||||
# Environment files
|
||||
.env
|
||||
.env.local
|
||||
|
||||
# IDE files
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# OS files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Package bundles
|
||||
*.tar.gz
|
||||
*.tgz
|
||||
38
codemods/tiptap-v4-migration/README.md
Normal file
38
codemods/tiptap-v4-migration/README.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# @tiptap/codemod-v3-to-v4
|
||||
|
||||
Codemod workflows for migrating Tiptap projects from v3 to v4.
|
||||
|
||||
## Current migrations
|
||||
|
||||
- Renames `@tiptap/vue-3` to `@tiptap/vue`.
|
||||
- Renames `@tiptap/extension-drag-handle-vue-3` to `@tiptap/extension-drag-handle-vue`.
|
||||
|
||||
## Local development
|
||||
|
||||
```bash
|
||||
pnpm install
|
||||
pnpm validate
|
||||
pnpm test
|
||||
pnpm exec codemod workflow run -w . -t /path/to/project --param vue_target_path=/path/to/project
|
||||
```
|
||||
|
||||
## Publish
|
||||
|
||||
Run these commands from this directory:
|
||||
|
||||
```bash
|
||||
pnpm exec codemod login
|
||||
pnpm validate
|
||||
pnpm test
|
||||
pnpm exec codemod publish
|
||||
```
|
||||
|
||||
## Verify the published workflow
|
||||
|
||||
Run the published workflow against a disposable project before documenting it in a release:
|
||||
|
||||
```bash
|
||||
npx codemod@latest @tiptap/codemod-v3-to-v4 \
|
||||
--target /path/to/project \
|
||||
--param vue_target_path=/path/to/project
|
||||
```
|
||||
21
codemods/tiptap-v4-migration/codemod.yaml
Normal file
21
codemods/tiptap-v4-migration/codemod.yaml
Normal file
@@ -0,0 +1,21 @@
|
||||
schema_version: '1.0'
|
||||
|
||||
name: '@tiptap/codemod-v3-to-v4'
|
||||
version: '0.1.0'
|
||||
description: 'Migrate Tiptap projects from v3 to v4'
|
||||
author: 'Tiptap'
|
||||
license: 'MIT'
|
||||
|
||||
workflows:
|
||||
- name: main
|
||||
default: true
|
||||
path: workflow.yaml
|
||||
|
||||
targets:
|
||||
languages: ['javascript', 'typescript', 'tsx', 'json', 'vue']
|
||||
|
||||
keywords: ['migration', 'tiptap', 'v3-to-v4']
|
||||
|
||||
registry:
|
||||
access: 'public'
|
||||
visibility: 'public'
|
||||
24
codemods/tiptap-v4-migration/package.json
Normal file
24
codemods/tiptap-v4-migration/package.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "@tiptap/codemod-v3-to-v4",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"description": "Migrate Tiptap Vue package imports for v4",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "pnpm validate",
|
||||
"check-types": "tsc --noEmit",
|
||||
"test": "pnpm test:javascript && pnpm test:typescript && pnpm test:tsx && pnpm test:json && pnpm test:vue",
|
||||
"test:javascript": "codemod jssg test -l javascript scripts/vue-package-renames.ts tests/javascript",
|
||||
"test:json": "codemod jssg test -l json scripts/vue-package-renames.ts tests/json",
|
||||
"test:tsx": "codemod jssg test -l tsx scripts/vue-package-renames.ts tests/tsx",
|
||||
"test:typescript": "codemod jssg test -l typescript scripts/vue-package-renames.ts tests/typescript",
|
||||
"test:vue": "node --test tests/vue-sfc-imports.test.mjs",
|
||||
"validate": "codemod workflow validate -w workflow.yaml"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@codemod.com/jssg-types": "^1.6.3",
|
||||
"codemod": "^1.15.2",
|
||||
"typescript": "^5.7.3"
|
||||
},
|
||||
"packageManager": "pnpm@11.2.2"
|
||||
}
|
||||
33
codemods/tiptap-v4-migration/scripts/vue-package-renames.ts
Normal file
33
codemods/tiptap-v4-migration/scripts/vue-package-renames.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import type { Codemod, Edit } from 'codemod:ast-grep'
|
||||
import type JSON from 'codemod:ast-grep/langs/json'
|
||||
import type JS from 'codemod:ast-grep/langs/javascript'
|
||||
import type TS from 'codemod:ast-grep/langs/typescript'
|
||||
import type TSX from 'codemod:ast-grep/langs/tsx'
|
||||
|
||||
const packageSpecifiers = new Map([
|
||||
["'@tiptap/vue-3'", "'@tiptap/vue'"],
|
||||
['"@tiptap/vue-3"', '"@tiptap/vue"'],
|
||||
["'@tiptap/extension-drag-handle-vue-3'", "'@tiptap/extension-drag-handle-vue'"],
|
||||
['"@tiptap/extension-drag-handle-vue-3"', '"@tiptap/extension-drag-handle-vue"'],
|
||||
])
|
||||
|
||||
type Language = JS | TS | TSX | JSON
|
||||
|
||||
const codemod: Codemod<Language> = async root => {
|
||||
const edits: Edit[] = []
|
||||
|
||||
root
|
||||
.root()
|
||||
.findAll({ rule: { kind: 'string' } })
|
||||
.forEach(node => {
|
||||
const replacement = packageSpecifiers.get(node.text())
|
||||
|
||||
if (replacement) {
|
||||
edits.push(node.replace(replacement))
|
||||
}
|
||||
})
|
||||
|
||||
return edits.length > 0 ? root.root().commitEdits(edits) : null
|
||||
}
|
||||
|
||||
export default codemod
|
||||
243
codemods/tiptap-v4-migration/scripts/vue-sfc-imports.mjs
Normal file
243
codemods/tiptap-v4-migration/scripts/vue-sfc-imports.mjs
Normal file
@@ -0,0 +1,243 @@
|
||||
import { readdir, readFile, writeFile } from 'node:fs/promises'
|
||||
import { resolve } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
const packageSpecifiers = new Map([
|
||||
['@tiptap/vue-3', '@tiptap/vue'],
|
||||
['@tiptap/extension-drag-handle-vue-3', '@tiptap/extension-drag-handle-vue'],
|
||||
])
|
||||
|
||||
const scriptPattern = /<script\b[^>]*>([\s\S]*?)<\/script>/gi
|
||||
const skippedDirectories = new Set(['.git', 'build', 'dist', 'node_modules'])
|
||||
|
||||
function skipTrivia(source, start) {
|
||||
let index = start
|
||||
|
||||
while (index < source.length) {
|
||||
if (/\s/.test(source[index])) {
|
||||
index += 1
|
||||
continue
|
||||
}
|
||||
|
||||
if (source.startsWith('//', index)) {
|
||||
const lineEnd = source.indexOf('\n', index + 2)
|
||||
index = lineEnd === -1 ? source.length : lineEnd + 1
|
||||
continue
|
||||
}
|
||||
|
||||
if (source.startsWith('/*', index)) {
|
||||
const commentEnd = source.indexOf('*/', index + 2)
|
||||
index = commentEnd === -1 ? source.length : commentEnd + 2
|
||||
continue
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
return index
|
||||
}
|
||||
|
||||
function readStringEnd(source, start) {
|
||||
const quote = source[start]
|
||||
|
||||
for (let index = start + 1; index < source.length; index += 1) {
|
||||
if (source[index] === '\\') {
|
||||
index += 1
|
||||
continue
|
||||
}
|
||||
|
||||
if (source[index] === quote) {
|
||||
return index
|
||||
}
|
||||
}
|
||||
|
||||
return source.length
|
||||
}
|
||||
|
||||
function readIdentifierEnd(source, start) {
|
||||
let index = start
|
||||
|
||||
while (index < source.length && /[\w$]/.test(source[index])) {
|
||||
index += 1
|
||||
}
|
||||
|
||||
return index
|
||||
}
|
||||
|
||||
function isIdentifierStart(character) {
|
||||
return /[A-Za-z_$]/.test(character)
|
||||
}
|
||||
|
||||
function getSpecifierEdit(source, start) {
|
||||
const quote = source[start]
|
||||
|
||||
if (quote !== "'" && quote !== '"') {
|
||||
return null
|
||||
}
|
||||
|
||||
const end = readStringEnd(source, start)
|
||||
const specifier = source.slice(start + 1, end)
|
||||
const replacement = packageSpecifiers.get(specifier)
|
||||
|
||||
if (!replacement || end === source.length) {
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
end: end + 1,
|
||||
start,
|
||||
text: `${quote}${replacement}${quote}`,
|
||||
}
|
||||
}
|
||||
|
||||
function findSpecifier(source, start, requiresCall) {
|
||||
let index = skipTrivia(source, start)
|
||||
|
||||
if (requiresCall) {
|
||||
if (source[index] !== '(') {
|
||||
return null
|
||||
}
|
||||
|
||||
index = skipTrivia(source, index + 1)
|
||||
}
|
||||
|
||||
return getSpecifierEdit(source, index)
|
||||
}
|
||||
|
||||
function findImportedSpecifier(source, start, limit = source.length) {
|
||||
const end = limit === -1 ? source.length : limit
|
||||
|
||||
for (let index = start; index < end; index += 1) {
|
||||
if (source.startsWith('//', index)) {
|
||||
const lineEnd = source.indexOf('\n', index + 2)
|
||||
index = lineEnd === -1 ? source.length : lineEnd
|
||||
continue
|
||||
}
|
||||
|
||||
if (source.startsWith('/*', index)) {
|
||||
const commentEnd = source.indexOf('*/', index + 2)
|
||||
index = commentEnd === -1 ? source.length : commentEnd + 1
|
||||
continue
|
||||
}
|
||||
|
||||
if (source[index] === "'" || source[index] === '"' || source[index] === '`') {
|
||||
index = readStringEnd(source, index)
|
||||
continue
|
||||
}
|
||||
|
||||
if (isIdentifierStart(source[index])) {
|
||||
const end = readIdentifierEnd(source, index)
|
||||
|
||||
if (source.slice(index, end) === 'from') {
|
||||
return findSpecifier(source, end, false)
|
||||
}
|
||||
|
||||
index = end - 1
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
function replacePackageSpecifiers(source) {
|
||||
const edits = []
|
||||
|
||||
for (let index = 0; index < source.length;) {
|
||||
if (source.startsWith('//', index)) {
|
||||
const lineEnd = source.indexOf('\n', index + 2)
|
||||
index = lineEnd === -1 ? source.length : lineEnd + 1
|
||||
continue
|
||||
}
|
||||
|
||||
if (source.startsWith('/*', index)) {
|
||||
const commentEnd = source.indexOf('*/', index + 2)
|
||||
index = commentEnd === -1 ? source.length : commentEnd + 2
|
||||
continue
|
||||
}
|
||||
|
||||
if (source[index] === "'" || source[index] === '"' || source[index] === '`') {
|
||||
index = readStringEnd(source, index) + 1
|
||||
continue
|
||||
}
|
||||
|
||||
if (!isIdentifierStart(source[index])) {
|
||||
index += 1
|
||||
continue
|
||||
}
|
||||
|
||||
const end = readIdentifierEnd(source, index)
|
||||
const identifier = source.slice(index, end)
|
||||
const previous = source.slice(0, index).trimEnd().at(-1)
|
||||
|
||||
if (previous === '.') {
|
||||
index = end
|
||||
continue
|
||||
}
|
||||
|
||||
const edit =
|
||||
identifier === 'import'
|
||||
? (findSpecifier(source, end, source[skipTrivia(source, end)] === '(') ??
|
||||
findImportedSpecifier(source, end))
|
||||
: identifier === 'export'
|
||||
? findImportedSpecifier(source, end, source.indexOf('\n', end))
|
||||
: identifier === 'require'
|
||||
? findSpecifier(source, end, true)
|
||||
: null
|
||||
|
||||
if (edit) {
|
||||
edits.push(edit)
|
||||
}
|
||||
|
||||
index = end
|
||||
}
|
||||
|
||||
return edits.reduceRight(
|
||||
(result, edit) => `${result.slice(0, edit.start)}${edit.text}${result.slice(edit.end)}`,
|
||||
source,
|
||||
)
|
||||
}
|
||||
|
||||
/** Rewrites known Tiptap package specifiers inside Vue script blocks. */
|
||||
export function transformVueSource(source) {
|
||||
return source.replace(scriptPattern, (script, content) =>
|
||||
script.replace(content, replacePackageSpecifiers(content)),
|
||||
)
|
||||
}
|
||||
|
||||
async function findVueFiles(directory) {
|
||||
const entries = await readdir(directory, { withFileTypes: true })
|
||||
const files = []
|
||||
|
||||
for (const entry of entries) {
|
||||
if (entry.isDirectory()) {
|
||||
if (!skippedDirectories.has(entry.name)) {
|
||||
files.push(...(await findVueFiles(resolve(directory, entry.name))))
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if (entry.isFile() && entry.name.endsWith('.vue')) {
|
||||
files.push(resolve(directory, entry.name))
|
||||
}
|
||||
}
|
||||
|
||||
return files
|
||||
}
|
||||
|
||||
async function migrateVueFiles(directory) {
|
||||
const files = await findVueFiles(directory)
|
||||
|
||||
for (const file of files) {
|
||||
const source = await readFile(file, 'utf8')
|
||||
const transformedSource = transformVueSource(source)
|
||||
|
||||
if (transformedSource !== source) {
|
||||
await writeFile(file, transformedSource)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
||||
await migrateVueFiles(resolve(process.argv[2] ?? process.cwd()))
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { Editor } from '@tiptap/vue'
|
||||
|
||||
const DragHandle = require('@tiptap/extension-drag-handle-vue')
|
||||
const legacyPackage = '@tiptap/vue-2'
|
||||
|
||||
export { Editor, DragHandle, legacyPackage }
|
||||
@@ -0,0 +1,6 @@
|
||||
import { Editor } from '@tiptap/vue-3'
|
||||
|
||||
const DragHandle = require('@tiptap/extension-drag-handle-vue-3')
|
||||
const legacyPackage = '@tiptap/vue-2'
|
||||
|
||||
export { Editor, DragHandle, legacyPackage }
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"@tiptap/vue": "^3.30.3",
|
||||
"@tiptap/extension-drag-handle-vue": "^3.30.3",
|
||||
"@tiptap/vue-2": "^3.30.3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"@tiptap/vue-3": "^3.30.3",
|
||||
"@tiptap/extension-drag-handle-vue-3": "^3.30.3",
|
||||
"@tiptap/vue-2": "^3.30.3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import { EditorContent } from '@tiptap/vue'
|
||||
|
||||
export const Content = () => <EditorContent />
|
||||
3
codemods/tiptap-v4-migration/tests/tsx/imports/input.tsx
Normal file
3
codemods/tiptap-v4-migration/tests/tsx/imports/input.tsx
Normal file
@@ -0,0 +1,3 @@
|
||||
import { EditorContent } from '@tiptap/vue-3'
|
||||
|
||||
export const Content = () => <EditorContent />
|
||||
@@ -0,0 +1,3 @@
|
||||
export { EditorContent } from '@tiptap/vue'
|
||||
|
||||
export const loadDragHandle = () => import('@tiptap/extension-drag-handle-vue')
|
||||
@@ -0,0 +1,3 @@
|
||||
export { EditorContent } from '@tiptap/vue-3'
|
||||
|
||||
export const loadDragHandle = () => import('@tiptap/extension-drag-handle-vue-3')
|
||||
48
codemods/tiptap-v4-migration/tests/vue-sfc-imports.test.mjs
Normal file
48
codemods/tiptap-v4-migration/tests/vue-sfc-imports.test.mjs
Normal file
@@ -0,0 +1,48 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import test from 'node:test'
|
||||
|
||||
import { transformVueSource } from '../scripts/vue-sfc-imports.mjs'
|
||||
|
||||
test('migrates imports inside Vue script blocks', () => {
|
||||
const source = `<template>{{ '@tiptap/vue-3' }}</template>
|
||||
<script setup lang="ts">
|
||||
import { EditorContent } from '@tiptap/vue-3'
|
||||
export { EditorContent } from '@tiptap/vue-3'
|
||||
export * from '@tiptap/extension-drag-handle-vue-3'
|
||||
import '@tiptap/vue-3'
|
||||
import '@tiptap/extension-drag-handle-vue-3'
|
||||
const DragHandle = require('@tiptap/extension-drag-handle-vue-3')
|
||||
const example = "import('@tiptap/vue-3')"
|
||||
// require('@tiptap/extension-drag-handle-vue-3')
|
||||
const legacyPackage = '@tiptap/vue-2'
|
||||
</script>`
|
||||
|
||||
const expected = `<template>{{ '@tiptap/vue-3' }}</template>
|
||||
<script setup lang="ts">
|
||||
import { EditorContent } from '@tiptap/vue'
|
||||
export { EditorContent } from '@tiptap/vue'
|
||||
export * from '@tiptap/extension-drag-handle-vue'
|
||||
import '@tiptap/vue'
|
||||
import '@tiptap/extension-drag-handle-vue'
|
||||
const DragHandle = require('@tiptap/extension-drag-handle-vue')
|
||||
const example = "import('@tiptap/vue-3')"
|
||||
// require('@tiptap/extension-drag-handle-vue-3')
|
||||
const legacyPackage = '@tiptap/vue-2'
|
||||
</script>`
|
||||
|
||||
assert.equal(transformVueSource(source), expected)
|
||||
})
|
||||
|
||||
test('does not scan past local exports', () => {
|
||||
const source = `<script setup>
|
||||
export { Local }
|
||||
import { EditorContent } from '@tiptap/vue-3'
|
||||
</script>`
|
||||
|
||||
const expected = `<script setup>
|
||||
export { Local }
|
||||
import { EditorContent } from '@tiptap/vue'
|
||||
</script>`
|
||||
|
||||
assert.equal(transformVueSource(source), expected)
|
||||
})
|
||||
18
codemods/tiptap-v4-migration/tsconfig.json
Normal file
18
codemods/tiptap-v4-migration/tsconfig.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"types": ["@codemod.com/jssg-types"],
|
||||
"allowImportingTsExtensions": true,
|
||||
"noEmit": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"erasableSyntaxOnly": true,
|
||||
"strict": true,
|
||||
"strictNullChecks": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"exclude": ["tests"]
|
||||
}
|
||||
51
codemods/tiptap-v4-migration/workflow.yaml
Normal file
51
codemods/tiptap-v4-migration/workflow.yaml
Normal file
@@ -0,0 +1,51 @@
|
||||
# yaml-language-server: $schema=https://raw.githubusercontent.com/codemod/codemod/refs/heads/main/schemas/workflow.json
|
||||
|
||||
version: '1'
|
||||
|
||||
params:
|
||||
schema:
|
||||
vue_target_path:
|
||||
name: Vue target path
|
||||
description: Directory containing Vue SFC files
|
||||
type: string
|
||||
default: '.'
|
||||
|
||||
nodes:
|
||||
- id: migrate-package-specifiers
|
||||
name: Migrate Vue package specifiers
|
||||
type: automatic
|
||||
steps:
|
||||
- name: Migrate JavaScript imports
|
||||
js-ast-grep:
|
||||
js_file: scripts/vue-package-renames.ts
|
||||
base_path: '.'
|
||||
language: 'javascript'
|
||||
include: ['**/*.{js,mjs,cjs}']
|
||||
exclude: ['**/node_modules/**', '**/dist/**', '**/build/**']
|
||||
|
||||
- name: Migrate TypeScript imports
|
||||
js-ast-grep:
|
||||
js_file: scripts/vue-package-renames.ts
|
||||
base_path: '.'
|
||||
language: 'typescript'
|
||||
include: ['**/*.{ts,mts,cts}']
|
||||
exclude: ['**/node_modules/**', '**/dist/**', '**/build/**']
|
||||
|
||||
- name: Migrate TSX imports
|
||||
js-ast-grep:
|
||||
js_file: scripts/vue-package-renames.ts
|
||||
base_path: '.'
|
||||
language: 'tsx'
|
||||
include: ['**/*.{tsx,jsx}']
|
||||
exclude: ['**/node_modules/**', '**/dist/**', '**/build/**']
|
||||
|
||||
- name: Migrate package manifests
|
||||
js-ast-grep:
|
||||
js_file: scripts/vue-package-renames.ts
|
||||
base_path: '.'
|
||||
language: 'json'
|
||||
include: ['**/package.json']
|
||||
exclude: ['**/node_modules/**']
|
||||
|
||||
- name: Migrate Vue SFC imports
|
||||
run: node scripts/vue-sfc-imports.mjs "${{ params.vue_target_path }}"
|
||||
1
commitlint.config.mjs
Normal file
1
commitlint.config.mjs
Normal file
@@ -0,0 +1 @@
|
||||
export default { extends: ['@commitlint/config-conventional'] }
|
||||
571
demos/CHANGELOG.md
Normal file
571
demos/CHANGELOG.md
Normal file
@@ -0,0 +1,571 @@
|
||||
# Change Log
|
||||
|
||||
## 3.0.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- a1c6243: Allow KaTeX 0.17
|
||||
|
||||
## 3.0.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 75e8404: Fix the `Selection` extension highlighting beyond the selected text on multi-line selections: the native browser selection is now hidden while the editor is blurred, so only the styled `.selection` decoration is shown.
|
||||
|
||||
## 3.0.1
|
||||
|
||||
### Major Changes
|
||||
|
||||
- a92f4a6: We are now building packages with tsup which does not support UMD builds, please repackage if you require UMD builds
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 89bd9c7: Enforce type imports so that the bundler ignores TypeScript type imports when generating the index.js file of the dist directory
|
||||
- 8c69002: Synced beta with stable features
|
||||
|
||||
## 3.0.0-beta.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 8c69002: Synced beta with stable features
|
||||
|
||||
## 3.0.0-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 89bd9c7: Enforce type imports so that the bundler ignores TypeScript type imports when generating the index.js file of the dist directory
|
||||
|
||||
## 3.0.0-next.1
|
||||
|
||||
### Major Changes
|
||||
|
||||
- a92f4a6: We are now building packages with tsup which does not support UMD builds, please repackage if you require UMD builds
|
||||
|
||||
## 3.0.0-next.0
|
||||
|
||||
### Major Changes
|
||||
|
||||
- a92f4a6: We are now building packages with tsup which does not support UMD builds, please repackage if you require UMD builds
|
||||
|
||||
## 2.5.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 98fffbb: Upgraded prosemirror-tables to 1.6.3 to fix cells being resizable while the editor is uneditable
|
||||
|
||||
## 2.5.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 7619215: The link extension's `validate` option now applies to both auto-linking and XSS mitigation. While, the new `shouldAutoLink` option is used to disable auto linking on an otherwise valid url.
|
||||
|
||||
## 2.5.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 6834a7f: Bundling of packages no longer includes tiptap dependency type definitions
|
||||
|
||||
## 2.4.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- d6e56c4: declare lowlight to be a peer dep of extension-code-block-lowlight, update usage to v3
|
||||
|
||||
## 2.4.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 85d21ca: Updated demos and reverted vue specific performance enhancements until we know they work
|
||||
|
||||
> in commit ff04353b3ee0e6fc63733a673e2b27d2272a3355 revert: "fix(vue-3): faster component rendering (#5206)"
|
||||
> This reverts commit 31f37464912b7b21f3a565ca63222b9f5b6cce00.
|
||||
|
||||
and
|
||||
|
||||
> in commit dbab8e42eac893a0237566fb30c14b4ed0f3674a revert: "fix(vue-3): fix editor.state updating too late during a transaction due to reactiveState fixes #4870 (#5252)"
|
||||
> This reverts commit 509676ed4a63b84b904a98c1e34d18449d25c2a7.
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [2.4.0](https://github.com/ueberdosis/tiptap/compare/v2.3.2...v2.4.0) (2024-05-14)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
## [2.3.2](https://github.com/ueberdosis/tiptap/compare/v2.3.1...v2.3.2) (2024-05-08)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- NodePos querySelectorAll function ([#5094](https://github.com/ueberdosis/tiptap/issues/5094)) ([4900a27](https://github.com/ueberdosis/tiptap/commit/4900a27c5389d9a2d0d69f407ca3db0155304315))
|
||||
|
||||
## [2.3.1](https://github.com/ueberdosis/tiptap/compare/v2.3.0...v2.3.1) (2024-04-30)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
# [2.3.0](https://github.com/ueberdosis/tiptap/compare/v2.2.6...v2.3.0) (2024-04-09)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **core:** fix nodepos child lookup ([#5038](https://github.com/ueberdosis/tiptap/issues/5038)) ([22ced31](https://github.com/ueberdosis/tiptap/commit/22ced318723003365fbfd8f59b8dac79c7563017))
|
||||
|
||||
### Features
|
||||
|
||||
- **core:** apply input and paste rules when using insertContent methods ([#5046](https://github.com/ueberdosis/tiptap/issues/5046)) ([96b6abc](https://github.com/ueberdosis/tiptap/commit/96b6abcf6edbc6cac03a391130d9feebb6de3a04))
|
||||
|
||||
## [2.2.6](https://github.com/ueberdosis/tiptap/compare/v2.2.5...v2.2.6) (2024-04-06)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
## [2.2.5](https://github.com/ueberdosis/tiptap/compare/v2.2.4...v2.2.5) (2024-04-05)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
## [2.2.4](https://github.com/ueberdosis/tiptap/compare/v2.2.3...v2.2.4) (2024-02-23)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
## [2.2.3](https://github.com/ueberdosis/tiptap/compare/v2.2.2...v2.2.3) (2024-02-15)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- fix test path ([21aa96d](https://github.com/ueberdosis/tiptap/commit/21aa96dee8deab1f439b7f655b8ed266a516a4cd))
|
||||
|
||||
## [2.2.2](https://github.com/ueberdosis/tiptap/compare/v2.2.1...v2.2.2) (2024-02-07)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
## [2.2.1](https://github.com/ueberdosis/tiptap/compare/v2.2.0...v2.2.1) (2024-01-31)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
# [2.2.0](https://github.com/ueberdosis/tiptap/compare/v2.1.16...v2.2.0) (2024-01-29)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **core:** fix new lines being added via elementFromString ([#4767](https://github.com/ueberdosis/tiptap/issues/4767)) ([b7a2504](https://github.com/ueberdosis/tiptap/commit/b7a2504f16f46563537c890930cb2c332c256175))
|
||||
- fix imports, fix demos, unpin y-prosemirror ([681aa57](https://github.com/ueberdosis/tiptap/commit/681aa577bff500015c3f925e300c55a71c73efaf))
|
||||
- fix newline stripping via insertContent ([8954007](https://github.com/ueberdosis/tiptap/commit/8954007b2b92b040d69b26a0866ae58fabf5e512))
|
||||
|
||||
# [2.2.0-rc.8](https://github.com/ueberdosis/tiptap/compare/v2.1.14...v2.2.0-rc.8) (2024-01-08)
|
||||
|
||||
# [2.2.0-rc.7](https://github.com/ueberdosis/tiptap/compare/v2.2.0-rc.6...v2.2.0-rc.7) (2023-11-27)
|
||||
|
||||
# [2.2.0-rc.6](https://github.com/ueberdosis/tiptap/compare/v2.2.0-rc.5...v2.2.0-rc.6) (2023-11-23)
|
||||
|
||||
# [2.2.0-rc.4](https://github.com/ueberdosis/tiptap/compare/v2.1.11...v2.2.0-rc.4) (2023-10-10)
|
||||
|
||||
# [2.2.0-rc.3](https://github.com/ueberdosis/tiptap/compare/v2.2.0-rc.2...v2.2.0-rc.3) (2023-08-18)
|
||||
|
||||
# [2.2.0-rc.1](https://github.com/ueberdosis/tiptap/compare/v2.2.0-rc.0...v2.2.0-rc.1) (2023-08-18)
|
||||
|
||||
# [2.2.0-rc.0](https://github.com/ueberdosis/tiptap/compare/v2.1.5...v2.2.0-rc.0) (2023-08-18)
|
||||
|
||||
### Features
|
||||
|
||||
- **placeholder:** allow editor-is-empty class on any node ([#4335](https://github.com/ueberdosis/tiptap/issues/4335)) ([ff929b1](https://github.com/ueberdosis/tiptap/commit/ff929b179de930619005a773bb4186ae2aa2ec58))
|
||||
|
||||
## [2.1.16](https://github.com/ueberdosis/tiptap/compare/v2.1.15...v2.1.16) (2024-01-10)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **core:** fix new lines being added via elementFromString ([#4767](https://github.com/ueberdosis/tiptap/issues/4767)) ([2235908](https://github.com/ueberdosis/tiptap/commit/2235908c28f388eda041d1d5d017554d513fe909))
|
||||
|
||||
## [2.1.15](https://github.com/ueberdosis/tiptap/compare/v2.1.14...v2.1.15) (2024-01-08)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **core:** fix insertContentAt keeping new lines in html content ([#4465](https://github.com/ueberdosis/tiptap/issues/4465)) ([135a12f](https://github.com/ueberdosis/tiptap/commit/135a12f7aa2df839a0b619704110a360b980c738))
|
||||
- **link:** fix tests ([d495d92](https://github.com/ueberdosis/tiptap/commit/d495d92a1f7b1c51e09ac8f4934e15a2d1cf070d))
|
||||
|
||||
## [2.1.14](https://github.com/ueberdosis/tiptap/compare/v2.1.13...v2.1.14) (2024-01-08)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **typography:** require spaces after divisions to not break date formats ([#4696](https://github.com/ueberdosis/tiptap/issues/4696)) ([f6d7e00](https://github.com/ueberdosis/tiptap/commit/f6d7e00a746a67fa440a3fa0f5362295959873d2))
|
||||
|
||||
## [2.1.13](https://github.com/ueberdosis/tiptap/compare/v2.1.12...v2.1.13) (2023-11-30)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
## [2.1.12](https://github.com/ueberdosis/tiptap/compare/v2.1.11...v2.1.12) (2023-10-11)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
## [2.1.11](https://github.com/ueberdosis/tiptap/compare/v2.1.10...v2.1.11) (2023-09-20)
|
||||
|
||||
### Reverts
|
||||
|
||||
- Revert "v2.2.11" ([6aa755a](https://github.com/ueberdosis/tiptap/commit/6aa755a04b9955fc175c7ab33dee527d0d5deef0))
|
||||
|
||||
## [2.1.10](https://github.com/ueberdosis/tiptap/compare/v2.1.9...v2.1.10) (2023-09-15)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
## [2.1.9](https://github.com/ueberdosis/tiptap/compare/v2.1.8...v2.1.9) (2023-09-14)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
## [2.1.8](https://github.com/ueberdosis/tiptap-workspace/compare/v2.1.7...v2.1.8) (2023-09-04)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
## [2.1.7](https://github.com/ueberdosis/tiptap-workspace/compare/v2.1.6...v2.1.7) (2023-09-04)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
## [2.1.6](https://github.com/ueberdosis/tiptap/compare/v2.1.5...v2.1.6) (2023-08-18)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
# [2.2.0-rc.2](https://github.com/ueberdosis/tiptap/compare/v2.1.6...v2.2.0-rc.2) (2023-08-18)
|
||||
|
||||
# [2.2.0-rc.1](https://github.com/ueberdosis/tiptap/compare/v2.2.0-rc.0...v2.2.0-rc.1) (2023-08-18)
|
||||
|
||||
# [2.2.0-rc.0](https://github.com/ueberdosis/tiptap/compare/v2.1.5...v2.2.0-rc.0) (2023-08-18)
|
||||
|
||||
### Features
|
||||
|
||||
- **placeholder:** allow editor-is-empty class on any node ([#4335](https://github.com/ueberdosis/tiptap/issues/4335)) ([ff929b1](https://github.com/ueberdosis/tiptap/commit/ff929b179de930619005a773bb4186ae2aa2ec58))
|
||||
|
||||
# [2.2.0-rc.1](https://github.com/ueberdosis/tiptap/compare/v2.2.0-rc.0...v2.2.0-rc.1) (2023-08-18)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
# [2.2.0-rc.0](https://github.com/ueberdosis/tiptap/compare/v2.1.5...v2.2.0-rc.0) (2023-08-18)
|
||||
|
||||
### Features
|
||||
|
||||
- **placeholder:** allow editor-is-empty class on any node ([#4335](https://github.com/ueberdosis/tiptap/issues/4335)) ([ff929b1](https://github.com/ueberdosis/tiptap/commit/ff929b179de930619005a773bb4186ae2aa2ec58))
|
||||
|
||||
## [2.1.6](https://github.com/ueberdosis/tiptap/compare/v2.1.5...v2.1.6) (2023-08-18)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
## [2.1.5](https://github.com/ueberdosis/tiptap/compare/v2.1.4...v2.1.5) (2023-08-18)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
## [2.1.4](https://github.com/ueberdosis/tiptap/compare/v2.1.3...v2.1.4) (2023-08-18)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
## [2.1.3](https://github.com/ueberdosis/tiptap/compare/v2.1.2...v2.1.3) (2023-08-18)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
## [2.1.2](https://github.com/ueberdosis/tiptap/compare/v2.1.1...v2.1.2) (2023-08-17)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **core:** fix error when merging class attributes ([#4340](https://github.com/ueberdosis/tiptap/issues/4340)) ([a251946](https://github.com/ueberdosis/tiptap/commit/a2519468589e2baa44901a66a3a06b24dc8626d6))
|
||||
|
||||
## [2.1.1](https://github.com/ueberdosis/tiptap/compare/v2.1.0...v2.1.1) (2023-08-16)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
# [2.1.0](https://github.com/ueberdosis/tiptap-workspace/compare/v2.1.0-rc.14...v2.1.0) (2023-08-16)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
# [2.1.0-rc.14](https://github.com/ueberdosis/tiptap-workspace/compare/v2.1.0-rc.13...v2.1.0-rc.14) (2023-08-11)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
# [2.1.0-rc.13](https://github.com/ueberdosis/tiptap-workspace/compare/v2.0.4...v2.1.0-rc.13) (2023-08-11)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **demos:** add missing extensions ([6383fd5](https://github.com/ueberdosis/tiptap-workspace/commit/6383fd54080b2ad555286cd0e7c4ad880200200f))
|
||||
- **demos:** update deps ([05a2edf](https://github.com/ueberdosis/tiptap-workspace/commit/05a2edfc16e297effa86d1583fb1680be0320f25))
|
||||
- **strikethrough:** update strikethrough shortcut ([#4288](https://github.com/ueberdosis/tiptap-workspace/issues/4288)) ([fd35db4](https://github.com/ueberdosis/tiptap-workspace/commit/fd35db4d090d9fdfef1196fb1f6f858f13cf53d1))
|
||||
|
||||
# [2.1.0-rc.12](https://github.com/ueberdosis/tiptap-workspace/compare/v2.1.0-rc.11...v2.1.0-rc.12) (2023-07-14)
|
||||
|
||||
# [2.1.0-rc.11](https://github.com/ueberdosis/tiptap-workspace/compare/v2.1.0-rc.10...v2.1.0-rc.11) (2023-07-07)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **tests:** fix link rel tests ([c1d1854](https://github.com/ueberdosis/tiptap-workspace/commit/c1d18543b03b1fb6b99a2f3546aa5da10c919920))
|
||||
|
||||
# [2.1.0-rc.10](https://github.com/ueberdosis/tiptap-workspace/compare/v2.1.0-rc.9...v2.1.0-rc.10) (2023-07-07)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **react:** check props.clientRect before creating ReactRenderer ([#4138](https://github.com/ueberdosis/tiptap-workspace/issues/4138)) ([d710846](https://github.com/ueberdosis/tiptap-workspace/commit/d710846ecb6a3059dfbc21300b9a4b887a8defa3))
|
||||
|
||||
# [2.1.0-rc.9](https://github.com/ueberdosis/tiptap-workspace/compare/v2.1.0-rc.8...v2.1.0-rc.9) (2023-06-15)
|
||||
|
||||
# [2.1.0-rc.8](https://github.com/ueberdosis/tiptap-workspace/compare/v2.1.0-rc.7...v2.1.0-rc.8) (2023-05-25)
|
||||
|
||||
# [2.1.0-rc.5](https://github.com/ueberdosis/tiptap-workspace/compare/v2.1.0-rc.4...v2.1.0-rc.5) (2023-05-25)
|
||||
|
||||
### Features
|
||||
|
||||
- add tiptap class ([614fc80](https://github.com/ueberdosis/tiptap-workspace/commit/614fc8082c376bf3c40a05c23ceda6b4a6fbf8d0))
|
||||
|
||||
# [2.1.0-rc.4](https://github.com/ueberdosis/tiptap-workspace/compare/v2.1.0-rc.3...v2.1.0-rc.4) (2023-04-27)
|
||||
|
||||
# [2.1.0-rc.3](https://github.com/ueberdosis/tiptap-workspace/compare/v2.1.0-rc.2...v2.1.0-rc.3) (2023-04-26)
|
||||
|
||||
# [2.1.0-rc.2](https://github.com/ueberdosis/tiptap-workspace/compare/v2.0.3...v2.1.0-rc.2) (2023-04-26)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **extension-link:** fix link not being kept when pasting url with link ([#3975](https://github.com/ueberdosis/tiptap-workspace/issues/3975)) ([e7d7d49](https://github.com/ueberdosis/tiptap-workspace/commit/e7d7d496376c8c11e24c342e20bd179a6ea7dcee))
|
||||
|
||||
# [2.1.0-rc.1](https://github.com/ueberdosis/tiptap-workspace/compare/v2.1.0-rc.0...v2.1.0-rc.1) (2023-04-12)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **bubble-menu:** fix debounce not working with collab/collaboration cursor ([#3956](https://github.com/ueberdosis/tiptap-workspace/issues/3956)) ([e8cef04](https://github.com/ueberdosis/tiptap-workspace/commit/e8cef0404b5039ec2657536976b8b31931afd337))
|
||||
|
||||
# [2.1.0-rc.0](https://github.com/ueberdosis/tiptap-workspace/compare/v2.0.2...v2.1.0-rc.0) (2023-04-05)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- clear nodes when cursor at start of empty isolating parent ([#3943](https://github.com/ueberdosis/tiptap-workspace/issues/3943)) ([7278ee2](https://github.com/ueberdosis/tiptap-workspace/commit/7278ee2b05de2f96efddf3b1dc3bfd3d52262cbb))
|
||||
- Update peerDependencies to fix lerna version tasks ([#3914](https://github.com/ueberdosis/tiptap-workspace/issues/3914)) ([0c1bba3](https://github.com/ueberdosis/tiptap-workspace/commit/0c1bba3137b535776bcef95ff3c55e13f5a2db46))
|
||||
|
||||
# [2.1.0-rc.12](https://github.com/ueberdosis/tiptap-workspace/compare/v2.1.0-rc.11...v2.1.0-rc.12) (2023-07-14)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
# [2.1.0-rc.11](https://github.com/ueberdosis/tiptap/compare/v2.1.0-rc.10...v2.1.0-rc.11) (2023-07-07)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **tests:** fix link rel tests ([c1d1854](https://github.com/ueberdosis/tiptap/commit/c1d18543b03b1fb6b99a2f3546aa5da10c919920))
|
||||
|
||||
# [2.1.0-rc.10](https://github.com/ueberdosis/tiptap/compare/v2.1.0-rc.9...v2.1.0-rc.10) (2023-07-07)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **react:** check props.clientRect before creating ReactRenderer ([#4138](https://github.com/ueberdosis/tiptap/issues/4138)) ([d710846](https://github.com/ueberdosis/tiptap/commit/d710846ecb6a3059dfbc21300b9a4b887a8defa3))
|
||||
|
||||
# [2.1.0-rc.9](https://github.com/ueberdosis/tiptap/compare/v2.1.0-rc.8...v2.1.0-rc.9) (2023-06-15)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
# [2.1.0-rc.8](https://github.com/ueberdosis/tiptap/compare/v2.1.0-rc.7...v2.1.0-rc.8) (2023-05-25)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
# [2.1.0-rc.7](https://github.com/ueberdosis/tiptap/compare/v2.1.0-rc.6...v2.1.0-rc.7) (2023-05-25)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
# [2.1.0-rc.6](https://github.com/ueberdosis/tiptap/compare/v2.1.0-rc.5...v2.1.0-rc.6) (2023-05-25)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
# [2.1.0-rc.5](https://github.com/ueberdosis/tiptap/compare/v2.1.0-rc.4...v2.1.0-rc.5) (2023-05-25)
|
||||
|
||||
### Features
|
||||
|
||||
- add tiptap class ([614fc80](https://github.com/ueberdosis/tiptap/commit/614fc8082c376bf3c40a05c23ceda6b4a6fbf8d0))
|
||||
|
||||
# [2.1.0-rc.4](https://github.com/ueberdosis/tiptap/compare/v2.1.0-rc.3...v2.1.0-rc.4) (2023-04-27)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
# [2.1.0-rc.3](https://github.com/ueberdosis/tiptap/compare/v2.1.0-rc.2...v2.1.0-rc.3) (2023-04-26)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
# [2.1.0-rc.2](https://github.com/ueberdosis/tiptap/compare/v2.0.3...v2.1.0-rc.2) (2023-04-26)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **extension-link:** fix link not being kept when pasting url with link ([#3975](https://github.com/ueberdosis/tiptap/issues/3975)) ([e7d7d49](https://github.com/ueberdosis/tiptap/commit/e7d7d496376c8c11e24c342e20bd179a6ea7dcee))
|
||||
|
||||
# [2.1.0-rc.1](https://github.com/ueberdosis/tiptap/compare/v2.1.0-rc.0...v2.1.0-rc.1) (2023-04-12)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **bubble-menu:** fix debounce not working with collab/collaboration cursor ([#3956](https://github.com/ueberdosis/tiptap/issues/3956)) ([e8cef04](https://github.com/ueberdosis/tiptap/commit/e8cef0404b5039ec2657536976b8b31931afd337))
|
||||
|
||||
# [2.1.0-rc.0](https://github.com/ueberdosis/tiptap/compare/v2.0.2...v2.1.0-rc.0) (2023-04-05)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- clear nodes when cursor at start of empty isolating parent ([#3943](https://github.com/ueberdosis/tiptap/issues/3943)) ([7278ee2](https://github.com/ueberdosis/tiptap/commit/7278ee2b05de2f96efddf3b1dc3bfd3d52262cbb))
|
||||
- Update peerDependencies to fix lerna version tasks ([#3914](https://github.com/ueberdosis/tiptap/issues/3914)) ([0c1bba3](https://github.com/ueberdosis/tiptap/commit/0c1bba3137b535776bcef95ff3c55e13f5a2db46))
|
||||
|
||||
# [2.1.0-rc.0](https://github.com/ueberdosis/tiptap/compare/v2.0.2...v2.1.0-rc.0) (2023-04-05)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- clear nodes when cursor at start of empty isolating parent ([#3943](https://github.com/ueberdosis/tiptap/issues/3943)) ([7278ee2](https://github.com/ueberdosis/tiptap/commit/7278ee2b05de2f96efddf3b1dc3bfd3d52262cbb))
|
||||
- **bubble-menu:** fix debounce not working with collab/collaboration cursor ([#3956](https://github.com/ueberdosis/tiptap/issues/3956)) ([a78f8cd](https://github.com/ueberdosis/tiptap/commit/a78f8cd9646008e4db938fa3c22b0714c8bb5849))
|
||||
|
||||
## [2.0.3](https://github.com/ueberdosis/tiptap/compare/v2.0.2...v2.0.3) (2023-04-13)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **bubble-menu:** fix debounce not working with collab/collaboration cursor ([#3956](https://github.com/ueberdosis/tiptap/issues/3956)) ([e8cef04](https://github.com/ueberdosis/tiptap/commit/e8cef0404b5039ec2657536976b8b31931afd337))
|
||||
|
||||
## [2.0.2](https://github.com/ueberdosis/tiptap/compare/v2.0.1...v2.0.2) (2023-04-03)
|
||||
|
||||
### Features
|
||||
|
||||
- add box-shadow to collab demo ([c5496c1](https://github.com/ueberdosis/tiptap/commit/c5496c1b27783150dafb5ebdf6bda43648a46316))
|
||||
- landingpage demo ([#3925](https://github.com/ueberdosis/tiptap/issues/3925)) ([958925f](https://github.com/ueberdosis/tiptap/commit/958925f2560ca786cd0cf52b83b7ae51deb7dd77))
|
||||
- Tiptap collab demo styling ([87840b0](https://github.com/ueberdosis/tiptap/commit/87840b0f0821ca65d9f104d9c90512021aa70113))
|
||||
|
||||
## [2.0.1](https://github.com/ueberdosis/tiptap/compare/v2.0.0...v2.0.1) (2023-03-30)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Update peerDependencies to fix lerna version tasks ([#3914](https://github.com/ueberdosis/tiptap/issues/3914)) ([0534f76](https://github.com/ueberdosis/tiptap/commit/0534f76401bf5399c01ca7f39d87f7221d91b4f7))
|
||||
|
||||
# [2.0.0-beta.220](https://github.com/ueberdosis/tiptap/compare/v2.0.0-beta.219...v2.0.0-beta.220) (2023-02-28)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **core:** fix destroyed view causing errors on dispatchTransaction ([#3799](https://github.com/ueberdosis/tiptap/issues/3799)) ([3c07ca0](https://github.com/ueberdosis/tiptap/commit/3c07ca0b9c48cef60d56acdd44812e20e05fc928))
|
||||
- **tests:** fix tests for lists ([02eec8a](https://github.com/ueberdosis/tiptap/commit/02eec8aaefc2709dc20f91c3c8f9eca84cddc12d))
|
||||
|
||||
# [2.0.0-beta.219](https://github.com/ueberdosis/tiptap/compare/v2.0.0-beta.218...v2.0.0-beta.219) (2023-02-27)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **core:** allow insertContentAt and insertContent text node arrays ([#3790](https://github.com/ueberdosis/tiptap/issues/3790)) ([0300630](https://github.com/ueberdosis/tiptap/commit/0300630a5b04b61d4eef8155f24ca0ef2d683966))
|
||||
|
||||
### Features
|
||||
|
||||
- [#3540](https://github.com/ueberdosis/tiptap/issues/3540) Ability to preserve marks on lists ([#3541](https://github.com/ueberdosis/tiptap/issues/3541)) ([36bb1e1](https://github.com/ueberdosis/tiptap/commit/36bb1e1041f91da6437272e7196702df868eae0f))
|
||||
|
||||
# [2.0.0-beta.218](https://github.com/ueberdosis/tiptap/compare/v2.0.0-beta.217...v2.0.0-beta.218) (2023-02-18)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
# [2.0.0-beta.217](https://github.com/ueberdosis/tiptap/compare/v2.0.0-beta.216...v2.0.0-beta.217) (2023-02-09)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
# [2.0.0-beta.216](https://github.com/ueberdosis/tiptap/compare/v2.0.0-beta.215...v2.0.0-beta.216) (2023-02-08)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
# [2.0.0-beta.215](https://github.com/ueberdosis/tiptap/compare/v2.0.0-beta.214...v2.0.0-beta.215) (2023-02-08)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
# [2.0.0-beta.214](https://github.com/ueberdosis/tiptap/compare/v2.0.0-beta.213...v2.0.0-beta.214) (2023-02-08)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
# [2.0.0-beta.213](https://github.com/ueberdosis/tiptap/compare/v2.0.0-beta.212...v2.0.0-beta.213) (2023-02-07)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
# [2.0.0-beta.212](https://github.com/ueberdosis/tiptap/compare/v2.0.0-beta.211...v2.0.0-beta.212) (2023-02-03)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
# [2.0.0-beta.211](https://github.com/ueberdosis/tiptap/compare/v2.0.0-beta.210...v2.0.0-beta.211) (2023-02-02)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
# [2.0.0-beta.210](https://github.com/ueberdosis/tiptap/compare/v2.0.0-beta.209...v2.0.0-beta.210) (2023-02-02)
|
||||
|
||||
### Features
|
||||
|
||||
- **pm:** new prosemirror package for dependency resolving ([f387ad3](https://github.com/ueberdosis/tiptap/commit/f387ad3dd4c2b30eaea33fb0ba0b42e0cd39263b))
|
||||
|
||||
# [2.0.0-beta.209](https://github.com/ueberdosis/tiptap/compare/v2.0.0-beta.208...v2.0.0-beta.209) (2022-12-16)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
# [2.0.0-beta.208](https://github.com/ueberdosis/tiptap/compare/v2.0.0-beta.207...v2.0.0-beta.208) (2022-12-16)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
# [2.0.0-beta.207](https://github.com/ueberdosis/tiptap/compare/v2.0.0-beta.206...v2.0.0-beta.207) (2022-12-08)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
# [2.0.0-beta.206](https://github.com/ueberdosis/tiptap/compare/v2.0.0-beta.205...v2.0.0-beta.206) (2022-12-08)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
# [2.0.0-beta.205](https://github.com/ueberdosis/tiptap/compare/v2.0.0-beta.204...v2.0.0-beta.205) (2022-12-05)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
# [2.0.0-beta.204](https://github.com/ueberdosis/tiptap/compare/v2.0.0-beta.203...v2.0.0-beta.204) (2022-11-25)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **tests:** fix autolink validation test ([5150095](https://github.com/ueberdosis/tiptap/commit/5150095c6b510c080f4aa35f54d2387543f86da8))
|
||||
|
||||
# [2.0.0-beta.203](https://github.com/ueberdosis/tiptap/compare/v2.0.0-beta.202...v2.0.0-beta.203) (2022-11-24)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **extension/table:** move dependency from @\_ueberdosis to [@tiptap](https://github.com/tiptap) ([#3448](https://github.com/ueberdosis/tiptap/issues/3448)) ([31c3a9a](https://github.com/ueberdosis/tiptap/commit/31c3a9aad9eb37f445eadcd27135611291178ca6))
|
||||
|
||||
# [2.0.0-beta.202](https://github.com/ueberdosis/tiptap/compare/v2.0.0-beta.201...v2.0.0-beta.202) (2022-11-04)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
# [2.0.0-beta.201](https://github.com/ueberdosis/tiptap/compare/v2.0.0-beta.200...v2.0.0-beta.201) (2022-11-04)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
# [2.0.0-beta.200](https://github.com/ueberdosis/tiptap/compare/v2.0.0-beta.199...v2.0.0-beta.200) (2022-11-04)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
# [2.0.0-beta.199](https://github.com/ueberdosis/tiptap/compare/v2.0.0-beta.198...v2.0.0-beta.199) (2022-09-30)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
# [2.0.0-beta.198](https://github.com/ueberdosis/tiptap/compare/v2.0.0-beta.197...v2.0.0-beta.198) (2022-09-29)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
# [2.0.0-beta.197](https://github.com/ueberdosis/tiptap/compare/v2.0.0-beta.196...v2.0.0-beta.197) (2022-09-26)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **core:** Can() does not work for setting marks ([#3223](https://github.com/ueberdosis/tiptap/issues/3223)) ([17a41da](https://github.com/ueberdosis/tiptap/commit/17a41da5a7a14879cf490c81914084791c4c494c))
|
||||
|
||||
# [2.0.0-beta.196](https://github.com/ueberdosis/tiptap/compare/v2.0.0-beta.195...v2.0.0-beta.196) (2022-09-20)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
# [2.0.0-beta.195](https://github.com/ueberdosis/tiptap/compare/v2.0.0-beta.194...v2.0.0-beta.195) (2022-09-14)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **extension/bubble-menu:** :bug: fix bubble menu and floating menu being available when editor not editable ([#3195](https://github.com/ueberdosis/tiptap/issues/3195)) ([fa96749](https://github.com/ueberdosis/tiptap/commit/fa96749ce22ec67125da491cfeeb38623b9f0d6e))
|
||||
|
||||
# [2.0.0-beta.194](https://github.com/ueberdosis/tiptap/compare/v2.0.0-beta.193...v2.0.0-beta.194) (2022-09-11)
|
||||
|
||||
**Note:** Version bump only for package tiptap-demos
|
||||
|
||||
# [2.0.0-beta.193](https://github.com/ueberdosis/tiptap/compare/v0.1.2...v2.0.0-beta.193) (2022-09-10)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- bump documents ([43611ea](https://github.com/ueberdosis/tiptap/commit/43611ea2e70d3dc66ff907ba7ca377bf74814543))
|
||||
- disable broken tests for experiements with further todo message ([b8ae9e2](https://github.com/ueberdosis/tiptap/commit/b8ae9e27622857093c6ca539901956da5cc291e5))
|
||||
- don’t check for active node in wrapIn command, fix [#1059](https://github.com/ueberdosis/tiptap/issues/1059) ([170ec4b](https://github.com/ueberdosis/tiptap/commit/170ec4be5b3c8362890ca3100a223b505f788381))
|
||||
- **extension/collaboration:** :ambulance: pin y-prosemirror version to 1.0.20 to fix broken functionality with vue ([5989f3b](https://github.com/ueberdosis/tiptap/commit/5989f3b780bb64b2884d81dcd41a95d98a0714b2))
|
||||
- fix RangeError bug when selecting all text, fix [#2490](https://github.com/ueberdosis/tiptap/issues/2490) ([70422dd](https://github.com/ueberdosis/tiptap/commit/70422dd107ed1ecdd8dfe41a8a93297124d2f1e0))
|
||||
- **maintainment:** fix cjs issues with prosemirror-tables ([eb92597](https://github.com/ueberdosis/tiptap/commit/eb925976038fbf59f6ba333ccc57ea84113da00e))
|
||||
- remove some magic strings ([6c34dec](https://github.com/ueberdosis/tiptap/commit/6c34dec33ac39c9f037a0a72e4525f3fc6d422bf))
|
||||
- **suggestion:** :bug: make clientrect prop optional as it can potentially be undefined ([#2813](https://github.com/ueberdosis/tiptap/issues/2813)) ([f019f70](https://github.com/ueberdosis/tiptap/commit/f019f70a19c34715e2d5c3921d348e11c7ac51a3)), closes [#2795](https://github.com/ueberdosis/tiptap/issues/2795)
|
||||
- temp fix collaboration demo ([4528756](https://github.com/ueberdosis/tiptap/commit/45287563f3cfb389095a2794cb2001d65e56d633))
|
||||
|
||||
### Features
|
||||
|
||||
- Add extension storage ([#2069](https://github.com/ueberdosis/tiptap/issues/2069)) ([7ffabf2](https://github.com/ueberdosis/tiptap/commit/7ffabf251c408a652eec1931cc78a8bd43cccb67))
|
||||
- add getText() and generateText() methods (fix [#1428](https://github.com/ueberdosis/tiptap/issues/1428)) ([#1875](https://github.com/ueberdosis/tiptap/issues/1875)) ([fe6a3e7](https://github.com/ueberdosis/tiptap/commit/fe6a3e7491f6a42123d3d8a92ab588f2a40d7799))
|
||||
- add some improvements to `CharacterCount` extension ([#2256](https://github.com/ueberdosis/tiptap/issues/2256)), fix [#1049](https://github.com/ueberdosis/tiptap/issues/1049), fix [#1550](https://github.com/ueberdosis/tiptap/issues/1550), fix [#1839](https://github.com/ueberdosis/tiptap/issues/1839), fix [#2245](https://github.com/ueberdosis/tiptap/issues/2245) ([5daa870](https://github.com/ueberdosis/tiptap/commit/5daa870b0906f0387fe07041681bc6f5b3774617))
|
||||
- Add support for autolink ([#2226](https://github.com/ueberdosis/tiptap/issues/2226)) ([3d68981](https://github.com/ueberdosis/tiptap/commit/3d68981b47d087fff40549d2143eb952fc9e0a50))
|
||||
- **extension-link:** :sparkles: add validate option to link extension ([23e67ad](https://github.com/ueberdosis/tiptap/commit/23e67adfa730df7364bc31220d0ed0e8ea522593)), closes [#2779](https://github.com/ueberdosis/tiptap/issues/2779)
|
||||
- **extension/youtube:** :sparkles: new youtube embed extension ([#2814](https://github.com/ueberdosis/tiptap/issues/2814)) ([1c0554b](https://github.com/ueberdosis/tiptap/commit/1c0554b7c06d80145274353e58d56608b097fbe4))
|
||||
- Integrate input rules and paste rules into the core ([#1997](https://github.com/ueberdosis/tiptap/issues/1997)) ([723b955](https://github.com/ueberdosis/tiptap/commit/723b955cecc5c92c8aad897ce16c60fb62976571))
|
||||
- parseHTML for attributes should return the value instead of an object now, fix [#1863](https://github.com/ueberdosis/tiptap/issues/1863) ([8a3b47a](https://github.com/ueberdosis/tiptap/commit/8a3b47a529d28b28b50d634c6ff69b8e5aad3080))
|
||||
39
demos/dedupeDeps.txt
Normal file
39
demos/dedupeDeps.txt
Normal file
@@ -0,0 +1,39 @@
|
||||
d3
|
||||
highlight.js/lib/languages/css
|
||||
highlight.js/lib/languages/javascript
|
||||
highlight.js/lib/languages/typescript
|
||||
highlight.js/lib/languages/xml
|
||||
highlight.js/lib/core
|
||||
linkifyjs
|
||||
lowlight
|
||||
react
|
||||
react-dom
|
||||
react-dom/client
|
||||
use-sync-external-store/shim
|
||||
use-sync-external-store/shim/with-selector
|
||||
shiki
|
||||
@floating-ui/dom
|
||||
uuid
|
||||
y-webrtc
|
||||
yjs
|
||||
prosemirror-changeset
|
||||
prosemirror-collab
|
||||
prosemirror-commands
|
||||
prosemirror-dropcursor
|
||||
prosemirror-gapcursor
|
||||
prosemirror-history
|
||||
prosemirror-inputrules
|
||||
prosemirror-keymap
|
||||
prosemirror-markdown
|
||||
prosemirror-menu
|
||||
prosemirror-model
|
||||
prosemirror-schema-basic
|
||||
prosemirror-schema-list
|
||||
prosemirror-state
|
||||
prosemirror-tables
|
||||
prosemirror-trailing-node
|
||||
prosemirror-transform
|
||||
prosemirror-view
|
||||
@hocuspocus/provider
|
||||
@lifeomic/attempt
|
||||
@tiptap/y-tiptap
|
||||
9
demos/index.html
Normal file
9
demos/index.html
Normal file
@@ -0,0 +1,9 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta http-equiv="refresh" content="0; url=/preview/" />
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
||||
71
demos/package.json
Normal file
71
demos/package.json
Normal file
@@ -0,0 +1,71 @@
|
||||
{
|
||||
"name": "tiptap-demos",
|
||||
"version": "3.0.3",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "vp dev --host",
|
||||
"start:e2e": "vp dev --host --port 4080",
|
||||
"build:demos": "vp build",
|
||||
"preview": "vp preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@hocuspocus/provider": "^2.15.0",
|
||||
"@hocuspocus/transformer": "^2.15.0",
|
||||
"@lexical/react": "^0.36.2",
|
||||
"@lifeomic/attempt": "3.1.0",
|
||||
"@shikijs/core": "1.10.3",
|
||||
"@tiptap/y-tiptap": "^3.0.7",
|
||||
"d3": "^7.9.0",
|
||||
"fast-glob": "^3.3.2",
|
||||
"highlight.js": "^11.11.1",
|
||||
"katex": "^0.18.0",
|
||||
"lexical": "^0.36.2",
|
||||
"linkifyjs": "^4.3.2",
|
||||
"lowlight": "^3.3.0",
|
||||
"prosemirror-changeset": "^2.4.1",
|
||||
"prosemirror-collab": "^1.3.1",
|
||||
"prosemirror-commands": "^1.7.1",
|
||||
"prosemirror-dropcursor": "^1.8.2",
|
||||
"prosemirror-gapcursor": "^1.4.1",
|
||||
"prosemirror-history": "^1.5.0",
|
||||
"prosemirror-inputrules": "^1.5.1",
|
||||
"prosemirror-keymap": "^1.2.3",
|
||||
"prosemirror-markdown": "^1.13.4",
|
||||
"prosemirror-menu": "^1.3.2",
|
||||
"prosemirror-model": "^1.25.9",
|
||||
"prosemirror-schema-basic": "^1.2.4",
|
||||
"prosemirror-schema-list": "^1.5.1",
|
||||
"prosemirror-state": "^1.4.4",
|
||||
"prosemirror-tables": "^1.8.5",
|
||||
"prosemirror-trailing-node": "^3.0.0",
|
||||
"prosemirror-transform": "^1.12.0",
|
||||
"prosemirror-view": "^1.42.3",
|
||||
"remixicon": "^2.5.0",
|
||||
"shiki": "^1.25.1",
|
||||
"y-webrtc": "10.3.0",
|
||||
"yjs": "13.6.23"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/vite-plugin-svelte": "7.2.0",
|
||||
"@types/node": "^22.12.0",
|
||||
"@vitejs/plugin-react": "6.0.5",
|
||||
"@vitejs/plugin-vue": "6.0.8",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"iframe-resizer": "^4.4.5",
|
||||
"postcss": "^8.5.23",
|
||||
"postcss-import": "^15.1.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"sass-embedded": "^1.99.0",
|
||||
"svelte": "^5.56.8",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"typescript": "^5.7.2",
|
||||
"uuid": "^14.0.0",
|
||||
"vite": "catalog:",
|
||||
"vite-plus": "catalog:",
|
||||
"vue": "^3.5.13",
|
||||
"vue-router": "^4.5.0"
|
||||
}
|
||||
}
|
||||
8
demos/postcss.config.cjs
Normal file
8
demos/postcss.config.cjs
Normal file
@@ -0,0 +1,8 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
'postcss-import': {},
|
||||
'tailwindcss/nesting': {},
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
233
demos/preview/Demo.vue
Normal file
233
demos/preview/Demo.vue
Normal file
@@ -0,0 +1,233 @@
|
||||
<template>
|
||||
<div class="overflow-hidden antialiased rounded-lg">
|
||||
<div class="px-3 py-1 bg-black flex items-center gap-0.5">
|
||||
<button
|
||||
v-for="(language, index) in sortedTabs"
|
||||
:key="index"
|
||||
@click="setTab(language.name)"
|
||||
class="px-3 py-2 text-sm text-white leading-[125%] font-semibold rounded-[0.625rem] transition-all"
|
||||
:class="[
|
||||
currentTab === language.name
|
||||
? 'opacity-100 bg-[#1C1917]'
|
||||
: 'opacity-50 bg-transparent hover:opacity-100 hover:bg-[#1C1917]',
|
||||
]"
|
||||
>
|
||||
{{ language.name }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="overflow-hidden">
|
||||
<div class="bg-white" :class="[hidePreview ? 'hidden' : '']">
|
||||
<demo-frame :src="currentIframeUrl" :key="currentIframeUrl" />
|
||||
</div>
|
||||
|
||||
<div class="text-white bg-black" v-if="!hideSource && currentFile">
|
||||
<div class="flex overflow-x-auto">
|
||||
<div class="flex flex-auto px-4 border-b-2 border-gray-800">
|
||||
<button
|
||||
class="inline-flex relative mr-4 py-2 pb-[calc(0.3rem + 2px)] mb-[-2px] border-b-2 border-transparent font-mono text-sm whitespace-nowrap"
|
||||
:class="[
|
||||
!showDebug && currentFile.content === file.content
|
||||
? 'text-white border-white font-bold'
|
||||
: 'text-gray-400',
|
||||
]"
|
||||
v-for="(file, index) in source"
|
||||
:key="index"
|
||||
@click="setFile(file.name)"
|
||||
>
|
||||
{{ file.name }}
|
||||
</button>
|
||||
|
||||
<button
|
||||
v-if="debugJSON"
|
||||
class="inline-flex relative py-2 pb-[calc(0.3rem + 2px)] mb-[-2px] border-b-2 border-transparent font-mono text-sm ml-auto"
|
||||
:class="[showDebug ? 'text-white border-white font-bold' : 'text-gray-400']"
|
||||
@click="showDebug = !showDebug"
|
||||
>
|
||||
Inspect
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="overflow-dark overflow-auto max-h-[500px] relative text-white">
|
||||
<shiki
|
||||
class="p-4 overflow-visible"
|
||||
:language="debugJSON && showDebug ? 'js' : getFileExtension(currentFile.name)"
|
||||
:code="debugJSON && showDebug ? debugJSON : currentFile.content"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between px-4 py-2 text-gray-400 border-t border-gray-800 text-md">
|
||||
<a
|
||||
class="flex-shrink min-w-0 overflow-hidden overflow-ellipsis whitespace-nowrap"
|
||||
:href="currentIframeUrl"
|
||||
>
|
||||
{{ name }}/{{ currentTab }}
|
||||
</a>
|
||||
<a class="pl-4 whitespace-nowrap" :href="githubUrl" target="_blank"> Edit on GitHub → </a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getDebugJSON } from '@tiptap/core'
|
||||
|
||||
import DemoFrame from './DemoFrame.vue'
|
||||
import Shiki from './Shiki.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
DemoFrame,
|
||||
Shiki,
|
||||
},
|
||||
|
||||
props: {
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
|
||||
tabs: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
data: [],
|
||||
sources: {},
|
||||
currentTab: null,
|
||||
currentFile: null,
|
||||
tabOrder: ['React', 'Vue', 'Svelte', 'JS'],
|
||||
debugJSON: null,
|
||||
showDebug: false,
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
currentIframeUrl() {
|
||||
return `/src/${this.name}/${this.currentTab}/`
|
||||
},
|
||||
|
||||
firstTabSelected() {
|
||||
return this.sortedTabs[0].name === this.currentTab
|
||||
},
|
||||
|
||||
sortedTabs() {
|
||||
return [...this.tabs].sort((a, b) => {
|
||||
return this.tabOrder.indexOf(a.name) - this.tabOrder.indexOf(b.name)
|
||||
})
|
||||
},
|
||||
|
||||
query() {
|
||||
return Object.fromEntries(
|
||||
Object.entries(this.$route.query).map(([key, value]) => [key, this.fromString(value)]),
|
||||
)
|
||||
},
|
||||
|
||||
inline() {
|
||||
return this.query.inline || false
|
||||
},
|
||||
|
||||
hideSource() {
|
||||
return this.query.hideSource || false
|
||||
},
|
||||
|
||||
hidePreview() {
|
||||
return this.query.hidePreview || false
|
||||
},
|
||||
|
||||
githubUrl() {
|
||||
return `https://github.com/ueberdosis/tiptap/tree/main/demos/src/${this.name}`
|
||||
},
|
||||
|
||||
source() {
|
||||
return this.sources[this.currentTab]
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
getFileExtension(name) {
|
||||
return name.split('.').pop()
|
||||
},
|
||||
|
||||
setTab(name, persist = true) {
|
||||
this.currentTab = name
|
||||
|
||||
if (persist) {
|
||||
localStorage.tab = name
|
||||
}
|
||||
},
|
||||
|
||||
setFile(name) {
|
||||
this.showDebug = false
|
||||
this.currentFile = this.source.find(item => item.name === name)
|
||||
},
|
||||
|
||||
onSource(event) {
|
||||
this.sources[this.currentTab] = event.detail
|
||||
this.setFile(this.source[0].name)
|
||||
},
|
||||
|
||||
onEditor(event) {
|
||||
const editor = event.detail
|
||||
|
||||
if (!editor) {
|
||||
this.debugJSON = null
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
this.debugJSON = JSON.stringify(getDebugJSON(editor.state.doc), null, ' ')
|
||||
|
||||
editor.on('update', () => {
|
||||
this.debugJSON = JSON.stringify(getDebugJSON(editor.state.doc), null, ' ')
|
||||
})
|
||||
},
|
||||
|
||||
fromString(value) {
|
||||
if (value === null) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (value.match(/^\d*(\.\d+)?$/)) {
|
||||
return Number(value)
|
||||
}
|
||||
|
||||
if (value === 'true') {
|
||||
return true
|
||||
}
|
||||
|
||||
if (value === 'false') {
|
||||
return false
|
||||
}
|
||||
|
||||
if (value === 'null') {
|
||||
return null
|
||||
}
|
||||
|
||||
return value
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
// TODO: load language from url params
|
||||
const intitialTab =
|
||||
localStorage.tab && this.tabs.some(tab => tab.name === localStorage.tab)
|
||||
? localStorage.tab
|
||||
: this.sortedTabs[0]?.name
|
||||
|
||||
this.setTab(intitialTab, false)
|
||||
|
||||
window.document.addEventListener('editor', this.onEditor, false)
|
||||
window.document.addEventListener('source', this.onSource, false)
|
||||
},
|
||||
|
||||
beforeUnmount() {
|
||||
window.document.removeEventListener('editor', this.onEditor)
|
||||
window.document.removeEventListener('source', this.onSource)
|
||||
},
|
||||
}
|
||||
</script>
|
||||
56
demos/preview/DemoFrame.vue
Normal file
56
demos/preview/DemoFrame.vue
Normal file
@@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<div class="flex flex-col relative min-h-[5rem]">
|
||||
<div
|
||||
class="absolute top-0 left-0 w-full h-full flex justify-center items-center pointer-events-none"
|
||||
v-if="isLoading"
|
||||
>
|
||||
<svg
|
||||
class="animate-spin -ml-1 mr-3 h-5 w-5"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" />
|
||||
<path
|
||||
class="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<iframe
|
||||
class="bg-transparent max-h-[28rem]"
|
||||
v-resize.quiet="{ scrolling: 'omit' }"
|
||||
:src="src"
|
||||
width="100%"
|
||||
height="0"
|
||||
frameborder="0"
|
||||
@load="onLoad"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
src: {
|
||||
required: true,
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
isLoading: true,
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
onLoad() {
|
||||
this.isLoading = false
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
73
demos/preview/Shiki.vue
Normal file
73
demos/preview/Shiki.vue
Normal file
@@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<div v-if="html" v-html="html" />
|
||||
<pre v-else><code>{{ code }}</code></pre>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// this import is a bugfix
|
||||
// otherwise the `onig.wasm` file is missing in the dist folder
|
||||
import 'shiki/dist/onig.wasm?url'
|
||||
|
||||
import Worker from './shiki.worker.js?worker'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
code: {
|
||||
default: '',
|
||||
type: String,
|
||||
},
|
||||
|
||||
language: {
|
||||
default: 'js',
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
worker: new Worker(),
|
||||
html: null,
|
||||
highlighter: window?.highlighter,
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
code: {
|
||||
immediate: true,
|
||||
handler() {
|
||||
this.render()
|
||||
},
|
||||
},
|
||||
|
||||
highlighter: {
|
||||
immediate: true,
|
||||
handler() {
|
||||
this.render()
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
render() {
|
||||
this.worker.postMessage({
|
||||
code: this.code,
|
||||
language: this.language,
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
created() {
|
||||
this.worker.addEventListener('message', event => {
|
||||
const { html } = event.data
|
||||
|
||||
this.html = html
|
||||
})
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.shiki {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
</style>
|
||||
12
demos/preview/index.html
Normal file
12
demos/preview/index.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Preview</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/preview/index.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
61
demos/preview/index.js
Normal file
61
demos/preview/index.js
Normal file
@@ -0,0 +1,61 @@
|
||||
import 'iframe-resizer/js/iframeResizer.contentWindow'
|
||||
import './style.css'
|
||||
|
||||
import { demos } from '@demos'
|
||||
import iframeResize from 'iframe-resizer/js/iframeResizer'
|
||||
import { createApp } from 'vue'
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
|
||||
import Demo from './Demo.vue'
|
||||
import App from './index.vue'
|
||||
|
||||
const routes = demos.map(({ name, tabs }) => {
|
||||
return {
|
||||
path: `/${name}`,
|
||||
component: Demo,
|
||||
props: {
|
||||
name,
|
||||
tabs,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory('preview'),
|
||||
routes,
|
||||
})
|
||||
|
||||
createApp(App)
|
||||
.directive('resize', {
|
||||
beforeMount: (el, { value = {} }) => {
|
||||
el.addEventListener('load', () => {
|
||||
iframeResize(
|
||||
{
|
||||
...value,
|
||||
// messageCallback(messageData) {
|
||||
// if (messageData.message.type !== 'resize') {
|
||||
// return
|
||||
// }
|
||||
|
||||
// const style = window.getComputedStyle(el.parentElement)
|
||||
// const maxHeight = parseInt(style.getPropertyValue('max-height'), 10)
|
||||
|
||||
// if (messageData.message.height > maxHeight) {
|
||||
// el.setAttribute('scrolling', 'auto')
|
||||
// } else {
|
||||
// el.setAttribute('scrolling', 'no')
|
||||
// }
|
||||
|
||||
// el?.iFrameResizer?.resize?.()
|
||||
// },
|
||||
},
|
||||
el,
|
||||
)
|
||||
})
|
||||
},
|
||||
unmounted(el) {
|
||||
el?.iFrameResizer?.removeListeners?.()
|
||||
},
|
||||
})
|
||||
.use(router)
|
||||
.mount('#app')
|
||||
95
demos/preview/index.vue
Normal file
95
demos/preview/index.vue
Normal file
@@ -0,0 +1,95 @@
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue'
|
||||
|
||||
const searchQuery = new URLSearchParams(window.location.search)
|
||||
|
||||
const searchValue = ref(searchQuery.get('search') || '')
|
||||
|
||||
// Update the window search param when the searchValue changes
|
||||
// this allows the user to bookmark / return to a specific search
|
||||
// and also allows the browser to handle back/forward navigation
|
||||
// without losing the search state
|
||||
watch(searchValue, (newValue, oldValue) => {
|
||||
if (newValue !== oldValue) {
|
||||
searchQuery.set('search', newValue)
|
||||
window.history.replaceState({}, '', `?${searchQuery.toString()}`)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<template v-if="$route.path === '/'">
|
||||
<input
|
||||
class="w-full p-3 my-3 focus:outline-none border-b"
|
||||
type="search"
|
||||
placeholder="Search for a demo..."
|
||||
autofocus
|
||||
v-model="searchValue"
|
||||
/>
|
||||
<ul>
|
||||
<li
|
||||
class="p-5 border-b-2 border-black"
|
||||
v-for="route in $router.options.routes.filter(route =>
|
||||
searchValue === ''
|
||||
? true
|
||||
: route.props.name.toLowerCase().includes(searchValue.toLowerCase()),
|
||||
)"
|
||||
:key="route.path"
|
||||
>
|
||||
<router-link class="block mb-2 font-medium" :to="route.path">
|
||||
{{ route.props.name }}
|
||||
</router-link>
|
||||
|
||||
<div class="flex">
|
||||
<a
|
||||
class="mr-4 text-sm text-gray-300 font-medium"
|
||||
v-for="(tab, index) in route.props.tabs"
|
||||
:key="index"
|
||||
:href="`/src/${route.props.name}/${tab.name}/`"
|
||||
>
|
||||
{{ tab.name }}
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
<router-view v-else />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
methods: {
|
||||
fromString(value) {
|
||||
if (value === null) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (value.match(/^\d*(\.\d+)?$/)) {
|
||||
return Number(value)
|
||||
}
|
||||
|
||||
if (value === 'true') {
|
||||
return true
|
||||
}
|
||||
|
||||
if (value === 'false') {
|
||||
return false
|
||||
}
|
||||
|
||||
if (value === 'null') {
|
||||
return null
|
||||
}
|
||||
|
||||
return value
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
query() {
|
||||
return Object.fromEntries(
|
||||
Object.entries(this.$route.query).map(([key, value]) => [key, this.fromString(value)]),
|
||||
)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
30
demos/preview/shiki.worker.js
Normal file
30
demos/preview/shiki.worker.js
Normal file
@@ -0,0 +1,30 @@
|
||||
import * as shiki from 'shiki'
|
||||
|
||||
let highlighter = null
|
||||
|
||||
async function init() {
|
||||
if (highlighter) {
|
||||
return highlighter
|
||||
}
|
||||
|
||||
highlighter = await shiki.createHighlighter({
|
||||
themes: ['material-theme-darker'],
|
||||
langs: ['html', 'js', 'jsx', 'ts', 'tsx', 'css', 'vue-html', 'vue', 'scss'],
|
||||
})
|
||||
|
||||
return highlighter
|
||||
}
|
||||
|
||||
// oxlint-disable-next-line
|
||||
self.addEventListener('message', async event => {
|
||||
init().then(async () => {
|
||||
const { code, language } = event.data
|
||||
|
||||
await highlighter.loadLanguage(language)
|
||||
|
||||
const html = highlighter.codeToHtml(code, { lang: language, theme: 'material-theme-darker' })
|
||||
|
||||
// oxlint-disable-next-line
|
||||
self.postMessage({ code, language, html })
|
||||
})
|
||||
})
|
||||
148
demos/preview/style.css
Normal file
148
demos/preview/style.css
Normal file
@@ -0,0 +1,148 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src:
|
||||
url('https://rsms.me/inter/font-files/Inter-Regular.woff2?v=3.19') format('woff2'),
|
||||
url('https://rsms.me/inter/font-files/Inter-Regular.woff?v=3.19') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src:
|
||||
url('https://rsms.me/inter/font-files/Inter-Italic.woff2?v=3.19') format('woff2'),
|
||||
url('https://rsms.me/inter/font-files/Inter-Italic.woff?v=3.19') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src:
|
||||
url('https://rsms.me/inter/font-files/Inter-Medium.woff2?v=3.19') format('woff2'),
|
||||
url('https://rsms.me/inter/font-files/Inter-Medium.woff?v=3.19') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: italic;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src:
|
||||
url('https://rsms.me/inter/font-files/Inter-MediumItalic.woff2?v=3.19') format('woff2'),
|
||||
url('https://rsms.me/inter/font-files/Inter-MediumItalic.woff?v=3.19') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src:
|
||||
url('https://rsms.me/inter/font-files/Inter-SemiBold.woff2?v=3.19') format('woff2'),
|
||||
url('https://rsms.me/inter/font-files/Inter-SemiBold.woff?v=3.19') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src:
|
||||
url('https://rsms.me/inter/font-files/Inter-SemiBoldItalic.woff2?v=3.19') format('woff2'),
|
||||
url('https://rsms.me/inter/font-files/Inter-SemiBoldItalic.woff?v=3.19') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src:
|
||||
url('https://rsms.me/inter/font-files/Inter-Bold.woff2?v=3.19') format('woff2'),
|
||||
url('https://rsms.me/inter/font-files/Inter-Bold.woff?v=3.19') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src:
|
||||
url('https://rsms.me/inter/font-files/Inter-BoldItalic.woff2?v=3.19') format('woff2'),
|
||||
url('https://rsms.me/inter/font-files/Inter-BoldItalic.woff?v=3.19') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'JetBrains Mono';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src:
|
||||
url('https://cdn.jsdelivr.net/gh/JetBrains/JetBrainsMono/web/woff2/JetBrainsMono-Regular.woff2')
|
||||
format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/gh/JetBrains/JetBrainsMono/web/woff2/JetBrainsMono-Regular.woff')
|
||||
format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'JetBrains Mono';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src:
|
||||
url('https://cdn.jsdelivr.net/gh/JetBrains/JetBrainsMono/web/woff2/JetBrainsMono-Bold.woff2')
|
||||
format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/gh/JetBrains/JetBrainsMono/web/woff2/JetBrainsMono-Bold.woff')
|
||||
format('woff');
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
border: 4px solid transparent;
|
||||
background-clip: padding-box;
|
||||
border-radius: 8px;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
border: 4px solid rgba(0, 0, 0, 0);
|
||||
background-clip: padding-box;
|
||||
border-radius: 8px;
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
:hover::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background-color: rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.overflow-dark:hover::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.overflow-dark::-webkit-scrollbar-thumb:hover {
|
||||
background-color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-button {
|
||||
display: none;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-corner {
|
||||
background-color: transparent;
|
||||
}
|
||||
1
demos/public/_redirects
Normal file
1
demos/public/_redirects
Normal file
@@ -0,0 +1 @@
|
||||
/preview/* /preview/index.html 200
|
||||
40
demos/setup/helper.ts
Normal file
40
demos/setup/helper.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
const waitUntilElementExists = (selector: any, callback: (element: Element) => void) => {
|
||||
const element = document.querySelector(selector)
|
||||
|
||||
if (element) {
|
||||
return callback(element)
|
||||
}
|
||||
|
||||
setTimeout(() => waitUntilElementExists(selector, callback), 500)
|
||||
}
|
||||
|
||||
const sendData = (eventName: string, data: any) => {
|
||||
const event = new CustomEvent(eventName, { detail: data })
|
||||
|
||||
window.parent.document.dispatchEvent(event)
|
||||
}
|
||||
|
||||
export function splitName(name: string) {
|
||||
const parts = name.split('/')
|
||||
|
||||
if (parts.length !== 3) {
|
||||
throw Error(
|
||||
'Demos must always be within exactly one category. Nested categories are not supported.',
|
||||
)
|
||||
}
|
||||
|
||||
return parts
|
||||
}
|
||||
|
||||
export function debug() {
|
||||
sendData('editor', null)
|
||||
// @ts-ignore
|
||||
sendData('source', window.source)
|
||||
|
||||
waitUntilElementExists('.tiptap', element => {
|
||||
// @ts-ignore
|
||||
const editor = element.editor
|
||||
|
||||
sendData('editor', editor)
|
||||
})
|
||||
}
|
||||
12
demos/setup/js.ts
Normal file
12
demos/setup/js.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import 'iframe-resizer/js/iframeResizer.contentWindow'
|
||||
import './style.scss'
|
||||
|
||||
import { debug } from './helper.js'
|
||||
|
||||
export default function init(name: string, source: any) {
|
||||
// @ts-ignore
|
||||
window.source = source
|
||||
document.title = name
|
||||
|
||||
debug()
|
||||
}
|
||||
35
demos/setup/react.ts
Normal file
35
demos/setup/react.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import 'iframe-resizer/js/iframeResizer.contentWindow'
|
||||
import './style.scss'
|
||||
|
||||
import React from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
|
||||
import { debug, splitName } from './helper.js'
|
||||
|
||||
export default function init(name: string, source: any) {
|
||||
// @ts-ignore
|
||||
window.source = source
|
||||
document.title = name
|
||||
|
||||
const [demoCategory, demoName, frameworkName] = splitName(name)
|
||||
|
||||
import(`../src/${demoCategory}/${demoName}/${frameworkName}/index.tsx`)
|
||||
.then(module => {
|
||||
const root = document.getElementById('app')
|
||||
|
||||
if (root) {
|
||||
createRoot(root).render(React.createElement(module.default))
|
||||
}
|
||||
debug()
|
||||
})
|
||||
.catch(() => {
|
||||
import(`../src/${demoCategory}/${demoName}/${frameworkName}/index.jsx`).then(module => {
|
||||
const root = document.getElementById('app')
|
||||
|
||||
if (root) {
|
||||
createRoot(root).render(React.createElement(module.default))
|
||||
}
|
||||
debug()
|
||||
})
|
||||
})
|
||||
}
|
||||
500
demos/setup/style.scss
Normal file
500
demos/setup/style.scss
Normal file
@@ -0,0 +1,500 @@
|
||||
/* Base HTML and global element styles*/
|
||||
:root {
|
||||
--white: #fff;
|
||||
--black: #2e2b29;
|
||||
--black-contrast: #110f0e;
|
||||
--gray-1: rgba(61, 37, 20, 0.05);
|
||||
--gray-2: rgba(61, 37, 20, 0.08);
|
||||
--gray-3: rgba(61, 37, 20, 0.12);
|
||||
--gray-4: rgba(53, 38, 28, 0.3);
|
||||
--gray-5: rgba(28, 25, 23, 0.6);
|
||||
--green: #22c55e;
|
||||
--purple: #6a00f5;
|
||||
--purple-contrast: #5800cc;
|
||||
--purple-light: rgba(88, 5, 255, 0.05);
|
||||
--yellow-contrast: #facc15;
|
||||
--yellow: rgba(250, 204, 21, 0.4);
|
||||
--yellow-light: #fffae5;
|
||||
--red: #ff5c33;
|
||||
--red-light: #ffebe5;
|
||||
--shadow: 0px 12px 33px 0px rgba(0, 0, 0, 0.06), 0px 3.618px 9.949px 0px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
font-family:
|
||||
Inter,
|
||||
ui-sans-serif,
|
||||
system-ui,
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
'Segoe UI',
|
||||
Roboto,
|
||||
'Helvetica Neue',
|
||||
Arial,
|
||||
'Noto Sans',
|
||||
sans-serif,
|
||||
'Apple Color Emoji',
|
||||
'Segoe UI Emoji',
|
||||
'Segoe UI Symbol',
|
||||
'Noto Color Emoji';
|
||||
line-height: 1.5;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 25rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.tiptap {
|
||||
caret-color: var(--purple);
|
||||
margin: 1.5rem;
|
||||
padding-bottom: 2rem;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Custom scrollbar styles */
|
||||
::-webkit-scrollbar {
|
||||
height: 14px;
|
||||
width: 14px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background-clip: padding-box;
|
||||
background-color: transparent;
|
||||
border: 4px solid transparent;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-clip: padding-box;
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
border: 4px solid rgba(0, 0, 0, 0);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
:hover::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background-color: rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-button {
|
||||
display: none;
|
||||
height: 0;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-corner {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/* Specific element and component styles */
|
||||
button,
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
background: var(--gray-2);
|
||||
border-radius: 0.5rem;
|
||||
border: none;
|
||||
color: var(--black);
|
||||
font-family: inherit;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
line-height: 1.15;
|
||||
margin: none;
|
||||
padding: 0.375rem 0.625rem;
|
||||
transition: all 0.2s cubic-bezier(0.65, 0.05, 0.36, 1);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--gray-3);
|
||||
color: var(--black-contrast);
|
||||
}
|
||||
|
||||
&[disabled] {
|
||||
background: var(--gray-1);
|
||||
color: var(--gray-4);
|
||||
}
|
||||
|
||||
&:checked {
|
||||
accent-color: var(--purple);
|
||||
}
|
||||
|
||||
&.primary {
|
||||
background: var(--black);
|
||||
color: var(--white);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--black-contrast);
|
||||
}
|
||||
|
||||
&[disabled] {
|
||||
background: var(--gray-1);
|
||||
color: var(--gray-4);
|
||||
}
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
background: var(--purple);
|
||||
color: var(--white);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--purple-contrast);
|
||||
color: var(--white);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[popover] {
|
||||
background-color: var(--white);
|
||||
box-shadow: var(--shadow);
|
||||
max-height: 18rem;
|
||||
overflow-y: auto;
|
||||
scrollbar-color: var(--gray-4) transparent;
|
||||
}
|
||||
|
||||
[popover]::-webkit-scrollbar-thumb {
|
||||
background-color: var(--gray-4);
|
||||
}
|
||||
|
||||
/* Details */
|
||||
[data-type='details'] {
|
||||
display: flex;
|
||||
gap: 0.25rem;
|
||||
margin: 1.5rem 0;
|
||||
border: 1px solid var(--gray-3);
|
||||
border-radius: 0.5rem;
|
||||
padding: 0.5rem;
|
||||
|
||||
summary {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
> button {
|
||||
align-items: center;
|
||||
background: transparent;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
font-size: 0.625rem;
|
||||
height: 1.25rem;
|
||||
justify-content: center;
|
||||
line-height: 1;
|
||||
margin-top: 0.1rem;
|
||||
padding: 0;
|
||||
width: 1.25rem;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--gray-3);
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: '\25B6';
|
||||
}
|
||||
}
|
||||
|
||||
&.is-open > button::before {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
> div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
width: 100%;
|
||||
|
||||
> [data-type='detailsContent'] > :last-child {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.details {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
}
|
||||
|
||||
[data-type='taskList'] {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
|
||||
[data-type='taskList'] {
|
||||
padding-left: 1.5rem;
|
||||
}
|
||||
|
||||
li {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
> li > div > p:first-child {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
> li {
|
||||
&:not(:first-child) {
|
||||
margin-top: 0.125rem;
|
||||
}
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 0.125rem;
|
||||
}
|
||||
}
|
||||
|
||||
[data-checked='true'] > div > p:first-child {
|
||||
text-decoration: line-through;
|
||||
color: var(--gray-4);
|
||||
}
|
||||
}
|
||||
|
||||
button:not([disabled]),
|
||||
select:not([disabled]) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input[type='text'],
|
||||
textarea {
|
||||
background-color: unset;
|
||||
border: 1px solid var(--gray-3);
|
||||
border-radius: 0.5rem;
|
||||
color: var(--black);
|
||||
|
||||
&::placeholder {
|
||||
color: var(--gray-4);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: unset;
|
||||
border-color: var(--gray-4);
|
||||
}
|
||||
|
||||
&:focus-visible,
|
||||
&:focus {
|
||||
border-color: var(--purple);
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
/* reset */
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="Gray" d="M7 10l5 5 5-5z"/></svg>');
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 0.1rem center;
|
||||
background-size: 1.25rem 1.25rem;
|
||||
padding-right: 1.25rem;
|
||||
|
||||
&:focus {
|
||||
outline: 0;
|
||||
}
|
||||
}
|
||||
|
||||
form {
|
||||
align-items: flex-start;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.hint {
|
||||
align-items: center;
|
||||
background-color: var(--yellow-light);
|
||||
border-radius: 0.5rem;
|
||||
border: 1px solid var(--gray-2);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
font-size: 0.75rem;
|
||||
gap: 0.25rem;
|
||||
line-height: 1.15;
|
||||
padding: 0.3rem 0.5rem;
|
||||
|
||||
&.purple-spinner,
|
||||
&.error {
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.badge {
|
||||
background-color: var(--gray-1);
|
||||
border: 1px solid var(--gray-3);
|
||||
border-radius: 2rem;
|
||||
color: var(--gray-5);
|
||||
font-size: 0.625rem;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
|
||||
&.purple-spinner {
|
||||
background-color: var(--purple-light);
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
background-image: url("data:image/svg+xml;utf8,<svg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' width='28px' height='30px' viewBox='0 0 24 30' style='enable-background:new 0 0 50 50;' xml:space='preserve'><rect x='0' y='10' width='6' height='10' fill='%236A00F5' rx='3' ry='3'><animateTransform attributeType='xml' attributeName='transform' type='translate' values='0 0; 0 5; 0 -5; 0 0' begin='0' dur='0.6s' repeatCount='indefinite'/></rect><rect x='10' y='10' width='6' height='10' fill='%236A00F5' rx='3' ry='3'><animateTransform attributeType='xml' attributeName='transform' type='translate' values='0 0; 0 5; 0 -5; 0 0' begin='0.2s' dur='0.6s' repeatCount='indefinite'/></rect><rect x='20' y='10' width='6' height='10' fill='%236A00F5' rx='3' ry='3'><animateTransform attributeType='xml' attributeName='transform' type='translate' values='0 0; 0 5; 0 -5; 0 0' begin='0.4s' dur='0.6s' repeatCount='indefinite'/></rect></svg>");
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
height: 1rem;
|
||||
width: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
&.error {
|
||||
background-color: var(--red-light);
|
||||
}
|
||||
}
|
||||
|
||||
.label,
|
||||
.label-small,
|
||||
.label-large {
|
||||
color: var(--black);
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 500;
|
||||
line-height: 1.15;
|
||||
}
|
||||
|
||||
.label-small {
|
||||
color: var(--gray-5);
|
||||
font-size: 0.75rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.label-large {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: none;
|
||||
border-top: 1px solid var(--gray-3);
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
kbd {
|
||||
background-color: var(--gray-2);
|
||||
border: 1px solid var(--gray-2);
|
||||
border-radius: 0.25rem;
|
||||
font-size: 0.6rem;
|
||||
line-height: 1.15;
|
||||
padding: 0.1rem 0.25rem;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
/* Layout and structure */
|
||||
#app,
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.control-group {
|
||||
align-items: flex-start;
|
||||
background-color: var(--white);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
padding: 1.5rem;
|
||||
|
||||
.sticky {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
[data-node-view-wrapper] > .control-group {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.flex-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.switch-group {
|
||||
align-items: center;
|
||||
background: var(--gray-2);
|
||||
border-radius: 0.5rem;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
flex: 0 1 auto;
|
||||
justify-content: flex-start;
|
||||
padding: 0.125rem;
|
||||
|
||||
label {
|
||||
align-items: center;
|
||||
border-radius: 0.375rem;
|
||||
color: var(--gray-5);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
gap: 0.25rem;
|
||||
line-height: 1.15;
|
||||
min-height: 1.5rem;
|
||||
padding: 0 0.375rem;
|
||||
transition: all 0.2s cubic-bezier(0.65, 0.05, 0.36, 1);
|
||||
|
||||
&:has(input:checked) {
|
||||
background-color: var(--white);
|
||||
color: var(--black-contrast);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--black);
|
||||
}
|
||||
|
||||
input {
|
||||
display: none;
|
||||
margin: unset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.output-group {
|
||||
background-color: var(--gray-1);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-family: 'JetBrainsMono', monospace;
|
||||
font-size: 0.75rem;
|
||||
gap: 1rem;
|
||||
margin-top: 2.5rem;
|
||||
padding: 1.5rem;
|
||||
|
||||
label {
|
||||
color: var(--black);
|
||||
font-size: 0.875rem;
|
||||
font-weight: 700;
|
||||
line-height: 1.15;
|
||||
}
|
||||
}
|
||||
21
demos/setup/svelte.ts
Normal file
21
demos/setup/svelte.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import 'iframe-resizer/js/iframeResizer.contentWindow'
|
||||
import './style.scss'
|
||||
|
||||
import { mount } from 'svelte'
|
||||
import { debug, splitName } from './helper.js'
|
||||
|
||||
export default function init(name: string, source: any) {
|
||||
// @ts-ignore
|
||||
window.source = source
|
||||
document.title = name
|
||||
|
||||
const [demoCategory, demoName, frameworkName] = splitName(name)
|
||||
|
||||
import(`../src/${demoCategory}/${demoName}/${frameworkName}/index.svelte`).then(Module => {
|
||||
const Component = Module.default
|
||||
|
||||
mount(Component, { target: document.querySelector('#app')! })
|
||||
|
||||
debug()
|
||||
})
|
||||
}
|
||||
24
demos/setup/vue.ts
Normal file
24
demos/setup/vue.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import 'iframe-resizer/js/iframeResizer.contentWindow'
|
||||
import './style.scss'
|
||||
|
||||
import { createApp } from 'vue'
|
||||
|
||||
import { debug, splitName } from './helper.js'
|
||||
|
||||
export default function init(name: string, source: any) {
|
||||
// @ts-ignore
|
||||
window.source = source
|
||||
document.title = name
|
||||
|
||||
const [demoCategory, demoName, frameworkName] = splitName(name)
|
||||
|
||||
import(`../src/${demoCategory}/${demoName}/${frameworkName}/index.vue`).then(module => {
|
||||
const app = createApp(module.default)
|
||||
|
||||
if (typeof module.configureApp === 'function') {
|
||||
module.configureApp(app)
|
||||
}
|
||||
app.mount('#app')
|
||||
debug()
|
||||
})
|
||||
}
|
||||
0
demos/src/Commands/Cut/React/index.html
Normal file
0
demos/src/Commands/Cut/React/index.html
Normal file
81
demos/src/Commands/Cut/React/index.jsx
Normal file
81
demos/src/Commands/Cut/React/index.jsx
Normal file
@@ -0,0 +1,81 @@
|
||||
import './styles.scss'
|
||||
|
||||
import { Color, TextStyle } from '@tiptap/extension-text-style'
|
||||
import { EditorContent, useEditor } from '@tiptap/react'
|
||||
import StarterKit from '@tiptap/starter-kit'
|
||||
import React, { useCallback } from 'react'
|
||||
|
||||
const MenuBar = ({ editor }) => {
|
||||
const onCutToStart = useCallback(() => {
|
||||
editor
|
||||
.chain()
|
||||
.cut({ from: editor.state.selection.$from.pos, to: editor.state.selection.$to.pos }, 1)
|
||||
.run()
|
||||
}, [editor])
|
||||
|
||||
const onCutToEnd = useCallback(() => {
|
||||
editor
|
||||
.chain()
|
||||
.cut(
|
||||
{ from: editor.state.selection.$from.pos, to: editor.state.selection.$to.pos },
|
||||
editor.state.doc.nodeSize - 2,
|
||||
)
|
||||
.run()
|
||||
}, [editor])
|
||||
|
||||
if (!editor) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="control-group">
|
||||
<div className="button-group">
|
||||
<button onClick={onCutToStart}>Cut content to start of document</button>
|
||||
<button onClick={onCutToEnd}>Cut content to end of document</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default () => {
|
||||
const editor = useEditor({
|
||||
extensions: [Color, TextStyle, StarterKit],
|
||||
content: `
|
||||
<h2>
|
||||
Hi there,
|
||||
</h2>
|
||||
<p>
|
||||
this is a <em>basic</em> example of <strong>Tiptap</strong>. Sure, there are all kind of basic text styles you’d probably expect from a text editor. But wait until you see the lists:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
That’s a bullet list with one …
|
||||
</li>
|
||||
<li>
|
||||
… or two list items.
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
Isn’t that great? And all of that is editable. But wait, there’s more. Let’s try a code block:
|
||||
</p>
|
||||
<pre><code class="language-css">body {
|
||||
display: none;
|
||||
}</code></pre>
|
||||
<p>
|
||||
I know, I know, this is impressive. It’s only the tip of the iceberg though. Give it a try and click a little bit around. Don’t forget to check the other examples too.
|
||||
</p>
|
||||
<blockquote>
|
||||
Wow, that’s amazing. Good work, boy! 👏
|
||||
<br />
|
||||
— Mom
|
||||
</blockquote>
|
||||
`,
|
||||
})
|
||||
|
||||
return (
|
||||
<>
|
||||
<MenuBar editor={editor} />
|
||||
<EditorContent editor={editor} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
91
demos/src/Commands/Cut/React/styles.scss
Normal file
91
demos/src/Commands/Cut/React/styles.scss
Normal file
@@ -0,0 +1,91 @@
|
||||
/* Basic editor styles */
|
||||
.tiptap {
|
||||
:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/* List styles */
|
||||
ul,
|
||||
ol {
|
||||
padding: 0 1rem;
|
||||
margin: 1.25rem 1rem 1.25rem 0.4rem;
|
||||
|
||||
li p {
|
||||
margin-top: 0.25em;
|
||||
margin-bottom: 0.25em;
|
||||
}
|
||||
}
|
||||
|
||||
/* Heading styles */
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
line-height: 1.1;
|
||||
margin-top: 2.5rem;
|
||||
text-wrap: pretty;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2 {
|
||||
margin-top: 3.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
/* Code and preformatted text styles */
|
||||
code {
|
||||
background-color: var(--purple-light);
|
||||
border-radius: 0.4rem;
|
||||
color: var(--black);
|
||||
font-size: 0.85rem;
|
||||
padding: 0.25em 0.3em;
|
||||
}
|
||||
|
||||
pre {
|
||||
background: var(--black);
|
||||
border-radius: 0.5rem;
|
||||
color: var(--white);
|
||||
font-family: 'JetBrainsMono', monospace;
|
||||
margin: 1.5rem 0;
|
||||
padding: 0.75rem 1rem;
|
||||
|
||||
code {
|
||||
background: none;
|
||||
color: inherit;
|
||||
font-size: 0.8rem;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-left: 3px solid var(--gray-3);
|
||||
margin: 1.5rem 0;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: none;
|
||||
border-top: 1px solid var(--gray-2);
|
||||
margin: 2rem 0;
|
||||
}
|
||||
}
|
||||
0
demos/src/Commands/InsertContent/React/index.html
Normal file
0
demos/src/Commands/InsertContent/React/index.html
Normal file
80
demos/src/Commands/InsertContent/React/index.jsx
Normal file
80
demos/src/Commands/InsertContent/React/index.jsx
Normal file
@@ -0,0 +1,80 @@
|
||||
import './styles.scss'
|
||||
|
||||
import { Image } from '@tiptap/extension-image'
|
||||
import Link from '@tiptap/extension-link'
|
||||
import { Color, TextStyle } from '@tiptap/extension-text-style'
|
||||
import { EditorProvider, useCurrentEditor } from '@tiptap/react'
|
||||
import StarterKit from '@tiptap/starter-kit'
|
||||
import React, { useCallback } from 'react'
|
||||
|
||||
const htmlContent = `
|
||||
<h1><a href="https://tiptap.dev/">Tiptap</a></h1>
|
||||
<p><strong>Hello World</strong></p>
|
||||
<p>This is a paragraph<br />with a break.</p>
|
||||
<p>And this is some additional string content.</p>
|
||||
`
|
||||
|
||||
const textContent = `Hello World
|
||||
This is content with a new line. Is this working?
|
||||
|
||||
Lets see if multiple new lines are inserted correctly
|
||||
|
||||
And more lines`
|
||||
|
||||
const MenuBar = () => {
|
||||
const { editor } = useCurrentEditor()
|
||||
|
||||
const insertImage = useCallback(() => {
|
||||
const url = prompt('Enter an image URL')
|
||||
|
||||
editor.chain().insertContent(`<img src="${url}" alt="Example image" />`).focus().run()
|
||||
}, [editor])
|
||||
|
||||
if (!editor) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="control-group">
|
||||
<div className="button-group">
|
||||
<button
|
||||
data-test-id="html-content"
|
||||
onClick={() => editor.chain().insertContent(htmlContent).focus().run()}
|
||||
>
|
||||
Insert HTML content
|
||||
</button>
|
||||
<button
|
||||
data-test-id="html-content-spans"
|
||||
onClick={() =>
|
||||
editor.chain().insertContent('<p><b>Hello</b> <i>World</i></p>').focus().run()
|
||||
}
|
||||
>
|
||||
Insert HTML with span tags content
|
||||
</button>
|
||||
<button
|
||||
data-test-id="text-content"
|
||||
onClick={() => editor.chain().insertContent(textContent).focus().run()}
|
||||
>
|
||||
Insert text content
|
||||
</button>
|
||||
<button data-test-id="image-content" onClick={insertImage}>
|
||||
Insert image
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const extensions = [Image, Color, TextStyle, Link, StarterKit]
|
||||
|
||||
const content = ''
|
||||
|
||||
export default () => {
|
||||
return (
|
||||
<EditorProvider
|
||||
slotBefore={<MenuBar />}
|
||||
extensions={extensions}
|
||||
content={content}
|
||||
></EditorProvider>
|
||||
)
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user