@bettercms-ai/next 0.21.1 → 0.23.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/README.md +321 -321
- package/dist/bettercms-snapshot.js +0 -0
- package/dist/bettercms-snapshot.js.map +1 -1
- package/dist/blocks.d.ts +12 -1
- package/dist/blocks.js +144 -15
- package/dist/blocks.js.map +1 -1
- package/dist/draft-bridge.js +144 -15
- package/dist/draft-bridge.js.map +1 -1
- package/dist/form.js +120 -6
- package/dist/form.js.map +1 -1
- package/dist/index.d.ts +16 -2
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/dist/live.js.map +1 -1
- package/dist/phone-field.js.map +1 -1
- package/dist/search.js.map +1 -1
- package/dist/visual-editing.js.map +1 -1
- package/package.json +94 -94
package/dist/search.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/search.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * <BcmsSearch> — an instant, typo-tolerant site-search box for React/Next.\n *\n * Debounced search-as-you-type against the public delivery search endpoint (via\n * `@bettercms-ai/sdk`'s `search`), rendering a dropdown of results under the input.\n * No API key — `project` (from the dashboard's Site Search settings) is the only\n * scope. All markup is unstyled and `bcms-search-*` class-driven, so the host\n * site owns the look. Mirrors <BcmsForm>.\n */\n\nimport { useEffect, useRef, useState, type ReactElement } from \"react\";\nimport { search, type SearchHit } from \"@bettercms-ai/sdk\";\n\nexport interface BcmsSearchProps {\n /** The project id (from Site Search settings). */\n project: string;\n /** API origin. Defaults to the SDK default (https://api.bettercms.ai). */\n apiBase?: string;\n /** Placeholder for the input. */\n placeholder?: string;\n /** Max results, 1–20 (default 8). */\n limit?: number;\n /** Debounce in ms (default 200). */\n debounceMs?: number;\n /** Class for the wrapper; inner elements use `bcms-search-*` classes. */\n className?: string;\n /** Called when a result is chosen; return false to suppress the default navigation. */\n onSelect?: (hit: SearchHit) => boolean | void;\n}\n\nexport function BcmsSearch({\n project,\n apiBase,\n placeholder = \"Search…\",\n limit,\n debounceMs = 200,\n className,\n onSelect,\n}: BcmsSearchProps): ReactElement {\n const [query, setQuery] = useState(\"\");\n const [hits, setHits] = useState<SearchHit[]>([]);\n const [open, setOpen] = useState(false);\n const [active, setActive] = useState(-1);\n const wrap = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const q = query.trim();\n if (q.length < 2) {\n setHits([]);\n setOpen(false);\n return;\n }\n const ctrl = new AbortController();\n const t = setTimeout(() => {\n search({ project, q, limit, baseUrl: apiBase, signal: ctrl.signal }).then((r) => {\n setHits(r);\n setActive(-1);\n setOpen(true);\n });\n }, debounceMs);\n return () => {\n clearTimeout(t);\n ctrl.abort();\n };\n }, [query, project, apiBase, limit, debounceMs]);\n\n // Close on outside click.\n useEffect(() => {\n const onClick = (e: MouseEvent) => {\n if (wrap.current && !wrap.current.contains(e.target as Node)) setOpen(false);\n };\n document.addEventListener(\"click\", onClick);\n return () => document.removeEventListener(\"click\", onClick);\n }, []);\n\n const choose = (hit: SearchHit) => {\n if (onSelect?.(hit) === false) return;\n window.location.href = hit.url;\n };\n\n const onKeyDown = (e: React.KeyboardEvent) => {\n if (!open || !hits.length) return;\n if (e.key === \"ArrowDown\") {\n e.preventDefault();\n setActive((i) => (i + 1) % hits.length);\n } else if (e.key === \"ArrowUp\") {\n e.preventDefault();\n setActive((i) => (i - 1 + hits.length) % hits.length);\n } else if (e.key === \"Enter\" && active >= 0) {\n e.preventDefault();\n choose(hits[active]);\n } else if (e.key === \"Escape\") {\n setOpen(false);\n }\n };\n\n return (\n <div ref={wrap} className={`bcms-search${className ? ` ${className}` : \"\"}`} style={{ position: \"relative\" }}>\n <input\n type=\"search\"\n className=\"bcms-search-input\"\n placeholder={placeholder}\n aria-label=\"Search this site\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n onKeyDown={onKeyDown}\n onFocus={() => hits.length && setOpen(true)}\n />\n {open && (\n <div className=\"bcms-search-results\" role=\"listbox\">\n {hits.length === 0 ? (\n <div className=\"bcms-search-empty\">No results</div>\n ) : (\n hits.map((hit, i) => (\n <a\n key={`${hit.type}:${hit.slug}`}\n href={hit.url}\n role=\"option\"\n aria-selected={i === active}\n className={`bcms-search-hit${i === active ? \" is-active\" : \"\"}`}\n onClick={(e) => {\n e.preventDefault();\n choose(hit);\n }}\n >\n <span className=\"bcms-search-title\">{hit.title}</span>\n {hit.snippet && (\n <span\n className=\"bcms-search-snippet\"\n dangerouslySetInnerHTML={{ __html: hit.snippet }}\n />\n )}\n </a>\n ))\n )}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/search.tsx"],"sourcesContent":["\"use client\";\r\n\r\n/**\r\n * <BcmsSearch> — an instant, typo-tolerant site-search box for React/Next.\r\n *\r\n * Debounced search-as-you-type against the public delivery search endpoint (via\r\n * `@bettercms-ai/sdk`'s `search`), rendering a dropdown of results under the input.\r\n * No API key — `project` (from the dashboard's Site Search settings) is the only\r\n * scope. All markup is unstyled and `bcms-search-*` class-driven, so the host\r\n * site owns the look. Mirrors <BcmsForm>.\r\n */\r\n\r\nimport { useEffect, useRef, useState, type ReactElement } from \"react\";\r\nimport { search, type SearchHit } from \"@bettercms-ai/sdk\";\r\n\r\nexport interface BcmsSearchProps {\r\n /** The project id (from Site Search settings). */\r\n project: string;\r\n /** API origin. Defaults to the SDK default (https://api.bettercms.ai). */\r\n apiBase?: string;\r\n /** Placeholder for the input. */\r\n placeholder?: string;\r\n /** Max results, 1–20 (default 8). */\r\n limit?: number;\r\n /** Debounce in ms (default 200). */\r\n debounceMs?: number;\r\n /** Class for the wrapper; inner elements use `bcms-search-*` classes. */\r\n className?: string;\r\n /** Called when a result is chosen; return false to suppress the default navigation. */\r\n onSelect?: (hit: SearchHit) => boolean | void;\r\n}\r\n\r\nexport function BcmsSearch({\r\n project,\r\n apiBase,\r\n placeholder = \"Search…\",\r\n limit,\r\n debounceMs = 200,\r\n className,\r\n onSelect,\r\n}: BcmsSearchProps): ReactElement {\r\n const [query, setQuery] = useState(\"\");\r\n const [hits, setHits] = useState<SearchHit[]>([]);\r\n const [open, setOpen] = useState(false);\r\n const [active, setActive] = useState(-1);\r\n const wrap = useRef<HTMLDivElement>(null);\r\n\r\n useEffect(() => {\r\n const q = query.trim();\r\n if (q.length < 2) {\r\n setHits([]);\r\n setOpen(false);\r\n return;\r\n }\r\n const ctrl = new AbortController();\r\n const t = setTimeout(() => {\r\n search({ project, q, limit, baseUrl: apiBase, signal: ctrl.signal }).then((r) => {\r\n setHits(r);\r\n setActive(-1);\r\n setOpen(true);\r\n });\r\n }, debounceMs);\r\n return () => {\r\n clearTimeout(t);\r\n ctrl.abort();\r\n };\r\n }, [query, project, apiBase, limit, debounceMs]);\r\n\r\n // Close on outside click.\r\n useEffect(() => {\r\n const onClick = (e: MouseEvent) => {\r\n if (wrap.current && !wrap.current.contains(e.target as Node)) setOpen(false);\r\n };\r\n document.addEventListener(\"click\", onClick);\r\n return () => document.removeEventListener(\"click\", onClick);\r\n }, []);\r\n\r\n const choose = (hit: SearchHit) => {\r\n if (onSelect?.(hit) === false) return;\r\n window.location.href = hit.url;\r\n };\r\n\r\n const onKeyDown = (e: React.KeyboardEvent) => {\r\n if (!open || !hits.length) return;\r\n if (e.key === \"ArrowDown\") {\r\n e.preventDefault();\r\n setActive((i) => (i + 1) % hits.length);\r\n } else if (e.key === \"ArrowUp\") {\r\n e.preventDefault();\r\n setActive((i) => (i - 1 + hits.length) % hits.length);\r\n } else if (e.key === \"Enter\" && active >= 0) {\r\n e.preventDefault();\r\n choose(hits[active]);\r\n } else if (e.key === \"Escape\") {\r\n setOpen(false);\r\n }\r\n };\r\n\r\n return (\r\n <div ref={wrap} className={`bcms-search${className ? ` ${className}` : \"\"}`} style={{ position: \"relative\" }}>\r\n <input\r\n type=\"search\"\r\n className=\"bcms-search-input\"\r\n placeholder={placeholder}\r\n aria-label=\"Search this site\"\r\n value={query}\r\n onChange={(e) => setQuery(e.target.value)}\r\n onKeyDown={onKeyDown}\r\n onFocus={() => hits.length && setOpen(true)}\r\n />\r\n {open && (\r\n <div className=\"bcms-search-results\" role=\"listbox\">\r\n {hits.length === 0 ? (\r\n <div className=\"bcms-search-empty\">No results</div>\r\n ) : (\r\n hits.map((hit, i) => (\r\n <a\r\n key={`${hit.type}:${hit.slug}`}\r\n href={hit.url}\r\n role=\"option\"\r\n aria-selected={i === active}\r\n className={`bcms-search-hit${i === active ? \" is-active\" : \"\"}`}\r\n onClick={(e) => {\r\n e.preventDefault();\r\n choose(hit);\r\n }}\r\n >\r\n <span className=\"bcms-search-title\">{hit.title}</span>\r\n {hit.snippet && (\r\n <span\r\n className=\"bcms-search-snippet\"\r\n dangerouslySetInnerHTML={{ __html: hit.snippet }}\r\n />\r\n )}\r\n </a>\r\n ))\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n );\r\n}\r\n"],"mappings":"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","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/visual-editing.tsx"],"sourcesContent":["\"use client\";\n/**\n * <VisualEditing> — click-to-edit overlay for draft preview. Alt+Click any value that\n * carries stega provenance to open the matching dashboard editor on that field.\n * Render it only in draft mode.\n *\n * import { VisualEditing } from \"@bettercms-ai/next/visual-editing\";\n * {isDraft && <VisualEditing studioUrl=\"https://bettercms.ai\" />}\n *\n * The decoder is NOT bundled here. It is one script served from the dashboard origin\n * (`/bcms-visual-editing.js`), shared with @bettercms-ai/astro and with plain-HTML sites\n * that install it by hand — a customer build is un-upgradable, so a copy baked into one\n * could never be fixed. Requires the read client to be in draft perspective with\n * `stega: true`; on a published build there is no provenance and the script is inert.\n */\nimport { useEffect } from \"react\";\n\nexport interface VisualEditingProps {\n /**\n * Studio origin, for the deep links and for the script itself. Defaults to\n * `https://bettercms.ai` — the same origin the dashboard hands out as\n * `NEXT_PUBLIC_BCMS_STUDIO_URL`, confirmed canonical 2026-08-16.\n * (`dashboard.bettercms.ai` resolves but 404s; `demo.bettercms.ai` still serves\n * but is a stale alias we no longer hand out.)\n */\n studioUrl?: string;\n}\n\nexport function VisualEditing({ studioUrl = \"https://bettercms.ai\" }: VisualEditingProps) {\n useEffect(() => {\n const src = `${studioUrl.replace(/\\/+$/, \"\")}/bcms-visual-editing.js`;\n if (document.querySelector(`script[src=\"${src}\"]`)) return;\n const el = document.createElement(\"script\");\n el.src = src;\n el.defer = true;\n el.dataset.bcmsStudio = studioUrl;\n document.head.appendChild(el);\n // No cleanup: removing the tag would not detach the listeners the script installed,\n // so a remove-and-remount would leave one set orphaned and add a second. The tag is\n // its own guard instead — this component is mounted once, in a draft-mode layout.\n }, [studioUrl]);\n return null;\n}\n"],"mappings":";;;;AAeA,SAAS,iBAAiB;AAanB,SAAS,cAAc,EAAE,YAAY,uBAAuB,GAAuB;AACxF,YAAU,MAAM;AACd,UAAM,MAAM,GAAG,UAAU,QAAQ,QAAQ,EAAE,CAAC;AAC5C,QAAI,SAAS,cAAc,eAAe,GAAG,IAAI,EAAG;AACpD,UAAM,KAAK,SAAS,cAAc,QAAQ;AAC1C,OAAG,MAAM;AACT,OAAG,QAAQ;AACX,OAAG,QAAQ,aAAa;AACxB,aAAS,KAAK,YAAY,EAAE;AAAA,EAI9B,GAAG,CAAC,SAAS,CAAC;AACd,SAAO;AACT;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/visual-editing.tsx"],"sourcesContent":["\"use client\";\r\n/**\r\n * <VisualEditing> — click-to-edit overlay for draft preview. Alt+Click any value that\r\n * carries stega provenance to open the matching dashboard editor on that field.\r\n * Render it only in draft mode.\r\n *\r\n * import { VisualEditing } from \"@bettercms-ai/next/visual-editing\";\r\n * {isDraft && <VisualEditing studioUrl=\"https://bettercms.ai\" />}\r\n *\r\n * The decoder is NOT bundled here. It is one script served from the dashboard origin\r\n * (`/bcms-visual-editing.js`), shared with @bettercms-ai/astro and with plain-HTML sites\r\n * that install it by hand — a customer build is un-upgradable, so a copy baked into one\r\n * could never be fixed. Requires the read client to be in draft perspective with\r\n * `stega: true`; on a published build there is no provenance and the script is inert.\r\n */\r\nimport { useEffect } from \"react\";\r\n\r\nexport interface VisualEditingProps {\r\n /**\r\n * Studio origin, for the deep links and for the script itself. Defaults to\r\n * `https://bettercms.ai` — the same origin the dashboard hands out as\r\n * `NEXT_PUBLIC_BCMS_STUDIO_URL`, confirmed canonical 2026-08-16.\r\n * (`dashboard.bettercms.ai` resolves but 404s; `demo.bettercms.ai` still serves\r\n * but is a stale alias we no longer hand out.)\r\n */\r\n studioUrl?: string;\r\n}\r\n\r\nexport function VisualEditing({ studioUrl = \"https://bettercms.ai\" }: VisualEditingProps) {\r\n useEffect(() => {\r\n const src = `${studioUrl.replace(/\\/+$/, \"\")}/bcms-visual-editing.js`;\r\n if (document.querySelector(`script[src=\"${src}\"]`)) return;\r\n const el = document.createElement(\"script\");\r\n el.src = src;\r\n el.defer = true;\r\n el.dataset.bcmsStudio = studioUrl;\r\n document.head.appendChild(el);\r\n // No cleanup: removing the tag would not detach the listeners the script installed,\r\n // so a remove-and-remount would leave one set orphaned and add a second. The tag is\r\n // its own guard instead — this component is mounted once, in a draft-mode layout.\r\n }, [studioUrl]);\r\n return null;\r\n}\r\n"],"mappings":";;;;AAeA,SAAS,iBAAiB;AAanB,SAAS,cAAc,EAAE,YAAY,uBAAuB,GAAuB;AACxF,YAAU,MAAM;AACd,UAAM,MAAM,GAAG,UAAU,QAAQ,QAAQ,EAAE,CAAC;AAC5C,QAAI,SAAS,cAAc,eAAe,GAAG,IAAI,EAAG;AACpD,UAAM,KAAK,SAAS,cAAc,QAAQ;AAC1C,OAAG,MAAM;AACT,OAAG,QAAQ;AACX,OAAG,QAAQ,aAAa;AACxB,aAAS,KAAK,YAAY,EAAE;AAAA,EAI9B,GAAG,CAAC,SAAS,CAAC;AACd,SAAO;AACT;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,94 +1,94 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@bettercms-ai/next",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"type": "module",
|
|
5
|
-
"description": "The BetterCMS adapter for Next.js — typed, cache-aware content reads (getEntry/listEntries), draft preview, build-time content snapshot, and llms.txt generation for the App Router. The /blocks, /form, /field, /search and /visual-editing entries are framework-free React and work in any React app (see the React+TS starter), which is why `next` is an OPTIONAL peer.",
|
|
6
|
-
"bin": {
|
|
7
|
-
"bettercms-snapshot": "./dist/bettercms-snapshot.js"
|
|
8
|
-
},
|
|
9
|
-
"main": "./dist/index.js",
|
|
10
|
-
"module": "./dist/index.js",
|
|
11
|
-
"types": "./dist/index.d.ts",
|
|
12
|
-
"sideEffects": false,
|
|
13
|
-
"exports": {
|
|
14
|
-
".": {
|
|
15
|
-
"types": "./dist/index.d.ts",
|
|
16
|
-
"import": "./dist/index.js",
|
|
17
|
-
"default": "./dist/index.js"
|
|
18
|
-
},
|
|
19
|
-
"./form": {
|
|
20
|
-
"types": "./dist/form.d.ts",
|
|
21
|
-
"import": "./dist/form.js",
|
|
22
|
-
"default": "./dist/form.js"
|
|
23
|
-
},
|
|
24
|
-
"./search": {
|
|
25
|
-
"types": "./dist/search.d.ts",
|
|
26
|
-
"import": "./dist/search.js",
|
|
27
|
-
"default": "./dist/search.js"
|
|
28
|
-
},
|
|
29
|
-
"./blocks": {
|
|
30
|
-
"types": "./dist/blocks.d.ts",
|
|
31
|
-
"import": "./dist/blocks.js",
|
|
32
|
-
"default": "./dist/blocks.js"
|
|
33
|
-
},
|
|
34
|
-
"./live": {
|
|
35
|
-
"types": "./dist/live.d.ts",
|
|
36
|
-
"import": "./dist/live.js",
|
|
37
|
-
"default": "./dist/live.js"
|
|
38
|
-
},
|
|
39
|
-
"./visual-editing": {
|
|
40
|
-
"types": "./dist/visual-editing.d.ts",
|
|
41
|
-
"import": "./dist/visual-editing.js",
|
|
42
|
-
"default": "./dist/visual-editing.js"
|
|
43
|
-
},
|
|
44
|
-
"./draft-bridge": {
|
|
45
|
-
"types": "./dist/draft-bridge.d.ts",
|
|
46
|
-
"import": "./dist/draft-bridge.js",
|
|
47
|
-
"default": "./dist/draft-bridge.js"
|
|
48
|
-
}
|
|
49
|
-
},
|
|
50
|
-
"files": [
|
|
51
|
-
"dist",
|
|
52
|
-
"README.md"
|
|
53
|
-
],
|
|
54
|
-
"scripts": {
|
|
55
|
-
"typecheck": "tsc --noEmit",
|
|
56
|
-
"build": "tsup",
|
|
57
|
-
"test": "vitest run",
|
|
58
|
-
"test:watch": "vitest"
|
|
59
|
-
},
|
|
60
|
-
"dependencies": {
|
|
61
|
-
"@bettercms-ai/icons": "^0.2.0",
|
|
62
|
-
"@bettercms-ai/image-url": "^0.4.0",
|
|
63
|
-
"@bettercms-ai/richtext": "^0.8.1",
|
|
64
|
-
"@bettercms-ai/sdk": "^1.
|
|
65
|
-
"@bettercms-ai/types": "^1.
|
|
66
|
-
"@bettercms-ai/ui": "^0.
|
|
67
|
-
"react-phone-number-input": "^3.4.18"
|
|
68
|
-
},
|
|
69
|
-
"peerDependencies": {
|
|
70
|
-
"next": ">=14",
|
|
71
|
-
"react": ">=18"
|
|
72
|
-
},
|
|
73
|
-
"peerDependenciesMeta": {
|
|
74
|
-
"next": {
|
|
75
|
-
"optional": true
|
|
76
|
-
},
|
|
77
|
-
"react": {
|
|
78
|
-
"optional": true
|
|
79
|
-
}
|
|
80
|
-
},
|
|
81
|
-
"devDependencies": {
|
|
82
|
-
"@types/node": "^20",
|
|
83
|
-
"@types/react": "^19.0.0",
|
|
84
|
-
"next": "^15.5.24",
|
|
85
|
-
"react": "^19.0.0",
|
|
86
|
-
"tsup": "^8.5.1",
|
|
87
|
-
"typescript": "^5",
|
|
88
|
-
"vitest": "^4.1.4"
|
|
89
|
-
},
|
|
90
|
-
"publishConfig": {
|
|
91
|
-
"registry": "https://registry.npmjs.org/",
|
|
92
|
-
"access": "public"
|
|
93
|
-
}
|
|
94
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@bettercms-ai/next",
|
|
3
|
+
"version": "0.23.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "The BetterCMS adapter for Next.js — typed, cache-aware content reads (getEntry/listEntries), draft preview, build-time content snapshot, and llms.txt generation for the App Router. The /blocks, /form, /field, /search and /visual-editing entries are framework-free React and work in any React app (see the React+TS starter), which is why `next` is an OPTIONAL peer.",
|
|
6
|
+
"bin": {
|
|
7
|
+
"bettercms-snapshot": "./dist/bettercms-snapshot.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"module": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"sideEffects": false,
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"import": "./dist/index.js",
|
|
17
|
+
"default": "./dist/index.js"
|
|
18
|
+
},
|
|
19
|
+
"./form": {
|
|
20
|
+
"types": "./dist/form.d.ts",
|
|
21
|
+
"import": "./dist/form.js",
|
|
22
|
+
"default": "./dist/form.js"
|
|
23
|
+
},
|
|
24
|
+
"./search": {
|
|
25
|
+
"types": "./dist/search.d.ts",
|
|
26
|
+
"import": "./dist/search.js",
|
|
27
|
+
"default": "./dist/search.js"
|
|
28
|
+
},
|
|
29
|
+
"./blocks": {
|
|
30
|
+
"types": "./dist/blocks.d.ts",
|
|
31
|
+
"import": "./dist/blocks.js",
|
|
32
|
+
"default": "./dist/blocks.js"
|
|
33
|
+
},
|
|
34
|
+
"./live": {
|
|
35
|
+
"types": "./dist/live.d.ts",
|
|
36
|
+
"import": "./dist/live.js",
|
|
37
|
+
"default": "./dist/live.js"
|
|
38
|
+
},
|
|
39
|
+
"./visual-editing": {
|
|
40
|
+
"types": "./dist/visual-editing.d.ts",
|
|
41
|
+
"import": "./dist/visual-editing.js",
|
|
42
|
+
"default": "./dist/visual-editing.js"
|
|
43
|
+
},
|
|
44
|
+
"./draft-bridge": {
|
|
45
|
+
"types": "./dist/draft-bridge.d.ts",
|
|
46
|
+
"import": "./dist/draft-bridge.js",
|
|
47
|
+
"default": "./dist/draft-bridge.js"
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"files": [
|
|
51
|
+
"dist",
|
|
52
|
+
"README.md"
|
|
53
|
+
],
|
|
54
|
+
"scripts": {
|
|
55
|
+
"typecheck": "tsc --noEmit",
|
|
56
|
+
"build": "tsup",
|
|
57
|
+
"test": "vitest run",
|
|
58
|
+
"test:watch": "vitest"
|
|
59
|
+
},
|
|
60
|
+
"dependencies": {
|
|
61
|
+
"@bettercms-ai/icons": "^0.2.0",
|
|
62
|
+
"@bettercms-ai/image-url": "^0.4.0",
|
|
63
|
+
"@bettercms-ai/richtext": "^0.8.1",
|
|
64
|
+
"@bettercms-ai/sdk": "^1.28.0",
|
|
65
|
+
"@bettercms-ai/types": "^1.40.0",
|
|
66
|
+
"@bettercms-ai/ui": "^0.15.0",
|
|
67
|
+
"react-phone-number-input": "^3.4.18"
|
|
68
|
+
},
|
|
69
|
+
"peerDependencies": {
|
|
70
|
+
"next": ">=14",
|
|
71
|
+
"react": ">=18"
|
|
72
|
+
},
|
|
73
|
+
"peerDependenciesMeta": {
|
|
74
|
+
"next": {
|
|
75
|
+
"optional": true
|
|
76
|
+
},
|
|
77
|
+
"react": {
|
|
78
|
+
"optional": true
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
"devDependencies": {
|
|
82
|
+
"@types/node": "^20",
|
|
83
|
+
"@types/react": "^19.0.0",
|
|
84
|
+
"next": "^15.5.24",
|
|
85
|
+
"react": "^19.0.0",
|
|
86
|
+
"tsup": "^8.5.1",
|
|
87
|
+
"typescript": "^5",
|
|
88
|
+
"vitest": "^4.1.4"
|
|
89
|
+
},
|
|
90
|
+
"publishConfig": {
|
|
91
|
+
"registry": "https://registry.npmjs.org/",
|
|
92
|
+
"access": "public"
|
|
93
|
+
}
|
|
94
|
+
}
|