@portabletext/plugin-paste-link 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2016 - 2026 Sanity.io
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,139 @@
1
+ # `@portabletext/plugin-paste-link`
2
+
3
+ > Allows pasting links in the Portable Text Editor
4
+
5
+ ## Installation
6
+
7
+ ```sh
8
+ npm install @portabletext/plugin-paste-link
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ Import the `PasteLinkPlugin` React component and place it inside the `EditorProvider`:
14
+
15
+ ```tsx
16
+ import {
17
+ defineSchema,
18
+ EditorProvider,
19
+ PortableTextEditable,
20
+ } from '@portabletext/editor'
21
+ import {PasteLinkPlugin} from '@portabletext/plugin-paste-link'
22
+
23
+ const schemaDefinition = defineSchema({
24
+ annotations: [{name: 'link', fields: [{name: 'href', type: 'string'}]}],
25
+ })
26
+
27
+ function App() {
28
+ return (
29
+ <EditorProvider initialConfig={{schemaDefinition}}>
30
+ <PortableTextEditable />
31
+ <PasteLinkPlugin />
32
+ </EditorProvider>
33
+ )
34
+ }
35
+ ```
36
+
37
+ By default, the plugin looks for a `link` annotation with an `href` field of type `string`.
38
+
39
+ ### Controlling when the plugin runs
40
+
41
+ Use the `guard` prop to control when the paste link behavior runs. Return `false` to skip the behavior and fall through to default paste handling:
42
+
43
+ ```tsx
44
+ import {getActiveStyle} from '@portabletext/editor/selectors'
45
+
46
+ ;<PasteLinkPlugin
47
+ guard={({snapshot}) => {
48
+ // Skip paste-link on h1 blocks (e.g., document titles)
49
+ return getActiveStyle(snapshot) !== 'h1'
50
+ }}
51
+ />
52
+ ```
53
+
54
+ ### Customizing the link annotation
55
+
56
+ You can customize the link annotation with the `link` prop:
57
+
58
+ ```tsx
59
+ <PasteLinkPlugin
60
+ link={({context, value}) => {
61
+ const schemaType = context.schema.annotations.find(
62
+ (annotation) => annotation.name === 'customLink',
63
+ )
64
+
65
+ if (!schemaType) return undefined
66
+
67
+ return {_type: schemaType.name, url: value.href}
68
+ }}
69
+ />
70
+ ```
71
+
72
+ ## Behaviors
73
+
74
+ ### Paste URL on selected text
75
+
76
+ When text is selected and a URL is pasted, the plugin adds a link annotation to the selection.
77
+
78
+ ### Paste URL on existing link
79
+
80
+ When text with an existing link annotation is selected and a URL is pasted, the plugin replaces the existing link with a new one containing the pasted URL.
81
+
82
+ ### Paste URL at caret
83
+
84
+ When the selection is collapsed and a URL is pasted, the plugin inserts the URL text with a link annotation. Existing decorators (bold, italic, etc.) are preserved.
85
+
86
+ ## API
87
+
88
+ ### `PasteLinkPlugin`
89
+
90
+ React component that registers paste behaviors for handling URLs.
91
+
92
+ #### Props
93
+
94
+ - `guard` (optional): A `PasteLinkGuard` function that controls when the paste link behavior runs.
95
+ - Parameters: `{snapshot, event, dom}` - standard behavior guard parameters
96
+ - Returns: `true` to allow the behavior, `false` to skip and fall through to default paste handling.
97
+ - Use this to disable paste-link behavior in certain contexts (e.g., title blocks, code blocks).
98
+
99
+ - `link` (optional): A `LinkMatcher` function that converts a pasted URL into a link annotation.
100
+ - Parameters:
101
+ - `context`: Contains `schema` and `keyGenerator` from the editor context
102
+ - `value`: Contains `href` (the pasted URL string)
103
+ - Returns: An object with `_type` (annotation type name), optional `_key`, and any additional properties. Return `undefined` to skip the behavior.
104
+ - Default: Looks for a `link` annotation with an `href` field of type `string`.
105
+
106
+ ### Types
107
+
108
+ ```typescript
109
+ type PasteLinkGuard = BehaviorGuard<
110
+ Extract<NativeBehaviorEvent, {type: 'clipboard.paste'}>,
111
+ true
112
+ >
113
+
114
+ type LinkMatcher = (params: {
115
+ context: LinkMatcherContext
116
+ value: LinkMatcherValue
117
+ }) => LinkMatcherResult | undefined
118
+
119
+ type LinkMatcherContext = Pick<EditorContext, 'schema' | 'keyGenerator'>
120
+ type LinkMatcherValue = {href: string}
121
+ type LinkMatcherResult = {
122
+ _type: string
123
+ _key?: string
124
+ [other: string]: unknown
125
+ }
126
+ ```
127
+
128
+ ## Supported protocols
129
+
130
+ - `http:`
131
+ - `https:`
132
+ - `mailto:`
133
+ - `tel:`
134
+
135
+ URLs with other protocols are pasted as plain text.
136
+
137
+ ## License
138
+
139
+ MIT
@@ -0,0 +1,80 @@
1
+ import type {EditorContext} from '@portabletext/editor'
2
+ import {
3
+ BehaviorGuard,
4
+ NativeBehaviorEvent,
5
+ } from '@portabletext/editor/behaviors'
6
+
7
+ /**
8
+ * Function that converts a pasted URL into a link annotation.
9
+ * Return `undefined` to skip handling.
10
+ * @public
11
+ */
12
+ export declare type LinkMatcher = (params: {
13
+ context: LinkMatcherContext
14
+ value: LinkMatcherValue
15
+ }) => LinkMatcherResult | undefined
16
+
17
+ /**
18
+ * Context provided to link matchers.
19
+ * @public
20
+ */
21
+ export declare type LinkMatcherContext = Pick<
22
+ EditorContext,
23
+ 'schema' | 'keyGenerator'
24
+ >
25
+
26
+ /**
27
+ * Object returned by link matchers.
28
+ * @public
29
+ */
30
+ export declare type LinkMatcherResult = {
31
+ _type: string
32
+ _key?: string
33
+ [other: string]: unknown
34
+ }
35
+
36
+ /**
37
+ * Value provided to link matchers.
38
+ * @public
39
+ */
40
+ export declare type LinkMatcherValue = {
41
+ href: string
42
+ }
43
+
44
+ /**
45
+ * Guard function that controls when the paste link behavior runs.
46
+ * Return `false` to skip the behavior and fall through to default paste handling.
47
+ * @public
48
+ */
49
+ export declare type PasteLinkGuard = BehaviorGuard<
50
+ Extract<
51
+ NativeBehaviorEvent,
52
+ {
53
+ type: 'clipboard.paste'
54
+ }
55
+ >,
56
+ true
57
+ >
58
+
59
+ /**
60
+ * Plugin that handles pasting URLs in the editor.
61
+ *
62
+ * When text is selected and a URL is pasted, adds a link annotation to the selection.
63
+ * When the caret is collapsed (no selection) and a URL is pasted, inserts the URL as text with a link annotation.
64
+ *
65
+ * @public
66
+ */
67
+ export declare function PasteLinkPlugin({
68
+ guard,
69
+ link,
70
+ }: PasteLinkPluginProps): null
71
+
72
+ /**
73
+ * @public
74
+ */
75
+ export declare type PasteLinkPluginProps = {
76
+ guard?: PasteLinkGuard
77
+ link?: LinkMatcher
78
+ }
79
+
80
+ export {}
package/dist/index.js ADDED
@@ -0,0 +1,159 @@
1
+ import { c } from "react/compiler-runtime";
2
+ import { useEditor } from "@portabletext/editor";
3
+ import { defineBehavior, raise } from "@portabletext/editor/behaviors";
4
+ import * as selectors from "@portabletext/editor/selectors";
5
+ import { useEffect } from "react";
6
+ function looksLikeUrl(text) {
7
+ try {
8
+ const url = new URL(text);
9
+ return sensibleProtocols.includes(url.protocol);
10
+ } catch {
11
+ return !1;
12
+ }
13
+ }
14
+ const sensibleProtocols = ["http:", "https:", "mailto:", "tel:"], defaultLinkMatcher = ({
15
+ context,
16
+ value
17
+ }) => {
18
+ const schemaType = context.schema.annotations.find((annotation) => annotation.name === "link"), hrefField = schemaType?.fields.find((field) => field.name === "href" && field.type === "string");
19
+ if (!(!schemaType || !hrefField))
20
+ return {
21
+ _type: schemaType.name,
22
+ [hrefField.name]: value.href
23
+ };
24
+ };
25
+ function PasteLinkPlugin(t0) {
26
+ const $ = c(5), {
27
+ guard,
28
+ link: t1
29
+ } = t0, link = t1 === void 0 ? defaultLinkMatcher : t1, editor = useEditor();
30
+ let t2, t3;
31
+ return $[0] !== editor || $[1] !== guard || $[2] !== link ? (t2 = () => {
32
+ const unregisterBehaviors = createPasteLinkBehaviors({
33
+ guard,
34
+ link
35
+ }).map((behavior) => editor.registerBehavior({
36
+ behavior
37
+ }));
38
+ return () => {
39
+ for (const unregisterBehavior of unregisterBehaviors)
40
+ unregisterBehavior();
41
+ };
42
+ }, t3 = [editor, guard, link], $[0] = editor, $[1] = guard, $[2] = link, $[3] = t2, $[4] = t3) : (t2 = $[3], t3 = $[4]), useEffect(t2, t3), null;
43
+ }
44
+ function createPasteLinkBehaviors(config) {
45
+ const pasteLinkOnSelection = defineBehavior({
46
+ on: "clipboard.paste",
47
+ guard: (guardParams) => {
48
+ if (config.guard && config.guard(guardParams) === !1)
49
+ return !1;
50
+ const {
51
+ snapshot,
52
+ event
53
+ } = guardParams;
54
+ if (selectors.isSelectionCollapsed(snapshot))
55
+ return !1;
56
+ const text = event.originEvent.dataTransfer.getData("text/plain"), href = looksLikeUrl(text) ? text : void 0;
57
+ if (!href)
58
+ return !1;
59
+ const result = config.link({
60
+ context: {
61
+ schema: snapshot.context.schema,
62
+ keyGenerator: snapshot.context.keyGenerator
63
+ },
64
+ value: {
65
+ href
66
+ }
67
+ });
68
+ if (!result)
69
+ return !1;
70
+ const {
71
+ _type,
72
+ _key,
73
+ ...value
74
+ } = result;
75
+ return {
76
+ annotation: {
77
+ name: _type,
78
+ _key,
79
+ value
80
+ }
81
+ };
82
+ },
83
+ actions: [(_, {
84
+ annotation
85
+ }) => [raise({
86
+ type: "annotation.add",
87
+ annotation
88
+ })]]
89
+ }), pasteLinkAtCaret = defineBehavior({
90
+ on: "clipboard.paste",
91
+ guard: (guardParams) => {
92
+ if (config.guard && config.guard(guardParams) === !1)
93
+ return !1;
94
+ const {
95
+ snapshot,
96
+ event
97
+ } = guardParams, focusTextBlock = selectors.getFocusTextBlock(snapshot), selectionCollapsed = selectors.isSelectionCollapsed(snapshot);
98
+ if (!focusTextBlock || !selectionCollapsed)
99
+ return !1;
100
+ const text = event.originEvent.dataTransfer.getData("text/plain"), href = looksLikeUrl(text) ? text : void 0;
101
+ if (!href)
102
+ return !1;
103
+ const result = config.link({
104
+ context: {
105
+ schema: snapshot.context.schema,
106
+ keyGenerator: snapshot.context.keyGenerator
107
+ },
108
+ value: {
109
+ href
110
+ }
111
+ });
112
+ if (!result)
113
+ return !1;
114
+ const {
115
+ _type,
116
+ _key,
117
+ ...value
118
+ } = result, markState = selectors.getMarkState(snapshot), decoratorNames = snapshot.context.schema.decorators.map((decorator) => decorator.name), activeDecorators = (markState?.marks ?? []).filter((mark) => decoratorNames.includes(mark)), markDefKey = _key ?? snapshot.context.keyGenerator(), markDef = {
119
+ _type,
120
+ _key: markDefKey,
121
+ ...value
122
+ };
123
+ return {
124
+ focusTextBlock,
125
+ markDef,
126
+ markDefKey,
127
+ href,
128
+ activeDecorators
129
+ };
130
+ },
131
+ actions: [({
132
+ snapshot
133
+ }, {
134
+ focusTextBlock,
135
+ markDef,
136
+ markDefKey,
137
+ href,
138
+ activeDecorators
139
+ }) => [raise({
140
+ type: "block.set",
141
+ at: focusTextBlock.path,
142
+ props: {
143
+ markDefs: [...focusTextBlock.node.markDefs ?? [], markDef]
144
+ }
145
+ }), raise({
146
+ type: "insert.child",
147
+ child: {
148
+ _type: snapshot.context.schema.span.name,
149
+ text: href,
150
+ marks: [...activeDecorators, markDefKey]
151
+ }
152
+ })]]
153
+ });
154
+ return [pasteLinkOnSelection, pasteLinkAtCaret];
155
+ }
156
+ export {
157
+ PasteLinkPlugin
158
+ };
159
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/looks-like-url.ts","../src/plugin.paste-link.tsx"],"sourcesContent":["export function looksLikeUrl(text: string) {\n try {\n const url = new URL(text)\n return sensibleProtocols.includes(url.protocol)\n } catch {\n return false\n }\n}\n\nconst sensibleProtocols = ['http:', 'https:', 'mailto:', 'tel:']\n","import type {EditorContext} from '@portabletext/editor'\nimport {useEditor} from '@portabletext/editor'\nimport {\n defineBehavior,\n raise,\n type BehaviorGuard,\n type NativeBehaviorEvent,\n} from '@portabletext/editor/behaviors'\nimport * as selectors from '@portabletext/editor/selectors'\nimport {useEffect} from 'react'\nimport {looksLikeUrl} from './looks-like-url'\n\n/**\n * Guard function that controls when the paste link behavior runs.\n * Return `false` to skip the behavior and fall through to default paste handling.\n * @public\n */\nexport type PasteLinkGuard = BehaviorGuard<\n Extract<NativeBehaviorEvent, {type: 'clipboard.paste'}>,\n true\n>\n\n/**\n * Context provided to link matchers.\n * @public\n */\nexport type LinkMatcherContext = Pick<EditorContext, 'schema' | 'keyGenerator'>\n\n/**\n * Value provided to link matchers.\n * @public\n */\nexport type LinkMatcherValue = {href: string}\n\n/**\n * Object returned by link matchers.\n * @public\n */\nexport type LinkMatcherResult = {\n _type: string\n _key?: string\n [other: string]: unknown\n}\n\n/**\n * Function that converts a pasted URL into a link annotation.\n * Return `undefined` to skip handling.\n * @public\n */\nexport type LinkMatcher = (params: {\n context: LinkMatcherContext\n value: LinkMatcherValue\n}) => LinkMatcherResult | undefined\n\n/**\n * @public\n */\nexport type PasteLinkPluginProps = {\n guard?: PasteLinkGuard\n link?: LinkMatcher\n}\n\nconst defaultLinkMatcher: LinkMatcher = ({context, value}) => {\n const schemaType = context.schema.annotations.find(\n (annotation) => annotation.name === 'link',\n )\n const hrefField = schemaType?.fields.find(\n (field) => field.name === 'href' && field.type === 'string',\n )\n\n if (!schemaType || !hrefField) {\n return undefined\n }\n\n return {\n _type: schemaType.name,\n [hrefField.name]: value.href,\n }\n}\n\n/**\n * Plugin that handles pasting URLs in the editor.\n *\n * When text is selected and a URL is pasted, adds a link annotation to the selection.\n * When the caret is collapsed (no selection) and a URL is pasted, inserts the URL as text with a link annotation.\n *\n * @public\n */\nexport function PasteLinkPlugin({\n guard,\n link = defaultLinkMatcher,\n}: PasteLinkPluginProps) {\n const editor = useEditor()\n\n useEffect(() => {\n const behaviors = createPasteLinkBehaviors({guard, link})\n const unregisterBehaviors = behaviors.map((behavior) =>\n editor.registerBehavior({behavior}),\n )\n\n return () => {\n for (const unregisterBehavior of unregisterBehaviors) {\n unregisterBehavior()\n }\n }\n }, [editor, guard, link])\n\n return null\n}\n\nfunction createPasteLinkBehaviors(\n config: Required<Pick<PasteLinkPluginProps, 'link'>> &\n Pick<PasteLinkPluginProps, 'guard'>,\n) {\n /**\n * When text is selected and a URL is pasted, add a link annotation to the\n * selection. If the selection already has a link annotation, the core\n * `preventOverlappingAnnotations` behavior will remove it first.\n */\n const pasteLinkOnSelection = defineBehavior({\n on: 'clipboard.paste',\n guard: (guardParams) => {\n if (config.guard && config.guard(guardParams) === false) {\n return false\n }\n\n const {snapshot, event} = guardParams\n const selectionCollapsed = selectors.isSelectionCollapsed(snapshot)\n\n if (selectionCollapsed) {\n return false\n }\n\n const text = event.originEvent.dataTransfer.getData('text/plain')\n const href = looksLikeUrl(text) ? text : undefined\n\n if (!href) {\n return false\n }\n\n const result = config.link({\n context: {\n schema: snapshot.context.schema,\n keyGenerator: snapshot.context.keyGenerator,\n },\n value: {href},\n })\n\n if (!result) {\n return false\n }\n\n const {_type, _key, ...value} = result\n\n return {annotation: {name: _type, _key, value}}\n },\n actions: [\n (_, {annotation}) => [\n raise({\n type: 'annotation.add',\n annotation,\n }),\n ],\n ],\n })\n\n /**\n * When the caret is collapsed (no selection) and a URL is pasted, insert the\n * URL as text with a link annotation. Existing decorators (bold, italic,\n * etc.) are preserved.\n */\n const pasteLinkAtCaret = defineBehavior({\n on: 'clipboard.paste',\n guard: (guardParams) => {\n if (config.guard && config.guard(guardParams) === false) {\n return false\n }\n\n const {snapshot, event} = guardParams\n const focusTextBlock = selectors.getFocusTextBlock(snapshot)\n const selectionCollapsed = selectors.isSelectionCollapsed(snapshot)\n\n if (!focusTextBlock || !selectionCollapsed) {\n return false\n }\n\n const text = event.originEvent.dataTransfer.getData('text/plain')\n const href = looksLikeUrl(text) ? text : undefined\n\n if (!href) {\n return false\n }\n\n const result = config.link({\n context: {\n schema: snapshot.context.schema,\n keyGenerator: snapshot.context.keyGenerator,\n },\n value: {href},\n })\n\n if (!result) {\n return false\n }\n\n const {_type, _key, ...value} = result\n\n const markState = selectors.getMarkState(snapshot)\n const decoratorNames = snapshot.context.schema.decorators.map(\n (decorator) => decorator.name,\n )\n const activeDecorators = (markState?.marks ?? []).filter((mark) =>\n decoratorNames.includes(mark),\n )\n\n const markDefKey = _key ?? snapshot.context.keyGenerator()\n const markDef = {\n _type,\n _key: markDefKey,\n ...value,\n }\n\n return {\n focusTextBlock,\n markDef,\n markDefKey,\n href,\n activeDecorators,\n }\n },\n actions: [\n (\n {snapshot},\n {focusTextBlock, markDef, markDefKey, href, activeDecorators},\n ) => [\n raise({\n type: 'block.set',\n at: focusTextBlock.path,\n props: {\n markDefs: [...(focusTextBlock.node.markDefs ?? []), markDef],\n },\n }),\n raise({\n type: 'insert.child',\n child: {\n _type: snapshot.context.schema.span.name,\n text: href,\n marks: [...activeDecorators, markDefKey],\n },\n }),\n ],\n ],\n })\n\n return [pasteLinkOnSelection, pasteLinkAtCaret]\n}\n"],"names":["looksLikeUrl","text","url","URL","sensibleProtocols","includes","protocol","defaultLinkMatcher","context","value","schemaType","schema","annotations","find","annotation","name","hrefField","fields","field","type","_type","href","PasteLinkPlugin","t0","$","_c","guard","link","t1","undefined","editor","useEditor","t2","t3","unregisterBehaviors","createPasteLinkBehaviors","map","behavior","registerBehavior","unregisterBehavior","useEffect","config","pasteLinkOnSelection","defineBehavior","on","guardParams","snapshot","event","selectors","isSelectionCollapsed","originEvent","dataTransfer","getData","result","keyGenerator","_key","actions","_","raise","pasteLinkAtCaret","focusTextBlock","getFocusTextBlock","selectionCollapsed","markState","getMarkState","decoratorNames","decorators","decorator","activeDecorators","marks","filter","mark","markDefKey","markDef","at","path","props","markDefs","node","child","span"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,79 @@
1
+ {
2
+ "name": "@portabletext/plugin-paste-link",
3
+ "version": "1.0.0",
4
+ "description": "Allows pasting links in the Portable Text Editor",
5
+ "keywords": [
6
+ "portabletext",
7
+ "plugin",
8
+ "paste",
9
+ "link",
10
+ "url",
11
+ "behaviors"
12
+ ],
13
+ "homepage": "https://portabletext.org",
14
+ "bugs": {
15
+ "url": "https://github.com/portabletext/editor/issues"
16
+ },
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/portabletext/editor.git",
20
+ "directory": "packages/plugin-paste-link"
21
+ },
22
+ "license": "MIT",
23
+ "author": "Sanity.io <hello@sanity.io>",
24
+ "sideEffects": false,
25
+ "type": "module",
26
+ "exports": {
27
+ ".": "./dist/index.js",
28
+ "./package.json": "./package.json"
29
+ },
30
+ "main": "./dist/index.js",
31
+ "types": "./dist/index.d.ts",
32
+ "files": [
33
+ "dist"
34
+ ],
35
+ "devDependencies": {
36
+ "@sanity/tsconfig": "^2.1.0",
37
+ "@types/react": "^19.2.7",
38
+ "@vitejs/plugin-react": "^5.1.1",
39
+ "@vitest/browser": "^4.0.16",
40
+ "@vitest/browser-playwright": "^4.0.16",
41
+ "babel-plugin-react-compiler": "^1.0.0",
42
+ "eslint": "^9.39.1",
43
+ "eslint-plugin-react-hooks": "^7.0.1",
44
+ "react": "^19.2.3",
45
+ "typescript": "5.9.3",
46
+ "typescript-eslint": "^8.48.0",
47
+ "vitest": "^4.0.16",
48
+ "@portabletext/editor": "^4.3.6",
49
+ "racejar": "2.0.2"
50
+ },
51
+ "peerDependencies": {
52
+ "react": "^19.2",
53
+ "@portabletext/editor": "^4.3.6"
54
+ },
55
+ "engines": {
56
+ "node": ">=20.19 <22 || >=22.12"
57
+ },
58
+ "publishConfig": {
59
+ "access": "public"
60
+ },
61
+ "scripts": {
62
+ "build": "pkg-utils build --strict --check --clean",
63
+ "check:lint": "biome lint .",
64
+ "check:react-compiler": "eslint .",
65
+ "check:types": "tsc",
66
+ "check:types:watch": "tsc --watch",
67
+ "clean": "del .turbo && del dist && del node_modules",
68
+ "dev": "pkg-utils watch",
69
+ "lint:fix": "biome lint --write .",
70
+ "test:browser": "vitest run",
71
+ "test:browser:chromium": "vitest run --project \"browser (chromium)\"",
72
+ "test:browser:chromium:watch": "vitest watch --project \"browser (chromium)\"",
73
+ "test:browser:firefox": "vitest run --project \"browser (firefox)\"",
74
+ "test:browser:firefox:watch": "vitest watch --project \"browser (firefox)\"",
75
+ "test:browser:webkit": "vitest run --project \"browser (webkit)\"",
76
+ "test:browser:webkit:watch": "vitest watch --project \"browser (webkit)\"",
77
+ "test:unit": "vitest run --project unit"
78
+ }
79
+ }