@drawnagency/primitives 0.1.71 → 0.1.73
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/{chunk-RTYJZAKR.js → chunk-7T2WQSWF.js} +12 -12
- package/dist/chunk-IFHRHV7S.js +117 -0
- package/dist/{chunk-2BHPWO7M.js → chunk-N6AE6NBB.js} +158 -32
- package/dist/{chunk-WXVKX6PY.js → chunk-NKVZGPR6.js} +41 -95
- package/dist/{chunk-3DCG4DNU.js → chunk-PZJL2S3Z.js} +8 -4
- package/dist/chunk-RBTSUR5V.js +37 -0
- package/dist/{chunk-6UDYJQL6.js → chunk-S55OZ7F2.js} +9 -1
- package/dist/{chunk-NR7UB6DZ.js → chunk-SFKYUKVN.js} +1 -1
- package/dist/{chunk-YMVYTYGQ.js → chunk-SWIHUIFT.js} +28 -16
- package/dist/{chunk-KWBM27UR.js → chunk-T7MP7ANP.js} +13 -64
- package/dist/{chunk-MU3CDERQ.js → chunk-XTRTK26E.js} +276 -96
- package/dist/{chunk-GKV77AA6.js → chunk-ZRWGV5O6.js} +3 -3
- package/dist/chunk-ZSPCE6MZ.js +70 -0
- package/dist/components/editor/SettingsForm.d.ts.map +1 -1
- package/dist/components/editor/index.js +6 -3
- package/dist/components/primitives/ResolvedMedia.d.ts.map +1 -1
- package/dist/components/primitives/index.js +7 -7
- package/dist/components/sections/Media/MediaBlock.d.ts.map +1 -1
- package/dist/components/sections/Video/VideoBlock.d.ts +31 -0
- package/dist/components/sections/Video/VideoBlock.d.ts.map +1 -0
- package/dist/components/sections/Video/index.d.ts +28 -0
- package/dist/components/sections/Video/index.d.ts.map +1 -0
- package/dist/components/sections/all-sections.d.ts +25 -0
- package/dist/components/sections/all-sections.d.ts.map +1 -1
- package/dist/components/sections/brand-guide.js +7 -7
- package/dist/components/sections/core-sections.d.ts +25 -0
- package/dist/components/sections/core-sections.d.ts.map +1 -1
- package/dist/components/sections/register-schemas.js +13 -12
- package/dist/components/sections/register.js +13 -12
- package/dist/components/shared/Tabs.d.ts +1 -1
- package/dist/components/shared/VideoSourceField.d.ts +8 -0
- package/dist/components/shared/VideoSourceField.d.ts.map +1 -0
- package/dist/components/shell/EditorShell.d.ts.map +1 -1
- package/dist/components/shell/MediaLibraryModal.d.ts +4 -1
- package/dist/components/shell/MediaLibraryModal.d.ts.map +1 -1
- package/dist/hooks/useEditorPublish.d.ts +4 -1
- package/dist/hooks/useEditorPublish.d.ts.map +1 -1
- package/dist/hooks/useMediaPipeline.d.ts +9 -1
- package/dist/hooks/useMediaPipeline.d.ts.map +1 -1
- package/dist/index.js +28 -24
- package/dist/lib/index.js +10 -10
- package/dist/lib/registry.d.ts +6 -0
- package/dist/lib/registry.d.ts.map +1 -1
- package/dist/lib/save-chunks.d.ts +28 -0
- package/dist/lib/save-chunks.d.ts.map +1 -0
- package/dist/lib/video-url.d.ts +7 -0
- package/dist/lib/video-url.d.ts.map +1 -0
- package/dist/lib/video-url.js +9 -0
- package/dist/media/index.js +6 -2
- package/dist/media/resolve.d.ts +14 -0
- package/dist/media/resolve.d.ts.map +1 -1
- package/dist/media/videoPoster.d.ts +1 -0
- package/dist/media/videoPoster.d.ts.map +1 -1
- package/dist/schemas/index.js +4 -4
- package/dist/schemas/link.d.ts.map +1 -1
- package/dist/schemas/link.js +1 -1
- package/dist/schemas/media.d.ts +13 -0
- package/dist/schemas/media.d.ts.map +1 -1
- package/dist/schemas/shared.d.ts.map +1 -1
- package/dist/schemas/site-config.d.ts +1 -0
- package/dist/schemas/site-config.d.ts.map +1 -1
- package/dist/storage/index.d.ts +1 -1
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/types.d.ts +26 -0
- package/dist/storage/types.d.ts.map +1 -1
- package/package.json +5 -1
- package/src/components/editor/SettingsForm.tsx +11 -0
- package/src/components/primitives/ResolvedMedia.tsx +10 -5
- package/src/components/sections/Media/MediaBlock.tsx +2 -1
- package/src/components/sections/Video/VideoBlock.tsx +149 -0
- package/src/components/sections/Video/index.tsx +111 -0
- package/src/components/sections/core-sections.ts +2 -0
- package/src/components/shared/Tabs.tsx +2 -2
- package/src/components/shared/VideoSourceField.tsx +144 -0
- package/src/components/shell/EditorShell.tsx +16 -2
- package/src/components/shell/MediaLibraryModal.tsx +47 -1
- package/src/hooks/useEditorPublish.ts +137 -50
- package/src/hooks/useMediaPipeline.ts +188 -25
- package/src/lib/registry.ts +7 -0
- package/src/lib/save-chunks.ts +53 -0
- package/src/lib/video-url.ts +39 -0
- package/src/media/resolve.ts +18 -0
- package/src/media/videoPoster.ts +2 -2
- package/src/schemas/link.ts +15 -4
- package/src/schemas/media.ts +12 -0
- package/src/schemas/shared.ts +23 -14
- package/src/storage/index.ts +1 -1
- package/src/storage/types.ts +23 -0
- package/dist/chunk-4E66N6UV.js +0 -10
- package/dist/chunk-I5737FJY.js +0 -62
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
EditablePlainText
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-T7MP7ANP.js";
|
|
4
4
|
import {
|
|
5
|
+
Button,
|
|
5
6
|
usePages
|
|
6
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-ZSPCE6MZ.js";
|
|
7
8
|
import {
|
|
8
9
|
sanitizeHtml,
|
|
9
10
|
toSectionId
|
|
@@ -78,61 +79,6 @@ function defineHeadingSection(config) {
|
|
|
78
79
|
// src/components/primitives/LinkPopover.tsx
|
|
79
80
|
import { useState } from "react";
|
|
80
81
|
|
|
81
|
-
// src/components/shared/Button.tsx
|
|
82
|
-
import { forwardRef } from "react";
|
|
83
|
-
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
84
|
-
var boxSize = {
|
|
85
|
-
sm: "rounded px-3 py-1.5 text-xs font-medium",
|
|
86
|
-
md: "rounded px-4 py-2 text-sm font-medium"
|
|
87
|
-
};
|
|
88
|
-
var ghostSize = {
|
|
89
|
-
sm: "text-xs font-medium",
|
|
90
|
-
md: "text-sm font-medium"
|
|
91
|
-
};
|
|
92
|
-
var variantClasses = {
|
|
93
|
-
primary: "bg-base-contrast text-base-accent hover:bg-base-contrast/90",
|
|
94
|
-
brand: "bg-primary text-primary-contrast hover:opacity-90",
|
|
95
|
-
secondary: "border border-base-200 text-base-contrast hover:bg-base-accent",
|
|
96
|
-
destructive: "border border-red-600 text-red-600 hover:bg-red-600 hover:text-white"
|
|
97
|
-
};
|
|
98
|
-
var ghostToneClasses = {
|
|
99
|
-
neutral: "text-base-contrast-light hover:text-base-contrast",
|
|
100
|
-
destructive: "text-red-600 hover:text-red-800"
|
|
101
|
-
};
|
|
102
|
-
var Button = forwardRef(function Button2({
|
|
103
|
-
variant = "primary",
|
|
104
|
-
size = "sm",
|
|
105
|
-
tone = "neutral",
|
|
106
|
-
fullWidth = false,
|
|
107
|
-
isLoading = false,
|
|
108
|
-
loadingLabel,
|
|
109
|
-
disabled,
|
|
110
|
-
className,
|
|
111
|
-
children,
|
|
112
|
-
...rest
|
|
113
|
-
}, ref) {
|
|
114
|
-
const isGhost = variant === "ghost";
|
|
115
|
-
const resolvedDisabled = disabled || isLoading;
|
|
116
|
-
const label = isLoading && loadingLabel ? loadingLabel : children;
|
|
117
|
-
return /* @__PURE__ */ jsx3(
|
|
118
|
-
"button",
|
|
119
|
-
{
|
|
120
|
-
ref,
|
|
121
|
-
disabled: resolvedDisabled,
|
|
122
|
-
className: cn(
|
|
123
|
-
"inline-flex items-center justify-center transition-colors",
|
|
124
|
-
isGhost ? ghostSize[size] : boxSize[size],
|
|
125
|
-
isGhost ? ghostToneClasses[tone] : variantClasses[variant],
|
|
126
|
-
fullWidth && "w-full",
|
|
127
|
-
resolvedDisabled ? "cursor-not-allowed opacity-50" : "cursor-pointer",
|
|
128
|
-
className
|
|
129
|
-
),
|
|
130
|
-
...rest,
|
|
131
|
-
children: label
|
|
132
|
-
}
|
|
133
|
-
);
|
|
134
|
-
});
|
|
135
|
-
|
|
136
82
|
// src/lib/links.ts
|
|
137
83
|
function internalHref(pageId, anchorSectionId) {
|
|
138
84
|
return `page://${pageId}${anchorSectionId ? `#${anchorSectionId}` : ""}`;
|
|
@@ -143,7 +89,7 @@ function parseInternalHref(href) {
|
|
|
143
89
|
}
|
|
144
90
|
|
|
145
91
|
// src/components/primitives/LinkPopover.tsx
|
|
146
|
-
import { Fragment, jsx as
|
|
92
|
+
import { Fragment, jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
147
93
|
var selectClasses = "w-full rounded border border-base-contrast/20 bg-base px-2 py-1 text-sm text-base-contrast focus:outline-none focus:ring-1 focus:ring-primary";
|
|
148
94
|
function LinkPopover({ editor, onClose }) {
|
|
149
95
|
const { pages, getPageHeadings } = usePages();
|
|
@@ -188,7 +134,7 @@ function LinkPopover({ editor, onClose }) {
|
|
|
188
134
|
onMouseDown: (e) => e.stopPropagation(),
|
|
189
135
|
onKeyDown: handleKeyDown,
|
|
190
136
|
children: [
|
|
191
|
-
/* @__PURE__ */
|
|
137
|
+
/* @__PURE__ */ jsx3("div", { className: "flex gap-2", role: "tablist", children: ["external", "internal"].map((kind) => /* @__PURE__ */ jsx3(
|
|
192
138
|
"button",
|
|
193
139
|
{
|
|
194
140
|
type: "button",
|
|
@@ -204,7 +150,7 @@ function LinkPopover({ editor, onClose }) {
|
|
|
204
150
|
kind
|
|
205
151
|
)) }),
|
|
206
152
|
mode === "external" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
207
|
-
/* @__PURE__ */
|
|
153
|
+
/* @__PURE__ */ jsx3(
|
|
208
154
|
"input",
|
|
209
155
|
{
|
|
210
156
|
autoFocus: true,
|
|
@@ -216,7 +162,7 @@ function LinkPopover({ editor, onClose }) {
|
|
|
216
162
|
}
|
|
217
163
|
),
|
|
218
164
|
/* @__PURE__ */ jsxs("label", { className: "flex cursor-pointer items-center gap-2 text-sm text-base-contrast", children: [
|
|
219
|
-
/* @__PURE__ */
|
|
165
|
+
/* @__PURE__ */ jsx3(
|
|
220
166
|
"input",
|
|
221
167
|
{
|
|
222
168
|
type: "checkbox",
|
|
@@ -240,8 +186,8 @@ function LinkPopover({ editor, onClose }) {
|
|
|
240
186
|
},
|
|
241
187
|
className: selectClasses,
|
|
242
188
|
children: [
|
|
243
|
-
/* @__PURE__ */
|
|
244
|
-
pages.map((p) => /* @__PURE__ */
|
|
189
|
+
/* @__PURE__ */ jsx3("option", { value: "", children: "Select a page\u2026" }),
|
|
190
|
+
pages.map((p) => /* @__PURE__ */ jsx3("option", { value: p.id, children: p.title }, p.id))
|
|
245
191
|
]
|
|
246
192
|
}
|
|
247
193
|
),
|
|
@@ -254,14 +200,14 @@ function LinkPopover({ editor, onClose }) {
|
|
|
254
200
|
onChange: (e) => setAnchorId(e.target.value),
|
|
255
201
|
className: cn(selectClasses, !pageId && "cursor-not-allowed opacity-50"),
|
|
256
202
|
children: [
|
|
257
|
-
/* @__PURE__ */
|
|
258
|
-
headings.map((h) => /* @__PURE__ */
|
|
203
|
+
/* @__PURE__ */ jsx3("option", { value: "", children: "None (top of page)" }),
|
|
204
|
+
headings.map((h) => /* @__PURE__ */ jsx3("option", { value: h.id, children: h.label }, h.id))
|
|
259
205
|
]
|
|
260
206
|
}
|
|
261
207
|
)
|
|
262
208
|
] }),
|
|
263
209
|
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
264
|
-
/* @__PURE__ */
|
|
210
|
+
/* @__PURE__ */ jsx3(
|
|
265
211
|
Button,
|
|
266
212
|
{
|
|
267
213
|
variant: "brand",
|
|
@@ -273,7 +219,7 @@ function LinkPopover({ editor, onClose }) {
|
|
|
273
219
|
children: "Apply"
|
|
274
220
|
}
|
|
275
221
|
),
|
|
276
|
-
hasLink && /* @__PURE__ */
|
|
222
|
+
hasLink && /* @__PURE__ */ jsx3(
|
|
277
223
|
Button,
|
|
278
224
|
{
|
|
279
225
|
variant: "secondary",
|
|
@@ -285,7 +231,7 @@ function LinkPopover({ editor, onClose }) {
|
|
|
285
231
|
children: "Remove"
|
|
286
232
|
}
|
|
287
233
|
),
|
|
288
|
-
/* @__PURE__ */
|
|
234
|
+
/* @__PURE__ */ jsx3(
|
|
289
235
|
Button,
|
|
290
236
|
{
|
|
291
237
|
variant: "secondary",
|
|
@@ -504,9 +450,9 @@ function useEditableRichText({
|
|
|
504
450
|
// src/components/primitives/RichTextToolbar.tsx
|
|
505
451
|
import { useState as useState3 } from "react";
|
|
506
452
|
import { useEditorState } from "@tiptap/react";
|
|
507
|
-
import { Fragment as Fragment2, jsx as
|
|
453
|
+
import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
508
454
|
function ToolbarButton({ isActive, onClick, title, children }) {
|
|
509
|
-
return /* @__PURE__ */
|
|
455
|
+
return /* @__PURE__ */ jsx4(
|
|
510
456
|
"button",
|
|
511
457
|
{
|
|
512
458
|
type: "button",
|
|
@@ -526,7 +472,7 @@ function ToolbarButton({ isActive, onClick, title, children }) {
|
|
|
526
472
|
);
|
|
527
473
|
}
|
|
528
474
|
function Separator() {
|
|
529
|
-
return /* @__PURE__ */
|
|
475
|
+
return /* @__PURE__ */ jsx4("div", { className: "mx-1 h-5 w-px bg-base-contrast/20" });
|
|
530
476
|
}
|
|
531
477
|
function RichTextToolbar({ editor, preset }) {
|
|
532
478
|
const [showLinkPopover, setShowLinkPopover] = useState3(false);
|
|
@@ -558,37 +504,37 @@ function RichTextToolbar({ editor, preset }) {
|
|
|
558
504
|
"data-testid": "rich-text-toolbar",
|
|
559
505
|
className: "flex items-center gap-1 rounded border border-base-contrast/10 bg-base-accent p-1 shadow-md",
|
|
560
506
|
children: [
|
|
561
|
-
/* @__PURE__ */
|
|
507
|
+
/* @__PURE__ */ jsx4(
|
|
562
508
|
ToolbarButton,
|
|
563
509
|
{
|
|
564
510
|
isActive: state.isBold,
|
|
565
511
|
onClick: () => editor.chain().focus().toggleBold().run(),
|
|
566
512
|
title: "Bold",
|
|
567
|
-
children: /* @__PURE__ */
|
|
513
|
+
children: /* @__PURE__ */ jsx4("strong", { children: "B" })
|
|
568
514
|
}
|
|
569
515
|
),
|
|
570
|
-
/* @__PURE__ */
|
|
516
|
+
/* @__PURE__ */ jsx4(
|
|
571
517
|
ToolbarButton,
|
|
572
518
|
{
|
|
573
519
|
isActive: state.isItalic,
|
|
574
520
|
onClick: () => editor.chain().focus().toggleItalic().run(),
|
|
575
521
|
title: "Italic",
|
|
576
|
-
children: /* @__PURE__ */
|
|
522
|
+
children: /* @__PURE__ */ jsx4("em", { children: "I" })
|
|
577
523
|
}
|
|
578
524
|
),
|
|
579
|
-
/* @__PURE__ */
|
|
525
|
+
/* @__PURE__ */ jsx4(
|
|
580
526
|
ToolbarButton,
|
|
581
527
|
{
|
|
582
528
|
isActive: state.isUnderline,
|
|
583
529
|
onClick: () => editor.chain().focus().toggleUnderline().run(),
|
|
584
530
|
title: "Underline",
|
|
585
|
-
children: /* @__PURE__ */
|
|
531
|
+
children: /* @__PURE__ */ jsx4("span", { className: "underline", children: "U" })
|
|
586
532
|
}
|
|
587
533
|
),
|
|
588
534
|
preset === "rich" && /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
589
|
-
/* @__PURE__ */
|
|
535
|
+
/* @__PURE__ */ jsx4(Separator, {}),
|
|
590
536
|
/* @__PURE__ */ jsxs2("div", { className: "relative", children: [
|
|
591
|
-
/* @__PURE__ */
|
|
537
|
+
/* @__PURE__ */ jsx4(
|
|
592
538
|
ToolbarButton,
|
|
593
539
|
{
|
|
594
540
|
isActive: state.isLink || showLinkPopover,
|
|
@@ -597,7 +543,7 @@ function RichTextToolbar({ editor, preset }) {
|
|
|
597
543
|
children: "\u{1F517}"
|
|
598
544
|
}
|
|
599
545
|
),
|
|
600
|
-
showLinkPopover && /* @__PURE__ */
|
|
546
|
+
showLinkPopover && /* @__PURE__ */ jsx4("div", { className: "absolute left-0 top-full z-50 mt-1", children: /* @__PURE__ */ jsx4(
|
|
601
547
|
LinkPopover,
|
|
602
548
|
{
|
|
603
549
|
editor,
|
|
@@ -605,8 +551,8 @@ function RichTextToolbar({ editor, preset }) {
|
|
|
605
551
|
}
|
|
606
552
|
) })
|
|
607
553
|
] }),
|
|
608
|
-
/* @__PURE__ */
|
|
609
|
-
/* @__PURE__ */
|
|
554
|
+
/* @__PURE__ */ jsx4(Separator, {}),
|
|
555
|
+
/* @__PURE__ */ jsx4(
|
|
610
556
|
ToolbarButton,
|
|
611
557
|
{
|
|
612
558
|
isActive: state.isBulletList,
|
|
@@ -615,7 +561,7 @@ function RichTextToolbar({ editor, preset }) {
|
|
|
615
561
|
children: "\u2022\u2261"
|
|
616
562
|
}
|
|
617
563
|
),
|
|
618
|
-
/* @__PURE__ */
|
|
564
|
+
/* @__PURE__ */ jsx4(
|
|
619
565
|
ToolbarButton,
|
|
620
566
|
{
|
|
621
567
|
isActive: state.isOrderedList,
|
|
@@ -624,8 +570,8 @@ function RichTextToolbar({ editor, preset }) {
|
|
|
624
570
|
children: "1\u2261"
|
|
625
571
|
}
|
|
626
572
|
),
|
|
627
|
-
/* @__PURE__ */
|
|
628
|
-
/* @__PURE__ */
|
|
573
|
+
/* @__PURE__ */ jsx4(Separator, {}),
|
|
574
|
+
/* @__PURE__ */ jsx4(
|
|
629
575
|
ToolbarButton,
|
|
630
576
|
{
|
|
631
577
|
isActive: state.isH3,
|
|
@@ -634,7 +580,7 @@ function RichTextToolbar({ editor, preset }) {
|
|
|
634
580
|
children: "H3"
|
|
635
581
|
}
|
|
636
582
|
),
|
|
637
|
-
/* @__PURE__ */
|
|
583
|
+
/* @__PURE__ */ jsx4(
|
|
638
584
|
ToolbarButton,
|
|
639
585
|
{
|
|
640
586
|
isActive: state.isH4,
|
|
@@ -643,7 +589,7 @@ function RichTextToolbar({ editor, preset }) {
|
|
|
643
589
|
children: "H4"
|
|
644
590
|
}
|
|
645
591
|
),
|
|
646
|
-
/* @__PURE__ */
|
|
592
|
+
/* @__PURE__ */ jsx4(
|
|
647
593
|
ToolbarButton,
|
|
648
594
|
{
|
|
649
595
|
isActive: state.isLarge,
|
|
@@ -652,7 +598,7 @@ function RichTextToolbar({ editor, preset }) {
|
|
|
652
598
|
children: "Lg"
|
|
653
599
|
}
|
|
654
600
|
),
|
|
655
|
-
/* @__PURE__ */
|
|
601
|
+
/* @__PURE__ */ jsx4(
|
|
656
602
|
ToolbarButton,
|
|
657
603
|
{
|
|
658
604
|
isActive: state.isLeadIn,
|
|
@@ -668,7 +614,7 @@ function RichTextToolbar({ editor, preset }) {
|
|
|
668
614
|
}
|
|
669
615
|
|
|
670
616
|
// src/components/primitives/EditableRichText.tsx
|
|
671
|
-
import { jsx as
|
|
617
|
+
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
672
618
|
function isContentEmpty(html) {
|
|
673
619
|
return html.replace(/<[^>]*>/g, "").trim().length === 0;
|
|
674
620
|
}
|
|
@@ -736,7 +682,7 @@ function EditableRichText({
|
|
|
736
682
|
}, 150);
|
|
737
683
|
}, [deactivate]);
|
|
738
684
|
if (!isEditMode) {
|
|
739
|
-
return /* @__PURE__ */
|
|
685
|
+
return /* @__PURE__ */ jsx5(
|
|
740
686
|
"div",
|
|
741
687
|
{
|
|
742
688
|
className,
|
|
@@ -747,9 +693,9 @@ function EditableRichText({
|
|
|
747
693
|
if (!isEditorActive || !editor) {
|
|
748
694
|
const empty = isContentEmpty(value);
|
|
749
695
|
if (empty) {
|
|
750
|
-
return /* @__PURE__ */
|
|
696
|
+
return /* @__PURE__ */ jsx5("div", { className, onClick: handleClick, children: /* @__PURE__ */ jsx5("p", { className: "tiptap-placeholder", children: placeholder }) });
|
|
751
697
|
}
|
|
752
|
-
return /* @__PURE__ */
|
|
698
|
+
return /* @__PURE__ */ jsx5(
|
|
753
699
|
"div",
|
|
754
700
|
{
|
|
755
701
|
className,
|
|
@@ -766,7 +712,7 @@ function EditableRichText({
|
|
|
766
712
|
onFocus: handleFocus,
|
|
767
713
|
onBlur: handleBlur,
|
|
768
714
|
children: [
|
|
769
|
-
/* @__PURE__ */
|
|
715
|
+
/* @__PURE__ */ jsx5(
|
|
770
716
|
BubbleMenu,
|
|
771
717
|
{
|
|
772
718
|
editor,
|
|
@@ -775,10 +721,10 @@ function EditableRichText({
|
|
|
775
721
|
const { from, to } = state.selection;
|
|
776
722
|
return ed.isFocused && from !== to;
|
|
777
723
|
},
|
|
778
|
-
children: /* @__PURE__ */
|
|
724
|
+
children: /* @__PURE__ */ jsx5(RichTextToolbar, { editor, preset })
|
|
779
725
|
}
|
|
780
726
|
),
|
|
781
|
-
/* @__PURE__ */
|
|
727
|
+
/* @__PURE__ */ jsx5(EditorContent, { editor })
|
|
782
728
|
]
|
|
783
729
|
}
|
|
784
730
|
);
|
|
@@ -11,12 +11,16 @@ function isSafeHref(href) {
|
|
|
11
11
|
return s === "http" || s === "https" || s === "mailto";
|
|
12
12
|
}
|
|
13
13
|
var LinkValueSchema = z.discriminatedUnion("kind", [
|
|
14
|
-
z.object({
|
|
14
|
+
z.object({
|
|
15
|
+
kind: z.literal("external"),
|
|
16
|
+
href: z.string().refine(isSafeHref, "Unsafe href scheme").describe("An absolute http(s)/mailto URL, a relative path, or empty (unset). javascript:, data:, and protocol-relative URLs are rejected."),
|
|
17
|
+
target: LinkTargetSchema.describe("Where the link opens: '_self' (same tab) or '_blank' (new tab).")
|
|
18
|
+
}),
|
|
15
19
|
z.object({
|
|
16
20
|
kind: z.literal("internal"),
|
|
17
|
-
pageId: z.string(),
|
|
18
|
-
anchorSectionId: z.string().nullable().optional(),
|
|
19
|
-
target: LinkTargetSchema
|
|
21
|
+
pageId: z.string().describe("Id of the page to link to, within the same site."),
|
|
22
|
+
anchorSectionId: z.string().nullable().optional().describe("Optional section id on the target page to scroll to; null or omitted links to the page itself."),
|
|
23
|
+
target: LinkTargetSchema.describe("Where the link opens: '_self' (same tab) or '_blank' (new tab).")
|
|
20
24
|
})
|
|
21
25
|
]);
|
|
22
26
|
var DEFAULT_LINK = { kind: "external", href: "", target: "_self" };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// src/lib/video-url.ts
|
|
2
|
+
var YT_ID = /^[\w-]{6,20}$/;
|
|
3
|
+
function parseVideoUrl(raw) {
|
|
4
|
+
let url;
|
|
5
|
+
try {
|
|
6
|
+
url = new URL(raw.trim());
|
|
7
|
+
} catch {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") return null;
|
|
11
|
+
const host = url.hostname.replace(/^www\./, "");
|
|
12
|
+
if (host === "youtube.com" || host === "m.youtube.com" || host === "youtube-nocookie.com") {
|
|
13
|
+
if (url.pathname === "/watch") {
|
|
14
|
+
const v = url.searchParams.get("v");
|
|
15
|
+
return v && YT_ID.test(v) ? { provider: "youtube", id: v } : null;
|
|
16
|
+
}
|
|
17
|
+
const m = url.pathname.match(/^\/(?:embed|shorts|live)\/([\w-]{6,20})$/);
|
|
18
|
+
return m ? { provider: "youtube", id: m[1] } : null;
|
|
19
|
+
}
|
|
20
|
+
if (host === "youtu.be") {
|
|
21
|
+
const m = url.pathname.match(/^\/([\w-]{6,20})$/);
|
|
22
|
+
return m ? { provider: "youtube", id: m[1] } : null;
|
|
23
|
+
}
|
|
24
|
+
if (host === "vimeo.com" || host === "player.vimeo.com") {
|
|
25
|
+
const m = url.pathname.match(/\/(\d{6,12})$/);
|
|
26
|
+
return m ? { provider: "vimeo", id: m[1] } : null;
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
function embedUrl(p) {
|
|
31
|
+
return p.provider === "youtube" ? `https://www.youtube-nocookie.com/embed/${p.id}` : `https://player.vimeo.com/video/${p.id}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export {
|
|
35
|
+
parseVideoUrl,
|
|
36
|
+
embedUrl
|
|
37
|
+
};
|
|
@@ -220,6 +220,12 @@ function resolveManifestReferences(content, manifest, sizes, provider) {
|
|
|
220
220
|
}
|
|
221
221
|
return content;
|
|
222
222
|
}
|
|
223
|
+
var BAKED_MEDIA_KEYS = ["src", "srcset", "alt", "width", "height", "poster"];
|
|
224
|
+
function rebindImageRef(ref, imageId) {
|
|
225
|
+
const fresh = { ...ref, imageId };
|
|
226
|
+
for (const key of BAKED_MEDIA_KEYS) delete fresh[key];
|
|
227
|
+
return fresh;
|
|
228
|
+
}
|
|
223
229
|
|
|
224
230
|
// src/media/videoPoster.ts
|
|
225
231
|
function generateVideoPoster(blob, quality) {
|
|
@@ -272,7 +278,7 @@ function generateVideoPoster(blob, quality) {
|
|
|
272
278
|
reject(new Error("Failed to create poster blob"));
|
|
273
279
|
return;
|
|
274
280
|
}
|
|
275
|
-
resolve({ posterBlob, width: w, height: h });
|
|
281
|
+
resolve({ posterBlob, width: w, height: h, duration: video.duration });
|
|
276
282
|
},
|
|
277
283
|
"image/webp",
|
|
278
284
|
quality / 100
|
|
@@ -312,6 +318,8 @@ export {
|
|
|
312
318
|
ProcessingQueue,
|
|
313
319
|
resolveMedia,
|
|
314
320
|
resolveManifestReferences,
|
|
321
|
+
BAKED_MEDIA_KEYS,
|
|
322
|
+
rebindImageRef,
|
|
315
323
|
generateVideoPoster,
|
|
316
324
|
setMediaProvider,
|
|
317
325
|
getMediaProvider
|
|
@@ -24,32 +24,32 @@ var TextLineSchema = z.discriminatedUnion("type", [
|
|
|
24
24
|
TextListItemLine
|
|
25
25
|
]);
|
|
26
26
|
var BaseMediaRef = z.object({
|
|
27
|
-
imageId: z.string().default(""),
|
|
28
|
-
caption: z.union([z.string(), z.array(z.string())]).optional(),
|
|
29
|
-
background: z.string().optional(),
|
|
30
|
-
invertFrom: z.string().optional(),
|
|
31
|
-
border: z.boolean().optional(),
|
|
32
|
-
objectFit: z.enum(["cover", "contain"]).optional()
|
|
27
|
+
imageId: z.string().default("").describe("Media-library id of the image/video asset."),
|
|
28
|
+
caption: z.union([z.string(), z.array(z.string())]).optional().describe("Optional caption: a single string, or multiple lines as an array of strings."),
|
|
29
|
+
background: z.string().optional().describe("Optional CSS background color/value shown behind a transparent image."),
|
|
30
|
+
invertFrom: z.string().optional().describe("Optional CSS color; the image inverts when the page background matches it (e.g. for dark-mode logo swaps)."),
|
|
31
|
+
border: z.boolean().optional().describe("When true, draw a border around the media."),
|
|
32
|
+
objectFit: z.enum(["cover", "contain"]).optional().describe("How the image fills its box: 'contain' (fit whole image) or 'cover' (fill and crop).")
|
|
33
33
|
});
|
|
34
34
|
var ImageRef = BaseMediaRef.extend({
|
|
35
35
|
type: z.literal("image")
|
|
36
36
|
});
|
|
37
37
|
var VideoRef = BaseMediaRef.extend({
|
|
38
38
|
type: z.literal("video"),
|
|
39
|
-
poster: z.string().optional(),
|
|
40
|
-
autoplay: z.boolean().optional(),
|
|
41
|
-
loop: z.boolean().optional(),
|
|
42
|
-
muted: z.boolean().optional()
|
|
39
|
+
poster: z.string().optional().describe("Optional poster-image URL shown before playback starts."),
|
|
40
|
+
autoplay: z.boolean().optional().describe("When true, the video starts playing automatically."),
|
|
41
|
+
loop: z.boolean().optional().describe("When true, the video repeats when it finishes."),
|
|
42
|
+
muted: z.boolean().optional().describe("When true, the video plays without sound.")
|
|
43
43
|
});
|
|
44
44
|
var DoDontImageRef = BaseMediaRef.extend({
|
|
45
45
|
type: z.literal("doDontImage"),
|
|
46
|
-
doDont: z.enum(["do", "dont"])
|
|
46
|
+
doDont: z.enum(["do", "dont"]).describe("Whether this is a 'do' (correct) or 'dont' (incorrect) example.")
|
|
47
47
|
});
|
|
48
48
|
var LinkedImageRef = BaseMediaRef.extend({
|
|
49
49
|
type: z.literal("linkedImage"),
|
|
50
|
-
href: z.string(),
|
|
51
|
-
target: z.string().optional(),
|
|
52
|
-
linkText: z.string().optional()
|
|
50
|
+
href: z.string().describe("URL the image links to."),
|
|
51
|
+
target: z.string().optional().describe("Link target, e.g. '_self' or '_blank'."),
|
|
52
|
+
linkText: z.string().optional().describe("Optional accessible link text/label.")
|
|
53
53
|
});
|
|
54
54
|
var MediaReferenceSchema = z.discriminatedUnion("type", [
|
|
55
55
|
ImageRef,
|
|
@@ -79,7 +79,16 @@ var MediaItemSchema = z2.object({
|
|
|
79
79
|
size: z2.number().int().nonnegative(),
|
|
80
80
|
folder: z2.string(),
|
|
81
81
|
variants: z2.array(VariantSchema),
|
|
82
|
-
alt: z2.string().default("")
|
|
82
|
+
alt: z2.string().default(""),
|
|
83
|
+
// Where the original bytes live. "github" = committed to the repo (images,
|
|
84
|
+
// small loops); "assets" = large original in the AssetStore bucket. Legacy
|
|
85
|
+
// manifests have no field — default keeps them on the git path.
|
|
86
|
+
storage: z2.enum(["github", "assets"]).default("github"),
|
|
87
|
+
// Public CDN URL for assets-backed originals, baked at upload time so
|
|
88
|
+
// resolution needs no env access in the browser or at SSR.
|
|
89
|
+
url: z2.string().optional(),
|
|
90
|
+
// Seconds; videos only. Informational (library UI).
|
|
91
|
+
duration: z2.number().positive().optional()
|
|
83
92
|
});
|
|
84
93
|
var ImageManifestSchema = z2.object({
|
|
85
94
|
images: z2.record(z2.string(), MediaItemSchema)
|
|
@@ -87,7 +96,10 @@ var ImageManifestSchema = z2.object({
|
|
|
87
96
|
var MediaConfigSchema = z2.object({
|
|
88
97
|
sizes: z2.array(z2.number()).default([640, 1080, 1920]),
|
|
89
98
|
maxFileSize: z2.number().default(5242880),
|
|
90
|
-
quality: z2.number().min(1).max(100).default(85)
|
|
99
|
+
quality: z2.number().min(1).max(100).default(85),
|
|
100
|
+
// Per-file cap for bucket-backed video uploads (bytes). The git-path cap is
|
|
101
|
+
// maxFileSize; anything between the two routes to the AssetStore.
|
|
102
|
+
maxAssetSize: z2.number().default(209715200)
|
|
91
103
|
});
|
|
92
104
|
|
|
93
105
|
// src/schemas/site-config.ts
|
|
@@ -3,11 +3,13 @@ import {
|
|
|
3
3
|
DeleteIcon,
|
|
4
4
|
DragHandle,
|
|
5
5
|
IconButton,
|
|
6
|
-
SettingsIcon
|
|
7
|
-
|
|
6
|
+
SettingsIcon,
|
|
7
|
+
useMediaLibrary,
|
|
8
|
+
useResolvedMedia
|
|
9
|
+
} from "./chunk-IFHRHV7S.js";
|
|
8
10
|
import {
|
|
9
|
-
|
|
10
|
-
} from "./chunk-
|
|
11
|
+
rebindImageRef
|
|
12
|
+
} from "./chunk-S55OZ7F2.js";
|
|
11
13
|
import {
|
|
12
14
|
cn
|
|
13
15
|
} from "./chunk-7EOSDFS3.js";
|
|
@@ -128,60 +130,6 @@ function EditablePlainText({
|
|
|
128
130
|
// src/components/primitives/ResolvedMedia.tsx
|
|
129
131
|
import { useState as useState2 } from "react";
|
|
130
132
|
import { ImageIcon } from "lucide-react";
|
|
131
|
-
|
|
132
|
-
// src/hooks/useResolvedMedia.ts
|
|
133
|
-
import { useMemo } from "react";
|
|
134
|
-
|
|
135
|
-
// src/components/shell/MediaLibraryContext.tsx
|
|
136
|
-
import { createContext, useContext } from "react";
|
|
137
|
-
var MediaLibraryContext = createContext(null);
|
|
138
|
-
function useMediaLibrary() {
|
|
139
|
-
return useContext(MediaLibraryContext);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// src/hooks/useResolvedMedia.ts
|
|
143
|
-
function useResolvedMedia(imageId) {
|
|
144
|
-
const ctx = useMediaLibrary();
|
|
145
|
-
return useMemo(() => {
|
|
146
|
-
if (!imageId || !ctx) {
|
|
147
|
-
return { src: void 0, srcset: void 0, poster: void 0, alt: "", kind: "image" };
|
|
148
|
-
}
|
|
149
|
-
const { manifest, pendingItems, pendingLocalUrls, siteConfig } = ctx;
|
|
150
|
-
const sizes = siteConfig?.media?.sizes ?? [640, 1080, 1920];
|
|
151
|
-
const manifestItem = manifest.images[imageId];
|
|
152
|
-
const pendingItem = pendingItems.find((i) => i.id === imageId);
|
|
153
|
-
const item = manifestItem ?? pendingItem;
|
|
154
|
-
const alt = item?.alt ?? "";
|
|
155
|
-
const kind = item?.kind ?? "image";
|
|
156
|
-
const width = item?.width;
|
|
157
|
-
const height = item?.height;
|
|
158
|
-
const localUrl = pendingLocalUrls[imageId];
|
|
159
|
-
if (localUrl) {
|
|
160
|
-
return { src: localUrl, srcset: void 0, poster: void 0, alt, kind, width, height };
|
|
161
|
-
}
|
|
162
|
-
if (manifestItem) {
|
|
163
|
-
const provider = getMediaProvider();
|
|
164
|
-
const resolved = provider.resolve(manifestItem, sizes);
|
|
165
|
-
if (resolved && resolved.tag === "img") {
|
|
166
|
-
return {
|
|
167
|
-
src: resolved.src,
|
|
168
|
-
srcset: "srcSet" in resolved ? resolved.srcSet : void 0,
|
|
169
|
-
poster: void 0,
|
|
170
|
-
alt,
|
|
171
|
-
kind,
|
|
172
|
-
width: resolved.width,
|
|
173
|
-
height: resolved.height
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
if (resolved && resolved.tag === "video") {
|
|
177
|
-
return { src: resolved.src, srcset: void 0, poster: resolved.poster, alt, kind, width: resolved.width, height: resolved.height };
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
return { src: void 0, srcset: void 0, poster: void 0, alt, kind, width, height };
|
|
181
|
-
}, [imageId, ctx]);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// src/components/primitives/ResolvedMedia.tsx
|
|
185
133
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
186
134
|
function ResolvedMedia({
|
|
187
135
|
imageId,
|
|
@@ -195,13 +143,14 @@ function ResolvedMedia({
|
|
|
195
143
|
invertFrom
|
|
196
144
|
}) {
|
|
197
145
|
const resolved = useResolvedMedia(imageId);
|
|
198
|
-
const
|
|
199
|
-
const
|
|
146
|
+
const hasResolved = !!resolved.src;
|
|
147
|
+
const src = hasResolved ? resolved.src : propSrc;
|
|
148
|
+
const srcset = hasResolved ? resolved.srcset : propSrcset;
|
|
200
149
|
const poster = resolved.poster;
|
|
201
|
-
const alt =
|
|
150
|
+
const alt = hasResolved ? resolved.alt : propAlt ?? "";
|
|
202
151
|
const kind = resolved.kind;
|
|
203
|
-
const width =
|
|
204
|
-
const height =
|
|
152
|
+
const width = hasResolved ? resolved.width : propWidth;
|
|
153
|
+
const height = hasResolved ? resolved.height : propHeight;
|
|
205
154
|
const [loaded, setLoaded] = useState2(false);
|
|
206
155
|
const invertClass = invertFrom === "light" ? "invert dark:invert-0" : invertFrom === "dark" ? "dark:invert" : void 0;
|
|
207
156
|
const mediaClass = cn("relative h-full w-full", imgClassName, invertClass);
|
|
@@ -314,7 +263,7 @@ function MediaBlock({ refValue, options, link, isEditMode, onRefChange }) {
|
|
|
314
263
|
const fitClass = fit === "cover" ? "object-cover" : "object-contain";
|
|
315
264
|
const showBorder = refValue.border ?? options.border;
|
|
316
265
|
const captionStr = refValue.caption ? Array.isArray(refValue.caption) ? refValue.caption.join("\n") : refValue.caption : "";
|
|
317
|
-
const replace = onRefChange ? (imageId) => onRefChange(
|
|
266
|
+
const replace = onRefChange ? (imageId) => onRefChange(rebindImageRef(refValue, imageId)) : void 0;
|
|
318
267
|
const baked = refValue;
|
|
319
268
|
const bakedWidth = typeof baked.width === "number" ? baked.width : void 0;
|
|
320
269
|
const bakedHeight = typeof baked.height === "number" ? baked.height : void 0;
|