@cosmic-labs/renderers 0.1.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/dist/atlas.css ADDED
@@ -0,0 +1,279 @@
1
+ .atlas-statcallout,
2
+ .atlas-comparison {
3
+ color: var(--atlas-text, #1f2937);
4
+ }
5
+
6
+ .atlas-statcallout {
7
+ background: var(--atlas-surface, #ffffff);
8
+ border: 1px solid var(--atlas-border, #d1d5db);
9
+ border-radius: var(--atlas-radius, 0.75rem);
10
+ padding: clamp(1.25rem, 3vw, 2rem);
11
+ }
12
+
13
+ .atlas-statcallout__title {
14
+ color: var(--atlas-accent, #374151);
15
+ font-family: var(--atlas-font-display, ui-serif, Georgia, serif);
16
+ font-size: 0.875rem;
17
+ font-weight: 700;
18
+ letter-spacing: 0.08em;
19
+ margin: 0 0 1.25rem;
20
+ text-transform: uppercase;
21
+ }
22
+
23
+ .atlas-statcallout__grid {
24
+ display: grid;
25
+ gap: 1.25rem;
26
+ margin: 0;
27
+ }
28
+
29
+ .atlas-statcallout[data-count="2"] .atlas-statcallout__grid {
30
+ grid-template-columns: repeat(2, minmax(0, 1fr));
31
+ }
32
+
33
+ .atlas-statcallout[data-count="3"] .atlas-statcallout__grid,
34
+ .atlas-statcallout[data-count="4"] .atlas-statcallout__grid {
35
+ grid-template-columns: repeat(3, minmax(0, 1fr));
36
+ }
37
+
38
+ .atlas-statcallout__item {
39
+ min-width: 0;
40
+ }
41
+
42
+ .atlas-statcallout__value,
43
+ .atlas-statcallout__label {
44
+ margin: 0;
45
+ }
46
+
47
+ .atlas-statcallout__value {
48
+ color: var(--atlas-accent, #374151);
49
+ font-family: var(--atlas-font-display, ui-serif, Georgia, serif);
50
+ font-size: clamp(1.5rem, 3vw, 2.25rem);
51
+ font-weight: 700;
52
+ line-height: 1.1;
53
+ }
54
+
55
+ .atlas-statcallout__label {
56
+ font-size: 0.9375rem;
57
+ font-weight: 600;
58
+ margin-top: 0.35rem;
59
+ }
60
+
61
+ .atlas-statcallout__detail,
62
+ .atlas-statcallout__source {
63
+ color: var(--atlas-text-muted, #6b7280);
64
+ font-size: 0.875rem;
65
+ }
66
+
67
+ .atlas-statcallout__detail {
68
+ margin: 0.5rem 0 0;
69
+ }
70
+
71
+ .atlas-statcallout__source {
72
+ border-top: 1px solid var(--atlas-border, #d1d5db);
73
+ margin: 1.25rem 0 0;
74
+ padding-top: 1rem;
75
+ }
76
+
77
+ .atlas-statcallout__source-link {
78
+ color: var(--atlas-accent, #374151);
79
+ text-decoration: underline;
80
+ text-underline-offset: 0.15em;
81
+ }
82
+
83
+ .atlas-comparison {
84
+ margin: 1.5rem 0;
85
+ }
86
+
87
+ .atlas-comparison__scroll {
88
+ border: 1px solid var(--atlas-border, #d1d5db);
89
+ border-radius: var(--atlas-radius, 0.75rem);
90
+ overflow-x: auto;
91
+ }
92
+
93
+ .atlas-comparison__table {
94
+ border-collapse: collapse;
95
+ min-width: 36rem;
96
+ width: 100%;
97
+ }
98
+
99
+ .atlas-comparison__caption {
100
+ caption-side: top;
101
+ color: var(--atlas-text, #1f2937);
102
+ font-family: var(--atlas-font-display, ui-serif, Georgia, serif);
103
+ font-size: 1.125rem;
104
+ font-weight: 700;
105
+ padding: 0 0 0.75rem;
106
+ text-align: left;
107
+ }
108
+
109
+ .atlas-comparison__col,
110
+ .atlas-comparison__rowhead,
111
+ .atlas-comparison__cell {
112
+ border-bottom: 1px solid var(--atlas-border, #d1d5db);
113
+ padding: 0.875rem 1rem;
114
+ text-align: left;
115
+ }
116
+
117
+ .atlas-comparison__col {
118
+ background: var(--atlas-surface, #ffffff);
119
+ color: var(--atlas-accent, #374151);
120
+ font-family: var(--atlas-font-display, ui-serif, Georgia, serif);
121
+ }
122
+
123
+ .atlas-comparison__rowhead {
124
+ color: var(--atlas-text, #1f2937);
125
+ font-weight: 600;
126
+ }
127
+
128
+ .atlas-comparison__cell {
129
+ color: var(--atlas-text-muted, #6b7280);
130
+ }
131
+
132
+ .atlas-comparison tbody tr:last-child .atlas-comparison__rowhead,
133
+ .atlas-comparison tbody tr:last-child .atlas-comparison__cell {
134
+ border-bottom: 0;
135
+ }
136
+
137
+ .atlas-quote,
138
+ .atlas-definition,
139
+ .atlas-checklist,
140
+ .atlas-image,
141
+ .atlas-divider,
142
+ .atlas-video {
143
+ color: var(--atlas-text, #1f2937);
144
+ margin: 1.5rem 0;
145
+ }
146
+
147
+ .atlas-quote {
148
+ border-inline-start: 0.25rem solid var(--atlas-accent, #374151);
149
+ padding-inline-start: 1.25rem;
150
+ }
151
+
152
+ .atlas-quote__text {
153
+ font-family: var(--atlas-font-display, ui-serif, Georgia, serif);
154
+ font-size: clamp(1.25rem, 2.5vw, 1.75rem);
155
+ line-height: 1.45;
156
+ margin: 0;
157
+ }
158
+
159
+ .atlas-quote__attribution {
160
+ color: var(--atlas-text-muted, #6b7280);
161
+ font-size: 0.9375rem;
162
+ margin-top: 0.75rem;
163
+ }
164
+
165
+ .atlas-definition,
166
+ .atlas-checklist {
167
+ background: var(--atlas-surface, #ffffff);
168
+ border: 1px solid var(--atlas-border, #d1d5db);
169
+ border-radius: var(--atlas-radius, 0.75rem);
170
+ padding: clamp(1.25rem, 3vw, 1.75rem);
171
+ }
172
+
173
+ .atlas-definition__term,
174
+ .atlas-definition__body,
175
+ .atlas-checklist__title,
176
+ .atlas-checklist__items {
177
+ margin: 0;
178
+ }
179
+
180
+ .atlas-definition__term,
181
+ .atlas-checklist__title {
182
+ color: var(--atlas-accent, #374151);
183
+ font-family: var(--atlas-font-display, ui-serif, Georgia, serif);
184
+ font-size: 1.125rem;
185
+ font-weight: 700;
186
+ }
187
+
188
+ .atlas-definition__body {
189
+ line-height: 1.6;
190
+ margin-top: 0.5rem;
191
+ }
192
+
193
+ .atlas-checklist__items {
194
+ list-style: none;
195
+ padding: 0;
196
+ }
197
+
198
+ .atlas-checklist__title + .atlas-checklist__items {
199
+ margin-top: 0.875rem;
200
+ }
201
+
202
+ .atlas-checklist__item {
203
+ line-height: 1.5;
204
+ padding-inline-start: 1.75rem;
205
+ position: relative;
206
+ }
207
+
208
+ .atlas-checklist__item + .atlas-checklist__item {
209
+ margin-top: 0.625rem;
210
+ }
211
+
212
+ .atlas-checklist__item::before {
213
+ color: var(--atlas-accent, #374151);
214
+ content: '✓';
215
+ inset-inline-start: 0;
216
+ position: absolute;
217
+ }
218
+
219
+ .atlas-image {
220
+ margin-inline: 0;
221
+ }
222
+
223
+ .atlas-image__img {
224
+ border-radius: var(--atlas-radius, 0.75rem);
225
+ display: block;
226
+ height: auto;
227
+ max-width: 100%;
228
+ }
229
+
230
+ .atlas-image__caption,
231
+ .atlas-image__credit,
232
+ .atlas-video__caption {
233
+ color: var(--atlas-text-muted, #6b7280);
234
+ font-size: 0.875rem;
235
+ }
236
+
237
+ .atlas-image__caption,
238
+ .atlas-video__caption {
239
+ margin-top: 0.625rem;
240
+ }
241
+
242
+ .atlas-image__credit::before {
243
+ content: ' · ';
244
+ }
245
+
246
+ .atlas-image[data-unresolved-image],
247
+ .atlas-video[data-unresolved-video] {
248
+ border: 1px dashed var(--atlas-border, #d1d5db);
249
+ color: var(--atlas-text-muted, #6b7280);
250
+ padding: 1rem;
251
+ }
252
+
253
+ .atlas-divider {
254
+ background: var(--atlas-border, #d1d5db);
255
+ border: 0;
256
+ height: 1px;
257
+ }
258
+
259
+ .atlas-video__frame {
260
+ aspect-ratio: var(--atlas-media-aspect-ratio, 16 / 9);
261
+ background: var(--atlas-surface, #ffffff);
262
+ border-radius: var(--atlas-radius, 0.75rem);
263
+ overflow: hidden;
264
+ }
265
+
266
+ .atlas-video__frame iframe {
267
+ border: 0;
268
+ display: block;
269
+ height: 100%;
270
+ width: 100%;
271
+ }
272
+
273
+ @media (max-width: 40rem) {
274
+ .atlas-statcallout[data-count="2"] .atlas-statcallout__grid,
275
+ .atlas-statcallout[data-count="3"] .atlas-statcallout__grid,
276
+ .atlas-statcallout[data-count="4"] .atlas-statcallout__grid {
277
+ grid-template-columns: 1fr;
278
+ }
279
+ }
@@ -0,0 +1,29 @@
1
+ import * as react from 'react';
2
+ import { ComponentType } from 'react';
3
+
4
+ type PortableTextBlock = {
5
+ _type: string;
6
+ _key?: string;
7
+ [field: string]: unknown;
8
+ };
9
+ type PortableTextTypeComponent = ComponentType<{
10
+ value: unknown;
11
+ }>;
12
+ type PortableTextRendererProps = {
13
+ value: PortableTextBlock | PortableTextBlock[];
14
+ /** Throw instead of emitting a marker when an unsupported block reaches the renderer. */
15
+ strict?: boolean;
16
+ /** Site components replace Atlas defaults by block type (for example, image adapters). */
17
+ components?: {
18
+ types?: Record<string, PortableTextTypeComponent>;
19
+ };
20
+ };
21
+ /** Every Portable Text object type rendered by this package. */
22
+ declare const capabilities: string[];
23
+ /**
24
+ * Shared Portable Text renderer. Unsupported blocks never use the library
25
+ * fallback: they render an SSR-visible marker, or fail immediately in strict mode.
26
+ */
27
+ declare function PortableTextRenderer({ value, strict, components: overrides }: PortableTextRendererProps): react.JSX.Element;
28
+
29
+ export { PortableTextRenderer, type PortableTextRendererProps, capabilities };
package/dist/index.js ADDED
@@ -0,0 +1,233 @@
1
+ // src/PortableTextRenderer.tsx
2
+ import { PortableText } from "@portabletext/react";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ var capabilities = [
5
+ "statCallout",
6
+ "comparisonTable",
7
+ "editorialQuote",
8
+ "definitionBox",
9
+ "checklistBox",
10
+ "captionedImage",
11
+ "botanicalDivider",
12
+ "videoEmbed"
13
+ ];
14
+ function readString(value) {
15
+ return typeof value === "string" ? value : void 0;
16
+ }
17
+ function readRecord(value) {
18
+ return typeof value === "object" && value !== null ? value : void 0;
19
+ }
20
+ function readStatCallout(value) {
21
+ const block = readRecord(value);
22
+ if (!block) {
23
+ return { stats: [] };
24
+ }
25
+ return {
26
+ title: readString(block.title),
27
+ stats: Array.isArray(block.stats) ? block.stats.map((stat) => {
28
+ const item = readRecord(stat) ?? {};
29
+ return {
30
+ value: readString(item.value),
31
+ label: readString(item.label),
32
+ detail: readString(item.detail)
33
+ };
34
+ }) : [],
35
+ source: readRecord(block.source) ? {
36
+ label: readString(readRecord(block.source)?.label),
37
+ url: readString(readRecord(block.source)?.url)
38
+ } : void 0
39
+ };
40
+ }
41
+ function StatCallout({ value }) {
42
+ const callout = readStatCallout(value);
43
+ if (callout.stats.length === 0) {
44
+ return null;
45
+ }
46
+ return /* @__PURE__ */ jsxs("aside", { className: "atlas-statcallout", "data-block": "statCallout", "data-count": callout.stats.length, children: [
47
+ callout.title ? /* @__PURE__ */ jsx("p", { className: "atlas-statcallout__title", children: callout.title }) : null,
48
+ /* @__PURE__ */ jsx("dl", { className: "atlas-statcallout__grid", children: callout.stats.map((stat, index) => /* @__PURE__ */ jsxs("div", { className: "atlas-statcallout__item", children: [
49
+ /* @__PURE__ */ jsx("dd", { className: "atlas-statcallout__value", children: stat.value ?? "" }),
50
+ /* @__PURE__ */ jsx("dt", { className: "atlas-statcallout__label", children: stat.label ?? "" }),
51
+ stat.detail ? /* @__PURE__ */ jsx("p", { className: "atlas-statcallout__detail", children: stat.detail }) : null
52
+ ] }, index)) }),
53
+ callout.source?.label && callout.source.url ? /* @__PURE__ */ jsx("p", { className: "atlas-statcallout__source", children: /* @__PURE__ */ jsx("a", { className: "atlas-statcallout__source-link", href: callout.source.url, target: "_blank", rel: "noreferrer", children: callout.source.label }) }) : null
54
+ ] });
55
+ }
56
+ function readComparisonTable(value) {
57
+ const block = readRecord(value);
58
+ if (!block) {
59
+ return { columns: [], rows: [] };
60
+ }
61
+ return {
62
+ caption: readString(block.caption),
63
+ rowHeader: readString(block.rowHeader),
64
+ columns: Array.isArray(block.columns) ? block.columns.map(readString).filter((column) => column !== void 0) : [],
65
+ rows: Array.isArray(block.rows) ? block.rows.map((row) => {
66
+ const item = readRecord(row) ?? {};
67
+ return {
68
+ label: readString(item.label),
69
+ values: Array.isArray(item.values) ? item.values.map(readString).filter((itemValue) => itemValue !== void 0) : []
70
+ };
71
+ }) : []
72
+ };
73
+ }
74
+ function ComparisonTable({ value }) {
75
+ const comparison = readComparisonTable(value);
76
+ if (comparison.columns.length === 0 || comparison.rows.length === 0) {
77
+ return null;
78
+ }
79
+ return /* @__PURE__ */ jsx("figure", { className: "atlas-comparison", "data-block": "comparisonTable", children: /* @__PURE__ */ jsx("div", { className: "atlas-comparison__scroll", children: /* @__PURE__ */ jsxs("table", { className: "atlas-comparison__table", children: [
80
+ comparison.caption ? /* @__PURE__ */ jsx("caption", { className: "atlas-comparison__caption", children: comparison.caption }) : null,
81
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
82
+ /* @__PURE__ */ jsx("th", { className: "atlas-comparison__col", scope: "col", children: comparison.rowHeader ?? "" }),
83
+ comparison.columns.map((column, index) => /* @__PURE__ */ jsx("th", { className: "atlas-comparison__col", scope: "col", children: column }, index))
84
+ ] }) }),
85
+ /* @__PURE__ */ jsx("tbody", { children: comparison.rows.map((row, rowIndex) => /* @__PURE__ */ jsxs("tr", { children: [
86
+ /* @__PURE__ */ jsx("th", { className: "atlas-comparison__rowhead", scope: "row", children: row.label ?? "" }),
87
+ comparison.columns.map((_, columnIndex) => /* @__PURE__ */ jsx("td", { className: "atlas-comparison__cell", children: row.values[columnIndex] ?? "" }, columnIndex))
88
+ ] }, rowIndex)) })
89
+ ] }) }) });
90
+ }
91
+ function readEditorialQuote(value) {
92
+ const block = readRecord(value) ?? {};
93
+ return { quote: readString(block.quote), attribution: readString(block.attribution) };
94
+ }
95
+ function EditorialQuote({ value }) {
96
+ const quote = readEditorialQuote(value);
97
+ return /* @__PURE__ */ jsxs("figure", { className: "atlas-quote", "data-block": "editorialQuote", children: [
98
+ /* @__PURE__ */ jsx("blockquote", { className: "atlas-quote__text", children: quote.quote ?? "" }),
99
+ quote.attribution ? /* @__PURE__ */ jsx("figcaption", { className: "atlas-quote__attribution", children: quote.attribution }) : null
100
+ ] });
101
+ }
102
+ function readDefinitionBox(value) {
103
+ const block = readRecord(value) ?? {};
104
+ return {
105
+ term: readString(block.term),
106
+ definition: readString(block.definition),
107
+ icon: readString(block.icon) ?? "olive"
108
+ };
109
+ }
110
+ function DefinitionBox({ value }) {
111
+ const definition = readDefinitionBox(value);
112
+ return /* @__PURE__ */ jsxs("aside", { className: "atlas-definition", "data-block": "definitionBox", "data-icon": definition.icon, children: [
113
+ /* @__PURE__ */ jsx("p", { className: "atlas-definition__term", children: definition.term ?? "" }),
114
+ /* @__PURE__ */ jsx("p", { className: "atlas-definition__body", children: definition.definition ?? "" })
115
+ ] });
116
+ }
117
+ function readChecklistBox(value) {
118
+ const block = readRecord(value) ?? {};
119
+ return {
120
+ title: readString(block.title),
121
+ items: Array.isArray(block.items) ? block.items.map(readString).filter((item) => item !== void 0) : []
122
+ };
123
+ }
124
+ function ChecklistBox({ value }) {
125
+ const checklist = readChecklistBox(value);
126
+ if (checklist.items.length === 0) {
127
+ return null;
128
+ }
129
+ return /* @__PURE__ */ jsxs("aside", { className: "atlas-checklist", "data-block": "checklistBox", children: [
130
+ checklist.title ? /* @__PURE__ */ jsx("h3", { className: "atlas-checklist__title", children: checklist.title }) : null,
131
+ /* @__PURE__ */ jsx("ul", { className: "atlas-checklist__items", children: checklist.items.map((item, index) => /* @__PURE__ */ jsx("li", { className: "atlas-checklist__item", children: item }, index)) })
132
+ ] });
133
+ }
134
+ function readCaptionedImage(value) {
135
+ const block = readRecord(value) ?? {};
136
+ const image = readRecord(block.image);
137
+ const asset = readRecord(image?.asset);
138
+ return {
139
+ image: image ? {
140
+ alt: readString(image.alt),
141
+ url: readString(image.url),
142
+ asset: asset ? { url: readString(asset.url) } : void 0
143
+ } : void 0,
144
+ caption: readString(block.caption),
145
+ credit: readString(block.credit)
146
+ };
147
+ }
148
+ function CaptionedImage({ value }) {
149
+ const image = readCaptionedImage(value);
150
+ const url = image.image?.url ?? image.image?.asset?.url;
151
+ if (!url) {
152
+ return /* @__PURE__ */ jsx("figure", { className: "atlas-image", "data-block": "captionedImage", "data-unresolved-image": "captionedImage", role: "alert", children: "Unresolved captioned image" });
153
+ }
154
+ return /* @__PURE__ */ jsxs("figure", { className: "atlas-image", "data-block": "captionedImage", children: [
155
+ /* @__PURE__ */ jsx("img", { className: "atlas-image__img", src: url, alt: image.image?.alt ?? "" }),
156
+ image.caption || image.credit ? /* @__PURE__ */ jsxs("figcaption", { className: "atlas-image__caption", children: [
157
+ image.caption,
158
+ image.credit ? /* @__PURE__ */ jsx("span", { className: "atlas-image__credit", children: image.credit }) : null
159
+ ] }) : null
160
+ ] });
161
+ }
162
+ function BotanicalDivider() {
163
+ return /* @__PURE__ */ jsx("hr", { className: "atlas-divider", "data-block": "botanicalDivider", role: "separator" });
164
+ }
165
+ var youtubeUrlPattern = /^(?:https?:\/\/)?(?:www\.)?(?:youtube\.com\/(?:watch\?(?:[^#]*&)?v=|embed\/|shorts\/)|youtu\.be\/)([A-Za-z0-9_-]{11})(?:[?&#/].*)?$/i;
166
+ function readVideoEmbed(value) {
167
+ const block = readRecord(value) ?? {};
168
+ return { url: readString(block.url), caption: readString(block.caption) };
169
+ }
170
+ function youtubeId(url) {
171
+ return url ? youtubeUrlPattern.exec(url)?.[1] : void 0;
172
+ }
173
+ function VideoEmbed({ value }) {
174
+ const video = readVideoEmbed(value);
175
+ const id = youtubeId(video.url);
176
+ if (!id) {
177
+ return /* @__PURE__ */ jsx("figure", { className: "atlas-video", "data-block": "videoEmbed", "data-unresolved-video": "videoEmbed", role: "alert", children: "Unresolved YouTube video" });
178
+ }
179
+ return /* @__PURE__ */ jsxs("figure", { className: "atlas-video", "data-block": "videoEmbed", children: [
180
+ /* @__PURE__ */ jsx("div", { className: "atlas-video__frame", children: /* @__PURE__ */ jsx(
181
+ "iframe",
182
+ {
183
+ src: `https://www.youtube-nocookie.com/embed/${id}`,
184
+ title: video.caption ? `Video: ${video.caption}` : "YouTube video",
185
+ loading: "lazy",
186
+ allowFullScreen: true
187
+ }
188
+ ) }),
189
+ video.caption ? /* @__PURE__ */ jsx("figcaption", { className: "atlas-video__caption", children: video.caption }) : null
190
+ ] });
191
+ }
192
+ function unknownBlockType(value) {
193
+ if (typeof value === "object" && value !== null && typeof value._type === "string") {
194
+ return value._type;
195
+ }
196
+ return "unknown";
197
+ }
198
+ var defaultTypeComponents = {
199
+ statCallout: StatCallout,
200
+ comparisonTable: ComparisonTable,
201
+ editorialQuote: EditorialQuote,
202
+ definitionBox: DefinitionBox,
203
+ checklistBox: ChecklistBox,
204
+ captionedImage: CaptionedImage,
205
+ botanicalDivider: BotanicalDivider,
206
+ videoEmbed: VideoEmbed
207
+ };
208
+ function PortableTextRenderer({ value, strict = false, components: overrides }) {
209
+ const components = {
210
+ types: { ...defaultTypeComponents, ...overrides?.types },
211
+ unknownType: ({ value: unknownValue }) => {
212
+ const type = unknownBlockType(unknownValue);
213
+ if (strict) {
214
+ throw new Error(`Unsupported Portable Text block type: ${type}`);
215
+ }
216
+ return /* @__PURE__ */ jsxs("span", { role: "alert", "data-unknown-block": type, children: [
217
+ "Unsupported Portable Text block: ",
218
+ type
219
+ ] });
220
+ }
221
+ };
222
+ return /* @__PURE__ */ jsx(
223
+ PortableText,
224
+ {
225
+ value,
226
+ components
227
+ }
228
+ );
229
+ }
230
+ export {
231
+ PortableTextRenderer,
232
+ capabilities
233
+ };
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@cosmic-labs/renderers",
3
+ "version": "0.1.0",
4
+ "description": "Portable Text renderers shared by Cosmic Atlas sites.",
5
+ "license": "UNLICENSED",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ },
14
+ "./styles.css": "./dist/atlas.css"
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "publishConfig": {
20
+ "access": "restricted"
21
+ },
22
+ "dependencies": {
23
+ "@cosmic-labs/content-contract": "0.1.0"
24
+ },
25
+ "peerDependencies": {
26
+ "@portabletext/react": "^3.2.0",
27
+ "react": "^18.2.0 || ^19.0.0"
28
+ },
29
+ "devDependencies": {
30
+ "@portabletext/react": "^3.2.0",
31
+ "@types/react": "^19.0.3",
32
+ "@types/react-dom": "^19.0.2",
33
+ "react": "^19.0.0",
34
+ "react-dom": "^19.0.0",
35
+ "tsup": "^8.3.5",
36
+ "typescript": "^5.7.2",
37
+ "vitest": "^3.0.0"
38
+ },
39
+ "engines": {
40
+ "node": ">=22.14"
41
+ },
42
+ "scripts": {
43
+ "build": "tsup src/index.ts --format esm --dts --clean --target es2022 --external react --external react-dom --external @portabletext/react && cp src/styles/atlas.css dist/atlas.css",
44
+ "test": "vitest run --root ../.. --project renderers",
45
+ "lint": "tsc --noEmit"
46
+ }
47
+ }