tinacms 3.12.1 → 3.13.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/index.js +1500 -907
- package/dist/internalClient/authProvider.d.ts +6 -0
- package/dist/internalClient/index.d.ts +14 -2
- package/dist/internalClient/types.d.ts +11 -0
- package/dist/rich-text/index.js +35 -35
- package/dist/rich-text/static.js +40 -37
- package/dist/toolkit/alerts/alerts.d.ts +7 -1
- package/dist/toolkit/components/media/media-manager.d.ts +3 -1
- package/dist/toolkit/components/media/utils.d.ts +12 -0
- package/dist/toolkit/core/cms.d.ts +2 -2
- package/dist/toolkit/core/errors.d.ts +1 -0
- package/dist/toolkit/core/index.d.ts +2 -0
- package/dist/toolkit/core/media-store.default.d.ts +41 -15
- package/dist/toolkit/core/media.d.ts +18 -0
- package/dist/toolkit/core/session-expired.d.ts +6 -0
- package/dist/toolkit/fields/components/image-upload/image-upload.d.ts +2 -0
- package/dist/toolkit/fields/plugins/image-field-plugin.d.ts +2 -0
- package/dist/toolkit/fields/plugins/mdx-field-plugin/plate/plugins/create-html-block/index.d.ts +2 -2
- package/dist/toolkit/form-builder/create-branch-modal.d.ts +2 -1
- package/dist/toolkit/form-builder/editorial-workflow-utils.d.ts +2 -0
- package/dist/toolkit/index.d.ts +1 -1
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -20,8 +20,7 @@ import { SlashInputPlugin, SlashPlugin } from "@udecode/plate-slash-command/reac
|
|
|
20
20
|
import { TablePlugin, TableRowPlugin, useTableCellElement, TableProvider, useTableElement, useTableMergeState, useTableBordersDropdownMenuContentState, TableCellPlugin, TableCellHeaderPlugin } from "@udecode/plate-table/react";
|
|
21
21
|
import { PlateElement, useReadOnly, useElement, PlateLeaf, useSelected, useFocused, useEditorRef, withRef as withRef$1, useComposedRef, useEditorPlugin, useElementSelector, withHOC, usePluginOption, useRemoveNodeButton, useEditorSelector, ParagraphPlugin, createPlatePlugin, PlateContent, PlateContainer, useEditorState, useMarkToolbarButtonState, useMarkToolbarButton, usePlateEditor, useFormInputProps, useEditorSelection, useEditorReadOnly, useOnClickOutside, useEventEditorValue, Plate } from "@udecode/plate/react";
|
|
22
22
|
import { isLangSupported, formatCodeBlock, insertEmptyCodeBlock, isCodeBlockEmpty, unwrapCodeBlock, isSelectionAtCodeBlockStart } from "@udecode/plate-code-block";
|
|
23
|
-
import { X, Search, ChevronDown, Check, AlertTriangle, BracesIcon, Heading6 as Heading6$1, Heading5 as Heading5$1, Heading4 as Heading4$1, Heading3 as Heading3$1, Heading2 as Heading2$1, Heading1 as Heading1$1, ChevronRight, Twitter, SunMedium, Moon, SeparatorHorizontal, Eye, Link2Off, Underline, Trash, Text, Table as Table$1, Superscript, Subscript, Strikethrough, Highlighter, Settings, RectangleHorizontal, RotateCcw, Pilcrow, Outdent, MoreHorizontal, Minus, WrapText, Keyboard, Indent, ExternalLink, Smile, Edit2, GripVertical, MessageSquarePlus, MessageSquare, Ungroup, Combine, RectangleVertical, Baseline, FileCode, PaintBucket, ChevronsUpDown, Quote, AlignRight as AlignRight$1, AlignLeft as AlignLeft$1, AlignJustify, AlignCenter as AlignCenter$1, Plus, PaintBucketIcon, CombineIcon, SquareSplitHorizontalIcon, Grid2X2Icon, Trash2Icon, ArrowUp, ArrowDown, XIcon, ArrowLeft, ArrowRight, EraserIcon, Info, CircleAlert, CircleCheck as CircleCheck$1, ChevronDownIcon as ChevronDownIcon$1, File as File$1, ChevronUp, Edit, Pencil, Clock, CalendarCheck, Calendar as Calendar$1, CalendarDays, RotateCw, ChevronLeft, WifiOff, TriangleAlert, RefreshCw, GitBranch, CircleHelp, Loader2, Lock, ArrowUpDown, GitFork, Globe, Copy, Folder as Folder$1, Clapperboard, LayoutGrid, List as List$1, CloudUpload, ArrowDownToLine, TextCursorInput, Images, AlertCircle, Image, Database, CheckCircle2, ImageOff, KeyRound, Cloud, LoaderCircle, ScrollText, RefreshCwOff, OctagonAlert, Menu, FilePlus, LogOut, FileStack, FolderOpen, History, Users, PanelLeft, Files, User, FileText, GitBranchIcon, Circle as Circle$1, ListOrdered, Grid3x3Icon, CircleX, Link, Unlink, SquarePen, Trash2, House, Unlock } from "lucide-react";
|
|
24
|
-
import mermaid from "mermaid";
|
|
23
|
+
import { X, Search, ChevronDown, Check, AlertTriangle, BracesIcon, Heading6 as Heading6$1, Heading5 as Heading5$1, Heading4 as Heading4$1, Heading3 as Heading3$1, Heading2 as Heading2$1, Heading1 as Heading1$1, ChevronRight, Twitter, SunMedium, Moon, SeparatorHorizontal, Eye, Link2Off, Underline, Trash, Text, Table as Table$1, Superscript, Subscript, Strikethrough, Highlighter, Settings, RectangleHorizontal, RotateCcw, Pilcrow, Outdent, MoreHorizontal, Minus, WrapText, Keyboard, Indent, ExternalLink, Smile, Edit2, GripVertical, MessageSquarePlus, MessageSquare, Ungroup, Combine, RectangleVertical, Baseline, FileCode, PaintBucket, ChevronsUpDown, Quote, AlignRight as AlignRight$1, AlignLeft as AlignLeft$1, AlignJustify, AlignCenter as AlignCenter$1, Plus, PaintBucketIcon, CombineIcon, SquareSplitHorizontalIcon, Grid2X2Icon, Trash2Icon, ArrowUp, ArrowDown, XIcon, ArrowLeft, ArrowRight, EraserIcon, Info, CircleAlert, CircleCheck as CircleCheck$1, CircleIcon, ChevronDownIcon as ChevronDownIcon$1, File as File$1, ChevronUp, Edit, Pencil, Clock, CalendarCheck, Calendar as Calendar$1, CalendarDays, RotateCw, ChevronLeft, WifiOff, TriangleAlert, RefreshCw, GitBranch, CircleHelp, Loader2, Lock, ArrowUpDown, GitFork, Globe, Copy, Folder as Folder$1, Clapperboard, LayoutGrid, List as List$1, CloudUpload, ArrowDownToLine, TextCursorInput, Images, AlertCircle, Image, Database, CheckCircle2, ImageOff, KeyRound, Cloud, LoaderCircle, ScrollText, RefreshCwOff, OctagonAlert, Menu, FilePlus, LogOut, FileStack, FolderOpen, History, Users, PanelLeft, Files, User, FileText, GitBranchIcon, Circle as Circle$1, ListOrdered, Grid3x3Icon, CircleX, Link, Unlink, SquarePen, Trash2, House, Unlock } from "lucide-react";
|
|
25
24
|
import { Slot } from "@radix-ui/react-slot";
|
|
26
25
|
import { cva } from "class-variance-authority";
|
|
27
26
|
import { Command as Command$2 } from "cmdk";
|
|
@@ -33,7 +32,7 @@ import { useHTMLInputCursorState, useComboboxInput } from "@udecode/plate-combob
|
|
|
33
32
|
import { useComboboxContext, Combobox as Combobox$1, useComboboxStore, ComboboxProvider, Portal, ComboboxPopover, ComboboxItem } from "@ariakit/react";
|
|
34
33
|
import { filterWords } from "@udecode/plate-combobox";
|
|
35
34
|
import { toggleList, unwrapList, getListItemEntry } from "@udecode/plate-list";
|
|
36
|
-
import { normalizeHeadingLevels, ALL_HEADING_LEVELS, ASYNC_POLLER_ERROR, canonicalPath, ERR_BRANCH_EXISTS, ERR_BRANCH_CONFLICT, isHeadingLevel, TinaSchema, addNamespaceToSchema, parseURL, ERR_HAS_REFERENCES, ERR_ALREADY_EXISTS, isValidRelativePath, RELATIVE_PATH_ALLOWED_CHARS_MESSAGE, resolveForm, RELATIVE_PATH_REGEX, normalizePath, ERR_NOT_INDEXED, validateSchema } from "@tinacms/schema-tools";
|
|
35
|
+
import { normalizeHeadingLevels, ALL_HEADING_LEVELS, MEDIA_MIME_TYPES, extensionOf, resolveMediaAccept, ASYNC_POLLER_ERROR, MEDIA_CATEGORIES, canonicalPath, ERR_BRANCH_EXISTS, ERR_BRANCH_CONFLICT, isHeadingLevel, TinaSchema, addNamespaceToSchema, parseURL, ERR_HAS_REFERENCES, ERR_ALREADY_EXISTS, isValidRelativePath, RELATIVE_PATH_ALLOWED_CHARS_MESSAGE, resolveForm, RELATIVE_PATH_REGEX, normalizePath, ERR_NOT_INDEXED, validateSchema } from "@tinacms/schema-tools";
|
|
37
36
|
import { NAMER, resolveField } from "@tinacms/schema-tools";
|
|
38
37
|
import { useBlockSelected, BlockSelectionPlugin } from "@udecode/plate-selection/react";
|
|
39
38
|
import "@udecode/plate-dnd";
|
|
@@ -63,12 +62,12 @@ import { ExitBreakPlugin, SoftBreakPlugin } from "@udecode/plate-break/react";
|
|
|
63
62
|
import { ResetNodePlugin } from "@udecode/plate-reset-node/react";
|
|
64
63
|
import { TrailingBlockPlugin } from "@udecode/plate-trailing-block";
|
|
65
64
|
import { BaseBlockquotePlugin } from "@udecode/plate-block-quote";
|
|
65
|
+
import { autoformatSmartQuotes, autoformatPunctuation, autoformatLegal, autoformatArrow, autoformatMath } from "@udecode/plate-autoformat";
|
|
66
66
|
import { HeadingPlugin } from "@udecode/plate-heading/react";
|
|
67
67
|
import { IndentListPlugin } from "@udecode/plate-indent-list/react";
|
|
68
68
|
import { NodeIdPlugin } from "@udecode/plate-node-id";
|
|
69
69
|
import { offset, flip, shift, limitShift, useVirtualFloating, getSelectionBoundingClientRect } from "@udecode/plate-floating";
|
|
70
70
|
import { getLinkAttributes } from "@udecode/plate-link";
|
|
71
|
-
import { autoformatSmartQuotes, autoformatPunctuation, autoformatLegal, autoformatArrow, autoformatMath } from "@udecode/plate-autoformat";
|
|
72
71
|
import * as yup from "yup";
|
|
73
72
|
import { format as format$3, formatDistanceToNow } from "date-fns";
|
|
74
73
|
import { enUS } from "date-fns/locale";
|
|
@@ -1396,20 +1395,6 @@ function CodeBlockCombobox({
|
|
|
1396
1395
|
)))))
|
|
1397
1396
|
));
|
|
1398
1397
|
}
|
|
1399
|
-
const MermaidElementWithRef = ({ config }) => {
|
|
1400
|
-
const mermaidRef = useRef(null);
|
|
1401
|
-
useEffect(() => {
|
|
1402
|
-
if (mermaidRef.current) {
|
|
1403
|
-
const renderMermaid = async () => {
|
|
1404
|
-
await mermaid.run({
|
|
1405
|
-
nodes: [mermaidRef.current.querySelector(".mermaid")]
|
|
1406
|
-
});
|
|
1407
|
-
};
|
|
1408
|
-
renderMermaid();
|
|
1409
|
-
}
|
|
1410
|
-
}, [config]);
|
|
1411
|
-
return /* @__PURE__ */ React__default.createElement("div", { contentEditable: false, className: "border-border border-b pt-10" }, /* @__PURE__ */ React__default.createElement("div", { ref: mermaidRef }, /* @__PURE__ */ React__default.createElement("pre", { className: "mermaid not-tina-prose" }, config)));
|
|
1412
|
-
};
|
|
1413
1398
|
function ErrorMessage$1({ error }) {
|
|
1414
1399
|
if (!error) return null;
|
|
1415
1400
|
return /* @__PURE__ */ React__default.createElement(
|
|
@@ -1423,6 +1408,52 @@ function ErrorMessage$1({ error }) {
|
|
|
1423
1408
|
/* @__PURE__ */ React__default.createElement("div", { className: "ml-3 flex-1" }, /* @__PURE__ */ React__default.createElement("pre", { className: "m-0 font-mono text-sm text-red-700 whitespace-pre-wrap break-words" }, error))
|
|
1424
1409
|
);
|
|
1425
1410
|
}
|
|
1411
|
+
const RENDERER_FAILED = "The diagram renderer did not load.";
|
|
1412
|
+
const MermaidElementWithRef = ({ config }) => {
|
|
1413
|
+
const mermaidRef = useRef(null);
|
|
1414
|
+
const [status, setStatus] = useState("loading");
|
|
1415
|
+
const [error, setError] = useState(null);
|
|
1416
|
+
useEffect(() => {
|
|
1417
|
+
let isCancelled = false;
|
|
1418
|
+
setStatus("loading");
|
|
1419
|
+
const render = async () => {
|
|
1420
|
+
var _a;
|
|
1421
|
+
try {
|
|
1422
|
+
const node = (_a = mermaidRef.current) == null ? void 0 : _a.querySelector(".mermaid");
|
|
1423
|
+
if (!node) throw new Error(RENDERER_FAILED);
|
|
1424
|
+
const { default: mermaid } = await import("mermaid");
|
|
1425
|
+
await mermaid.run({ nodes: [node] });
|
|
1426
|
+
if (!isCancelled) {
|
|
1427
|
+
setError(null);
|
|
1428
|
+
setStatus("ready");
|
|
1429
|
+
}
|
|
1430
|
+
} catch (cause) {
|
|
1431
|
+
if (isCancelled) {
|
|
1432
|
+
return;
|
|
1433
|
+
}
|
|
1434
|
+
if (cause instanceof Error) {
|
|
1435
|
+
setError(cause.message || RENDERER_FAILED);
|
|
1436
|
+
} else {
|
|
1437
|
+
setError(RENDERER_FAILED);
|
|
1438
|
+
}
|
|
1439
|
+
setStatus("failed");
|
|
1440
|
+
}
|
|
1441
|
+
};
|
|
1442
|
+
render();
|
|
1443
|
+
return () => {
|
|
1444
|
+
isCancelled = true;
|
|
1445
|
+
};
|
|
1446
|
+
}, [config]);
|
|
1447
|
+
return /* @__PURE__ */ React__default.createElement("div", { contentEditable: false, className: "border-border border-b pt-10" }, /* @__PURE__ */ React__default.createElement("div", { className: "relative" }, /* @__PURE__ */ React__default.createElement(
|
|
1448
|
+
"div",
|
|
1449
|
+
{
|
|
1450
|
+
ref: mermaidRef,
|
|
1451
|
+
className: status === "loading" ? "opacity-0" : void 0
|
|
1452
|
+
},
|
|
1453
|
+
/* @__PURE__ */ React__default.createElement("pre", { className: "mermaid not-tina-prose" }, config)
|
|
1454
|
+
), status === "loading" && /* @__PURE__ */ React__default.createElement("div", { className: "absolute inset-0 flex items-center justify-center text-sm text-muted-foreground" }, "Loading diagram…")), status === "failed" && /* @__PURE__ */ React__default.createElement(ErrorMessage$1, { error }));
|
|
1455
|
+
};
|
|
1456
|
+
const PARSE_FAILED = "An error occurred while parsing the diagram.";
|
|
1426
1457
|
function codeLineToString(content) {
|
|
1427
1458
|
return (content.children || []).map(
|
|
1428
1459
|
(line) => (line.children || []).map((textNode) => textNode.text).join("")
|
|
@@ -1440,17 +1471,21 @@ const CodeBlockElement = withRef(
|
|
|
1440
1471
|
let isCancelled = false;
|
|
1441
1472
|
const validateMermaid = async () => {
|
|
1442
1473
|
try {
|
|
1474
|
+
const { default: mermaid } = await import("mermaid");
|
|
1443
1475
|
await mermaid.parse(
|
|
1444
1476
|
codeLineToString(element)
|
|
1445
1477
|
);
|
|
1446
1478
|
if (!isCancelled) {
|
|
1447
1479
|
setCodeBlockError(null);
|
|
1448
1480
|
}
|
|
1449
|
-
} catch (
|
|
1450
|
-
if (
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1481
|
+
} catch (cause) {
|
|
1482
|
+
if (isCancelled) {
|
|
1483
|
+
return;
|
|
1484
|
+
}
|
|
1485
|
+
if (cause instanceof Error) {
|
|
1486
|
+
setCodeBlockError(cause.message || PARSE_FAILED);
|
|
1487
|
+
} else {
|
|
1488
|
+
setCodeBlockError(PARSE_FAILED);
|
|
1454
1489
|
}
|
|
1455
1490
|
}
|
|
1456
1491
|
};
|
|
@@ -2759,7 +2794,7 @@ const DropdownMenuTrigger$1 = DropdownMenuPrimitive.Trigger;
|
|
|
2759
2794
|
const DropdownMenuGroup = DropdownMenuPrimitive.Group;
|
|
2760
2795
|
const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
|
|
2761
2796
|
const DropdownMenuSub = DropdownMenuPrimitive.Sub;
|
|
2762
|
-
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
|
|
2797
|
+
const DropdownMenuRadioGroup$1 = DropdownMenuPrimitive.RadioGroup;
|
|
2763
2798
|
const DropdownMenuSubTrigger = withRef(({ children, className, inset, ...props }, ref) => /* @__PURE__ */ React__default.createElement(
|
|
2764
2799
|
DropdownMenuPrimitive.SubTrigger,
|
|
2765
2800
|
{
|
|
@@ -2818,7 +2853,7 @@ const DropdownMenuCheckboxItem = withRef(({ children, className, ...props }, ref
|
|
|
2818
2853
|
/* @__PURE__ */ React__default.createElement("span", { className: "absolute left-2 flex size-3.5 items-center justify-center" }, /* @__PURE__ */ React__default.createElement(DropdownMenuPrimitive.ItemIndicator, null, /* @__PURE__ */ React__default.createElement(Icons.check, { className: "size-4" }))),
|
|
2819
2854
|
children
|
|
2820
2855
|
));
|
|
2821
|
-
const DropdownMenuRadioItem = withRef(({ children, className, hideIcon, ...props }, ref) => /* @__PURE__ */ React__default.createElement(
|
|
2856
|
+
const DropdownMenuRadioItem$1 = withRef(({ children, className, hideIcon, ...props }, ref) => /* @__PURE__ */ React__default.createElement(
|
|
2822
2857
|
DropdownMenuPrimitive.RadioItem,
|
|
2823
2858
|
{
|
|
2824
2859
|
className: cn$1(
|
|
@@ -2842,7 +2877,7 @@ const dropdownMenuLabelVariants = cva(
|
|
|
2842
2877
|
}
|
|
2843
2878
|
}
|
|
2844
2879
|
);
|
|
2845
|
-
const DropdownMenuLabel = withVariants(
|
|
2880
|
+
const DropdownMenuLabel$1 = withVariants(
|
|
2846
2881
|
DropdownMenuPrimitive.Label,
|
|
2847
2882
|
dropdownMenuLabelVariants,
|
|
2848
2883
|
["inset"]
|
|
@@ -4166,6 +4201,17 @@ TinaField.propTypes = {
|
|
|
4166
4201
|
Component: PropTypes.any.isRequired,
|
|
4167
4202
|
children: PropTypes.any
|
|
4168
4203
|
};
|
|
4204
|
+
const isErrorNamed = (error, name2) => error instanceof Error && error.name === name2;
|
|
4205
|
+
class SessionExpiredError extends Error {
|
|
4206
|
+
constructor() {
|
|
4207
|
+
super("Your session has ended. Please sign in again.");
|
|
4208
|
+
this.name = "SessionExpiredError";
|
|
4209
|
+
}
|
|
4210
|
+
}
|
|
4211
|
+
const isSessionExpiredError = (error) => error instanceof SessionExpiredError || isErrorNamed(error, "SessionExpiredError");
|
|
4212
|
+
const dispatchSessionExpired = (events) => {
|
|
4213
|
+
events.dispatch({ type: "cms:session-expired" });
|
|
4214
|
+
};
|
|
4169
4215
|
const LoadingDots = ({
|
|
4170
4216
|
dotSize = 8,
|
|
4171
4217
|
color = "white"
|
|
@@ -4474,6 +4520,54 @@ function DropdownMenuItem({
|
|
|
4474
4520
|
}
|
|
4475
4521
|
);
|
|
4476
4522
|
}
|
|
4523
|
+
function DropdownMenuRadioGroup({
|
|
4524
|
+
...props
|
|
4525
|
+
}) {
|
|
4526
|
+
return /* @__PURE__ */ React.createElement(
|
|
4527
|
+
DropdownMenuPrimitive.RadioGroup,
|
|
4528
|
+
{
|
|
4529
|
+
"data-slot": "dropdown-menu-radio-group",
|
|
4530
|
+
...props
|
|
4531
|
+
}
|
|
4532
|
+
);
|
|
4533
|
+
}
|
|
4534
|
+
function DropdownMenuRadioItem({
|
|
4535
|
+
className,
|
|
4536
|
+
children,
|
|
4537
|
+
...props
|
|
4538
|
+
}) {
|
|
4539
|
+
return /* @__PURE__ */ React.createElement(
|
|
4540
|
+
DropdownMenuPrimitive.RadioItem,
|
|
4541
|
+
{
|
|
4542
|
+
"data-slot": "dropdown-menu-radio-item",
|
|
4543
|
+
className: cn(
|
|
4544
|
+
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
4545
|
+
className
|
|
4546
|
+
),
|
|
4547
|
+
...props
|
|
4548
|
+
},
|
|
4549
|
+
/* @__PURE__ */ React.createElement("span", { className: "pointer-events-none absolute left-2 flex size-3.5 items-center justify-center" }, /* @__PURE__ */ React.createElement(DropdownMenuPrimitive.ItemIndicator, null, /* @__PURE__ */ React.createElement(CircleIcon, { className: "size-2 fill-current" }))),
|
|
4550
|
+
children
|
|
4551
|
+
);
|
|
4552
|
+
}
|
|
4553
|
+
function DropdownMenuLabel({
|
|
4554
|
+
className,
|
|
4555
|
+
inset,
|
|
4556
|
+
...props
|
|
4557
|
+
}) {
|
|
4558
|
+
return /* @__PURE__ */ React.createElement(
|
|
4559
|
+
DropdownMenuPrimitive.Label,
|
|
4560
|
+
{
|
|
4561
|
+
"data-slot": "dropdown-menu-label",
|
|
4562
|
+
"data-inset": inset,
|
|
4563
|
+
className: cn(
|
|
4564
|
+
"px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
|
|
4565
|
+
className
|
|
4566
|
+
),
|
|
4567
|
+
...props
|
|
4568
|
+
}
|
|
4569
|
+
);
|
|
4570
|
+
}
|
|
4477
4571
|
function DropdownMenuSeparator({
|
|
4478
4572
|
className,
|
|
4479
4573
|
...props
|
|
@@ -5557,12 +5651,38 @@ const dropzoneAcceptFromString = (str) => {
|
|
|
5557
5651
|
...(str || DEFAULT_MEDIA_UPLOAD_TYPES).split(",").map((x) => ({ [x]: [] }))
|
|
5558
5652
|
);
|
|
5559
5653
|
};
|
|
5560
|
-
const
|
|
5561
|
-
|
|
5562
|
-
};
|
|
5563
|
-
const
|
|
5564
|
-
|
|
5654
|
+
const dropzoneAcceptFromExtensions = (extensions) => {
|
|
5655
|
+
if (!extensions.length) return void 0;
|
|
5656
|
+
const accept = {};
|
|
5657
|
+
for (const ext of extensions) {
|
|
5658
|
+
const mimeType = MEDIA_MIME_TYPES[ext];
|
|
5659
|
+
accept[mimeType] ?? (accept[mimeType] = []);
|
|
5660
|
+
accept[mimeType].push(`.${ext}`);
|
|
5661
|
+
}
|
|
5662
|
+
return accept;
|
|
5565
5663
|
};
|
|
5664
|
+
const PREVIEWABLE_IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
5665
|
+
"gif",
|
|
5666
|
+
"jpg",
|
|
5667
|
+
"jpeg",
|
|
5668
|
+
"tiff",
|
|
5669
|
+
"png",
|
|
5670
|
+
"svg",
|
|
5671
|
+
"webp",
|
|
5672
|
+
"avif"
|
|
5673
|
+
]);
|
|
5674
|
+
const PREVIEWABLE_VIDEO_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
5675
|
+
"mp4",
|
|
5676
|
+
"webm",
|
|
5677
|
+
"ogg",
|
|
5678
|
+
"m4v",
|
|
5679
|
+
"mov",
|
|
5680
|
+
"avi",
|
|
5681
|
+
"flv",
|
|
5682
|
+
"mkv"
|
|
5683
|
+
]);
|
|
5684
|
+
const isImage = (filename) => PREVIEWABLE_IMAGE_EXTENSIONS.has(extensionOf(filename ?? ""));
|
|
5685
|
+
const isVideo = (filename) => PREVIEWABLE_VIDEO_EXTENSIONS.has(extensionOf(filename ?? ""));
|
|
5566
5686
|
const absoluteImgURL = (str) => {
|
|
5567
5687
|
if (str.startsWith("http")) return str;
|
|
5568
5688
|
return `${window.location.origin}${str}`;
|
|
@@ -5624,12 +5744,13 @@ const StyledImage = ({ src }) => {
|
|
|
5624
5744
|
const StyledFile = ({ src }) => {
|
|
5625
5745
|
return /* @__PURE__ */ React.createElement("div", { className: "max-w-full w-full flex-1 flex justify-start items-center gap-3" }, /* @__PURE__ */ React.createElement("div", { className: "w-12 h-12 bg-white shadow border border-gray-100 rounded flex justify-center flex-none" }, /* @__PURE__ */ React.createElement(File$1, { className: "w-3/5 h-full text-gray-300" })), /* @__PURE__ */ React.createElement("span", { className: "text-base text-left flex-1 text-gray-500 w-full break-words truncate" }, src));
|
|
5626
5746
|
};
|
|
5627
|
-
const ImageUpload = React.forwardRef(({ onDrop, onClear, onClick, value, src, loading }, ref) => {
|
|
5747
|
+
const ImageUpload = React.forwardRef(({ onDrop, onClear, onClick, value, src, loading, accept }, ref) => {
|
|
5628
5748
|
const cms = useCMS();
|
|
5629
5749
|
const { getRootProps, getInputProps } = useDropzone$1({
|
|
5630
|
-
accept
|
|
5631
|
-
|
|
5632
|
-
|
|
5750
|
+
// A field's `accept` overrides the global `media.accept`, so a file the
|
|
5751
|
+
// field would refuse is rejected here rather than uploaded and then
|
|
5752
|
+
// discarded by the insert guard.
|
|
5753
|
+
accept: dropzoneAcceptFromExtensions(resolveMediaAccept(accept)) ?? dropzoneAcceptFromString(cms.media.accept || DEFAULT_MEDIA_UPLOAD_TYPES),
|
|
5633
5754
|
onDrop,
|
|
5634
5755
|
noClick: !!onClick
|
|
5635
5756
|
});
|
|
@@ -7349,6 +7470,16 @@ const ImageField = wrapFieldsWithMeta(
|
|
|
7349
7470
|
var _a, _b;
|
|
7350
7471
|
if (media) {
|
|
7351
7472
|
const item = Array.isArray(media) ? media[0] : media;
|
|
7473
|
+
const allowed = resolveMediaAccept(
|
|
7474
|
+
props.field.accept
|
|
7475
|
+
);
|
|
7476
|
+
const ext = extensionOf(item.filename) || extensionOf(item.src ?? "");
|
|
7477
|
+
if (allowed.length && !allowed.includes(ext)) {
|
|
7478
|
+
cms.alerts.error(
|
|
7479
|
+
`${item.filename} is not an accepted file type. This field accepts ${allowed.join(", ")}.`
|
|
7480
|
+
);
|
|
7481
|
+
return;
|
|
7482
|
+
}
|
|
7352
7483
|
const parsedValue = typeof ((_b = (_a = cms == null ? void 0 : cms.media) == null ? void 0 : _a.store) == null ? void 0 : _b.parse) === "function" ? cms.media.store.parse(item) : item.src || item;
|
|
7353
7484
|
props.input.onChange(parsedValue);
|
|
7354
7485
|
}
|
|
@@ -7361,12 +7492,14 @@ const ImageField = wrapFieldsWithMeta(
|
|
|
7361
7492
|
value,
|
|
7362
7493
|
src,
|
|
7363
7494
|
loading: isImgUploading,
|
|
7495
|
+
accept: props.field.accept,
|
|
7364
7496
|
onClick: () => {
|
|
7365
7497
|
const directory = uploadDir(props.form.getState().values);
|
|
7366
7498
|
cms.media.open({
|
|
7367
7499
|
allowDelete: true,
|
|
7368
7500
|
directory,
|
|
7369
|
-
onSelect: onChange
|
|
7501
|
+
onSelect: onChange,
|
|
7502
|
+
accept: props.field.accept
|
|
7370
7503
|
});
|
|
7371
7504
|
},
|
|
7372
7505
|
onDrop: async ([file], fileRejections) => {
|
|
@@ -9938,9 +10071,21 @@ let Alerts$1 = class Alerts {
|
|
|
9938
10071
|
this.add(level, message, timeout);
|
|
9939
10072
|
}
|
|
9940
10073
|
});
|
|
10074
|
+
/**
|
|
10075
|
+
* While suppressed (the window between a session expiring and the user
|
|
10076
|
+
* signing back in), new alerts are dropped so nothing paints over the
|
|
10077
|
+
* login modal. Alerts already showing are left alone.
|
|
10078
|
+
*/
|
|
10079
|
+
__publicField(this, "suppressed", false);
|
|
9941
10080
|
this.events = events;
|
|
9942
10081
|
this.map = map;
|
|
9943
10082
|
this.events.subscribe("*", this.mapEventToAlert);
|
|
10083
|
+
this.events.subscribe("cms:session-expired", () => {
|
|
10084
|
+
this.suppressed = true;
|
|
10085
|
+
});
|
|
10086
|
+
this.events.subscribe("cms:login", () => {
|
|
10087
|
+
this.suppressed = false;
|
|
10088
|
+
});
|
|
9944
10089
|
}
|
|
9945
10090
|
setMap(eventsToAlerts) {
|
|
9946
10091
|
this.map = {
|
|
@@ -9949,6 +10094,10 @@ let Alerts$1 = class Alerts {
|
|
|
9949
10094
|
};
|
|
9950
10095
|
}
|
|
9951
10096
|
add(level, message, timeout = 4e3) {
|
|
10097
|
+
if (this.suppressed) {
|
|
10098
|
+
return () => {
|
|
10099
|
+
};
|
|
10100
|
+
}
|
|
9952
10101
|
const alert = {
|
|
9953
10102
|
level,
|
|
9954
10103
|
message,
|
|
@@ -9989,219 +10138,6 @@ let Alerts$1 = class Alerts {
|
|
|
9989
10138
|
return this.add("error", message, timeout);
|
|
9990
10139
|
}
|
|
9991
10140
|
};
|
|
9992
|
-
class PluginTypeManager {
|
|
9993
|
-
constructor(events) {
|
|
9994
|
-
/**
|
|
9995
|
-
* @ignore
|
|
9996
|
-
*/
|
|
9997
|
-
__publicField(this, "plugins", {});
|
|
9998
|
-
this.events = events;
|
|
9999
|
-
}
|
|
10000
|
-
/**
|
|
10001
|
-
* Gets the [[PluginType|collection of plugins]] for the given type.
|
|
10002
|
-
*
|
|
10003
|
-
* #### Example: Basic Usage
|
|
10004
|
-
*
|
|
10005
|
-
* ```ts
|
|
10006
|
-
* const colorPlugins = cms.plugins.get("color")
|
|
10007
|
-
* ```
|
|
10008
|
-
*
|
|
10009
|
-
* #### Example: Advanced Types
|
|
10010
|
-
*
|
|
10011
|
-
* A type param can be added to specify the kind of Plugin
|
|
10012
|
-
* that is being listed.
|
|
10013
|
-
*
|
|
10014
|
-
* ```ts
|
|
10015
|
-
* const colorPlugins = cms.plugins.get<ColorPlugin>("color")
|
|
10016
|
-
* ```
|
|
10017
|
-
*
|
|
10018
|
-
* @param type The type of plugins to be retrieved
|
|
10019
|
-
* @typeparam P A subclass of Plugin. Optional.
|
|
10020
|
-
*/
|
|
10021
|
-
getType(type2) {
|
|
10022
|
-
return this.plugins[type2] = this.plugins[type2] || new PluginType(type2, this.events);
|
|
10023
|
-
}
|
|
10024
|
-
/**
|
|
10025
|
-
* An alias to [[get]]
|
|
10026
|
-
*
|
|
10027
|
-
* ### !DEPRECATED!
|
|
10028
|
-
*
|
|
10029
|
-
* This name is unnecessarily verbose and weird.
|
|
10030
|
-
*/
|
|
10031
|
-
findOrCreateMap(type2) {
|
|
10032
|
-
return this.getType(type2);
|
|
10033
|
-
}
|
|
10034
|
-
/**
|
|
10035
|
-
* Adds a Plugin to the CMS.
|
|
10036
|
-
*
|
|
10037
|
-
* #### Example: Basic Usage
|
|
10038
|
-
*
|
|
10039
|
-
* ```js
|
|
10040
|
-
* cms.plugins.add({ __type: "color", name: "red" })
|
|
10041
|
-
* ```
|
|
10042
|
-
*
|
|
10043
|
-
* #### Example: Advanced Types
|
|
10044
|
-
*
|
|
10045
|
-
* ```ts
|
|
10046
|
-
* interface ColorPlugin extends Plugin {
|
|
10047
|
-
* __type: "color"
|
|
10048
|
-
* hex: string
|
|
10049
|
-
* rgb: string
|
|
10050
|
-
* }
|
|
10051
|
-
*
|
|
10052
|
-
* cms.plugins.add<ColorPlugin>({
|
|
10053
|
-
* __type: "color",
|
|
10054
|
-
* name: "red",
|
|
10055
|
-
* hex: "#FF0000",
|
|
10056
|
-
* rgb: "RGBA(255, 0, 0, 1)"
|
|
10057
|
-
* })
|
|
10058
|
-
* ```
|
|
10059
|
-
*
|
|
10060
|
-
* @typeparam P A subclass of Plugin. Optional.
|
|
10061
|
-
* @param plugin
|
|
10062
|
-
* @todo Consider returning the plugin which was just added.
|
|
10063
|
-
*/
|
|
10064
|
-
add(plugin) {
|
|
10065
|
-
this.findOrCreateMap(plugin.__type).add(plugin);
|
|
10066
|
-
}
|
|
10067
|
-
/**
|
|
10068
|
-
* Removes the given plugin from the CMS.
|
|
10069
|
-
*
|
|
10070
|
-
* #### Example
|
|
10071
|
-
*
|
|
10072
|
-
* In this example a plugin is added to the CMS and removed
|
|
10073
|
-
* 5 seconds later.
|
|
10074
|
-
*
|
|
10075
|
-
* ```ts
|
|
10076
|
-
* const redPlugin = {
|
|
10077
|
-
* __type: "color",
|
|
10078
|
-
* name: "red",
|
|
10079
|
-
* hex: "#FF0000",
|
|
10080
|
-
* rgb: "RGBA(255, 0, 0, 1)"
|
|
10081
|
-
* }
|
|
10082
|
-
*
|
|
10083
|
-
* cms.plugins.add(redPlugin)
|
|
10084
|
-
*
|
|
10085
|
-
* setTimeout(() => {
|
|
10086
|
-
* cms.plugins.remove(redPlugin)
|
|
10087
|
-
* }, 5000)
|
|
10088
|
-
* ```
|
|
10089
|
-
*
|
|
10090
|
-
* @typeparam P A subclass of Plugin. Optional.
|
|
10091
|
-
* @param plugin The plugin to be removed from the CMS.
|
|
10092
|
-
*/
|
|
10093
|
-
remove(plugin) {
|
|
10094
|
-
this.findOrCreateMap(plugin.__type).remove(plugin);
|
|
10095
|
-
}
|
|
10096
|
-
/**
|
|
10097
|
-
* Returns a list of all the plugins of the given type.
|
|
10098
|
-
*
|
|
10099
|
-
* #### Example: Basic Usage
|
|
10100
|
-
*
|
|
10101
|
-
* ```ts
|
|
10102
|
-
* cms.plugins.all("color").forEach(color => {
|
|
10103
|
-
* console.log(color.name)
|
|
10104
|
-
* })
|
|
10105
|
-
* ```
|
|
10106
|
-
*
|
|
10107
|
-
* #### Example: Advanced Types
|
|
10108
|
-
*
|
|
10109
|
-
* A generic can be added to specify the type of plugins
|
|
10110
|
-
* that are being retrieved.
|
|
10111
|
-
*
|
|
10112
|
-
* ```ts
|
|
10113
|
-
* cms.plugins.all<ColorPlugin>("color").forEach(color => {
|
|
10114
|
-
* console.log(color.name, color.hex)
|
|
10115
|
-
* })
|
|
10116
|
-
* ```
|
|
10117
|
-
*
|
|
10118
|
-
* @typeparam P A subclass of Plugin. Optional.
|
|
10119
|
-
* @param type The name of the plugin
|
|
10120
|
-
* @returns An array of all plugins of the given type.
|
|
10121
|
-
*/
|
|
10122
|
-
all(type2) {
|
|
10123
|
-
return this.findOrCreateMap(type2).all();
|
|
10124
|
-
}
|
|
10125
|
-
}
|
|
10126
|
-
class PluginType {
|
|
10127
|
-
/**
|
|
10128
|
-
*
|
|
10129
|
-
* @param __type The `__type` of plugin being managed.
|
|
10130
|
-
*/
|
|
10131
|
-
constructor(__type, events) {
|
|
10132
|
-
/**
|
|
10133
|
-
* @ignore
|
|
10134
|
-
*/
|
|
10135
|
-
__publicField(this, "__plugins", {});
|
|
10136
|
-
this.__type = __type;
|
|
10137
|
-
this.events = events;
|
|
10138
|
-
}
|
|
10139
|
-
/**
|
|
10140
|
-
* Adds a new plugin to the collection.
|
|
10141
|
-
*
|
|
10142
|
-
* ### Example
|
|
10143
|
-
*
|
|
10144
|
-
* ```ts
|
|
10145
|
-
* interface ColorPlugin extends Plugin {
|
|
10146
|
-
* hex: string
|
|
10147
|
-
* }
|
|
10148
|
-
*
|
|
10149
|
-
* const colorPlugins = new PluginType<ColorPlugin>("color")
|
|
10150
|
-
*
|
|
10151
|
-
* colorPlugins.add({ name: "red", hex: "#f00" })
|
|
10152
|
-
* ```
|
|
10153
|
-
*
|
|
10154
|
-
* @param plugin A new plugin. The `__type` is optional and will be added if it's missing.
|
|
10155
|
-
*/
|
|
10156
|
-
add(plugin) {
|
|
10157
|
-
const p = plugin;
|
|
10158
|
-
if (!p.__type) {
|
|
10159
|
-
p.__type = this.__type;
|
|
10160
|
-
}
|
|
10161
|
-
this.__plugins[p.name] = p;
|
|
10162
|
-
this.events.dispatch({ type: `plugin:add:${this.__type}` });
|
|
10163
|
-
}
|
|
10164
|
-
all() {
|
|
10165
|
-
return Object.keys(this.__plugins).map((name2) => this.__plugins[name2]);
|
|
10166
|
-
}
|
|
10167
|
-
/**
|
|
10168
|
-
*
|
|
10169
|
-
* Looks up a plugin by it's `name`.
|
|
10170
|
-
*
|
|
10171
|
-
* ### Example
|
|
10172
|
-
*
|
|
10173
|
-
* ```ts
|
|
10174
|
-
* const colorPlugins = new PluginType<ColorPlugin>("color")
|
|
10175
|
-
*
|
|
10176
|
-
* colorPlugins.add({ name: "red", hex: "#f00" })
|
|
10177
|
-
*
|
|
10178
|
-
* colorPlugins.find("red") // { __type: "color", name: "red", hex: "#f00" }
|
|
10179
|
-
* colorPlugin.find("large") // undefined
|
|
10180
|
-
* ```
|
|
10181
|
-
*
|
|
10182
|
-
* @param name The `name` of the plugin to be retrieved.
|
|
10183
|
-
*/
|
|
10184
|
-
find(name2) {
|
|
10185
|
-
return this.__plugins[name2];
|
|
10186
|
-
}
|
|
10187
|
-
/**
|
|
10188
|
-
* Pass this function a plugin or the `name` of a plugin to have
|
|
10189
|
-
* it be removed from the CMS.
|
|
10190
|
-
*
|
|
10191
|
-
* @param pluginOrName The `name` of a plugin, or the plugin itself.
|
|
10192
|
-
* @returns The plugin that was removed, or `undefined` if it was not found.
|
|
10193
|
-
*/
|
|
10194
|
-
remove(pluginOrName) {
|
|
10195
|
-
const name2 = typeof pluginOrName === "string" ? pluginOrName : pluginOrName.name;
|
|
10196
|
-
const plugin = this.__plugins[name2];
|
|
10197
|
-
delete this.__plugins[name2];
|
|
10198
|
-
this.events.dispatch({ type: `plugin:remove:${this.__type}` });
|
|
10199
|
-
return plugin;
|
|
10200
|
-
}
|
|
10201
|
-
subscribe(cb) {
|
|
10202
|
-
return this.events.subscribe(`plugin:*:${this.__type}`, cb);
|
|
10203
|
-
}
|
|
10204
|
-
}
|
|
10205
10141
|
class EventBus {
|
|
10206
10142
|
constructor() {
|
|
10207
10143
|
__publicField(this, "listeners", /* @__PURE__ */ new Set());
|
|
@@ -10280,6 +10216,19 @@ class Listener {
|
|
|
10280
10216
|
return !ignoresEvent;
|
|
10281
10217
|
}
|
|
10282
10218
|
}
|
|
10219
|
+
class Flags {
|
|
10220
|
+
constructor(events) {
|
|
10221
|
+
__publicField(this, "_flags", /* @__PURE__ */ new Map());
|
|
10222
|
+
this.events = events;
|
|
10223
|
+
}
|
|
10224
|
+
get(key) {
|
|
10225
|
+
return this._flags.get(key);
|
|
10226
|
+
}
|
|
10227
|
+
set(key, value) {
|
|
10228
|
+
this._flags.set(key, value);
|
|
10229
|
+
this.events.dispatch({ type: "flag:set", key, value });
|
|
10230
|
+
}
|
|
10231
|
+
}
|
|
10283
10232
|
const EDITORIAL_WORKFLOW_STATUS = {
|
|
10284
10233
|
QUEUED: "queued",
|
|
10285
10234
|
PROCESSING: "processing",
|
|
@@ -10367,6 +10316,26 @@ const MEDIA_WORKFLOW_STATUS_TO_STEP = {
|
|
|
10367
10316
|
[EDITORIAL_WORKFLOW_STATUS.COMPLETE]: MEDIA_WORKFLOW_STEP.COMPLETE
|
|
10368
10317
|
};
|
|
10369
10318
|
const s3ErrorRegex = /<Error>.*<Code>(.+)<\/Code>.*<Message>(.+)<\/Message>.*/;
|
|
10319
|
+
const CANONICAL_THUMBNAIL_SIZES = [
|
|
10320
|
+
{ w: 75, h: 75 },
|
|
10321
|
+
{ w: 400, h: 400 },
|
|
10322
|
+
{ w: 1e3, h: 1e3 }
|
|
10323
|
+
];
|
|
10324
|
+
const RENAME_ERROR_CODES = [
|
|
10325
|
+
"NOT_FOUND",
|
|
10326
|
+
"NAME_COLLISION",
|
|
10327
|
+
"INVALID_FILENAME",
|
|
10328
|
+
"INVALID_PATH",
|
|
10329
|
+
"UNAUTHORIZED",
|
|
10330
|
+
"UNSUPPORTED",
|
|
10331
|
+
"BACKEND_FAILURE"
|
|
10332
|
+
];
|
|
10333
|
+
class MediaRenameCancelled extends Error {
|
|
10334
|
+
constructor() {
|
|
10335
|
+
super("Media rename cancelled.");
|
|
10336
|
+
__publicField(this, "ERR_TYPE", "MediaRenameCancelled");
|
|
10337
|
+
}
|
|
10338
|
+
}
|
|
10370
10339
|
class DummyMediaStore {
|
|
10371
10340
|
constructor() {
|
|
10372
10341
|
__publicField(this, "accept", "*");
|
|
@@ -10408,10 +10377,9 @@ class TinaMediaStore {
|
|
|
10408
10377
|
__publicField(this, "workflowBranchOverride");
|
|
10409
10378
|
__publicField(this, "mediaWorkflowInProgress", false);
|
|
10410
10379
|
/**
|
|
10411
|
-
*
|
|
10412
|
-
* instance property rather than a prototype method on purpose: the media
|
|
10380
|
+
* An instance property rather than a prototype method on purpose: the media
|
|
10413
10381
|
* manager decides whether to offer the action by checking whether the store
|
|
10414
|
-
* defines `rename`, so
|
|
10382
|
+
* defines `rename`, so an instance that cannot rename must not carry one — a
|
|
10415
10383
|
* prototype method would advertise support everywhere and surface an action
|
|
10416
10384
|
* that always fails.
|
|
10417
10385
|
*/
|
|
@@ -10429,8 +10397,8 @@ class TinaMediaStore {
|
|
|
10429
10397
|
this.isStatic = true;
|
|
10430
10398
|
this.staticMedia = staticMedia;
|
|
10431
10399
|
}
|
|
10432
|
-
if (!this.isStatic && (
|
|
10433
|
-
this.rename = (from, to2) => this.rename_local(from, to2);
|
|
10400
|
+
if (!this.isStatic && !this.isUnsupportedSelfHostedRepoMedia()) {
|
|
10401
|
+
this.rename = ((_b = (_a = cms == null ? void 0 : cms.api) == null ? void 0 : _a.tina) == null ? void 0 : _b.isLocalMode) ? (from, to2) => this.rename_local(from, to2) : (from, to2) => this.rename_cloud(from, to2);
|
|
10434
10402
|
}
|
|
10435
10403
|
}
|
|
10436
10404
|
/**
|
|
@@ -10476,27 +10444,29 @@ class TinaMediaStore {
|
|
|
10476
10444
|
return await this.api.authProvider.isAuthenticated();
|
|
10477
10445
|
}
|
|
10478
10446
|
/**
|
|
10479
|
-
*
|
|
10480
|
-
*
|
|
10481
|
-
*
|
|
10482
|
-
*
|
|
10483
|
-
*
|
|
10484
|
-
* value is concatenated into a URL.
|
|
10485
|
-
*
|
|
10486
|
-
* `Client.setBranch()` runs the constructor's `options.branch` through
|
|
10487
|
-
* `encodeURIComponent` without a guard, so an unset `options.branch`
|
|
10488
|
-
* lands here as the literal string `"undefined"`. We treat that and the
|
|
10489
|
-
* empty case as no-branch so we don't send `?branch=undefined` to the
|
|
10490
|
-
* assets-api (which would route the call to a non-existent staging path).
|
|
10447
|
+
* The v2 cloud endpoint and the local dev server both filter by `ext` before
|
|
10448
|
+
* paginating. `staticMedia` is a build-time snapshot with no such pass, and
|
|
10449
|
+
* filtering a page client-side would leave a near-empty grid while matches
|
|
10450
|
+
* sit further down — so a static store reports no support and the control
|
|
10451
|
+
* stays hidden, the same way `searchable` is gated.
|
|
10491
10452
|
*/
|
|
10492
|
-
|
|
10493
|
-
|
|
10494
|
-
|
|
10495
|
-
|
|
10453
|
+
get extensionFilterable() {
|
|
10454
|
+
return !this.isStatic;
|
|
10455
|
+
}
|
|
10456
|
+
/**
|
|
10457
|
+
* `Client.setBranch()` encodes an unset branch to the literal `"undefined"`,
|
|
10458
|
+
* which the assets API would read as a real (non-existent) staging path.
|
|
10459
|
+
*/
|
|
10460
|
+
currentBranch() {
|
|
10461
|
+
if (this.workflowBranchOverride) return this.workflowBranchOverride;
|
|
10496
10462
|
if (!this.api.branch) return "";
|
|
10497
10463
|
const decoded = decodeURIComponent(this.api.branch);
|
|
10498
|
-
|
|
10499
|
-
|
|
10464
|
+
return decoded === "undefined" ? "" : decoded;
|
|
10465
|
+
}
|
|
10466
|
+
/** `this.api.branch` arrives encoded, so encode the decoded name once here. */
|
|
10467
|
+
encodedBranchParam() {
|
|
10468
|
+
const branch = this.currentBranch();
|
|
10469
|
+
return branch ? encodeURIComponent(branch) : "";
|
|
10500
10470
|
}
|
|
10501
10471
|
shortStableHash(input) {
|
|
10502
10472
|
let hash = 2166136261;
|
|
@@ -10534,7 +10504,7 @@ class TinaMediaStore {
|
|
|
10534
10504
|
const encodedBranch = this.encodedBranchParam();
|
|
10535
10505
|
return encodedBranch ? `?branch=${encodedBranch}` : "";
|
|
10536
10506
|
}
|
|
10537
|
-
requestMediaBranchChoice(branchName, baseBranch,
|
|
10507
|
+
requestMediaBranchChoice(branchName, baseBranch, request) {
|
|
10538
10508
|
if (!this.cms.events.hasExplicitListenerFor("media:workflow:confirm-branch")) {
|
|
10539
10509
|
throw new Error(
|
|
10540
10510
|
"Cannot start a media editorial workflow: no branch prompt is mounted. Ensure <MediaWorkflowOverlay /> is rendered (TinaCloudProvider mounts it automatically)."
|
|
@@ -10545,12 +10515,13 @@ class TinaMediaStore {
|
|
|
10545
10515
|
type: "media:workflow:confirm-branch",
|
|
10546
10516
|
branchName,
|
|
10547
10517
|
baseBranch,
|
|
10518
|
+
// A rename on a protected branch can only go through the workflow.
|
|
10519
|
+
allowSaveToProtectedBranch: request.opType !== "rename",
|
|
10548
10520
|
onConfirm: async (selectedBranchName) => {
|
|
10549
10521
|
const context = await this.prepareMediaBranch(
|
|
10550
10522
|
selectedBranchName,
|
|
10551
10523
|
baseBranch,
|
|
10552
|
-
|
|
10553
|
-
repoPath
|
|
10524
|
+
request
|
|
10554
10525
|
);
|
|
10555
10526
|
resolve({
|
|
10556
10527
|
kind: "workflow",
|
|
@@ -10562,7 +10533,7 @@ class TinaMediaStore {
|
|
|
10562
10533
|
});
|
|
10563
10534
|
});
|
|
10564
10535
|
}
|
|
10565
|
-
async prepareMediaBranch(branchName, baseBranch,
|
|
10536
|
+
async prepareMediaBranch(branchName, baseBranch, request) {
|
|
10566
10537
|
if (this.mediaWorkflowInProgress) {
|
|
10567
10538
|
throw new Error("A media workflow is already in progress.");
|
|
10568
10539
|
}
|
|
@@ -10577,8 +10548,9 @@ class TinaMediaStore {
|
|
|
10577
10548
|
branchName,
|
|
10578
10549
|
baseBranch,
|
|
10579
10550
|
prTitle: getEditorialWorkflowPrTitle(branchName),
|
|
10580
|
-
operation: opType,
|
|
10581
|
-
repoPath
|
|
10551
|
+
operation: request.opType,
|
|
10552
|
+
repoPath: request.repoPath,
|
|
10553
|
+
targetRepoPath: request.targetRepoPath
|
|
10582
10554
|
});
|
|
10583
10555
|
const branchContext = {
|
|
10584
10556
|
branchName: workflow.branchName || branchName,
|
|
@@ -10654,12 +10626,10 @@ class TinaMediaStore {
|
|
|
10654
10626
|
const branchName = `media-${opType}-${mediaSlug}`;
|
|
10655
10627
|
const repoFilename = opType === "upload" && filename ? sanitizeFilename(filename) : filename;
|
|
10656
10628
|
const repoPath = this.joinMediaPath(directory, repoFilename);
|
|
10657
|
-
return this.requestMediaBranchChoice(
|
|
10658
|
-
branchName,
|
|
10659
|
-
baseBranch,
|
|
10629
|
+
return this.requestMediaBranchChoice(branchName, baseBranch, {
|
|
10660
10630
|
opType,
|
|
10661
10631
|
repoPath
|
|
10662
|
-
);
|
|
10632
|
+
});
|
|
10663
10633
|
}
|
|
10664
10634
|
async waitForRequestStatus(requestId, timeoutMessage) {
|
|
10665
10635
|
const startTime = Date.now();
|
|
@@ -10923,6 +10893,160 @@ class TinaMediaStore {
|
|
|
10923
10893
|
lastSlash === -1 ? to2 : to2.slice(lastSlash + 1)
|
|
10924
10894
|
);
|
|
10925
10895
|
}
|
|
10896
|
+
/**
|
|
10897
|
+
* `from`/`to` are media-root-relative `directory/filename` paths. `to` is
|
|
10898
|
+
* already sanitised by `previewRename` and is passed through untouched.
|
|
10899
|
+
*/
|
|
10900
|
+
async rename_cloud(from, to2) {
|
|
10901
|
+
this.setup();
|
|
10902
|
+
if (!await this.isAuthenticated()) {
|
|
10903
|
+
throw new MediaRenameError({
|
|
10904
|
+
code: "UNAUTHORIZED",
|
|
10905
|
+
message: "You don't have permission to rename this file."
|
|
10906
|
+
});
|
|
10907
|
+
}
|
|
10908
|
+
const decision = await this.prepareRenameBranch(from, to2);
|
|
10909
|
+
if (decision.kind === "cancelled") {
|
|
10910
|
+
throw new MediaRenameCancelled();
|
|
10911
|
+
}
|
|
10912
|
+
if (decision.kind === "workflow") {
|
|
10913
|
+
return this.renameCloudViaWorkflow(from, to2, decision.context);
|
|
10914
|
+
}
|
|
10915
|
+
return this.renameCloudDirect(from, to2);
|
|
10916
|
+
}
|
|
10917
|
+
/** `currentBranch()` is the workflow branch while one is active. */
|
|
10918
|
+
async postCloudRename(from, to2) {
|
|
10919
|
+
const branch = this.currentBranch() || void 0;
|
|
10920
|
+
const res = await this.api.authProvider.fetchWithToken(
|
|
10921
|
+
`${this.url}/rename`,
|
|
10922
|
+
{
|
|
10923
|
+
method: "POST",
|
|
10924
|
+
headers: { "Content-Type": "application/json" },
|
|
10925
|
+
body: JSON.stringify({ from, to: to2, branch })
|
|
10926
|
+
}
|
|
10927
|
+
);
|
|
10928
|
+
const body = await res.json().catch(() => null);
|
|
10929
|
+
if (res.status !== 200 || (body == null ? void 0 : body.success) !== true) {
|
|
10930
|
+
throw this.toCloudRenameError(res.status, body);
|
|
10931
|
+
}
|
|
10932
|
+
return body;
|
|
10933
|
+
}
|
|
10934
|
+
async renameCloudDirect(from, to2) {
|
|
10935
|
+
const result = await this.postCloudRename(from, to2);
|
|
10936
|
+
if (result.requestId) {
|
|
10937
|
+
try {
|
|
10938
|
+
await this.waitForRequestStatus(
|
|
10939
|
+
result.requestId,
|
|
10940
|
+
"Time out waiting for rename to complete"
|
|
10941
|
+
);
|
|
10942
|
+
} catch (error) {
|
|
10943
|
+
const detail = error instanceof Error && error.message ? `${error.message}. ` : "Failed to confirm the rename. ";
|
|
10944
|
+
throw new MediaRenameError({
|
|
10945
|
+
code: "BACKEND_FAILURE",
|
|
10946
|
+
message: `${detail}The file may still have been renamed — refresh the media library to check.`
|
|
10947
|
+
});
|
|
10948
|
+
}
|
|
10949
|
+
}
|
|
10950
|
+
return this.resolveRenamedMedia(result.path || to2, result.src);
|
|
10951
|
+
}
|
|
10952
|
+
/**
|
|
10953
|
+
* Completion is the workflow status, not the rename request status: only the
|
|
10954
|
+
* former waits for the branch, the media index and the pull request.
|
|
10955
|
+
*/
|
|
10956
|
+
async renameCloudViaWorkflow(from, to2, branchContext) {
|
|
10957
|
+
let renamed;
|
|
10958
|
+
try {
|
|
10959
|
+
const result = await this.postCloudRename(from, to2);
|
|
10960
|
+
await this.finalizeMediaWorkflow(branchContext, async () => {
|
|
10961
|
+
renamed = await this.resolveRenamedMedia(result.path || to2, result.src);
|
|
10962
|
+
});
|
|
10963
|
+
} catch (err) {
|
|
10964
|
+
this.resetWorkflowState();
|
|
10965
|
+
this.cms.events.dispatch({ type: "media:workflow:finish" });
|
|
10966
|
+
throw err;
|
|
10967
|
+
}
|
|
10968
|
+
if (!renamed) {
|
|
10969
|
+
throw new MediaRenameError({
|
|
10970
|
+
code: "BACKEND_FAILURE",
|
|
10971
|
+
message: "The rename was staged but the branch workflow did not complete. Check the branch in TinaCloud before trying again."
|
|
10972
|
+
});
|
|
10973
|
+
}
|
|
10974
|
+
return renamed;
|
|
10975
|
+
}
|
|
10976
|
+
async prepareRenameBranch(from, to2) {
|
|
10977
|
+
if (!this.api.usingProtectedBranch()) return { kind: "direct" };
|
|
10978
|
+
const baseBranch = decodeURIComponent(this.api.branch || "");
|
|
10979
|
+
const { directory, filename } = this.splitMediaPath(from);
|
|
10980
|
+
const branchName = `media-rename-${this.branchSlugForMediaPath(
|
|
10981
|
+
directory,
|
|
10982
|
+
filename
|
|
10983
|
+
)}`;
|
|
10984
|
+
return this.requestMediaBranchChoice(branchName, baseBranch, {
|
|
10985
|
+
opType: "rename",
|
|
10986
|
+
repoPath: from,
|
|
10987
|
+
targetRepoPath: to2
|
|
10988
|
+
});
|
|
10989
|
+
}
|
|
10990
|
+
splitMediaPath(path) {
|
|
10991
|
+
const lastSlash = path.lastIndexOf("/");
|
|
10992
|
+
return lastSlash === -1 ? { directory: "", filename: path } : {
|
|
10993
|
+
directory: path.slice(0, lastSlash),
|
|
10994
|
+
filename: path.slice(lastSlash + 1)
|
|
10995
|
+
};
|
|
10996
|
+
}
|
|
10997
|
+
/**
|
|
10998
|
+
* The listing is the source of truth for `src` (staging path, per-stage CDN
|
|
10999
|
+
* host) and thumbnails; the rename response is the fallback when the entry
|
|
11000
|
+
* isn't on the first page.
|
|
11001
|
+
*/
|
|
11002
|
+
async resolveRenamedMedia(targetPath, srcFromResponse) {
|
|
11003
|
+
const { directory, filename } = this.splitMediaPath(targetPath);
|
|
11004
|
+
try {
|
|
11005
|
+
const listed = await this.list({
|
|
11006
|
+
directory,
|
|
11007
|
+
limit: 100,
|
|
11008
|
+
thumbnailSizes: CANONICAL_THUMBNAIL_SIZES
|
|
11009
|
+
});
|
|
11010
|
+
const entry = listed.items.find(
|
|
11011
|
+
(item) => item.type === "file" && item.filename === filename
|
|
11012
|
+
);
|
|
11013
|
+
if (entry) return entry;
|
|
11014
|
+
} catch (error) {
|
|
11015
|
+
console.error("Failed to fetch the canonical media entry:", error);
|
|
11016
|
+
}
|
|
11017
|
+
const src = srcFromResponse || "";
|
|
11018
|
+
return {
|
|
11019
|
+
type: "file",
|
|
11020
|
+
id: filename,
|
|
11021
|
+
filename,
|
|
11022
|
+
directory,
|
|
11023
|
+
src,
|
|
11024
|
+
thumbnails: CANONICAL_THUMBNAIL_SIZES.reduce(
|
|
11025
|
+
(acc, size) => {
|
|
11026
|
+
acc[`${size.w}x${size.h}`] = this.genThumbnail(src, size);
|
|
11027
|
+
return acc;
|
|
11028
|
+
},
|
|
11029
|
+
{}
|
|
11030
|
+
)
|
|
11031
|
+
};
|
|
11032
|
+
}
|
|
11033
|
+
toCloudRenameError(status, body) {
|
|
11034
|
+
const message = typeof (body == null ? void 0 : body.message) === "string" && body.message ? body.message : "Failed to rename the file.";
|
|
11035
|
+
if (RENAME_ERROR_CODES.includes(body == null ? void 0 : body.code)) {
|
|
11036
|
+
return new MediaRenameError({ code: body.code, message });
|
|
11037
|
+
}
|
|
11038
|
+
let code;
|
|
11039
|
+
if (status === 401 || status === 403) {
|
|
11040
|
+
code = "UNAUTHORIZED";
|
|
11041
|
+
} else if (status === 404) {
|
|
11042
|
+
code = "NOT_FOUND";
|
|
11043
|
+
} else if (status === 409) {
|
|
11044
|
+
code = "NAME_COLLISION";
|
|
11045
|
+
} else {
|
|
11046
|
+
code = "BACKEND_FAILURE";
|
|
11047
|
+
}
|
|
11048
|
+
return new MediaRenameError({ code, message });
|
|
11049
|
+
}
|
|
10926
11050
|
toRenameError(status, body) {
|
|
10927
11051
|
if (body == null ? void 0 : body.code) {
|
|
10928
11052
|
return new MediaRenameError({
|
|
@@ -10950,6 +11074,7 @@ class TinaMediaStore {
|
|
|
10950
11074
|
return !this.isLocal ? `${src}?fit=crop&max-w=${dimensions.w}&max-h=${dimensions.h}` : src;
|
|
10951
11075
|
}
|
|
10952
11076
|
async list(options) {
|
|
11077
|
+
var _a, _b;
|
|
10953
11078
|
this.setup();
|
|
10954
11079
|
if (this.staticMedia) {
|
|
10955
11080
|
const offset2 = options.offset || 0;
|
|
@@ -10981,13 +11106,16 @@ class TinaMediaStore {
|
|
|
10981
11106
|
nextOffset: hasMore2 ? Number(offset2) + 20 : null
|
|
10982
11107
|
};
|
|
10983
11108
|
}
|
|
10984
|
-
return {
|
|
11109
|
+
return {
|
|
11110
|
+
items: media,
|
|
11111
|
+
nextOffset: hasMore ? Number(offset2) + 20 : null
|
|
11112
|
+
};
|
|
10985
11113
|
}
|
|
10986
11114
|
let res;
|
|
10987
11115
|
if (!this.isLocal) {
|
|
10988
11116
|
const encodedBranch = this.encodedBranchParam();
|
|
10989
11117
|
res = await this.api.authProvider.fetchWithToken(
|
|
10990
|
-
`${this.listUrl}/list/${options.directory || ""}?limit=${options.limit || 20}${options.offset ? `&cursor=${options.offset}` : ""}${encodedBranch ? `&branch=${encodedBranch}` : ""}${options.search ? `&search=${encodeURIComponent(options.search)}` : ""}`
|
|
11118
|
+
`${this.listUrl}/list/${options.directory || ""}?limit=${options.limit || 20}${options.offset ? `&cursor=${options.offset}` : ""}${encodedBranch ? `&branch=${encodedBranch}` : ""}${options.search ? `&search=${encodeURIComponent(options.search)}` : ""}${((_a = options.ext) == null ? void 0 : _a.length) ? `&ext=${encodeURIComponent(options.ext.join(","))}` : ""}`
|
|
10991
11119
|
);
|
|
10992
11120
|
if (res.status == 401) {
|
|
10993
11121
|
throw E_UNAUTHORIZED;
|
|
@@ -10997,7 +11125,7 @@ class TinaMediaStore {
|
|
|
10997
11125
|
}
|
|
10998
11126
|
} else {
|
|
10999
11127
|
res = await this.fetchFunction(
|
|
11000
|
-
`${this.url}/list/${options.directory || ""}?limit=${options.limit || 20}${options.offset ? `&cursor=${options.offset}` : ""}${options.search ? `&search=${encodeURIComponent(options.search)}` : ""}`
|
|
11128
|
+
`${this.url}/list/${options.directory || ""}?limit=${options.limit || 20}${options.offset ? `&cursor=${options.offset}` : ""}${options.search ? `&search=${encodeURIComponent(options.search)}` : ""}${((_b = options.ext) == null ? void 0 : _b.length) ? `&ext=${encodeURIComponent(options.ext.join(","))}` : ""}`
|
|
11001
11129
|
);
|
|
11002
11130
|
if (res.status == 404) {
|
|
11003
11131
|
throw E_BAD_ROUTE;
|
|
@@ -11242,17 +11370,217 @@ const E_SELF_HOSTED_MEDIA = new MediaListError({
|
|
|
11242
11370
|
message: "Self-hosted TinaCMS can't serve media from your repo through TinaCloud. Configure an external media store (e.g. S3, Cloudinary, DigitalOcean Spaces, or Azure) with media.loadCustomStore, or add media to your repo manually.",
|
|
11243
11371
|
docsLink: "https://tina.io/docs/r/backend-media-handler/"
|
|
11244
11372
|
});
|
|
11245
|
-
class
|
|
11373
|
+
class PluginTypeManager {
|
|
11246
11374
|
constructor(events) {
|
|
11247
|
-
|
|
11375
|
+
/**
|
|
11376
|
+
* @ignore
|
|
11377
|
+
*/
|
|
11378
|
+
__publicField(this, "plugins", {});
|
|
11248
11379
|
this.events = events;
|
|
11249
11380
|
}
|
|
11250
|
-
|
|
11251
|
-
|
|
11381
|
+
/**
|
|
11382
|
+
* Gets the [[PluginType|collection of plugins]] for the given type.
|
|
11383
|
+
*
|
|
11384
|
+
* #### Example: Basic Usage
|
|
11385
|
+
*
|
|
11386
|
+
* ```ts
|
|
11387
|
+
* const colorPlugins = cms.plugins.get("color")
|
|
11388
|
+
* ```
|
|
11389
|
+
*
|
|
11390
|
+
* #### Example: Advanced Types
|
|
11391
|
+
*
|
|
11392
|
+
* A type param can be added to specify the kind of Plugin
|
|
11393
|
+
* that is being listed.
|
|
11394
|
+
*
|
|
11395
|
+
* ```ts
|
|
11396
|
+
* const colorPlugins = cms.plugins.get<ColorPlugin>("color")
|
|
11397
|
+
* ```
|
|
11398
|
+
*
|
|
11399
|
+
* @param type The type of plugins to be retrieved
|
|
11400
|
+
* @typeparam P A subclass of Plugin. Optional.
|
|
11401
|
+
*/
|
|
11402
|
+
getType(type2) {
|
|
11403
|
+
return this.plugins[type2] = this.plugins[type2] || new PluginType(type2, this.events);
|
|
11252
11404
|
}
|
|
11253
|
-
|
|
11254
|
-
|
|
11255
|
-
|
|
11405
|
+
/**
|
|
11406
|
+
* An alias to [[get]]
|
|
11407
|
+
*
|
|
11408
|
+
* ### !DEPRECATED!
|
|
11409
|
+
*
|
|
11410
|
+
* This name is unnecessarily verbose and weird.
|
|
11411
|
+
*/
|
|
11412
|
+
findOrCreateMap(type2) {
|
|
11413
|
+
return this.getType(type2);
|
|
11414
|
+
}
|
|
11415
|
+
/**
|
|
11416
|
+
* Adds a Plugin to the CMS.
|
|
11417
|
+
*
|
|
11418
|
+
* #### Example: Basic Usage
|
|
11419
|
+
*
|
|
11420
|
+
* ```js
|
|
11421
|
+
* cms.plugins.add({ __type: "color", name: "red" })
|
|
11422
|
+
* ```
|
|
11423
|
+
*
|
|
11424
|
+
* #### Example: Advanced Types
|
|
11425
|
+
*
|
|
11426
|
+
* ```ts
|
|
11427
|
+
* interface ColorPlugin extends Plugin {
|
|
11428
|
+
* __type: "color"
|
|
11429
|
+
* hex: string
|
|
11430
|
+
* rgb: string
|
|
11431
|
+
* }
|
|
11432
|
+
*
|
|
11433
|
+
* cms.plugins.add<ColorPlugin>({
|
|
11434
|
+
* __type: "color",
|
|
11435
|
+
* name: "red",
|
|
11436
|
+
* hex: "#FF0000",
|
|
11437
|
+
* rgb: "RGBA(255, 0, 0, 1)"
|
|
11438
|
+
* })
|
|
11439
|
+
* ```
|
|
11440
|
+
*
|
|
11441
|
+
* @typeparam P A subclass of Plugin. Optional.
|
|
11442
|
+
* @param plugin
|
|
11443
|
+
* @todo Consider returning the plugin which was just added.
|
|
11444
|
+
*/
|
|
11445
|
+
add(plugin) {
|
|
11446
|
+
this.findOrCreateMap(plugin.__type).add(plugin);
|
|
11447
|
+
}
|
|
11448
|
+
/**
|
|
11449
|
+
* Removes the given plugin from the CMS.
|
|
11450
|
+
*
|
|
11451
|
+
* #### Example
|
|
11452
|
+
*
|
|
11453
|
+
* In this example a plugin is added to the CMS and removed
|
|
11454
|
+
* 5 seconds later.
|
|
11455
|
+
*
|
|
11456
|
+
* ```ts
|
|
11457
|
+
* const redPlugin = {
|
|
11458
|
+
* __type: "color",
|
|
11459
|
+
* name: "red",
|
|
11460
|
+
* hex: "#FF0000",
|
|
11461
|
+
* rgb: "RGBA(255, 0, 0, 1)"
|
|
11462
|
+
* }
|
|
11463
|
+
*
|
|
11464
|
+
* cms.plugins.add(redPlugin)
|
|
11465
|
+
*
|
|
11466
|
+
* setTimeout(() => {
|
|
11467
|
+
* cms.plugins.remove(redPlugin)
|
|
11468
|
+
* }, 5000)
|
|
11469
|
+
* ```
|
|
11470
|
+
*
|
|
11471
|
+
* @typeparam P A subclass of Plugin. Optional.
|
|
11472
|
+
* @param plugin The plugin to be removed from the CMS.
|
|
11473
|
+
*/
|
|
11474
|
+
remove(plugin) {
|
|
11475
|
+
this.findOrCreateMap(plugin.__type).remove(plugin);
|
|
11476
|
+
}
|
|
11477
|
+
/**
|
|
11478
|
+
* Returns a list of all the plugins of the given type.
|
|
11479
|
+
*
|
|
11480
|
+
* #### Example: Basic Usage
|
|
11481
|
+
*
|
|
11482
|
+
* ```ts
|
|
11483
|
+
* cms.plugins.all("color").forEach(color => {
|
|
11484
|
+
* console.log(color.name)
|
|
11485
|
+
* })
|
|
11486
|
+
* ```
|
|
11487
|
+
*
|
|
11488
|
+
* #### Example: Advanced Types
|
|
11489
|
+
*
|
|
11490
|
+
* A generic can be added to specify the type of plugins
|
|
11491
|
+
* that are being retrieved.
|
|
11492
|
+
*
|
|
11493
|
+
* ```ts
|
|
11494
|
+
* cms.plugins.all<ColorPlugin>("color").forEach(color => {
|
|
11495
|
+
* console.log(color.name, color.hex)
|
|
11496
|
+
* })
|
|
11497
|
+
* ```
|
|
11498
|
+
*
|
|
11499
|
+
* @typeparam P A subclass of Plugin. Optional.
|
|
11500
|
+
* @param type The name of the plugin
|
|
11501
|
+
* @returns An array of all plugins of the given type.
|
|
11502
|
+
*/
|
|
11503
|
+
all(type2) {
|
|
11504
|
+
return this.findOrCreateMap(type2).all();
|
|
11505
|
+
}
|
|
11506
|
+
}
|
|
11507
|
+
class PluginType {
|
|
11508
|
+
/**
|
|
11509
|
+
*
|
|
11510
|
+
* @param __type The `__type` of plugin being managed.
|
|
11511
|
+
*/
|
|
11512
|
+
constructor(__type, events) {
|
|
11513
|
+
/**
|
|
11514
|
+
* @ignore
|
|
11515
|
+
*/
|
|
11516
|
+
__publicField(this, "__plugins", {});
|
|
11517
|
+
this.__type = __type;
|
|
11518
|
+
this.events = events;
|
|
11519
|
+
}
|
|
11520
|
+
/**
|
|
11521
|
+
* Adds a new plugin to the collection.
|
|
11522
|
+
*
|
|
11523
|
+
* ### Example
|
|
11524
|
+
*
|
|
11525
|
+
* ```ts
|
|
11526
|
+
* interface ColorPlugin extends Plugin {
|
|
11527
|
+
* hex: string
|
|
11528
|
+
* }
|
|
11529
|
+
*
|
|
11530
|
+
* const colorPlugins = new PluginType<ColorPlugin>("color")
|
|
11531
|
+
*
|
|
11532
|
+
* colorPlugins.add({ name: "red", hex: "#f00" })
|
|
11533
|
+
* ```
|
|
11534
|
+
*
|
|
11535
|
+
* @param plugin A new plugin. The `__type` is optional and will be added if it's missing.
|
|
11536
|
+
*/
|
|
11537
|
+
add(plugin) {
|
|
11538
|
+
const p = plugin;
|
|
11539
|
+
if (!p.__type) {
|
|
11540
|
+
p.__type = this.__type;
|
|
11541
|
+
}
|
|
11542
|
+
this.__plugins[p.name] = p;
|
|
11543
|
+
this.events.dispatch({ type: `plugin:add:${this.__type}` });
|
|
11544
|
+
}
|
|
11545
|
+
all() {
|
|
11546
|
+
return Object.keys(this.__plugins).map((name2) => this.__plugins[name2]);
|
|
11547
|
+
}
|
|
11548
|
+
/**
|
|
11549
|
+
*
|
|
11550
|
+
* Looks up a plugin by it's `name`.
|
|
11551
|
+
*
|
|
11552
|
+
* ### Example
|
|
11553
|
+
*
|
|
11554
|
+
* ```ts
|
|
11555
|
+
* const colorPlugins = new PluginType<ColorPlugin>("color")
|
|
11556
|
+
*
|
|
11557
|
+
* colorPlugins.add({ name: "red", hex: "#f00" })
|
|
11558
|
+
*
|
|
11559
|
+
* colorPlugins.find("red") // { __type: "color", name: "red", hex: "#f00" }
|
|
11560
|
+
* colorPlugin.find("large") // undefined
|
|
11561
|
+
* ```
|
|
11562
|
+
*
|
|
11563
|
+
* @param name The `name` of the plugin to be retrieved.
|
|
11564
|
+
*/
|
|
11565
|
+
find(name2) {
|
|
11566
|
+
return this.__plugins[name2];
|
|
11567
|
+
}
|
|
11568
|
+
/**
|
|
11569
|
+
* Pass this function a plugin or the `name` of a plugin to have
|
|
11570
|
+
* it be removed from the CMS.
|
|
11571
|
+
*
|
|
11572
|
+
* @param pluginOrName The `name` of a plugin, or the plugin itself.
|
|
11573
|
+
* @returns The plugin that was removed, or `undefined` if it was not found.
|
|
11574
|
+
*/
|
|
11575
|
+
remove(pluginOrName) {
|
|
11576
|
+
const name2 = typeof pluginOrName === "string" ? pluginOrName : pluginOrName.name;
|
|
11577
|
+
const plugin = this.__plugins[name2];
|
|
11578
|
+
delete this.__plugins[name2];
|
|
11579
|
+
this.events.dispatch({ type: `plugin:remove:${this.__type}` });
|
|
11580
|
+
return plugin;
|
|
11581
|
+
}
|
|
11582
|
+
subscribe(cb) {
|
|
11583
|
+
return this.events.subscribe(`plugin:*:${this.__type}`, cb);
|
|
11256
11584
|
}
|
|
11257
11585
|
}
|
|
11258
11586
|
const _CMS = class _CMS {
|
|
@@ -11365,14 +11693,28 @@ const _CMS = class _CMS {
|
|
|
11365
11693
|
this.unsubscribeHooks[name2]();
|
|
11366
11694
|
}
|
|
11367
11695
|
if (api.events instanceof EventBus) {
|
|
11368
|
-
|
|
11369
|
-
|
|
11370
|
-
|
|
11371
|
-
|
|
11372
|
-
|
|
11373
|
-
|
|
11374
|
-
|
|
11375
|
-
|
|
11696
|
+
let forwardingToCms = null;
|
|
11697
|
+
let forwardingToApi = null;
|
|
11698
|
+
const unsubscribeHost = api.events.subscribe("*", (e) => {
|
|
11699
|
+
if (e === forwardingToApi) return;
|
|
11700
|
+
const previous = forwardingToCms;
|
|
11701
|
+
forwardingToCms = e;
|
|
11702
|
+
try {
|
|
11703
|
+
this.events.dispatch(e);
|
|
11704
|
+
} finally {
|
|
11705
|
+
forwardingToCms = previous;
|
|
11706
|
+
}
|
|
11707
|
+
});
|
|
11708
|
+
const unsubscribeGuest = this.events.subscribe("*", (e) => {
|
|
11709
|
+
if (e === forwardingToCms) return;
|
|
11710
|
+
const previous = forwardingToApi;
|
|
11711
|
+
forwardingToApi = e;
|
|
11712
|
+
try {
|
|
11713
|
+
api.events.dispatch(e);
|
|
11714
|
+
} finally {
|
|
11715
|
+
forwardingToApi = previous;
|
|
11716
|
+
}
|
|
11717
|
+
});
|
|
11376
11718
|
this.unsubscribeHooks[name2] = () => {
|
|
11377
11719
|
unsubscribeHost();
|
|
11378
11720
|
unsubscribeGuest();
|
|
@@ -11715,6 +12057,10 @@ const RenameModal$1 = ({
|
|
|
11715
12057
|
await renameFunc(sanitized);
|
|
11716
12058
|
close2();
|
|
11717
12059
|
} catch (e) {
|
|
12060
|
+
if ((e == null ? void 0 : e.ERR_TYPE) === "MediaRenameCancelled") {
|
|
12061
|
+
close2();
|
|
12062
|
+
return;
|
|
12063
|
+
}
|
|
11718
12064
|
setError(renameErrorMessage(e, sanitized));
|
|
11719
12065
|
setProcessing(false);
|
|
11720
12066
|
}
|
|
@@ -11824,6 +12170,7 @@ function MediaPicker({
|
|
|
11824
12170
|
allowDelete,
|
|
11825
12171
|
onSelect,
|
|
11826
12172
|
close: close2,
|
|
12173
|
+
accept: fieldAccept,
|
|
11827
12174
|
...props
|
|
11828
12175
|
}) {
|
|
11829
12176
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
@@ -11860,6 +12207,7 @@ function MediaPicker({
|
|
|
11860
12207
|
const [mediaFilter, setMediaFilter] = useState(
|
|
11861
12208
|
"all"
|
|
11862
12209
|
);
|
|
12210
|
+
const [typeFilter, setTypeFilter] = useState("all");
|
|
11863
12211
|
const [offsetHistory, setOffsetHistory] = useState([]);
|
|
11864
12212
|
const offset2 = offsetHistory[offsetHistory.length - 1];
|
|
11865
12213
|
const resetOffset = () => setOffsetHistory([]);
|
|
@@ -11872,8 +12220,25 @@ function MediaPicker({
|
|
|
11872
12220
|
resetList();
|
|
11873
12221
|
setActiveItem(false);
|
|
11874
12222
|
};
|
|
12223
|
+
const changeTypeFilter = (next) => {
|
|
12224
|
+
if (next === typeFilter) return;
|
|
12225
|
+
setTypeFilter(next);
|
|
12226
|
+
setLoadFolders(true);
|
|
12227
|
+
resetOffset();
|
|
12228
|
+
resetList();
|
|
12229
|
+
setActiveItem(false);
|
|
12230
|
+
};
|
|
11875
12231
|
const listRequestRef = useRef(0);
|
|
11876
12232
|
const newMediaSrcsRef = useRef(/* @__PURE__ */ new Set());
|
|
12233
|
+
const fieldExtensions = React__default.useMemo(
|
|
12234
|
+
() => resolveMediaAccept(fieldAccept),
|
|
12235
|
+
[fieldAccept]
|
|
12236
|
+
);
|
|
12237
|
+
const acceptedExtensions = React__default.useMemo(() => {
|
|
12238
|
+
if (!cms.media.store.extensionFilterable) return [];
|
|
12239
|
+
if (fieldExtensions.length) return fieldExtensions;
|
|
12240
|
+
return typeFilter === "all" ? [] : resolveMediaAccept(typeFilter);
|
|
12241
|
+
}, [fieldExtensions, typeFilter, cms.media.store.extensionFilterable]);
|
|
11877
12242
|
async function loadMedia(loadFolders2 = true) {
|
|
11878
12243
|
const requestId = ++listRequestRef.current;
|
|
11879
12244
|
setListState("loading");
|
|
@@ -11888,7 +12253,8 @@ function MediaPicker({
|
|
|
11888
12253
|
{ w: 1e3, h: 1e3 }
|
|
11889
12254
|
],
|
|
11890
12255
|
filesOnly: !loadFolders2,
|
|
11891
|
-
search: debouncedSearch || void 0
|
|
12256
|
+
search: debouncedSearch || void 0,
|
|
12257
|
+
ext: acceptedExtensions.length ? acceptedExtensions : void 0
|
|
11892
12258
|
});
|
|
11893
12259
|
if (requestId !== listRequestRef.current) return;
|
|
11894
12260
|
setList((prev) => {
|
|
@@ -11931,9 +12297,6 @@ function MediaPicker({
|
|
|
11931
12297
|
}, [refreshing]);
|
|
11932
12298
|
useEffect(() => {
|
|
11933
12299
|
if (!cms.media.isConfigured) return;
|
|
11934
|
-
if (refreshing) return;
|
|
11935
|
-
loadMedia(loadFolders);
|
|
11936
|
-
if (loadFolders) setLoadFolders(false);
|
|
11937
12300
|
return cms.events.subscribe(
|
|
11938
12301
|
["media:delete:success", "media:rename:success", "media:pageSize"],
|
|
11939
12302
|
(event) => {
|
|
@@ -11947,7 +12310,19 @@ function MediaPicker({
|
|
|
11947
12310
|
resetList();
|
|
11948
12311
|
}
|
|
11949
12312
|
);
|
|
11950
|
-
}, [
|
|
12313
|
+
}, [cms.media.isConfigured]);
|
|
12314
|
+
useEffect(() => {
|
|
12315
|
+
if (!cms.media.isConfigured) return;
|
|
12316
|
+
if (refreshing) return;
|
|
12317
|
+
loadMedia(loadFolders);
|
|
12318
|
+
if (loadFolders) setLoadFolders(false);
|
|
12319
|
+
}, [
|
|
12320
|
+
offset2,
|
|
12321
|
+
directory,
|
|
12322
|
+
debouncedSearch,
|
|
12323
|
+
acceptedExtensions,
|
|
12324
|
+
cms.media.isConfigured
|
|
12325
|
+
]);
|
|
11951
12326
|
const onClickMediaItem = (item) => {
|
|
11952
12327
|
if (!item) {
|
|
11953
12328
|
setActiveItem(false);
|
|
@@ -11981,12 +12356,12 @@ function MediaPicker({
|
|
|
11981
12356
|
};
|
|
11982
12357
|
}
|
|
11983
12358
|
const [uploading, setUploading] = useState(false);
|
|
11984
|
-
const
|
|
12359
|
+
const globalAccept = Array.isArray(
|
|
11985
12360
|
(_c = (_b = (_a = cms.api.tina.schema.schema) == null ? void 0 : _a.config) == null ? void 0 : _b.media) == null ? void 0 : _c.accept
|
|
11986
12361
|
) ? (_f = (_e = (_d = cms.api.tina.schema.schema) == null ? void 0 : _d.config) == null ? void 0 : _e.media) == null ? void 0 : _f.accept.join(",") : (_i = (_h = (_g = cms.api.tina.schema.schema) == null ? void 0 : _g.config) == null ? void 0 : _h.media) == null ? void 0 : _i.accept;
|
|
11987
12362
|
const { getRootProps, getInputProps, isDragActive } = useDropzone({
|
|
11988
|
-
accept: dropzoneAcceptFromString(
|
|
11989
|
-
|
|
12363
|
+
accept: dropzoneAcceptFromExtensions(fieldExtensions) ?? dropzoneAcceptFromString(
|
|
12364
|
+
globalAccept || cms.media.accept || DEFAULT_MEDIA_UPLOAD_TYPES
|
|
11990
12365
|
),
|
|
11991
12366
|
maxSize: cms.media.maxSize,
|
|
11992
12367
|
multiple: true,
|
|
@@ -12203,7 +12578,15 @@ function MediaPicker({
|
|
|
12203
12578
|
setValue: setSearch,
|
|
12204
12579
|
clear: () => setSearch("")
|
|
12205
12580
|
}
|
|
12206
|
-
), /* @__PURE__ */ React__default.createElement("div", { className: "ml-auto" }, /* @__PURE__ */ React__default.createElement(
|
|
12581
|
+
), /* @__PURE__ */ React__default.createElement("div", { className: "ml-auto flex items-center gap-3" }, cms.media.store.extensionFilterable && /* @__PURE__ */ React__default.createElement(
|
|
12582
|
+
MediaTypeFilter,
|
|
12583
|
+
{
|
|
12584
|
+
value: typeFilter,
|
|
12585
|
+
setValue: changeTypeFilter,
|
|
12586
|
+
lockedTo: fieldAccept,
|
|
12587
|
+
lockedExtensions: fieldExtensions
|
|
12588
|
+
}
|
|
12589
|
+
), /* @__PURE__ */ React__default.createElement(
|
|
12207
12590
|
MediaFilterToggle,
|
|
12208
12591
|
{
|
|
12209
12592
|
value: mediaFilter,
|
|
@@ -12468,6 +12851,62 @@ const MediaFilterToggle = ({
|
|
|
12468
12851
|
option.label
|
|
12469
12852
|
)));
|
|
12470
12853
|
};
|
|
12854
|
+
const TYPE_FILTER_LABELS = {
|
|
12855
|
+
image: "Images",
|
|
12856
|
+
video: "Video",
|
|
12857
|
+
audio: "Audio",
|
|
12858
|
+
document: "Documents"
|
|
12859
|
+
};
|
|
12860
|
+
const acceptEntryLabel = (entry) => entry in MEDIA_CATEGORIES ? TYPE_FILTER_LABELS[entry] : entry.toUpperCase();
|
|
12861
|
+
const MediaTypeFilter = ({
|
|
12862
|
+
value,
|
|
12863
|
+
setValue,
|
|
12864
|
+
lockedTo,
|
|
12865
|
+
lockedExtensions
|
|
12866
|
+
}) => {
|
|
12867
|
+
if (lockedExtensions.length) {
|
|
12868
|
+
const entries = Array.isArray(lockedTo) ? lockedTo : [lockedTo];
|
|
12869
|
+
const label = entries.length > 3 ? `${entries.length} types only` : `${entries.map(acceptEntryLabel).join(", ")} only`;
|
|
12870
|
+
const description = `This field accepts ${lockedExtensions.join(", ")}`;
|
|
12871
|
+
return /* @__PURE__ */ React__default.createElement(
|
|
12872
|
+
"span",
|
|
12873
|
+
{
|
|
12874
|
+
className: "grow-0 flex items-center gap-1.5 whitespace-nowrap rounded-lg border border-dashed border-gray-200 bg-gray-100 px-3 py-1.5 text-sm text-gray-500",
|
|
12875
|
+
title: description
|
|
12876
|
+
},
|
|
12877
|
+
/* @__PURE__ */ React__default.createElement(Lock, { className: "w-3.5 h-3.5 opacity-80", "aria-hidden": "true" }),
|
|
12878
|
+
label,
|
|
12879
|
+
/* @__PURE__ */ React__default.createElement("span", { className: "sr-only" }, description)
|
|
12880
|
+
);
|
|
12881
|
+
}
|
|
12882
|
+
const active = value !== "all";
|
|
12883
|
+
return /* @__PURE__ */ React__default.createElement(DropdownMenu, null, /* @__PURE__ */ React__default.createElement(DropdownMenuTrigger, { asChild: true }, /* @__PURE__ */ React__default.createElement(
|
|
12884
|
+
"button",
|
|
12885
|
+
{
|
|
12886
|
+
type: "button",
|
|
12887
|
+
className: `grow-0 flex items-center gap-2 whitespace-nowrap rounded-lg border bg-white px-3 py-1.5 text-sm transition-colors ease-out duration-150 ${active ? "border-tina-orange text-tina-orange font-medium" : "border-gray-200 text-gray-700 hover:border-gray-300"}`
|
|
12888
|
+
},
|
|
12889
|
+
active ? TYPE_FILTER_LABELS[value] : "Any type",
|
|
12890
|
+
/* @__PURE__ */ React__default.createElement(
|
|
12891
|
+
ChevronDown,
|
|
12892
|
+
{
|
|
12893
|
+
className: `w-4 h-4 ${active ? "opacity-90" : "text-gray-400"}`
|
|
12894
|
+
}
|
|
12895
|
+
)
|
|
12896
|
+
)), /* @__PURE__ */ React__default.createElement(DropdownMenuContent, { align: "end", className: "min-w-52" }, /* @__PURE__ */ React__default.createElement(
|
|
12897
|
+
DropdownMenuRadioGroup,
|
|
12898
|
+
{
|
|
12899
|
+
value,
|
|
12900
|
+
onValueChange: (next) => setValue(next)
|
|
12901
|
+
},
|
|
12902
|
+
/* @__PURE__ */ React__default.createElement(DropdownMenuRadioItem, { value: "all" }, "Any type"),
|
|
12903
|
+
/* @__PURE__ */ React__default.createElement(DropdownMenuSeparator, null),
|
|
12904
|
+
/* @__PURE__ */ React__default.createElement(DropdownMenuLabel, null, "Category"),
|
|
12905
|
+
Object.keys(MEDIA_CATEGORIES).map(
|
|
12906
|
+
(category) => /* @__PURE__ */ React__default.createElement(DropdownMenuRadioItem, { key: category, value: category }, TYPE_FILTER_LABELS[category])
|
|
12907
|
+
)
|
|
12908
|
+
)));
|
|
12909
|
+
};
|
|
12471
12910
|
const ViewModeToggle = ({ viewMode, setViewMode }) => {
|
|
12472
12911
|
const base = "relative flex items-center justify-center w-9 py-1.5 transition-colors ease-out duration-150";
|
|
12473
12912
|
const active = "bg-white text-tina-orange";
|
|
@@ -13719,7 +14158,7 @@ const FormModal = ({ plugin, close: close2 }) => {
|
|
|
13719
14158
|
const name = "tinacms";
|
|
13720
14159
|
const type = "module";
|
|
13721
14160
|
const typings = "dist/index.d.ts";
|
|
13722
|
-
const version = "3.
|
|
14161
|
+
const version = "3.13.0";
|
|
13723
14162
|
const main = "dist/index.js";
|
|
13724
14163
|
const module$1 = "./dist/index.js";
|
|
13725
14164
|
const exports$1 = { ".": "./dist/index.js", "./dist/client": "./dist/client.js", "./react": "./dist/react.js", "./dist/react": "./dist/react.js", "./tina-field": "./dist/tina-field.js", "./dist/tina-field": "./dist/tina-field.js", "./dist/rich-text": "./dist/rich-text/index.js", "./dist/rich-text/static": "./dist/rich-text/static.js", "./dist/rich-text/prism": "./dist/rich-text/prism.js" };
|
|
@@ -13827,7 +14266,11 @@ const useGetEvents = (cms, cursor, existingEvents) => {
|
|
|
13827
14266
|
var _a, _b, _c, _d, _e;
|
|
13828
14267
|
let doFetchEvents = false;
|
|
13829
14268
|
if (!((_b = (_a = cms.api) == null ? void 0 : _a.tina) == null ? void 0 : _b.isCustomContentApi)) {
|
|
13830
|
-
|
|
14269
|
+
try {
|
|
14270
|
+
doFetchEvents = await ((_e = (_d = (_c = cms.api) == null ? void 0 : _c.tina) == null ? void 0 : _d.authProvider) == null ? void 0 : _e.isAuthenticated());
|
|
14271
|
+
} catch {
|
|
14272
|
+
doFetchEvents = false;
|
|
14273
|
+
}
|
|
13831
14274
|
}
|
|
13832
14275
|
if (doFetchEvents) {
|
|
13833
14276
|
try {
|
|
@@ -13860,7 +14303,11 @@ function useSyncStatus(cms) {
|
|
|
13860
14303
|
var _a2, _b2, _c, _d, _e;
|
|
13861
14304
|
let doFetchEvents = false;
|
|
13862
14305
|
if (!((_b2 = (_a2 = cms.api) == null ? void 0 : _a2.tina) == null ? void 0 : _b2.isCustomContentApi)) {
|
|
13863
|
-
|
|
14306
|
+
try {
|
|
14307
|
+
doFetchEvents = await ((_e = (_d = (_c = cms.api) == null ? void 0 : _c.tina) == null ? void 0 : _d.authProvider) == null ? void 0 : _e.isAuthenticated());
|
|
14308
|
+
} catch {
|
|
14309
|
+
doFetchEvents = false;
|
|
14310
|
+
}
|
|
13864
14311
|
}
|
|
13865
14312
|
if (doFetchEvents) {
|
|
13866
14313
|
const { events } = await cms.api.tina.fetchEvents();
|
|
@@ -17144,6 +17591,35 @@ const CreateBranchModal = ({
|
|
|
17144
17591
|
}
|
|
17145
17592
|
);
|
|
17146
17593
|
};
|
|
17594
|
+
const getInitials = (name2) => {
|
|
17595
|
+
const atIndex = name2.indexOf("@");
|
|
17596
|
+
const localPart = atIndex === -1 ? name2 : name2.slice(0, atIndex);
|
|
17597
|
+
return localPart.split(/[\s._-]+/).filter(Boolean).slice(0, 2).map((part) => [...part][0].toUpperCase()).join("");
|
|
17598
|
+
};
|
|
17599
|
+
const CommittingAs = () => {
|
|
17600
|
+
var _a, _b, _c;
|
|
17601
|
+
const cms = useCMS$1();
|
|
17602
|
+
const user = (_b = (_a = cms.api) == null ? void 0 : _a.tina) == null ? void 0 : _b.user;
|
|
17603
|
+
const author = typeof user === "object" && user !== null ? user : void 0;
|
|
17604
|
+
const mode = (_c = author == null ? void 0 : author.gitAuthoring) == null ? void 0 : _c.mode;
|
|
17605
|
+
if (mode !== "bot" && mode !== "user") {
|
|
17606
|
+
return null;
|
|
17607
|
+
}
|
|
17608
|
+
const authorName = mode === "bot" ? "TinaCloud bot" : author.fullName || author.email;
|
|
17609
|
+
if (!authorName) {
|
|
17610
|
+
return null;
|
|
17611
|
+
}
|
|
17612
|
+
return /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-3 mt-4 pt-4 border-t border-gray-100" }, /* @__PURE__ */ React.createElement("span", { className: "flex items-center justify-center w-8 h-8 rounded-full bg-gray-100 text-gray-600 text-xs font-medium flex-shrink-0" }, getInitials(authorName)), /* @__PURE__ */ React.createElement("div", { className: "flex-1 min-w-0" }, /* @__PURE__ */ React.createElement("p", { className: "text-xs text-gray-500" }, "Committing as"), /* @__PURE__ */ React.createElement("p", { className: "text-sm text-gray-700 font-medium truncate" }, authorName)), /* @__PURE__ */ React.createElement(
|
|
17613
|
+
"a",
|
|
17614
|
+
{
|
|
17615
|
+
className: "text-sm underline text-tina-orange-dark font-medium flex-shrink-0",
|
|
17616
|
+
href: cms.api.tina.gitSettingsLink,
|
|
17617
|
+
target: "_blank",
|
|
17618
|
+
rel: "noreferrer"
|
|
17619
|
+
},
|
|
17620
|
+
"Change"
|
|
17621
|
+
));
|
|
17622
|
+
};
|
|
17147
17623
|
const CreateBranchPromptModal = ({
|
|
17148
17624
|
branchName,
|
|
17149
17625
|
close: close2,
|
|
@@ -17153,7 +17629,8 @@ const CreateBranchPromptModal = ({
|
|
|
17153
17629
|
onCreateBranch,
|
|
17154
17630
|
onSaveToProtectedBranch,
|
|
17155
17631
|
showSaveOptions = false,
|
|
17156
|
-
disablePublish = false
|
|
17632
|
+
disablePublish = false,
|
|
17633
|
+
allowSaveToProtectedBranch = true
|
|
17157
17634
|
}) => {
|
|
17158
17635
|
const [lastChoice, setLastChoice] = useLocalStorage(
|
|
17159
17636
|
SAVE_CHOICE_KEY,
|
|
@@ -17200,7 +17677,7 @@ const CreateBranchPromptModal = ({
|
|
|
17200
17677
|
onBranchNameChange(e.target.value);
|
|
17201
17678
|
}
|
|
17202
17679
|
}
|
|
17203
|
-
))), /* @__PURE__ */ React.createElement(ModalActions, { align: "end" }, /* @__PURE__ */ React.createElement(
|
|
17680
|
+
), /* @__PURE__ */ React.createElement(CommittingAs, null))), /* @__PURE__ */ React.createElement(ModalActions, { align: "end" }, /* @__PURE__ */ React.createElement(
|
|
17204
17681
|
Button$2,
|
|
17205
17682
|
{
|
|
17206
17683
|
variant: "secondary",
|
|
@@ -17233,7 +17710,7 @@ const CreateBranchPromptModal = ({
|
|
|
17233
17710
|
},
|
|
17234
17711
|
/* @__PURE__ */ React.createElement(MainIcon, { className: "w-4 h-4 mr-1", style: { fill: "none" } }),
|
|
17235
17712
|
mainChoice.label
|
|
17236
|
-
) : /* @__PURE__ */ React.createElement(
|
|
17713
|
+
) : allowSaveToProtectedBranch ? /* @__PURE__ */ React.createElement(
|
|
17237
17714
|
DropdownButton,
|
|
17238
17715
|
{
|
|
17239
17716
|
variant: "primary",
|
|
@@ -17257,6 +17734,25 @@ const CreateBranchPromptModal = ({
|
|
|
17257
17734
|
}
|
|
17258
17735
|
),
|
|
17259
17736
|
"Save to a new branch"
|
|
17737
|
+
) : (
|
|
17738
|
+
// A plain button: the dropdown's only item is the one being dropped.
|
|
17739
|
+
/* @__PURE__ */ React.createElement(
|
|
17740
|
+
Button$2,
|
|
17741
|
+
{
|
|
17742
|
+
variant: "primary",
|
|
17743
|
+
className: "w-full sm:w-auto",
|
|
17744
|
+
disabled,
|
|
17745
|
+
onClick: () => onCreateBranch(false)
|
|
17746
|
+
},
|
|
17747
|
+
/* @__PURE__ */ React.createElement(
|
|
17748
|
+
GitBranchIcon,
|
|
17749
|
+
{
|
|
17750
|
+
className: "w-4 h-4 mr-1",
|
|
17751
|
+
style: { fill: "none" }
|
|
17752
|
+
}
|
|
17753
|
+
),
|
|
17754
|
+
"Save to a new branch"
|
|
17755
|
+
)
|
|
17260
17756
|
))));
|
|
17261
17757
|
};
|
|
17262
17758
|
const PrefixedTextField = ({
|
|
@@ -17609,6 +18105,10 @@ const FormBuilder = ({
|
|
|
17609
18105
|
setDeletedBranchModalOpen(true);
|
|
17610
18106
|
return;
|
|
17611
18107
|
}
|
|
18108
|
+
if (isSessionExpiredError(error)) {
|
|
18109
|
+
captureWorkflowChoice(false, errorMsg);
|
|
18110
|
+
return;
|
|
18111
|
+
}
|
|
17612
18112
|
captureEvent(SaveContentErrorEvent, {
|
|
17613
18113
|
documentPath: tinaForm.path,
|
|
17614
18114
|
error: errorMsg
|
|
@@ -18709,7 +19209,7 @@ function HeadingsMenu(props) {
|
|
|
18709
19209
|
className: "min-w-0 rounded-none rounded-bl rounded-br border-border"
|
|
18710
19210
|
},
|
|
18711
19211
|
/* @__PURE__ */ React__default.createElement(
|
|
18712
|
-
DropdownMenuRadioGroup,
|
|
19212
|
+
DropdownMenuRadioGroup$1,
|
|
18713
19213
|
{
|
|
18714
19214
|
className: "flex flex-col gap-0.5",
|
|
18715
19215
|
onValueChange: (type2) => {
|
|
@@ -18725,7 +19225,7 @@ function HeadingsMenu(props) {
|
|
|
18725
19225
|
}
|
|
18726
19226
|
return true;
|
|
18727
19227
|
}).map(({ icon: Icon, label, value: itemValue }) => /* @__PURE__ */ React__default.createElement(
|
|
18728
|
-
DropdownMenuRadioItem,
|
|
19228
|
+
DropdownMenuRadioItem$1,
|
|
18729
19229
|
{
|
|
18730
19230
|
className: "min-w-[180px]",
|
|
18731
19231
|
key: itemValue,
|
|
@@ -19481,549 +19981,6 @@ const useCreateEditor = ({
|
|
|
19481
19981
|
components
|
|
19482
19982
|
});
|
|
19483
19983
|
};
|
|
19484
|
-
const popoverVariants = cva(
|
|
19485
|
-
"z-[999999] w-auto rounded-md border bg-popover p-1 text-popover-foreground shadow-md outline-hidden"
|
|
19486
|
-
);
|
|
19487
|
-
const inputVariants = cva(
|
|
19488
|
-
"flex h-[28px] w-full rounded-md border-none bg-transparent px-1.5 py-1 text-base placeholder:text-muted-foreground focus-visible:ring-transparent focus-visible:outline-none md:text-sm"
|
|
19489
|
-
);
|
|
19490
|
-
function LinkFloatingToolbar({
|
|
19491
|
-
state
|
|
19492
|
-
}) {
|
|
19493
|
-
const activeCommentId = usePluginOption({ key: "comment" }, "activeId");
|
|
19494
|
-
const activeSuggestionId = usePluginOption({ key: "suggestion" }, "activeId");
|
|
19495
|
-
const { api, editor } = useEditorPlugin(LinkPlugin);
|
|
19496
|
-
const isUrlValidator = usePluginOption(LinkPlugin, "isUrl");
|
|
19497
|
-
const [currentUrl, setCurrentUrl] = React.useState("");
|
|
19498
|
-
const [isValidUrl, setIsValidUrl] = React.useState(true);
|
|
19499
|
-
const handleUrlInput = React.useCallback(
|
|
19500
|
-
(e) => {
|
|
19501
|
-
const value = e.currentTarget.value;
|
|
19502
|
-
setCurrentUrl(value);
|
|
19503
|
-
if (value && isUrlValidator) {
|
|
19504
|
-
const valid = isUrlValidator(value);
|
|
19505
|
-
setIsValidUrl(valid);
|
|
19506
|
-
} else {
|
|
19507
|
-
setIsValidUrl(true);
|
|
19508
|
-
}
|
|
19509
|
-
},
|
|
19510
|
-
[isUrlValidator]
|
|
19511
|
-
);
|
|
19512
|
-
const floatingOptions = React.useMemo(() => {
|
|
19513
|
-
return {
|
|
19514
|
-
middleware: [
|
|
19515
|
-
offset(8),
|
|
19516
|
-
flip({
|
|
19517
|
-
fallbackPlacements: ["bottom-end", "top-start", "top-end"],
|
|
19518
|
-
padding: 12
|
|
19519
|
-
}),
|
|
19520
|
-
shift({ padding: 8, limiter: limitShift() })
|
|
19521
|
-
],
|
|
19522
|
-
placement: activeSuggestionId || activeCommentId ? "top-start" : "bottom-start"
|
|
19523
|
-
};
|
|
19524
|
-
}, [activeCommentId, activeSuggestionId]);
|
|
19525
|
-
const insertState = useFloatingLinkInsertState({
|
|
19526
|
-
...state,
|
|
19527
|
-
floatingOptions: {
|
|
19528
|
-
...floatingOptions,
|
|
19529
|
-
...state == null ? void 0 : state.floatingOptions
|
|
19530
|
-
}
|
|
19531
|
-
});
|
|
19532
|
-
const {
|
|
19533
|
-
hidden,
|
|
19534
|
-
props: insertProps,
|
|
19535
|
-
ref: insertRef,
|
|
19536
|
-
textInputProps
|
|
19537
|
-
} = useFloatingLinkInsert(insertState);
|
|
19538
|
-
const editState = useFloatingLinkEditState({
|
|
19539
|
-
...state,
|
|
19540
|
-
floatingOptions: {
|
|
19541
|
-
...floatingOptions,
|
|
19542
|
-
...state == null ? void 0 : state.floatingOptions
|
|
19543
|
-
}
|
|
19544
|
-
});
|
|
19545
|
-
const {
|
|
19546
|
-
editButtonProps,
|
|
19547
|
-
props: editProps,
|
|
19548
|
-
ref: editRef,
|
|
19549
|
-
unlinkButtonProps
|
|
19550
|
-
} = useFloatingLinkEdit(editState);
|
|
19551
|
-
const inputProps = useFormInputProps({
|
|
19552
|
-
preventDefaultOnEnterKeydown: true
|
|
19553
|
-
});
|
|
19554
|
-
if (hidden) return null;
|
|
19555
|
-
const input = /* @__PURE__ */ React.createElement(
|
|
19556
|
-
"div",
|
|
19557
|
-
{
|
|
19558
|
-
className: "z-[999999] flex w-[330px] flex-col relative",
|
|
19559
|
-
...inputProps
|
|
19560
|
-
},
|
|
19561
|
-
!isValidUrl && currentUrl && /* @__PURE__ */ React.createElement("div", { className: "absolute -top-16 left-0 right-0 z-[1000000] mb-2" }, /* @__PURE__ */ React.createElement(
|
|
19562
|
-
"div",
|
|
19563
|
-
{
|
|
19564
|
-
className: "bg-red-50 border border-red-200 rounded-md p-2 shadow-lg",
|
|
19565
|
-
role: "alert",
|
|
19566
|
-
"aria-live": "polite"
|
|
19567
|
-
},
|
|
19568
|
-
/* @__PURE__ */ React.createElement("div", { className: "flex items-center" }, /* @__PURE__ */ React.createElement(CircleX, { className: "size-4 text-red-500 mr-2 flex-shrink-0" }), /* @__PURE__ */ React.createElement("span", { className: "text-sm text-red-700 text-wrap" }, "Invalid URL. Please prefix link with https:// or use a relative path like /about")),
|
|
19569
|
-
/* @__PURE__ */ React.createElement("div", { className: "absolute -bottom-1 left-4 w-2 h-2 bg-red-50 border-r border-b border-red-200 transform rotate-45" })
|
|
19570
|
-
)),
|
|
19571
|
-
/* @__PURE__ */ React.createElement("div", { className: "flex items-center" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center pr-1 pl-2 text-muted-foreground" }, /* @__PURE__ */ React.createElement(Link, { className: "size-4" })), /* @__PURE__ */ React.createElement(
|
|
19572
|
-
FloatingLinkUrlInput,
|
|
19573
|
-
{
|
|
19574
|
-
className: inputVariants(),
|
|
19575
|
-
placeholder: "Paste link",
|
|
19576
|
-
"data-plate-focus": true,
|
|
19577
|
-
onInput: handleUrlInput
|
|
19578
|
-
}
|
|
19579
|
-
)),
|
|
19580
|
-
/* @__PURE__ */ React.createElement(Separator, { className: "my-1" }),
|
|
19581
|
-
/* @__PURE__ */ React.createElement("div", { className: "flex items-center" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center pr-1 pl-2 text-muted-foreground" }, /* @__PURE__ */ React.createElement(Text, { className: "size-4" })), /* @__PURE__ */ React.createElement(
|
|
19582
|
-
"input",
|
|
19583
|
-
{
|
|
19584
|
-
className: inputVariants(),
|
|
19585
|
-
placeholder: "Text to display",
|
|
19586
|
-
"data-plate-focus": true,
|
|
19587
|
-
...textInputProps
|
|
19588
|
-
}
|
|
19589
|
-
)),
|
|
19590
|
-
/* @__PURE__ */ React.createElement(Separator, { className: "my-1" }),
|
|
19591
|
-
/* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-end gap-2 px-2 py-1" }, /* @__PURE__ */ React.createElement(
|
|
19592
|
-
"button",
|
|
19593
|
-
{
|
|
19594
|
-
type: "button",
|
|
19595
|
-
className: buttonVariants$2({ size: "sm", variant: "ghost" }),
|
|
19596
|
-
onClick: () => {
|
|
19597
|
-
api.floatingLink.hide();
|
|
19598
|
-
}
|
|
19599
|
-
},
|
|
19600
|
-
"Cancel"
|
|
19601
|
-
), /* @__PURE__ */ React.createElement(
|
|
19602
|
-
"button",
|
|
19603
|
-
{
|
|
19604
|
-
type: "button",
|
|
19605
|
-
className: buttonVariants$2({
|
|
19606
|
-
size: "sm",
|
|
19607
|
-
variant: "tinaPrimary"
|
|
19608
|
-
}),
|
|
19609
|
-
onClick: () => {
|
|
19610
|
-
if (isValidUrl && currentUrl) {
|
|
19611
|
-
submitFloatingLink(editor);
|
|
19612
|
-
}
|
|
19613
|
-
},
|
|
19614
|
-
disabled: !isValidUrl && !!currentUrl
|
|
19615
|
-
},
|
|
19616
|
-
"OK"
|
|
19617
|
-
))
|
|
19618
|
-
);
|
|
19619
|
-
const editContent = editState.isEditing ? input : /* @__PURE__ */ React.createElement("div", { className: "box-content flex items-center" }, /* @__PURE__ */ React.createElement(
|
|
19620
|
-
"button",
|
|
19621
|
-
{
|
|
19622
|
-
className: buttonVariants$2({ size: "sm", variant: "ghost" }),
|
|
19623
|
-
type: "button",
|
|
19624
|
-
...editButtonProps
|
|
19625
|
-
},
|
|
19626
|
-
"Edit link"
|
|
19627
|
-
), /* @__PURE__ */ React.createElement(Separator, { orientation: "vertical" }), /* @__PURE__ */ React.createElement(LinkOpenButton, null), /* @__PURE__ */ React.createElement(Separator, { orientation: "vertical" }), /* @__PURE__ */ React.createElement(
|
|
19628
|
-
"button",
|
|
19629
|
-
{
|
|
19630
|
-
className: buttonVariants$2({
|
|
19631
|
-
size: "icon",
|
|
19632
|
-
variant: "ghost"
|
|
19633
|
-
}),
|
|
19634
|
-
type: "button",
|
|
19635
|
-
...unlinkButtonProps
|
|
19636
|
-
},
|
|
19637
|
-
/* @__PURE__ */ React.createElement(Unlink, { width: 18 })
|
|
19638
|
-
));
|
|
19639
|
-
return /* @__PURE__ */ React.createElement(PortalBody, null, /* @__PURE__ */ React.createElement(
|
|
19640
|
-
"div",
|
|
19641
|
-
{
|
|
19642
|
-
ref: insertRef,
|
|
19643
|
-
className: popoverVariants(),
|
|
19644
|
-
...insertProps,
|
|
19645
|
-
style: {
|
|
19646
|
-
...insertProps.style,
|
|
19647
|
-
zIndex: 999999
|
|
19648
|
-
}
|
|
19649
|
-
},
|
|
19650
|
-
input
|
|
19651
|
-
), /* @__PURE__ */ React.createElement(
|
|
19652
|
-
"div",
|
|
19653
|
-
{
|
|
19654
|
-
ref: editRef,
|
|
19655
|
-
className: popoverVariants(),
|
|
19656
|
-
...editProps,
|
|
19657
|
-
style: { ...editProps.style, zIndex: 999999 }
|
|
19658
|
-
},
|
|
19659
|
-
editContent
|
|
19660
|
-
));
|
|
19661
|
-
}
|
|
19662
|
-
function LinkOpenButton() {
|
|
19663
|
-
const editor = useEditorRef();
|
|
19664
|
-
const selection = useEditorSelection();
|
|
19665
|
-
const attributes = React.useMemo(
|
|
19666
|
-
() => {
|
|
19667
|
-
const entry = editor.api.node({
|
|
19668
|
-
match: { type: editor.getType(LinkPlugin) }
|
|
19669
|
-
});
|
|
19670
|
-
if (!entry) {
|
|
19671
|
-
return {};
|
|
19672
|
-
}
|
|
19673
|
-
const [element] = entry;
|
|
19674
|
-
return getLinkAttributes(editor, element);
|
|
19675
|
-
},
|
|
19676
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
19677
|
-
[editor, selection]
|
|
19678
|
-
);
|
|
19679
|
-
return /* @__PURE__ */ React.createElement(
|
|
19680
|
-
"a",
|
|
19681
|
-
{
|
|
19682
|
-
...attributes,
|
|
19683
|
-
className: buttonVariants$2({
|
|
19684
|
-
size: "icon",
|
|
19685
|
-
variant: "ghost"
|
|
19686
|
-
}),
|
|
19687
|
-
onMouseOver: (e) => {
|
|
19688
|
-
e.stopPropagation();
|
|
19689
|
-
},
|
|
19690
|
-
"aria-label": "Open link in a new tab",
|
|
19691
|
-
target: "_blank"
|
|
19692
|
-
},
|
|
19693
|
-
/* @__PURE__ */ React.createElement(ExternalLink, { width: 18 })
|
|
19694
|
-
);
|
|
19695
|
-
}
|
|
19696
|
-
const protocolAndDomainRE = /^(?:\w+:)?\/\/(\S+)$/;
|
|
19697
|
-
const emailLintRE = /mailto:([^?\\]+)/;
|
|
19698
|
-
const telLintRE = /tel:([\d-]+)/;
|
|
19699
|
-
const localhostDomainRE = /^localhost[\d:?]*(?:[^\d:?]\S*)?$/;
|
|
19700
|
-
const nonLocalhostDomainRE = /^[^\s.]+\.\S{2,}$/;
|
|
19701
|
-
const localUrlRE = /^\/\S+/;
|
|
19702
|
-
const isUrl = (string) => {
|
|
19703
|
-
if (typeof string !== "string") {
|
|
19704
|
-
return false;
|
|
19705
|
-
}
|
|
19706
|
-
if (string.startsWith("#")) {
|
|
19707
|
-
return true;
|
|
19708
|
-
}
|
|
19709
|
-
const generalMatch = string.match(protocolAndDomainRE);
|
|
19710
|
-
const emailLinkMatch = string.match(emailLintRE);
|
|
19711
|
-
const telLinkMatch = string.match(telLintRE);
|
|
19712
|
-
const localUrlMatch = string.match(localUrlRE);
|
|
19713
|
-
if (emailLinkMatch || telLinkMatch || localUrlMatch) {
|
|
19714
|
-
return true;
|
|
19715
|
-
}
|
|
19716
|
-
if (generalMatch) {
|
|
19717
|
-
const everythingAfterProtocol = generalMatch[1];
|
|
19718
|
-
if (!everythingAfterProtocol) {
|
|
19719
|
-
return false;
|
|
19720
|
-
}
|
|
19721
|
-
try {
|
|
19722
|
-
new URL(string);
|
|
19723
|
-
} catch {
|
|
19724
|
-
return false;
|
|
19725
|
-
}
|
|
19726
|
-
return localhostDomainRE.test(everythingAfterProtocol) || nonLocalhostDomainRE.test(everythingAfterProtocol);
|
|
19727
|
-
}
|
|
19728
|
-
return false;
|
|
19729
|
-
};
|
|
19730
|
-
const buildError = (element) => {
|
|
19731
|
-
return {
|
|
19732
|
-
message: element.message,
|
|
19733
|
-
position: element.position && {
|
|
19734
|
-
endColumn: element.position.end.column,
|
|
19735
|
-
startColumn: element.position.start.column,
|
|
19736
|
-
startLineNumber: element.position.start.line,
|
|
19737
|
-
endLineNumber: element.position.end.line
|
|
19738
|
-
}
|
|
19739
|
-
};
|
|
19740
|
-
};
|
|
19741
|
-
const buildErrorMessage = (element) => {
|
|
19742
|
-
if (!element) {
|
|
19743
|
-
return "";
|
|
19744
|
-
}
|
|
19745
|
-
const errorMessage = buildError(element);
|
|
19746
|
-
const message = errorMessage ? `${errorMessage.message}${errorMessage.position ? ` at line: ${errorMessage.position.startLineNumber}, column: ${errorMessage.position.startColumn}` : ""}` : null;
|
|
19747
|
-
return message;
|
|
19748
|
-
};
|
|
19749
|
-
const ELEMENT_INVALID_MARKDOWN = "invalid_markdown";
|
|
19750
|
-
const createInvalidMarkdownPlugin = createPlatePlugin({
|
|
19751
|
-
key: ELEMENT_INVALID_MARKDOWN,
|
|
19752
|
-
options: {
|
|
19753
|
-
isElement: true,
|
|
19754
|
-
isVoid: true,
|
|
19755
|
-
isInline: false
|
|
19756
|
-
},
|
|
19757
|
-
node: {
|
|
19758
|
-
component: InvalidMarkdownElement
|
|
19759
|
-
}
|
|
19760
|
-
});
|
|
19761
|
-
function InvalidMarkdownElement({
|
|
19762
|
-
attributes,
|
|
19763
|
-
element,
|
|
19764
|
-
children
|
|
19765
|
-
}) {
|
|
19766
|
-
return /* @__PURE__ */ React__default.createElement("div", { ...attributes }, /* @__PURE__ */ React__default.createElement(ErrorMessage, { error: element }), children);
|
|
19767
|
-
}
|
|
19768
|
-
function ErrorMessage({ error }) {
|
|
19769
|
-
const message = buildErrorMessage(error);
|
|
19770
|
-
const { setRawMode } = useEditorContext();
|
|
19771
|
-
return /* @__PURE__ */ React__default.createElement("div", { contentEditable: false, className: "bg-red-50 sm:rounded-lg" }, /* @__PURE__ */ React__default.createElement("div", { className: "px-4 py-5 sm:p-6" }, /* @__PURE__ */ React__default.createElement("h3", { className: "text-lg leading-6 font-medium text-red-800" }, "❌ Error parsing markdown"), /* @__PURE__ */ React__default.createElement("div", { className: "mt-2 max-w-xl text-sm text-red-800 space-y-4" }, /* @__PURE__ */ React__default.createElement("p", null, message), /* @__PURE__ */ React__default.createElement("p", null, "To fix these errors, edit the content in raw-mode."), /* @__PURE__ */ React__default.createElement(
|
|
19772
|
-
"button",
|
|
19773
|
-
{
|
|
19774
|
-
type: "button",
|
|
19775
|
-
onClick: () => setRawMode(true),
|
|
19776
|
-
className: "rounded-l border-r-0 shadow rounded bg-white cursor-pointer relative inline-flex items-center px-2 py-2 border border-gray-200 hover:text-white text-sm font-medium transition-all ease-out duration-150 hover:bg-gray-500 focus:z-10 focus:outline-none focus:ring-1 focus:ring-gray-500 focus:border-gray-500"
|
|
19777
|
-
},
|
|
19778
|
-
"Switch to raw-mode"
|
|
19779
|
-
))));
|
|
19780
|
-
}
|
|
19781
|
-
const useCustomFloatingToolbarState = ({
|
|
19782
|
-
editorId,
|
|
19783
|
-
floatingOptions,
|
|
19784
|
-
focusedEditorId,
|
|
19785
|
-
hideToolbar,
|
|
19786
|
-
showWhenReadOnly
|
|
19787
|
-
}) => {
|
|
19788
|
-
const editor = useEditorRef();
|
|
19789
|
-
const selectionExpanded = useEditorSelector(
|
|
19790
|
-
() => editor.api.isExpanded(),
|
|
19791
|
-
[]
|
|
19792
|
-
);
|
|
19793
|
-
const selectionText = useEditorSelector(() => editor.api.string(), []);
|
|
19794
|
-
const readOnly = useEditorReadOnly();
|
|
19795
|
-
const focused = useFocused();
|
|
19796
|
-
const [open, setOpen] = React__default.useState(false);
|
|
19797
|
-
const [waitForCollapsedSelection, setWaitForCollapsedSelection] = React__default.useState(false);
|
|
19798
|
-
const [mousedown, setMousedown] = React__default.useState(false);
|
|
19799
|
-
const floating = useVirtualFloating(
|
|
19800
|
-
mergeProps(
|
|
19801
|
-
{
|
|
19802
|
-
open,
|
|
19803
|
-
getBoundingClientRect: () => getSelectionBoundingClientRect(editor),
|
|
19804
|
-
onOpenChange: setOpen
|
|
19805
|
-
},
|
|
19806
|
-
floatingOptions
|
|
19807
|
-
)
|
|
19808
|
-
);
|
|
19809
|
-
return {
|
|
19810
|
-
editorId,
|
|
19811
|
-
floating,
|
|
19812
|
-
focused,
|
|
19813
|
-
focusedEditorId,
|
|
19814
|
-
hideToolbar,
|
|
19815
|
-
mousedown,
|
|
19816
|
-
open,
|
|
19817
|
-
readOnly,
|
|
19818
|
-
selectionExpanded,
|
|
19819
|
-
selectionText,
|
|
19820
|
-
setMousedown,
|
|
19821
|
-
setOpen,
|
|
19822
|
-
setWaitForCollapsedSelection,
|
|
19823
|
-
showWhenReadOnly,
|
|
19824
|
-
waitForCollapsedSelection
|
|
19825
|
-
};
|
|
19826
|
-
};
|
|
19827
|
-
const useCustomFloatingToolbar = ({
|
|
19828
|
-
editorId,
|
|
19829
|
-
floating,
|
|
19830
|
-
focusedEditorId,
|
|
19831
|
-
hideToolbar,
|
|
19832
|
-
mousedown,
|
|
19833
|
-
open,
|
|
19834
|
-
readOnly,
|
|
19835
|
-
selectionExpanded,
|
|
19836
|
-
selectionText,
|
|
19837
|
-
setMousedown,
|
|
19838
|
-
setOpen,
|
|
19839
|
-
setWaitForCollapsedSelection,
|
|
19840
|
-
showWhenReadOnly,
|
|
19841
|
-
waitForCollapsedSelection
|
|
19842
|
-
}) => {
|
|
19843
|
-
React__default.useEffect(() => {
|
|
19844
|
-
if (!(editorId === focusedEditorId)) {
|
|
19845
|
-
setWaitForCollapsedSelection(true);
|
|
19846
|
-
}
|
|
19847
|
-
if (!selectionExpanded) {
|
|
19848
|
-
setWaitForCollapsedSelection(false);
|
|
19849
|
-
}
|
|
19850
|
-
}, [
|
|
19851
|
-
editorId,
|
|
19852
|
-
focusedEditorId,
|
|
19853
|
-
selectionExpanded,
|
|
19854
|
-
setWaitForCollapsedSelection
|
|
19855
|
-
]);
|
|
19856
|
-
React__default.useEffect(() => {
|
|
19857
|
-
const mouseup = () => setMousedown(false);
|
|
19858
|
-
const mousedown2 = () => setMousedown(true);
|
|
19859
|
-
document.addEventListener("mouseup", mouseup);
|
|
19860
|
-
document.addEventListener("mousedown", mousedown2);
|
|
19861
|
-
return () => {
|
|
19862
|
-
document.removeEventListener("mouseup", mouseup);
|
|
19863
|
-
document.removeEventListener("mousedown", mousedown2);
|
|
19864
|
-
};
|
|
19865
|
-
}, []);
|
|
19866
|
-
React__default.useEffect(() => {
|
|
19867
|
-
if (!selectionExpanded || !selectionText || mousedown && !open || hideToolbar || readOnly && !showWhenReadOnly) {
|
|
19868
|
-
setOpen(false);
|
|
19869
|
-
} else if (selectionText && selectionExpanded && (!waitForCollapsedSelection || readOnly)) {
|
|
19870
|
-
setOpen(true);
|
|
19871
|
-
}
|
|
19872
|
-
}, [
|
|
19873
|
-
setOpen,
|
|
19874
|
-
editorId,
|
|
19875
|
-
focusedEditorId,
|
|
19876
|
-
hideToolbar,
|
|
19877
|
-
showWhenReadOnly,
|
|
19878
|
-
selectionExpanded,
|
|
19879
|
-
selectionText,
|
|
19880
|
-
mousedown,
|
|
19881
|
-
waitForCollapsedSelection,
|
|
19882
|
-
open,
|
|
19883
|
-
readOnly
|
|
19884
|
-
]);
|
|
19885
|
-
const { update } = floating;
|
|
19886
|
-
useEditorSelector(() => {
|
|
19887
|
-
update == null ? void 0 : update();
|
|
19888
|
-
}, [update]);
|
|
19889
|
-
const clickOutsideRef = useOnClickOutside(
|
|
19890
|
-
() => {
|
|
19891
|
-
setOpen(false);
|
|
19892
|
-
},
|
|
19893
|
-
{
|
|
19894
|
-
ignoreClass: "ignore-click-outside/toolbar"
|
|
19895
|
-
}
|
|
19896
|
-
);
|
|
19897
|
-
return {
|
|
19898
|
-
clickOutsideRef,
|
|
19899
|
-
hidden: !open,
|
|
19900
|
-
props: {
|
|
19901
|
-
style: floating.style
|
|
19902
|
-
},
|
|
19903
|
-
ref: floating.refs.setFloating
|
|
19904
|
-
};
|
|
19905
|
-
};
|
|
19906
|
-
const FloatingToolbar = withRef(({ children, state, ...props }, propRef) => {
|
|
19907
|
-
const editorId = useEditorRef();
|
|
19908
|
-
const focusedEditorId = useEventEditorValue("focus");
|
|
19909
|
-
const test = useCustomFloatingToolbarState({
|
|
19910
|
-
editorId: editorId.id,
|
|
19911
|
-
focusedEditorId,
|
|
19912
|
-
...state,
|
|
19913
|
-
floatingOptions: {
|
|
19914
|
-
middleware: [
|
|
19915
|
-
offset(12),
|
|
19916
|
-
flip({
|
|
19917
|
-
fallbackPlacements: [
|
|
19918
|
-
"top-start",
|
|
19919
|
-
"top-end",
|
|
19920
|
-
"bottom-start",
|
|
19921
|
-
"bottom-end"
|
|
19922
|
-
],
|
|
19923
|
-
padding: 12
|
|
19924
|
-
})
|
|
19925
|
-
],
|
|
19926
|
-
placement: "top",
|
|
19927
|
-
...state == null ? void 0 : state.floatingOptions
|
|
19928
|
-
}
|
|
19929
|
-
});
|
|
19930
|
-
const {
|
|
19931
|
-
hidden,
|
|
19932
|
-
props: rootProps,
|
|
19933
|
-
ref: floatingRef
|
|
19934
|
-
} = useCustomFloatingToolbar(test);
|
|
19935
|
-
const ref = useComposedRef$1(propRef, floatingRef);
|
|
19936
|
-
if (hidden) return null;
|
|
19937
|
-
return /* @__PURE__ */ React__default.createElement(PortalBody, null, /* @__PURE__ */ React__default.createElement(
|
|
19938
|
-
Toolbar,
|
|
19939
|
-
{
|
|
19940
|
-
className: cn$1(
|
|
19941
|
-
"absolute z-[10799] whitespace-nowrap border bg-popover px-1 opacity-100 shadow-md print:hidden rounded-md"
|
|
19942
|
-
),
|
|
19943
|
-
...props,
|
|
19944
|
-
...rootProps,
|
|
19945
|
-
ref
|
|
19946
|
-
},
|
|
19947
|
-
children
|
|
19948
|
-
));
|
|
19949
|
-
});
|
|
19950
|
-
function TurnIntoDropdownMenu(props) {
|
|
19951
|
-
const { headingLevels } = useToolbarContext();
|
|
19952
|
-
const items = [
|
|
19953
|
-
paragraphItem,
|
|
19954
|
-
...headingLevels.map((level) => headingItemsByLevel[level])
|
|
19955
|
-
];
|
|
19956
|
-
const value = useEditorSelector((editor2) => {
|
|
19957
|
-
let initialNodeType = ParagraphPlugin.key;
|
|
19958
|
-
let allNodesMatchInitialNodeType = false;
|
|
19959
|
-
const codeBlockEntries = editor2.api.nodes({
|
|
19960
|
-
match: (n) => editor2.api.isBlock(n),
|
|
19961
|
-
mode: "highest"
|
|
19962
|
-
});
|
|
19963
|
-
const nodes = Array.from(codeBlockEntries);
|
|
19964
|
-
if (nodes.length > 0) {
|
|
19965
|
-
initialNodeType = nodes[0][0].type;
|
|
19966
|
-
allNodesMatchInitialNodeType = nodes.every(([node]) => {
|
|
19967
|
-
const type2 = (node == null ? void 0 : node.type) || ParagraphPlugin.key;
|
|
19968
|
-
return type2 === initialNodeType;
|
|
19969
|
-
});
|
|
19970
|
-
}
|
|
19971
|
-
return allNodesMatchInitialNodeType ? initialNodeType : ParagraphPlugin.key;
|
|
19972
|
-
}, []);
|
|
19973
|
-
const editor = useEditorRef();
|
|
19974
|
-
const openState = useOpenState();
|
|
19975
|
-
const selectedItem = getHeadingItem(value) ?? paragraphItem;
|
|
19976
|
-
const { icon: SelectedItemIcon, label: selectedItemLabel } = selectedItem;
|
|
19977
|
-
const editorState = useEditorState();
|
|
19978
|
-
const userInTable = helpers.isNodeActive(editorState, TablePlugin.key);
|
|
19979
|
-
if (userInTable) return null;
|
|
19980
|
-
return /* @__PURE__ */ React__default.createElement(DropdownMenu$1, { modal: false, ...openState, ...props }, /* @__PURE__ */ React__default.createElement(DropdownMenuTrigger$1, { asChild: true }, /* @__PURE__ */ React__default.createElement(
|
|
19981
|
-
ToolbarButton,
|
|
19982
|
-
{
|
|
19983
|
-
className: "lg:min-w-[130px]",
|
|
19984
|
-
isDropdown: true,
|
|
19985
|
-
showArrow: true,
|
|
19986
|
-
pressed: openState.open,
|
|
19987
|
-
tooltip: "Turn into"
|
|
19988
|
-
},
|
|
19989
|
-
/* @__PURE__ */ React__default.createElement("span", { className: "" }, selectedItemLabel)
|
|
19990
|
-
)), /* @__PURE__ */ React__default.createElement(DropdownMenuContent$1, { align: "start", className: "min-w-0" }, /* @__PURE__ */ React__default.createElement(DropdownMenuLabel, null, "Turn into"), /* @__PURE__ */ React__default.createElement(
|
|
19991
|
-
DropdownMenuRadioGroup,
|
|
19992
|
-
{
|
|
19993
|
-
className: "flex flex-col gap-0.5",
|
|
19994
|
-
onValueChange: (type2) => {
|
|
19995
|
-
if (type2 === "ul" || type2 === "ol") {
|
|
19996
|
-
toggleList(editor, { type: type2 });
|
|
19997
|
-
} else {
|
|
19998
|
-
unwrapList(editor);
|
|
19999
|
-
editor.tf.toggleBlock(type2);
|
|
20000
|
-
}
|
|
20001
|
-
editor.tf.collapse();
|
|
20002
|
-
editor.tf.focus();
|
|
20003
|
-
},
|
|
20004
|
-
value
|
|
20005
|
-
},
|
|
20006
|
-
items.map(({ icon: Icon, label, value: itemValue }) => /* @__PURE__ */ React__default.createElement(
|
|
20007
|
-
DropdownMenuRadioItem,
|
|
20008
|
-
{
|
|
20009
|
-
className: "min-w-[180px]",
|
|
20010
|
-
key: itemValue,
|
|
20011
|
-
value: itemValue
|
|
20012
|
-
},
|
|
20013
|
-
/* @__PURE__ */ React__default.createElement(Icon, { className: "mr-2 size-5" }),
|
|
20014
|
-
label
|
|
20015
|
-
))
|
|
20016
|
-
)));
|
|
20017
|
-
}
|
|
20018
|
-
function FloatingToolbarButtons() {
|
|
20019
|
-
return /* @__PURE__ */ React__default.createElement("div", { className: "rounded-md" }, /* @__PURE__ */ React__default.createElement(TurnIntoDropdownMenu, null));
|
|
20020
|
-
}
|
|
20021
|
-
const FloatingToolbarPlugin = createPlatePlugin({
|
|
20022
|
-
key: "floating-toolbar",
|
|
20023
|
-
render: {
|
|
20024
|
-
afterEditable: () => /* @__PURE__ */ React__default.createElement(FloatingToolbar, null, /* @__PURE__ */ React__default.createElement(FloatingToolbarButtons, null))
|
|
20025
|
-
}
|
|
20026
|
-
});
|
|
20027
19984
|
function _1c(hljs) {
|
|
20028
19985
|
const UNDERSCORE_IDENT_RE = "[A-Za-zА-Яа-яёЁ_][A-Za-zА-Яа-яёЁ_0-9]+";
|
|
20029
19986
|
const v7_keywords = "далее ";
|
|
@@ -70669,6 +70626,252 @@ class HastEmitter {
|
|
|
70669
70626
|
return "";
|
|
70670
70627
|
}
|
|
70671
70628
|
}
|
|
70629
|
+
const popoverVariants = cva(
|
|
70630
|
+
"z-[999999] w-auto rounded-md border bg-popover p-1 text-popover-foreground shadow-md outline-hidden"
|
|
70631
|
+
);
|
|
70632
|
+
const inputVariants = cva(
|
|
70633
|
+
"flex h-[28px] w-full rounded-md border-none bg-transparent px-1.5 py-1 text-base placeholder:text-muted-foreground focus-visible:ring-transparent focus-visible:outline-none md:text-sm"
|
|
70634
|
+
);
|
|
70635
|
+
function LinkFloatingToolbar({
|
|
70636
|
+
state
|
|
70637
|
+
}) {
|
|
70638
|
+
const activeCommentId = usePluginOption({ key: "comment" }, "activeId");
|
|
70639
|
+
const activeSuggestionId = usePluginOption({ key: "suggestion" }, "activeId");
|
|
70640
|
+
const { api, editor } = useEditorPlugin(LinkPlugin);
|
|
70641
|
+
const isUrlValidator = usePluginOption(LinkPlugin, "isUrl");
|
|
70642
|
+
const [currentUrl, setCurrentUrl] = React.useState("");
|
|
70643
|
+
const [isValidUrl, setIsValidUrl] = React.useState(true);
|
|
70644
|
+
const handleUrlInput = React.useCallback(
|
|
70645
|
+
(e) => {
|
|
70646
|
+
const value = e.currentTarget.value;
|
|
70647
|
+
setCurrentUrl(value);
|
|
70648
|
+
if (value && isUrlValidator) {
|
|
70649
|
+
const valid = isUrlValidator(value);
|
|
70650
|
+
setIsValidUrl(valid);
|
|
70651
|
+
} else {
|
|
70652
|
+
setIsValidUrl(true);
|
|
70653
|
+
}
|
|
70654
|
+
},
|
|
70655
|
+
[isUrlValidator]
|
|
70656
|
+
);
|
|
70657
|
+
const floatingOptions = React.useMemo(() => {
|
|
70658
|
+
return {
|
|
70659
|
+
middleware: [
|
|
70660
|
+
offset(8),
|
|
70661
|
+
flip({
|
|
70662
|
+
fallbackPlacements: ["bottom-end", "top-start", "top-end"],
|
|
70663
|
+
padding: 12
|
|
70664
|
+
}),
|
|
70665
|
+
shift({ padding: 8, limiter: limitShift() })
|
|
70666
|
+
],
|
|
70667
|
+
placement: activeSuggestionId || activeCommentId ? "top-start" : "bottom-start"
|
|
70668
|
+
};
|
|
70669
|
+
}, [activeCommentId, activeSuggestionId]);
|
|
70670
|
+
const insertState = useFloatingLinkInsertState({
|
|
70671
|
+
...state,
|
|
70672
|
+
floatingOptions: {
|
|
70673
|
+
...floatingOptions,
|
|
70674
|
+
...state == null ? void 0 : state.floatingOptions
|
|
70675
|
+
}
|
|
70676
|
+
});
|
|
70677
|
+
const {
|
|
70678
|
+
hidden,
|
|
70679
|
+
props: insertProps,
|
|
70680
|
+
ref: insertRef,
|
|
70681
|
+
textInputProps
|
|
70682
|
+
} = useFloatingLinkInsert(insertState);
|
|
70683
|
+
const editState = useFloatingLinkEditState({
|
|
70684
|
+
...state,
|
|
70685
|
+
floatingOptions: {
|
|
70686
|
+
...floatingOptions,
|
|
70687
|
+
...state == null ? void 0 : state.floatingOptions
|
|
70688
|
+
}
|
|
70689
|
+
});
|
|
70690
|
+
const {
|
|
70691
|
+
editButtonProps,
|
|
70692
|
+
props: editProps,
|
|
70693
|
+
ref: editRef,
|
|
70694
|
+
unlinkButtonProps
|
|
70695
|
+
} = useFloatingLinkEdit(editState);
|
|
70696
|
+
const inputProps = useFormInputProps({
|
|
70697
|
+
preventDefaultOnEnterKeydown: true
|
|
70698
|
+
});
|
|
70699
|
+
if (hidden) return null;
|
|
70700
|
+
const input = /* @__PURE__ */ React.createElement(
|
|
70701
|
+
"div",
|
|
70702
|
+
{
|
|
70703
|
+
className: "z-[999999] flex w-[330px] flex-col relative",
|
|
70704
|
+
...inputProps
|
|
70705
|
+
},
|
|
70706
|
+
!isValidUrl && currentUrl && /* @__PURE__ */ React.createElement("div", { className: "absolute -top-16 left-0 right-0 z-[1000000] mb-2" }, /* @__PURE__ */ React.createElement(
|
|
70707
|
+
"div",
|
|
70708
|
+
{
|
|
70709
|
+
className: "bg-red-50 border border-red-200 rounded-md p-2 shadow-lg",
|
|
70710
|
+
role: "alert",
|
|
70711
|
+
"aria-live": "polite"
|
|
70712
|
+
},
|
|
70713
|
+
/* @__PURE__ */ React.createElement("div", { className: "flex items-center" }, /* @__PURE__ */ React.createElement(CircleX, { className: "size-4 text-red-500 mr-2 flex-shrink-0" }), /* @__PURE__ */ React.createElement("span", { className: "text-sm text-red-700 text-wrap" }, "Invalid URL. Please prefix link with https:// or use a relative path like /about")),
|
|
70714
|
+
/* @__PURE__ */ React.createElement("div", { className: "absolute -bottom-1 left-4 w-2 h-2 bg-red-50 border-r border-b border-red-200 transform rotate-45" })
|
|
70715
|
+
)),
|
|
70716
|
+
/* @__PURE__ */ React.createElement("div", { className: "flex items-center" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center pr-1 pl-2 text-muted-foreground" }, /* @__PURE__ */ React.createElement(Link, { className: "size-4" })), /* @__PURE__ */ React.createElement(
|
|
70717
|
+
FloatingLinkUrlInput,
|
|
70718
|
+
{
|
|
70719
|
+
className: inputVariants(),
|
|
70720
|
+
placeholder: "Paste link",
|
|
70721
|
+
"data-plate-focus": true,
|
|
70722
|
+
onInput: handleUrlInput
|
|
70723
|
+
}
|
|
70724
|
+
)),
|
|
70725
|
+
/* @__PURE__ */ React.createElement(Separator, { className: "my-1" }),
|
|
70726
|
+
/* @__PURE__ */ React.createElement("div", { className: "flex items-center" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center pr-1 pl-2 text-muted-foreground" }, /* @__PURE__ */ React.createElement(Text, { className: "size-4" })), /* @__PURE__ */ React.createElement(
|
|
70727
|
+
"input",
|
|
70728
|
+
{
|
|
70729
|
+
className: inputVariants(),
|
|
70730
|
+
placeholder: "Text to display",
|
|
70731
|
+
"data-plate-focus": true,
|
|
70732
|
+
...textInputProps
|
|
70733
|
+
}
|
|
70734
|
+
)),
|
|
70735
|
+
/* @__PURE__ */ React.createElement(Separator, { className: "my-1" }),
|
|
70736
|
+
/* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-end gap-2 px-2 py-1" }, /* @__PURE__ */ React.createElement(
|
|
70737
|
+
"button",
|
|
70738
|
+
{
|
|
70739
|
+
type: "button",
|
|
70740
|
+
className: buttonVariants$2({ size: "sm", variant: "ghost" }),
|
|
70741
|
+
onClick: () => {
|
|
70742
|
+
api.floatingLink.hide();
|
|
70743
|
+
}
|
|
70744
|
+
},
|
|
70745
|
+
"Cancel"
|
|
70746
|
+
), /* @__PURE__ */ React.createElement(
|
|
70747
|
+
"button",
|
|
70748
|
+
{
|
|
70749
|
+
type: "button",
|
|
70750
|
+
className: buttonVariants$2({
|
|
70751
|
+
size: "sm",
|
|
70752
|
+
variant: "tinaPrimary"
|
|
70753
|
+
}),
|
|
70754
|
+
onClick: () => {
|
|
70755
|
+
if (isValidUrl && currentUrl) {
|
|
70756
|
+
submitFloatingLink(editor);
|
|
70757
|
+
}
|
|
70758
|
+
},
|
|
70759
|
+
disabled: !isValidUrl && !!currentUrl
|
|
70760
|
+
},
|
|
70761
|
+
"OK"
|
|
70762
|
+
))
|
|
70763
|
+
);
|
|
70764
|
+
const editContent = editState.isEditing ? input : /* @__PURE__ */ React.createElement("div", { className: "box-content flex items-center" }, /* @__PURE__ */ React.createElement(
|
|
70765
|
+
"button",
|
|
70766
|
+
{
|
|
70767
|
+
className: buttonVariants$2({ size: "sm", variant: "ghost" }),
|
|
70768
|
+
type: "button",
|
|
70769
|
+
...editButtonProps
|
|
70770
|
+
},
|
|
70771
|
+
"Edit link"
|
|
70772
|
+
), /* @__PURE__ */ React.createElement(Separator, { orientation: "vertical" }), /* @__PURE__ */ React.createElement(LinkOpenButton, null), /* @__PURE__ */ React.createElement(Separator, { orientation: "vertical" }), /* @__PURE__ */ React.createElement(
|
|
70773
|
+
"button",
|
|
70774
|
+
{
|
|
70775
|
+
className: buttonVariants$2({
|
|
70776
|
+
size: "icon",
|
|
70777
|
+
variant: "ghost"
|
|
70778
|
+
}),
|
|
70779
|
+
type: "button",
|
|
70780
|
+
...unlinkButtonProps
|
|
70781
|
+
},
|
|
70782
|
+
/* @__PURE__ */ React.createElement(Unlink, { width: 18 })
|
|
70783
|
+
));
|
|
70784
|
+
return /* @__PURE__ */ React.createElement(PortalBody, null, /* @__PURE__ */ React.createElement(
|
|
70785
|
+
"div",
|
|
70786
|
+
{
|
|
70787
|
+
ref: insertRef,
|
|
70788
|
+
className: popoverVariants(),
|
|
70789
|
+
...insertProps,
|
|
70790
|
+
style: {
|
|
70791
|
+
...insertProps.style,
|
|
70792
|
+
zIndex: 999999
|
|
70793
|
+
}
|
|
70794
|
+
},
|
|
70795
|
+
input
|
|
70796
|
+
), /* @__PURE__ */ React.createElement(
|
|
70797
|
+
"div",
|
|
70798
|
+
{
|
|
70799
|
+
ref: editRef,
|
|
70800
|
+
className: popoverVariants(),
|
|
70801
|
+
...editProps,
|
|
70802
|
+
style: { ...editProps.style, zIndex: 999999 }
|
|
70803
|
+
},
|
|
70804
|
+
editContent
|
|
70805
|
+
));
|
|
70806
|
+
}
|
|
70807
|
+
function LinkOpenButton() {
|
|
70808
|
+
const editor = useEditorRef();
|
|
70809
|
+
const selection = useEditorSelection();
|
|
70810
|
+
const attributes = React.useMemo(
|
|
70811
|
+
() => {
|
|
70812
|
+
const entry = editor.api.node({
|
|
70813
|
+
match: { type: editor.getType(LinkPlugin) }
|
|
70814
|
+
});
|
|
70815
|
+
if (!entry) {
|
|
70816
|
+
return {};
|
|
70817
|
+
}
|
|
70818
|
+
const [element] = entry;
|
|
70819
|
+
return getLinkAttributes(editor, element);
|
|
70820
|
+
},
|
|
70821
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
70822
|
+
[editor, selection]
|
|
70823
|
+
);
|
|
70824
|
+
return /* @__PURE__ */ React.createElement(
|
|
70825
|
+
"a",
|
|
70826
|
+
{
|
|
70827
|
+
...attributes,
|
|
70828
|
+
className: buttonVariants$2({
|
|
70829
|
+
size: "icon",
|
|
70830
|
+
variant: "ghost"
|
|
70831
|
+
}),
|
|
70832
|
+
onMouseOver: (e) => {
|
|
70833
|
+
e.stopPropagation();
|
|
70834
|
+
},
|
|
70835
|
+
"aria-label": "Open link in a new tab",
|
|
70836
|
+
target: "_blank"
|
|
70837
|
+
},
|
|
70838
|
+
/* @__PURE__ */ React.createElement(ExternalLink, { width: 18 })
|
|
70839
|
+
);
|
|
70840
|
+
}
|
|
70841
|
+
const protocolAndDomainRE = /^(?:\w+:)?\/\/(\S+)$/;
|
|
70842
|
+
const emailLintRE = /mailto:([^?\\]+)/;
|
|
70843
|
+
const telLintRE = /tel:([\d-]+)/;
|
|
70844
|
+
const localhostDomainRE = /^localhost[\d:?]*(?:[^\d:?]\S*)?$/;
|
|
70845
|
+
const nonLocalhostDomainRE = /^[^\s.]+\.\S{2,}$/;
|
|
70846
|
+
const localUrlRE = /^\/\S+/;
|
|
70847
|
+
const isUrl = (string) => {
|
|
70848
|
+
if (typeof string !== "string") {
|
|
70849
|
+
return false;
|
|
70850
|
+
}
|
|
70851
|
+
if (string.startsWith("#")) {
|
|
70852
|
+
return true;
|
|
70853
|
+
}
|
|
70854
|
+
const generalMatch = string.match(protocolAndDomainRE);
|
|
70855
|
+
const emailLinkMatch = string.match(emailLintRE);
|
|
70856
|
+
const telLinkMatch = string.match(telLintRE);
|
|
70857
|
+
const localUrlMatch = string.match(localUrlRE);
|
|
70858
|
+
if (emailLinkMatch || telLinkMatch || localUrlMatch) {
|
|
70859
|
+
return true;
|
|
70860
|
+
}
|
|
70861
|
+
if (generalMatch) {
|
|
70862
|
+
const everythingAfterProtocol = generalMatch[1];
|
|
70863
|
+
if (!everythingAfterProtocol) {
|
|
70864
|
+
return false;
|
|
70865
|
+
}
|
|
70866
|
+
try {
|
|
70867
|
+
new URL(string);
|
|
70868
|
+
} catch {
|
|
70869
|
+
return false;
|
|
70870
|
+
}
|
|
70871
|
+
return localhostDomainRE.test(everythingAfterProtocol) || nonLocalhostDomainRE.test(everythingAfterProtocol);
|
|
70872
|
+
}
|
|
70873
|
+
return false;
|
|
70874
|
+
};
|
|
70672
70875
|
const createHTMLBlockPlugin = createPlatePlugin({
|
|
70673
70876
|
key: "html",
|
|
70674
70877
|
node: {
|
|
@@ -70685,18 +70888,26 @@ const createHTMLInlinePlugin = createPlatePlugin({
|
|
|
70685
70888
|
isInline: true
|
|
70686
70889
|
}
|
|
70687
70890
|
});
|
|
70688
|
-
const
|
|
70689
|
-
const
|
|
70690
|
-
key
|
|
70891
|
+
const KEY_HARD_BREAK = "tina-hard-break";
|
|
70892
|
+
const NO_HARD_BREAK = [
|
|
70893
|
+
CodeBlockPlugin.key,
|
|
70894
|
+
TableCellPlugin.key,
|
|
70895
|
+
TableCellHeaderPlugin.key
|
|
70896
|
+
];
|
|
70897
|
+
const createHardBreakPlugin = createPlatePlugin({
|
|
70898
|
+
key: KEY_HARD_BREAK,
|
|
70691
70899
|
handlers: {
|
|
70692
70900
|
onKeyDown: ({ editor, event }) => {
|
|
70693
70901
|
var _a;
|
|
70694
|
-
if (event.key !== "Enter") return;
|
|
70695
|
-
const
|
|
70902
|
+
if (event.key !== "Enter" || event.metaKey || event.ctrlKey) return;
|
|
70903
|
+
const inBlockquote = editor.api.above({
|
|
70696
70904
|
match: { type: BlockquotePlugin.key }
|
|
70697
70905
|
});
|
|
70698
|
-
if (!
|
|
70906
|
+
if (!event.shiftKey && !inBlockquote) return;
|
|
70907
|
+
if (editor.api.some({ match: { type: NO_HARD_BREAK } })) return;
|
|
70908
|
+
if (!editor.selection) return;
|
|
70699
70909
|
event.preventDefault();
|
|
70910
|
+
if (editor.api.isExpanded()) editor.tf.delete();
|
|
70700
70911
|
const cursorPosition = (_a = editor.selection) == null ? void 0 : _a.focus;
|
|
70701
70912
|
if (!cursorPosition) return;
|
|
70702
70913
|
editor.tf.insertNodes(
|
|
@@ -70724,6 +70935,303 @@ const createBreakPlugin = createPlatePlugin({
|
|
|
70724
70935
|
}
|
|
70725
70936
|
}
|
|
70726
70937
|
});
|
|
70938
|
+
const buildError = (element) => {
|
|
70939
|
+
return {
|
|
70940
|
+
message: element.message,
|
|
70941
|
+
position: element.position && {
|
|
70942
|
+
endColumn: element.position.end.column,
|
|
70943
|
+
startColumn: element.position.start.column,
|
|
70944
|
+
startLineNumber: element.position.start.line,
|
|
70945
|
+
endLineNumber: element.position.end.line
|
|
70946
|
+
}
|
|
70947
|
+
};
|
|
70948
|
+
};
|
|
70949
|
+
const buildErrorMessage = (element) => {
|
|
70950
|
+
if (!element) {
|
|
70951
|
+
return "";
|
|
70952
|
+
}
|
|
70953
|
+
const errorMessage = buildError(element);
|
|
70954
|
+
const message = errorMessage ? `${errorMessage.message}${errorMessage.position ? ` at line: ${errorMessage.position.startLineNumber}, column: ${errorMessage.position.startColumn}` : ""}` : null;
|
|
70955
|
+
return message;
|
|
70956
|
+
};
|
|
70957
|
+
const ELEMENT_INVALID_MARKDOWN = "invalid_markdown";
|
|
70958
|
+
const createInvalidMarkdownPlugin = createPlatePlugin({
|
|
70959
|
+
key: ELEMENT_INVALID_MARKDOWN,
|
|
70960
|
+
options: {
|
|
70961
|
+
isElement: true,
|
|
70962
|
+
isVoid: true,
|
|
70963
|
+
isInline: false
|
|
70964
|
+
},
|
|
70965
|
+
node: {
|
|
70966
|
+
component: InvalidMarkdownElement
|
|
70967
|
+
}
|
|
70968
|
+
});
|
|
70969
|
+
function InvalidMarkdownElement({
|
|
70970
|
+
attributes,
|
|
70971
|
+
element,
|
|
70972
|
+
children
|
|
70973
|
+
}) {
|
|
70974
|
+
return /* @__PURE__ */ React__default.createElement("div", { ...attributes }, /* @__PURE__ */ React__default.createElement(ErrorMessage, { error: element }), children);
|
|
70975
|
+
}
|
|
70976
|
+
function ErrorMessage({ error }) {
|
|
70977
|
+
const message = buildErrorMessage(error);
|
|
70978
|
+
const { setRawMode } = useEditorContext();
|
|
70979
|
+
return /* @__PURE__ */ React__default.createElement("div", { contentEditable: false, className: "bg-red-50 sm:rounded-lg" }, /* @__PURE__ */ React__default.createElement("div", { className: "px-4 py-5 sm:p-6" }, /* @__PURE__ */ React__default.createElement("h3", { className: "text-lg leading-6 font-medium text-red-800" }, "❌ Error parsing markdown"), /* @__PURE__ */ React__default.createElement("div", { className: "mt-2 max-w-xl text-sm text-red-800 space-y-4" }, /* @__PURE__ */ React__default.createElement("p", null, message), /* @__PURE__ */ React__default.createElement("p", null, "To fix these errors, edit the content in raw-mode."), /* @__PURE__ */ React__default.createElement(
|
|
70980
|
+
"button",
|
|
70981
|
+
{
|
|
70982
|
+
type: "button",
|
|
70983
|
+
onClick: () => setRawMode(true),
|
|
70984
|
+
className: "rounded-l border-r-0 shadow rounded bg-white cursor-pointer relative inline-flex items-center px-2 py-2 border border-gray-200 hover:text-white text-sm font-medium transition-all ease-out duration-150 hover:bg-gray-500 focus:z-10 focus:outline-none focus:ring-1 focus:ring-gray-500 focus:border-gray-500"
|
|
70985
|
+
},
|
|
70986
|
+
"Switch to raw-mode"
|
|
70987
|
+
))));
|
|
70988
|
+
}
|
|
70989
|
+
const useCustomFloatingToolbarState = ({
|
|
70990
|
+
editorId,
|
|
70991
|
+
floatingOptions,
|
|
70992
|
+
focusedEditorId,
|
|
70993
|
+
hideToolbar,
|
|
70994
|
+
showWhenReadOnly
|
|
70995
|
+
}) => {
|
|
70996
|
+
const editor = useEditorRef();
|
|
70997
|
+
const selectionExpanded = useEditorSelector(
|
|
70998
|
+
() => editor.api.isExpanded(),
|
|
70999
|
+
[]
|
|
71000
|
+
);
|
|
71001
|
+
const selectionText = useEditorSelector(() => editor.api.string(), []);
|
|
71002
|
+
const readOnly = useEditorReadOnly();
|
|
71003
|
+
const focused = useFocused();
|
|
71004
|
+
const [open, setOpen] = React__default.useState(false);
|
|
71005
|
+
const [waitForCollapsedSelection, setWaitForCollapsedSelection] = React__default.useState(false);
|
|
71006
|
+
const [mousedown, setMousedown] = React__default.useState(false);
|
|
71007
|
+
const floating = useVirtualFloating(
|
|
71008
|
+
mergeProps(
|
|
71009
|
+
{
|
|
71010
|
+
open,
|
|
71011
|
+
getBoundingClientRect: () => getSelectionBoundingClientRect(editor),
|
|
71012
|
+
onOpenChange: setOpen
|
|
71013
|
+
},
|
|
71014
|
+
floatingOptions
|
|
71015
|
+
)
|
|
71016
|
+
);
|
|
71017
|
+
return {
|
|
71018
|
+
editorId,
|
|
71019
|
+
floating,
|
|
71020
|
+
focused,
|
|
71021
|
+
focusedEditorId,
|
|
71022
|
+
hideToolbar,
|
|
71023
|
+
mousedown,
|
|
71024
|
+
open,
|
|
71025
|
+
readOnly,
|
|
71026
|
+
selectionExpanded,
|
|
71027
|
+
selectionText,
|
|
71028
|
+
setMousedown,
|
|
71029
|
+
setOpen,
|
|
71030
|
+
setWaitForCollapsedSelection,
|
|
71031
|
+
showWhenReadOnly,
|
|
71032
|
+
waitForCollapsedSelection
|
|
71033
|
+
};
|
|
71034
|
+
};
|
|
71035
|
+
const useCustomFloatingToolbar = ({
|
|
71036
|
+
editorId,
|
|
71037
|
+
floating,
|
|
71038
|
+
focusedEditorId,
|
|
71039
|
+
hideToolbar,
|
|
71040
|
+
mousedown,
|
|
71041
|
+
open,
|
|
71042
|
+
readOnly,
|
|
71043
|
+
selectionExpanded,
|
|
71044
|
+
selectionText,
|
|
71045
|
+
setMousedown,
|
|
71046
|
+
setOpen,
|
|
71047
|
+
setWaitForCollapsedSelection,
|
|
71048
|
+
showWhenReadOnly,
|
|
71049
|
+
waitForCollapsedSelection
|
|
71050
|
+
}) => {
|
|
71051
|
+
React__default.useEffect(() => {
|
|
71052
|
+
if (!(editorId === focusedEditorId)) {
|
|
71053
|
+
setWaitForCollapsedSelection(true);
|
|
71054
|
+
}
|
|
71055
|
+
if (!selectionExpanded) {
|
|
71056
|
+
setWaitForCollapsedSelection(false);
|
|
71057
|
+
}
|
|
71058
|
+
}, [
|
|
71059
|
+
editorId,
|
|
71060
|
+
focusedEditorId,
|
|
71061
|
+
selectionExpanded,
|
|
71062
|
+
setWaitForCollapsedSelection
|
|
71063
|
+
]);
|
|
71064
|
+
React__default.useEffect(() => {
|
|
71065
|
+
const mouseup = () => setMousedown(false);
|
|
71066
|
+
const mousedown2 = () => setMousedown(true);
|
|
71067
|
+
document.addEventListener("mouseup", mouseup);
|
|
71068
|
+
document.addEventListener("mousedown", mousedown2);
|
|
71069
|
+
return () => {
|
|
71070
|
+
document.removeEventListener("mouseup", mouseup);
|
|
71071
|
+
document.removeEventListener("mousedown", mousedown2);
|
|
71072
|
+
};
|
|
71073
|
+
}, []);
|
|
71074
|
+
React__default.useEffect(() => {
|
|
71075
|
+
if (!selectionExpanded || !selectionText || mousedown && !open || hideToolbar || readOnly && !showWhenReadOnly) {
|
|
71076
|
+
setOpen(false);
|
|
71077
|
+
} else if (selectionText && selectionExpanded && (!waitForCollapsedSelection || readOnly)) {
|
|
71078
|
+
setOpen(true);
|
|
71079
|
+
}
|
|
71080
|
+
}, [
|
|
71081
|
+
setOpen,
|
|
71082
|
+
editorId,
|
|
71083
|
+
focusedEditorId,
|
|
71084
|
+
hideToolbar,
|
|
71085
|
+
showWhenReadOnly,
|
|
71086
|
+
selectionExpanded,
|
|
71087
|
+
selectionText,
|
|
71088
|
+
mousedown,
|
|
71089
|
+
waitForCollapsedSelection,
|
|
71090
|
+
open,
|
|
71091
|
+
readOnly
|
|
71092
|
+
]);
|
|
71093
|
+
const { update } = floating;
|
|
71094
|
+
useEditorSelector(() => {
|
|
71095
|
+
update == null ? void 0 : update();
|
|
71096
|
+
}, [update]);
|
|
71097
|
+
const clickOutsideRef = useOnClickOutside(
|
|
71098
|
+
() => {
|
|
71099
|
+
setOpen(false);
|
|
71100
|
+
},
|
|
71101
|
+
{
|
|
71102
|
+
ignoreClass: "ignore-click-outside/toolbar"
|
|
71103
|
+
}
|
|
71104
|
+
);
|
|
71105
|
+
return {
|
|
71106
|
+
clickOutsideRef,
|
|
71107
|
+
hidden: !open,
|
|
71108
|
+
props: {
|
|
71109
|
+
style: floating.style
|
|
71110
|
+
},
|
|
71111
|
+
ref: floating.refs.setFloating
|
|
71112
|
+
};
|
|
71113
|
+
};
|
|
71114
|
+
const FloatingToolbar = withRef(({ children, state, ...props }, propRef) => {
|
|
71115
|
+
const editorId = useEditorRef();
|
|
71116
|
+
const focusedEditorId = useEventEditorValue("focus");
|
|
71117
|
+
const test = useCustomFloatingToolbarState({
|
|
71118
|
+
editorId: editorId.id,
|
|
71119
|
+
focusedEditorId,
|
|
71120
|
+
...state,
|
|
71121
|
+
floatingOptions: {
|
|
71122
|
+
middleware: [
|
|
71123
|
+
offset(12),
|
|
71124
|
+
flip({
|
|
71125
|
+
fallbackPlacements: [
|
|
71126
|
+
"top-start",
|
|
71127
|
+
"top-end",
|
|
71128
|
+
"bottom-start",
|
|
71129
|
+
"bottom-end"
|
|
71130
|
+
],
|
|
71131
|
+
padding: 12
|
|
71132
|
+
})
|
|
71133
|
+
],
|
|
71134
|
+
placement: "top",
|
|
71135
|
+
...state == null ? void 0 : state.floatingOptions
|
|
71136
|
+
}
|
|
71137
|
+
});
|
|
71138
|
+
const {
|
|
71139
|
+
hidden,
|
|
71140
|
+
props: rootProps,
|
|
71141
|
+
ref: floatingRef
|
|
71142
|
+
} = useCustomFloatingToolbar(test);
|
|
71143
|
+
const ref = useComposedRef$1(propRef, floatingRef);
|
|
71144
|
+
if (hidden) return null;
|
|
71145
|
+
return /* @__PURE__ */ React__default.createElement(PortalBody, null, /* @__PURE__ */ React__default.createElement(
|
|
71146
|
+
Toolbar,
|
|
71147
|
+
{
|
|
71148
|
+
className: cn$1(
|
|
71149
|
+
"absolute z-[10799] whitespace-nowrap border bg-popover px-1 opacity-100 shadow-md print:hidden rounded-md"
|
|
71150
|
+
),
|
|
71151
|
+
...props,
|
|
71152
|
+
...rootProps,
|
|
71153
|
+
ref
|
|
71154
|
+
},
|
|
71155
|
+
children
|
|
71156
|
+
));
|
|
71157
|
+
});
|
|
71158
|
+
function TurnIntoDropdownMenu(props) {
|
|
71159
|
+
const { headingLevels } = useToolbarContext();
|
|
71160
|
+
const items = [
|
|
71161
|
+
paragraphItem,
|
|
71162
|
+
...headingLevels.map((level) => headingItemsByLevel[level])
|
|
71163
|
+
];
|
|
71164
|
+
const value = useEditorSelector((editor2) => {
|
|
71165
|
+
let initialNodeType = ParagraphPlugin.key;
|
|
71166
|
+
let allNodesMatchInitialNodeType = false;
|
|
71167
|
+
const codeBlockEntries = editor2.api.nodes({
|
|
71168
|
+
match: (n) => editor2.api.isBlock(n),
|
|
71169
|
+
mode: "highest"
|
|
71170
|
+
});
|
|
71171
|
+
const nodes = Array.from(codeBlockEntries);
|
|
71172
|
+
if (nodes.length > 0) {
|
|
71173
|
+
initialNodeType = nodes[0][0].type;
|
|
71174
|
+
allNodesMatchInitialNodeType = nodes.every(([node]) => {
|
|
71175
|
+
const type2 = (node == null ? void 0 : node.type) || ParagraphPlugin.key;
|
|
71176
|
+
return type2 === initialNodeType;
|
|
71177
|
+
});
|
|
71178
|
+
}
|
|
71179
|
+
return allNodesMatchInitialNodeType ? initialNodeType : ParagraphPlugin.key;
|
|
71180
|
+
}, []);
|
|
71181
|
+
const editor = useEditorRef();
|
|
71182
|
+
const openState = useOpenState();
|
|
71183
|
+
const selectedItem = getHeadingItem(value) ?? paragraphItem;
|
|
71184
|
+
const { icon: SelectedItemIcon, label: selectedItemLabel } = selectedItem;
|
|
71185
|
+
const editorState = useEditorState();
|
|
71186
|
+
const userInTable = helpers.isNodeActive(editorState, TablePlugin.key);
|
|
71187
|
+
if (userInTable) return null;
|
|
71188
|
+
return /* @__PURE__ */ React__default.createElement(DropdownMenu$1, { modal: false, ...openState, ...props }, /* @__PURE__ */ React__default.createElement(DropdownMenuTrigger$1, { asChild: true }, /* @__PURE__ */ React__default.createElement(
|
|
71189
|
+
ToolbarButton,
|
|
71190
|
+
{
|
|
71191
|
+
className: "lg:min-w-[130px]",
|
|
71192
|
+
isDropdown: true,
|
|
71193
|
+
showArrow: true,
|
|
71194
|
+
pressed: openState.open,
|
|
71195
|
+
tooltip: "Turn into"
|
|
71196
|
+
},
|
|
71197
|
+
/* @__PURE__ */ React__default.createElement("span", { className: "" }, selectedItemLabel)
|
|
71198
|
+
)), /* @__PURE__ */ React__default.createElement(DropdownMenuContent$1, { align: "start", className: "min-w-0" }, /* @__PURE__ */ React__default.createElement(DropdownMenuLabel$1, null, "Turn into"), /* @__PURE__ */ React__default.createElement(
|
|
71199
|
+
DropdownMenuRadioGroup$1,
|
|
71200
|
+
{
|
|
71201
|
+
className: "flex flex-col gap-0.5",
|
|
71202
|
+
onValueChange: (type2) => {
|
|
71203
|
+
if (type2 === "ul" || type2 === "ol") {
|
|
71204
|
+
toggleList(editor, { type: type2 });
|
|
71205
|
+
} else {
|
|
71206
|
+
unwrapList(editor);
|
|
71207
|
+
editor.tf.toggleBlock(type2);
|
|
71208
|
+
}
|
|
71209
|
+
editor.tf.collapse();
|
|
71210
|
+
editor.tf.focus();
|
|
71211
|
+
},
|
|
71212
|
+
value
|
|
71213
|
+
},
|
|
71214
|
+
items.map(({ icon: Icon, label, value: itemValue }) => /* @__PURE__ */ React__default.createElement(
|
|
71215
|
+
DropdownMenuRadioItem$1,
|
|
71216
|
+
{
|
|
71217
|
+
className: "min-w-[180px]",
|
|
71218
|
+
key: itemValue,
|
|
71219
|
+
value: itemValue
|
|
71220
|
+
},
|
|
71221
|
+
/* @__PURE__ */ React__default.createElement(Icon, { className: "mr-2 size-5" }),
|
|
71222
|
+
label
|
|
71223
|
+
))
|
|
71224
|
+
)));
|
|
71225
|
+
}
|
|
71226
|
+
function FloatingToolbarButtons() {
|
|
71227
|
+
return /* @__PURE__ */ React__default.createElement("div", { className: "rounded-md" }, /* @__PURE__ */ React__default.createElement(TurnIntoDropdownMenu, null));
|
|
71228
|
+
}
|
|
71229
|
+
const FloatingToolbarPlugin = createPlatePlugin({
|
|
71230
|
+
key: "floating-toolbar",
|
|
71231
|
+
render: {
|
|
71232
|
+
afterEditable: () => /* @__PURE__ */ React__default.createElement(FloatingToolbar, null, /* @__PURE__ */ React__default.createElement(FloatingToolbarButtons, null))
|
|
71233
|
+
}
|
|
71234
|
+
});
|
|
70727
71235
|
[
|
|
70728
71236
|
HEADING_KEYS.h1,
|
|
70729
71237
|
HEADING_KEYS.h2,
|
|
@@ -70788,7 +71296,7 @@ const createEditorPlugins = ({
|
|
|
70788
71296
|
createImgPlugin,
|
|
70789
71297
|
createHTMLBlockPlugin,
|
|
70790
71298
|
createHTMLInlinePlugin,
|
|
70791
|
-
|
|
71299
|
+
createHardBreakPlugin,
|
|
70792
71300
|
createInvalidMarkdownPlugin,
|
|
70793
71301
|
CorrectNodeBehaviorPlugin,
|
|
70794
71302
|
ClearHighlightOnEnterPlugin,
|
|
@@ -70887,6 +71395,8 @@ const createEditorPlugins = ({
|
|
|
70887
71395
|
]
|
|
70888
71396
|
}
|
|
70889
71397
|
}),
|
|
71398
|
+
// Only reached where `createHardBreakPlugin` bails (code blocks, table
|
|
71399
|
+
// cells) — everywhere else it has already inserted a `break` element.
|
|
70890
71400
|
SoftBreakPlugin.configure({
|
|
70891
71401
|
options: {
|
|
70892
71402
|
rules: [
|
|
@@ -71067,6 +71577,7 @@ const MediaWorkflowOverlay = () => {
|
|
|
71067
71577
|
phase: "confirming",
|
|
71068
71578
|
branchName: event.branchName,
|
|
71069
71579
|
baseBranch: event.baseBranch,
|
|
71580
|
+
allowSaveToProtectedBranch: event.allowSaveToProtectedBranch,
|
|
71070
71581
|
onConfirm: event.onConfirm,
|
|
71071
71582
|
onCancel: event.onCancel,
|
|
71072
71583
|
onSaveToProtectedBranch: event.onSaveToProtectedBranch
|
|
@@ -71201,6 +71712,7 @@ const MediaWorkflowOverlay = () => {
|
|
|
71201
71712
|
);
|
|
71202
71713
|
},
|
|
71203
71714
|
onCreateBranch: handleCreateBranch,
|
|
71715
|
+
allowSaveToProtectedBranch: state.allowSaveToProtectedBranch,
|
|
71204
71716
|
onSaveToProtectedBranch: () => {
|
|
71205
71717
|
abortPreflight();
|
|
71206
71718
|
state.onSaveToProtectedBranch();
|
|
@@ -71390,6 +71902,14 @@ const DefaultSessionProvider = ({
|
|
|
71390
71902
|
children
|
|
71391
71903
|
}) => /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, children);
|
|
71392
71904
|
class AbstractAuthProvider {
|
|
71905
|
+
constructor() {
|
|
71906
|
+
/**
|
|
71907
|
+
* Turns a tokened 401 into the auth wall re-arming. The Client assigns this
|
|
71908
|
+
* automatically; AbstractAuthProvider.fetchWithToken invokes it. A provider
|
|
71909
|
+
* that implements its own fetchWithToken must invoke it itself.
|
|
71910
|
+
*/
|
|
71911
|
+
__publicField(this, "sessionExpiredListener");
|
|
71912
|
+
}
|
|
71393
71913
|
/**
|
|
71394
71914
|
* Wraps the normal fetch function with same API but adds the authorization header token.
|
|
71395
71915
|
*
|
|
@@ -71400,15 +71920,20 @@ class AbstractAuthProvider {
|
|
|
71400
71920
|
* @param init fetch function init
|
|
71401
71921
|
*/
|
|
71402
71922
|
async fetchWithToken(input, init) {
|
|
71923
|
+
var _a;
|
|
71403
71924
|
const headers = (init == null ? void 0 : init.headers) || {};
|
|
71404
71925
|
const accessToken = await this.getAccessToken();
|
|
71405
71926
|
if (accessToken) {
|
|
71406
71927
|
headers["Authorization"] = "Bearer " + accessToken;
|
|
71407
71928
|
}
|
|
71408
|
-
|
|
71929
|
+
const res = await fetch(input, {
|
|
71409
71930
|
...init || {},
|
|
71410
71931
|
headers: new Headers(headers)
|
|
71411
71932
|
});
|
|
71933
|
+
if (res.status === 401 && accessToken) {
|
|
71934
|
+
(_a = this.sessionExpiredListener) == null ? void 0 : _a.call(this);
|
|
71935
|
+
}
|
|
71936
|
+
return res;
|
|
71412
71937
|
}
|
|
71413
71938
|
async getAccessToken() {
|
|
71414
71939
|
const token = await this.getToken();
|
|
@@ -71529,9 +72054,14 @@ class TinaCloudAuthProvider extends AbstractAuthProvider {
|
|
|
71529
72054
|
}
|
|
71530
72055
|
return null;
|
|
71531
72056
|
}
|
|
71532
|
-
|
|
71533
|
-
|
|
71534
|
-
|
|
72057
|
+
let res;
|
|
72058
|
+
try {
|
|
72059
|
+
res = await this.fetchWithToken(url, { method: "GET" });
|
|
72060
|
+
} catch (networkError) {
|
|
72061
|
+
console.warn("TinaCMS: session check failed, retrying.", networkError);
|
|
72062
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
72063
|
+
res = await this.fetchWithToken(url, { method: "GET" });
|
|
72064
|
+
}
|
|
71535
72065
|
const val = await res.json();
|
|
71536
72066
|
if (!res.status.toString().startsWith("2")) {
|
|
71537
72067
|
console.error(
|
|
@@ -71542,6 +72072,9 @@ class TinaCloudAuthProvider extends AbstractAuthProvider {
|
|
|
71542
72072
|
}
|
|
71543
72073
|
return val;
|
|
71544
72074
|
} catch (e) {
|
|
72075
|
+
if (e instanceof TypeError || isErrorNamed(e, "AbortError")) {
|
|
72076
|
+
throw e;
|
|
72077
|
+
}
|
|
71545
72078
|
console.error(e);
|
|
71546
72079
|
return null;
|
|
71547
72080
|
}
|
|
@@ -71658,6 +72191,8 @@ class Client {
|
|
|
71658
72191
|
// automatically hooked into global event bus when attached via cms.
|
|
71659
72192
|
__publicField(this, "protectedBranches", []);
|
|
71660
72193
|
__publicField(this, "usingEditorialWorkflow", false);
|
|
72194
|
+
// Whatever the auth provider returned from getUser().
|
|
72195
|
+
__publicField(this, "user");
|
|
71661
72196
|
__publicField(this, "addPendingContent", async (props) => {
|
|
71662
72197
|
const mutation = `#graphql
|
|
71663
72198
|
mutation addPendingDocumentMutation(
|
|
@@ -71757,13 +72292,24 @@ mutation addPendingDocumentMutation(
|
|
|
71757
72292
|
}
|
|
71758
72293
|
);
|
|
71759
72294
|
this.clientId = options.clientId;
|
|
71760
|
-
this.authProvider =
|
|
71761
|
-
|
|
71762
|
-
|
|
71763
|
-
|
|
71764
|
-
|
|
71765
|
-
|
|
71766
|
-
|
|
72295
|
+
this.authProvider = this.adoptAuthProvider(
|
|
72296
|
+
((_u = (_t = (_s = this.schema) == null ? void 0 : _s.config) == null ? void 0 : _t.config) == null ? void 0 : _u.authProvider) || new TinaCloudAuthProvider({
|
|
72297
|
+
clientId: options.clientId,
|
|
72298
|
+
identityApiUrl: this.identityApiUrl,
|
|
72299
|
+
getTokenFn: options.getTokenFn,
|
|
72300
|
+
tokenStorage,
|
|
72301
|
+
frontendUrl: this.frontendUrl
|
|
72302
|
+
})
|
|
72303
|
+
);
|
|
72304
|
+
}
|
|
72305
|
+
/**
|
|
72306
|
+
* Every provider swap must come through here: the listener is what turns a
|
|
72307
|
+
* REST 401 into the auth wall re-arming, and a bare field assignment would
|
|
72308
|
+
* silently drop it.
|
|
72309
|
+
*/
|
|
72310
|
+
adoptAuthProvider(provider) {
|
|
72311
|
+
provider.sessionExpiredListener = () => dispatchSessionExpired(this.events);
|
|
72312
|
+
return provider;
|
|
71767
72313
|
}
|
|
71768
72314
|
get isLocalMode() {
|
|
71769
72315
|
return false;
|
|
@@ -71808,6 +72354,17 @@ mutation addPendingDocumentMutation(
|
|
|
71808
72354
|
variables
|
|
71809
72355
|
})
|
|
71810
72356
|
});
|
|
72357
|
+
if (res.status === 401) {
|
|
72358
|
+
res.json().catch(() => {
|
|
72359
|
+
});
|
|
72360
|
+
if (this.isCustomContentApi) {
|
|
72361
|
+
console.error(
|
|
72362
|
+
`TinaCMS: the content API at ${this.contentApiUrl} returned 401. If signing in again does not resolve this, check the backend's auth configuration (clientId: ${this.options.clientId}, branch: ${this.branch}).`
|
|
72363
|
+
);
|
|
72364
|
+
}
|
|
72365
|
+
dispatchSessionExpired(this.events);
|
|
72366
|
+
throw new SessionExpiredError();
|
|
72367
|
+
}
|
|
71811
72368
|
if (res.status !== 200) {
|
|
71812
72369
|
let errorMessage = `Unable to complete request, ${res.statusText}`;
|
|
71813
72370
|
const resBody = await res.json();
|
|
@@ -72229,6 +72786,10 @@ mutation addPendingDocumentMutation(
|
|
|
72229
72786
|
throw error;
|
|
72230
72787
|
}
|
|
72231
72788
|
}
|
|
72789
|
+
/**
|
|
72790
|
+
* The server normalises `branchName`, so follow-up calls must use the
|
|
72791
|
+
* `branchName` from the response rather than the one they sent.
|
|
72792
|
+
*/
|
|
72232
72793
|
async startMediaEditorialWorkflow(options) {
|
|
72233
72794
|
const url = `${this.contentApiBase}/editorial-workflow/${this.clientId}/media`;
|
|
72234
72795
|
return await this.postEditorialWorkflow(
|
|
@@ -72253,7 +72814,9 @@ class LocalClient extends Client {
|
|
|
72253
72814
|
customContentApiUrl: props && props.customContentApiUrl ? props.customContentApiUrl : DEFAULT_LOCAL_TINA_GQL_SERVER_URL
|
|
72254
72815
|
};
|
|
72255
72816
|
super(clientProps);
|
|
72256
|
-
this.authProvider =
|
|
72817
|
+
this.authProvider = this.adoptAuthProvider(
|
|
72818
|
+
((_c = (_b = (_a = this.schema) == null ? void 0 : _a.config) == null ? void 0 : _b.config) == null ? void 0 : _c.authProvider) || new LocalAuthProvider()
|
|
72819
|
+
);
|
|
72257
72820
|
}
|
|
72258
72821
|
get isLocalMode() {
|
|
72259
72822
|
return true;
|
|
@@ -72544,6 +73107,7 @@ const AuthWallInner = ({
|
|
|
72544
73107
|
if (!mounted) return;
|
|
72545
73108
|
if (isAuthorized) {
|
|
72546
73109
|
const user = await client.authProvider.getUser();
|
|
73110
|
+
client.user = user;
|
|
72547
73111
|
if (user.passwordChangeRequired) {
|
|
72548
73112
|
window.location.hash = "#/screens/change_password";
|
|
72549
73113
|
}
|
|
@@ -72599,7 +73163,7 @@ const AuthWallInner = ({
|
|
|
72599
73163
|
}
|
|
72600
73164
|
return onAuthenticated();
|
|
72601
73165
|
} catch (e) {
|
|
72602
|
-
if (e instanceof AuthenticationCancelledError || (e
|
|
73166
|
+
if (e instanceof AuthenticationCancelledError || isErrorNamed(e, "AuthenticationCancelledError")) {
|
|
72603
73167
|
return;
|
|
72604
73168
|
}
|
|
72605
73169
|
console.error(e);
|
|
@@ -73948,6 +74512,7 @@ const CollectionListPage = () => {
|
|
|
73948
74512
|
);
|
|
73949
74513
|
return (_b2 = (_a2 = doc == null ? void 0 : doc.document) == null ? void 0 : _a2._sys) == null ? void 0 : _b2.hasReferences;
|
|
73950
74514
|
} catch (error) {
|
|
74515
|
+
if (isSessionExpiredError(error)) throw error;
|
|
73951
74516
|
cms.alerts.error(
|
|
73952
74517
|
"Document was not found, ask a developer for help or check the console for an error message"
|
|
73953
74518
|
);
|
|
@@ -73963,6 +74528,7 @@ const CollectionListPage = () => {
|
|
|
73963
74528
|
);
|
|
73964
74529
|
reFetchCollection();
|
|
73965
74530
|
} catch (error) {
|
|
74531
|
+
if (isSessionExpiredError(error)) throw error;
|
|
73966
74532
|
if (error.message.includes(ERR_HAS_REFERENCES)) {
|
|
73967
74533
|
cms.alerts.error(
|
|
73968
74534
|
error.message.split("\n ").filter(Boolean)[1]
|
|
@@ -73992,6 +74558,7 @@ const CollectionListPage = () => {
|
|
|
73992
74558
|
);
|
|
73993
74559
|
reFetchCollection();
|
|
73994
74560
|
} catch (error) {
|
|
74561
|
+
if (isSessionExpiredError(error)) throw error;
|
|
73995
74562
|
cms.alerts.warn(
|
|
73996
74563
|
"Document was not deleted, ask a developer for help or check the console for an error message"
|
|
73997
74564
|
);
|
|
@@ -74024,6 +74591,7 @@ const CollectionListPage = () => {
|
|
|
74024
74591
|
);
|
|
74025
74592
|
reFetchCollection();
|
|
74026
74593
|
} catch (error) {
|
|
74594
|
+
if (isSessionExpiredError(error)) throw error;
|
|
74027
74595
|
if (error.message && error.message.includes(ERR_ALREADY_EXISTS)) {
|
|
74028
74596
|
cms.alerts.error(
|
|
74029
74597
|
`Document was not renamed. The filename "${vars.newRelativePath}" is already used by another document, please choose a different name.`
|
|
@@ -74070,6 +74638,7 @@ const CollectionListPage = () => {
|
|
|
74070
74638
|
);
|
|
74071
74639
|
cms.alerts.info("Folder was successfully created");
|
|
74072
74640
|
} catch (error) {
|
|
74641
|
+
if (isSessionExpiredError(error)) throw error;
|
|
74073
74642
|
if (error.message && error.message.includes(ERR_ALREADY_EXISTS)) {
|
|
74074
74643
|
cms.alerts.error(
|
|
74075
74644
|
`Folder was not created, folder with name "${vars.folderName}" already exists ${folder.name ? `in ${folder.name}` : ""}`
|
|
@@ -74633,6 +75202,7 @@ const DeleteModal = ({
|
|
|
74633
75202
|
useEffect(() => {
|
|
74634
75203
|
checkRefsFunc().then((result) => {
|
|
74635
75204
|
setHasRefs(result);
|
|
75205
|
+
}).catch(() => {
|
|
74636
75206
|
});
|
|
74637
75207
|
}, [filename, checkRefsFunc]);
|
|
74638
75208
|
return /* @__PURE__ */ React__default.createElement(Modal, null, /* @__PURE__ */ React__default.createElement(PopupModal, null, /* @__PURE__ */ React__default.createElement(ModalHeader, { close: close2 }, "Delete ", filename), /* @__PURE__ */ React__default.createElement(ModalBody, { padded: true }, /* @__PURE__ */ React__default.createElement("p", null, `Are you sure you want to delete ${filename}?${hasRefs ? " References to this document will also be deleted." : ""}`)), /* @__PURE__ */ React__default.createElement(ModalActions, null, /* @__PURE__ */ React__default.createElement(Button$2, { style: { flexGrow: 2 }, onClick: close2 }, "Cancel"), /* @__PURE__ */ React__default.createElement(
|
|
@@ -74641,7 +75211,10 @@ const DeleteModal = ({
|
|
|
74641
75211
|
style: { flexGrow: 3 },
|
|
74642
75212
|
variant: "danger",
|
|
74643
75213
|
onClick: async () => {
|
|
74644
|
-
|
|
75214
|
+
try {
|
|
75215
|
+
await deleteFunc();
|
|
75216
|
+
} catch {
|
|
75217
|
+
}
|
|
74645
75218
|
close2();
|
|
74646
75219
|
}
|
|
74647
75220
|
},
|
|
@@ -74849,16 +75422,18 @@ const useGetCollection = (cms, collectionName, includeDocuments = true, folder,
|
|
|
74849
75422
|
);
|
|
74850
75423
|
setCollection(collection2);
|
|
74851
75424
|
} else if (!cancelled) {
|
|
74852
|
-
cms.events
|
|
75425
|
+
dispatchSessionExpired(cms.events);
|
|
74853
75426
|
setCollection(void 0);
|
|
74854
75427
|
}
|
|
74855
75428
|
} catch (error2) {
|
|
74856
|
-
cms.alerts.error(
|
|
74857
|
-
`[${error2.name}] GetCollection failed: ${error2.message}`
|
|
74858
|
-
);
|
|
74859
|
-
console.error(error2);
|
|
74860
75429
|
setCollection(void 0);
|
|
74861
|
-
|
|
75430
|
+
if (!isSessionExpiredError(error2)) {
|
|
75431
|
+
cms.alerts.error(
|
|
75432
|
+
`[${error2.name}] GetCollection failed: ${error2.message}`
|
|
75433
|
+
);
|
|
75434
|
+
console.error(error2);
|
|
75435
|
+
setError(error2);
|
|
75436
|
+
}
|
|
74862
75437
|
}
|
|
74863
75438
|
if (!cancelled) {
|
|
74864
75439
|
setLoading(false);
|
|
@@ -74910,6 +75485,15 @@ const useSearchCollection = (cms, collectionName, includeDocuments = true, folde
|
|
|
74910
75485
|
return api.fetchDocument(collection2, relativePath, false);
|
|
74911
75486
|
})
|
|
74912
75487
|
);
|
|
75488
|
+
if (docs.some(
|
|
75489
|
+
(p) => p.status === "rejected" && isSessionExpiredError(p.reason)
|
|
75490
|
+
)) {
|
|
75491
|
+
setCollection(void 0);
|
|
75492
|
+
if (!cancelled) {
|
|
75493
|
+
setLoading(false);
|
|
75494
|
+
}
|
|
75495
|
+
return;
|
|
75496
|
+
}
|
|
74913
75497
|
const edges = docs.filter((p) => {
|
|
74914
75498
|
var _a;
|
|
74915
75499
|
return p.status === "fulfilled" && !!((_a = p.value) == null ? void 0 : _a.document);
|
|
@@ -74932,16 +75516,18 @@ const useSearchCollection = (cms, collectionName, includeDocuments = true, folde
|
|
|
74932
75516
|
};
|
|
74933
75517
|
setCollection(collectionData);
|
|
74934
75518
|
} else if (!cancelled) {
|
|
74935
|
-
cms.events
|
|
75519
|
+
dispatchSessionExpired(cms.events);
|
|
74936
75520
|
setCollection(void 0);
|
|
74937
75521
|
}
|
|
74938
75522
|
} catch (error2) {
|
|
74939
|
-
cms.alerts.error(
|
|
74940
|
-
`[${error2.name}] GetCollection failed: ${error2.message}`
|
|
74941
|
-
);
|
|
74942
|
-
console.error(error2);
|
|
74943
75523
|
setCollection(void 0);
|
|
74944
|
-
|
|
75524
|
+
if (!isSessionExpiredError(error2)) {
|
|
75525
|
+
cms.alerts.error(
|
|
75526
|
+
`[${error2.name}] GetCollection failed: ${error2.message}`
|
|
75527
|
+
);
|
|
75528
|
+
console.error(error2);
|
|
75529
|
+
setError(error2);
|
|
75530
|
+
}
|
|
74945
75531
|
}
|
|
74946
75532
|
if (!cancelled) {
|
|
74947
75533
|
setLoading(false);
|
|
@@ -75051,9 +75637,7 @@ const createDocument = async (cms, collection, template, mutationInfo, folder, v
|
|
|
75051
75637
|
if (await api.isAuthenticated()) {
|
|
75052
75638
|
await api.createDocument(collection, relativePath, params);
|
|
75053
75639
|
} else {
|
|
75054
|
-
|
|
75055
|
-
cms.alerts.error(authMessage);
|
|
75056
|
-
console.error(authMessage);
|
|
75640
|
+
dispatchSessionExpired(cms.events);
|
|
75057
75641
|
return false;
|
|
75058
75642
|
}
|
|
75059
75643
|
};
|
|
@@ -75236,7 +75820,7 @@ const RenderForm$1 = ({
|
|
|
75236
75820
|
onSubmit: async (values) => {
|
|
75237
75821
|
try {
|
|
75238
75822
|
const folderName2 = folder.fullyQualifiedName ? folder.name : "";
|
|
75239
|
-
await createDocument(
|
|
75823
|
+
const result = await createDocument(
|
|
75240
75824
|
cms,
|
|
75241
75825
|
collection,
|
|
75242
75826
|
template,
|
|
@@ -75244,6 +75828,7 @@ const RenderForm$1 = ({
|
|
|
75244
75828
|
folderName2,
|
|
75245
75829
|
values
|
|
75246
75830
|
);
|
|
75831
|
+
if (result === false) return;
|
|
75247
75832
|
cms.alerts.success("Document created!");
|
|
75248
75833
|
setTimeout(() => {
|
|
75249
75834
|
navigate(
|
|
@@ -75251,6 +75836,7 @@ const RenderForm$1 = ({
|
|
|
75251
75836
|
);
|
|
75252
75837
|
}, 10);
|
|
75253
75838
|
} catch (error) {
|
|
75839
|
+
if (isSessionExpiredError(error)) throw error;
|
|
75254
75840
|
const defaultErrorText = "There was a problem saving your document.";
|
|
75255
75841
|
if (error.message && error.message.includes(ERR_ALREADY_EXISTS)) {
|
|
75256
75842
|
cms.alerts.error(
|
|
@@ -75323,17 +75909,19 @@ const useGetDocument = (cms, collectionName, relativePath) => {
|
|
|
75323
75909
|
setDocument(response.document);
|
|
75324
75910
|
}
|
|
75325
75911
|
} else if (!isCancelled) {
|
|
75326
|
-
cms.events
|
|
75912
|
+
dispatchSessionExpired(cms.events);
|
|
75327
75913
|
setDocument(void 0);
|
|
75328
75914
|
}
|
|
75329
75915
|
} catch (error2) {
|
|
75330
75916
|
if (!isCancelled) {
|
|
75331
|
-
cms.alerts.error(
|
|
75332
|
-
`[${error2.name}] GetDocument failed: ${error2.message}`
|
|
75333
|
-
);
|
|
75334
|
-
console.error(error2);
|
|
75335
75917
|
setDocument(void 0);
|
|
75336
|
-
|
|
75918
|
+
if (!isSessionExpiredError(error2)) {
|
|
75919
|
+
cms.alerts.error(
|
|
75920
|
+
`[${error2.name}] GetDocument failed: ${error2.message}`
|
|
75921
|
+
);
|
|
75922
|
+
console.error(error2);
|
|
75923
|
+
setError(error2);
|
|
75924
|
+
}
|
|
75337
75925
|
}
|
|
75338
75926
|
}
|
|
75339
75927
|
if (!isCancelled) {
|
|
@@ -75419,9 +76007,7 @@ const updateDocument = async (cms, relativePath, collection, mutationInfo, value
|
|
|
75419
76007
|
if (await api.isAuthenticated()) {
|
|
75420
76008
|
await api.updateDocument(collection, relativePath, params);
|
|
75421
76009
|
} else {
|
|
75422
|
-
|
|
75423
|
-
cms.alerts.error(authMessage);
|
|
75424
|
-
console.error(authMessage);
|
|
76010
|
+
dispatchSessionExpired(cms.events);
|
|
75425
76011
|
return false;
|
|
75426
76012
|
}
|
|
75427
76013
|
};
|
|
@@ -75499,15 +76085,17 @@ const RenderForm = ({
|
|
|
75499
76085
|
initialValues: document2._values,
|
|
75500
76086
|
onSubmit: async (values) => {
|
|
75501
76087
|
try {
|
|
75502
|
-
await updateDocument(
|
|
76088
|
+
const result = await updateDocument(
|
|
75503
76089
|
cms,
|
|
75504
76090
|
relativePath,
|
|
75505
76091
|
collection,
|
|
75506
76092
|
mutationInfo,
|
|
75507
76093
|
values
|
|
75508
76094
|
);
|
|
76095
|
+
if (result === false) return;
|
|
75509
76096
|
cms.alerts.success("Document updated!");
|
|
75510
76097
|
} catch (error) {
|
|
76098
|
+
if (isSessionExpiredError(error)) throw error;
|
|
75511
76099
|
cms.alerts.error(
|
|
75512
76100
|
() => ErrorDialog({
|
|
75513
76101
|
title: "There was a problem saving your document",
|
|
@@ -75717,6 +76305,9 @@ const CheckSchema = ({
|
|
|
75717
76305
|
);
|
|
75718
76306
|
}
|
|
75719
76307
|
}).catch((error) => {
|
|
76308
|
+
if (isSessionExpiredError(error)) {
|
|
76309
|
+
return;
|
|
76310
|
+
}
|
|
75720
76311
|
if (error.message.includes(ERR_NOT_INDEXED)) {
|
|
75721
76312
|
setSchemaMissingError(true);
|
|
75722
76313
|
} else {
|
|
@@ -76181,6 +76772,7 @@ export {
|
|
|
76181
76772
|
Select$1 as Select,
|
|
76182
76773
|
SelectField,
|
|
76183
76774
|
SelectFieldPlugin,
|
|
76775
|
+
SessionExpiredError,
|
|
76184
76776
|
SettingsIcon,
|
|
76185
76777
|
StrikethroughIcon,
|
|
76186
76778
|
StyledFile,
|
|
@@ -76233,6 +76825,7 @@ export {
|
|
|
76233
76825
|
get,
|
|
76234
76826
|
getFilteredBranchList,
|
|
76235
76827
|
getStaticPropsForTina,
|
|
76828
|
+
isSessionExpiredError,
|
|
76236
76829
|
passwordFieldClasses,
|
|
76237
76830
|
resolveField,
|
|
76238
76831
|
safeAssertShape,
|