@kopexa/tiptap 17.11.4 → 17.12.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 +1944 -1921
- package/dist/index.mjs +1992 -1969
- package/package.json +25 -25
package/dist/index.mjs
CHANGED
|
@@ -4435,499 +4435,26 @@ function useUiEditorState(editor) {
|
|
|
4435
4435
|
}
|
|
4436
4436
|
|
|
4437
4437
|
// src/ui/bubble-menu/index.tsx
|
|
4438
|
-
import { Toolbar, ToolbarGroup, ToolbarSeparator } from "@kopexa/toolbar";
|
|
4438
|
+
import { Toolbar, ToolbarGroup, ToolbarSeparator as ToolbarSeparator2 } from "@kopexa/toolbar";
|
|
4439
4439
|
import { BubbleMenu as TiptapBubbleMenu } from "@tiptap/react/menus";
|
|
4440
4440
|
|
|
4441
|
-
// src/ui/
|
|
4442
|
-
import { IconButton as IconButton6 } from "@kopexa/button";
|
|
4441
|
+
// src/ui/color-highlight-button/color-highlight-button.tsx
|
|
4443
4442
|
import { useTiptapEditor as useTiptapEditor3 } from "@kopexa/editor-utils";
|
|
4444
|
-
import {
|
|
4445
|
-
CheckIcon,
|
|
4446
|
-
EditIcon as EditIcon2,
|
|
4447
|
-
ExternalLinkIcon,
|
|
4448
|
-
LinkIcon as LinkIcon2,
|
|
4449
|
-
TrashIcon as TrashIcon3
|
|
4450
|
-
} from "@kopexa/icons";
|
|
4451
|
-
import { Input as Input3 } from "@kopexa/input";
|
|
4452
|
-
import { Popover } from "@kopexa/popover";
|
|
4443
|
+
import { colorHighlightButton } from "@kopexa/theme";
|
|
4453
4444
|
import { ToolbarButton } from "@kopexa/toolbar";
|
|
4454
|
-
import { useCallback as useCallback15,
|
|
4445
|
+
import { useCallback as useCallback15, useMemo as useMemo13 } from "react";
|
|
4455
4446
|
|
|
4456
|
-
// src/ui/
|
|
4457
|
-
import {
|
|
4458
|
-
|
|
4447
|
+
// src/ui/color-highlight-button/use-color-highlight.ts
|
|
4448
|
+
import {
|
|
4449
|
+
isMarkInSchema,
|
|
4450
|
+
isNodeTypeSelected,
|
|
4451
|
+
useTiptapEditor as useTiptapEditor2
|
|
4452
|
+
} from "@kopexa/editor-utils";
|
|
4453
|
+
import { HighlighterIcon } from "@kopexa/icons";
|
|
4454
|
+
import { useIsMobile } from "@kopexa/use-is-mobile";
|
|
4459
4455
|
import * as React7 from "react";
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
var MAX_FILE_SIZE = 5 * 1024 * 1024;
|
|
4463
|
-
var handleImageUpload = async (file, onProgress, abortSignal) => {
|
|
4464
|
-
if (!file) {
|
|
4465
|
-
throw new Error("No file provided");
|
|
4466
|
-
}
|
|
4467
|
-
if (file.size > MAX_FILE_SIZE) {
|
|
4468
|
-
throw new Error(
|
|
4469
|
-
`File size exceeds maximum allowed (${MAX_FILE_SIZE / (1024 * 1024)}MB)`
|
|
4470
|
-
);
|
|
4471
|
-
}
|
|
4472
|
-
for (let progress = 0; progress <= 100; progress += 10) {
|
|
4473
|
-
if (abortSignal == null ? void 0 : abortSignal.aborted) {
|
|
4474
|
-
throw new Error("Upload cancelled");
|
|
4475
|
-
}
|
|
4476
|
-
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
4477
|
-
onProgress == null ? void 0 : onProgress({ progress });
|
|
4478
|
-
}
|
|
4479
|
-
return "/images/placeholder-image.png";
|
|
4480
|
-
};
|
|
4481
|
-
var convertFileToBase64 = (file, abortSignal) => {
|
|
4482
|
-
if (!file) {
|
|
4483
|
-
return Promise.reject(new Error("No file provided"));
|
|
4484
|
-
}
|
|
4485
|
-
return new Promise((resolve, reject) => {
|
|
4486
|
-
const reader = new FileReader();
|
|
4487
|
-
const abortHandler = () => {
|
|
4488
|
-
reader.abort();
|
|
4489
|
-
reject(new Error("Upload cancelled"));
|
|
4490
|
-
};
|
|
4491
|
-
if (abortSignal) {
|
|
4492
|
-
abortSignal.addEventListener("abort", abortHandler);
|
|
4493
|
-
}
|
|
4494
|
-
reader.onloadend = () => {
|
|
4495
|
-
if (abortSignal) {
|
|
4496
|
-
abortSignal.removeEventListener("abort", abortHandler);
|
|
4497
|
-
}
|
|
4498
|
-
if (typeof reader.result === "string") {
|
|
4499
|
-
resolve(reader.result);
|
|
4500
|
-
} else {
|
|
4501
|
-
reject(new Error("Failed to convert File to base64"));
|
|
4502
|
-
}
|
|
4503
|
-
};
|
|
4504
|
-
reader.onerror = (error) => reject(new Error(`File reading error: ${error}`));
|
|
4505
|
-
reader.readAsDataURL(file);
|
|
4506
|
-
});
|
|
4507
|
-
};
|
|
4508
|
-
var ATTR_WHITESPACE = (
|
|
4509
|
-
// eslint-disable-next-line no-control-regex
|
|
4510
|
-
// biome-ignore lint/suspicious/noControlCharactersInRegex: we can do this yay
|
|
4511
|
-
/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g
|
|
4512
|
-
);
|
|
4513
|
-
function isAllowedUri(uri, protocols) {
|
|
4514
|
-
const allowedProtocols = [
|
|
4515
|
-
"http",
|
|
4516
|
-
"https",
|
|
4517
|
-
"ftp",
|
|
4518
|
-
"ftps",
|
|
4519
|
-
"mailto",
|
|
4520
|
-
"tel",
|
|
4521
|
-
"callto",
|
|
4522
|
-
"sms",
|
|
4523
|
-
"cid",
|
|
4524
|
-
"xmpp"
|
|
4525
|
-
];
|
|
4526
|
-
if (protocols) {
|
|
4527
|
-
for (const protocol of protocols) {
|
|
4528
|
-
const nextProtocol = typeof protocol === "string" ? protocol : protocol.scheme;
|
|
4529
|
-
if (nextProtocol) {
|
|
4530
|
-
allowedProtocols.push(nextProtocol);
|
|
4531
|
-
}
|
|
4532
|
-
}
|
|
4533
|
-
}
|
|
4534
|
-
return !uri || uri.replace(ATTR_WHITESPACE, "").match(
|
|
4535
|
-
new RegExp(
|
|
4536
|
-
// eslint-disable-next-line no-useless-escape
|
|
4537
|
-
`^(?:(?:${allowedProtocols.join("|")}):|[^a-z]|[a-z0-9+.-]+(?:[^a-z+.-:]|$))`,
|
|
4538
|
-
"i"
|
|
4539
|
-
)
|
|
4540
|
-
);
|
|
4541
|
-
}
|
|
4542
|
-
function sanitizeUrl(inputUrl, baseUrl, protocols) {
|
|
4543
|
-
try {
|
|
4544
|
-
const url = new URL(inputUrl, baseUrl);
|
|
4545
|
-
if (isAllowedUri(url.href, protocols)) {
|
|
4546
|
-
return url.href;
|
|
4547
|
-
}
|
|
4548
|
-
} catch {
|
|
4549
|
-
}
|
|
4550
|
-
return "#";
|
|
4551
|
-
}
|
|
4552
|
-
|
|
4553
|
-
// src/ui/link-popover/use-link-popover.ts
|
|
4554
|
-
function canSetLink(editor) {
|
|
4555
|
-
if (!editor || !editor.isEditable) return false;
|
|
4556
|
-
return editor.can().setMark("link");
|
|
4557
|
-
}
|
|
4558
|
-
function isLinkActive(editor) {
|
|
4559
|
-
if (!editor || !editor.isEditable) return false;
|
|
4560
|
-
return editor.isActive("link");
|
|
4561
|
-
}
|
|
4562
|
-
function shouldShowLinkButton(props) {
|
|
4563
|
-
const { editor, hideWhenUnavailable } = props;
|
|
4564
|
-
const linkInSchema = isMarkInSchema("link", editor);
|
|
4565
|
-
if (!linkInSchema || !editor) {
|
|
4566
|
-
return false;
|
|
4567
|
-
}
|
|
4568
|
-
if (hideWhenUnavailable && !editor.isActive("code")) {
|
|
4569
|
-
return canSetLink(editor);
|
|
4570
|
-
}
|
|
4571
|
-
return true;
|
|
4572
|
-
}
|
|
4573
|
-
function useLinkHandler(props) {
|
|
4574
|
-
const { editor, onSetLink } = props;
|
|
4575
|
-
const [url, setUrl] = React7.useState(null);
|
|
4576
|
-
React7.useEffect(() => {
|
|
4577
|
-
if (!editor) return;
|
|
4578
|
-
const { href } = editor.getAttributes("link");
|
|
4579
|
-
if (isLinkActive(editor) && url === null) {
|
|
4580
|
-
setUrl(href || "");
|
|
4581
|
-
}
|
|
4582
|
-
}, [editor, url]);
|
|
4583
|
-
React7.useEffect(() => {
|
|
4584
|
-
if (!editor) return;
|
|
4585
|
-
const updateLinkState = () => {
|
|
4586
|
-
const { href } = editor.getAttributes("link");
|
|
4587
|
-
setUrl(href || "");
|
|
4588
|
-
};
|
|
4589
|
-
editor.on("selectionUpdate", updateLinkState);
|
|
4590
|
-
return () => {
|
|
4591
|
-
editor.off("selectionUpdate", updateLinkState);
|
|
4592
|
-
};
|
|
4593
|
-
}, [editor]);
|
|
4594
|
-
const setLink = React7.useCallback(() => {
|
|
4595
|
-
if (!url || !editor) return;
|
|
4596
|
-
const { selection } = editor.state;
|
|
4597
|
-
const isEmpty = selection.empty;
|
|
4598
|
-
let chain = editor.chain().focus();
|
|
4599
|
-
chain = chain.extendMarkRange("link").setLink({ href: url });
|
|
4600
|
-
if (isEmpty) {
|
|
4601
|
-
chain = chain.insertContent({ type: "text", text: url });
|
|
4602
|
-
}
|
|
4603
|
-
chain.run();
|
|
4604
|
-
setUrl(null);
|
|
4605
|
-
onSetLink == null ? void 0 : onSetLink();
|
|
4606
|
-
}, [editor, onSetLink, url]);
|
|
4607
|
-
const removeLink = React7.useCallback(() => {
|
|
4608
|
-
if (!editor) return;
|
|
4609
|
-
editor.chain().focus().extendMarkRange("link").unsetLink().setMeta("preventAutolink", true).run();
|
|
4610
|
-
setUrl("");
|
|
4611
|
-
}, [editor]);
|
|
4612
|
-
const openLink = React7.useCallback(
|
|
4613
|
-
(target = "_blank", features = "noopener,noreferrer") => {
|
|
4614
|
-
if (!url) return;
|
|
4615
|
-
const safeUrl = sanitizeUrl(url, window.location.href);
|
|
4616
|
-
if (safeUrl !== "#") {
|
|
4617
|
-
window.open(safeUrl, target, features);
|
|
4618
|
-
}
|
|
4619
|
-
},
|
|
4620
|
-
[url]
|
|
4621
|
-
);
|
|
4622
|
-
return {
|
|
4623
|
-
url: url || "",
|
|
4624
|
-
setUrl,
|
|
4625
|
-
setLink,
|
|
4626
|
-
removeLink,
|
|
4627
|
-
openLink
|
|
4628
|
-
};
|
|
4629
|
-
}
|
|
4630
|
-
function useLinkState(props) {
|
|
4631
|
-
const { editor, hideWhenUnavailable = false } = props;
|
|
4632
|
-
const canSet = canSetLink(editor);
|
|
4633
|
-
const isActive = isLinkActive(editor);
|
|
4634
|
-
const [isVisible, setIsVisible] = React7.useState(false);
|
|
4635
|
-
React7.useEffect(() => {
|
|
4636
|
-
if (!editor) return;
|
|
4637
|
-
const handleSelectionUpdate = () => {
|
|
4638
|
-
setIsVisible(
|
|
4639
|
-
shouldShowLinkButton({
|
|
4640
|
-
editor,
|
|
4641
|
-
hideWhenUnavailable
|
|
4642
|
-
})
|
|
4643
|
-
);
|
|
4644
|
-
};
|
|
4645
|
-
handleSelectionUpdate();
|
|
4646
|
-
editor.on("selectionUpdate", handleSelectionUpdate);
|
|
4647
|
-
return () => {
|
|
4648
|
-
editor.off("selectionUpdate", handleSelectionUpdate);
|
|
4649
|
-
};
|
|
4650
|
-
}, [editor, hideWhenUnavailable]);
|
|
4651
|
-
return {
|
|
4652
|
-
isVisible,
|
|
4653
|
-
canSet,
|
|
4654
|
-
isActive
|
|
4655
|
-
};
|
|
4656
|
-
}
|
|
4657
|
-
function useLinkPopover(config) {
|
|
4658
|
-
const {
|
|
4659
|
-
editor: providedEditor,
|
|
4660
|
-
hideWhenUnavailable = false,
|
|
4661
|
-
onSetLink
|
|
4662
|
-
} = config || {};
|
|
4663
|
-
const { editor } = useTiptapEditor2(providedEditor);
|
|
4664
|
-
const { isVisible, canSet, isActive } = useLinkState({
|
|
4665
|
-
editor,
|
|
4666
|
-
hideWhenUnavailable
|
|
4667
|
-
});
|
|
4668
|
-
const linkHandler = useLinkHandler({
|
|
4669
|
-
editor,
|
|
4670
|
-
onSetLink
|
|
4671
|
-
});
|
|
4672
|
-
return {
|
|
4673
|
-
isVisible,
|
|
4674
|
-
canSet,
|
|
4675
|
-
isActive,
|
|
4676
|
-
label: "Link",
|
|
4677
|
-
Icon: LinkIcon,
|
|
4678
|
-
...linkHandler
|
|
4679
|
-
};
|
|
4680
|
-
}
|
|
4681
|
-
|
|
4682
|
-
// src/ui/link-popover/link-popover.tsx
|
|
4683
|
-
import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
4684
|
-
var LinkButton = ({ className, children, ...props }) => {
|
|
4685
|
-
return /* @__PURE__ */ jsx16(
|
|
4686
|
-
ToolbarButton,
|
|
4687
|
-
{
|
|
4688
|
-
type: "button",
|
|
4689
|
-
className,
|
|
4690
|
-
variant: "ghost",
|
|
4691
|
-
color: "default",
|
|
4692
|
-
tabIndex: -1,
|
|
4693
|
-
"aria-label": "Link",
|
|
4694
|
-
title: "Link",
|
|
4695
|
-
isIconOnly: !children,
|
|
4696
|
-
...props,
|
|
4697
|
-
children: children || /* @__PURE__ */ jsx16(LinkIcon2, {})
|
|
4698
|
-
}
|
|
4699
|
-
);
|
|
4700
|
-
};
|
|
4701
|
-
var LinkMain = ({
|
|
4702
|
-
url,
|
|
4703
|
-
setUrl,
|
|
4704
|
-
setLink,
|
|
4705
|
-
removeLink,
|
|
4706
|
-
openLink,
|
|
4707
|
-
isActive,
|
|
4708
|
-
onSave
|
|
4709
|
-
}) => {
|
|
4710
|
-
const [isEditing, setIsEditing] = useState15(!isActive || !url);
|
|
4711
|
-
useEffect16(() => {
|
|
4712
|
-
setIsEditing(!isActive || !url);
|
|
4713
|
-
}, [isActive, url]);
|
|
4714
|
-
const handleKeyDown = (event) => {
|
|
4715
|
-
if (event.key === "Enter") {
|
|
4716
|
-
event.preventDefault();
|
|
4717
|
-
setLink();
|
|
4718
|
-
setIsEditing(false);
|
|
4719
|
-
onSave == null ? void 0 : onSave();
|
|
4720
|
-
} else if (event.key === "Escape") {
|
|
4721
|
-
event.preventDefault();
|
|
4722
|
-
setIsEditing(false);
|
|
4723
|
-
}
|
|
4724
|
-
};
|
|
4725
|
-
const handleSave = () => {
|
|
4726
|
-
setLink();
|
|
4727
|
-
setIsEditing(false);
|
|
4728
|
-
onSave == null ? void 0 : onSave();
|
|
4729
|
-
};
|
|
4730
|
-
const handleEdit = () => {
|
|
4731
|
-
setIsEditing(true);
|
|
4732
|
-
};
|
|
4733
|
-
if (isEditing) {
|
|
4734
|
-
return /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-1 min-w-[280px]", children: [
|
|
4735
|
-
/* @__PURE__ */ jsx16(
|
|
4736
|
-
Input3,
|
|
4737
|
-
{
|
|
4738
|
-
type: "url",
|
|
4739
|
-
placeholder: "Enter URL...",
|
|
4740
|
-
value: url,
|
|
4741
|
-
onChange: (e) => setUrl(e.target.value),
|
|
4742
|
-
onKeyDown: handleKeyDown,
|
|
4743
|
-
autoComplete: "off",
|
|
4744
|
-
autoCorrect: "off",
|
|
4745
|
-
autoCapitalize: "off",
|
|
4746
|
-
className: "flex-1 h-8 text-sm",
|
|
4747
|
-
autoFocus: true
|
|
4748
|
-
}
|
|
4749
|
-
),
|
|
4750
|
-
/* @__PURE__ */ jsx16(
|
|
4751
|
-
IconButton6,
|
|
4752
|
-
{
|
|
4753
|
-
type: "button",
|
|
4754
|
-
size: "sm",
|
|
4755
|
-
variant: "ghost",
|
|
4756
|
-
onClick: handleSave,
|
|
4757
|
-
"aria-label": "Save link",
|
|
4758
|
-
disabled: !url,
|
|
4759
|
-
children: /* @__PURE__ */ jsx16(CheckIcon, { className: "size-4" })
|
|
4760
|
-
}
|
|
4761
|
-
)
|
|
4762
|
-
] });
|
|
4763
|
-
}
|
|
4764
|
-
return /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-1 min-w-[280px]", children: [
|
|
4765
|
-
/* @__PURE__ */ jsx16(
|
|
4766
|
-
"a",
|
|
4767
|
-
{
|
|
4768
|
-
href: url,
|
|
4769
|
-
target: "_blank",
|
|
4770
|
-
rel: "noopener noreferrer",
|
|
4771
|
-
className: "flex-1 text-sm text-primary truncate max-w-[200px] hover:underline px-2",
|
|
4772
|
-
onClick: (e) => {
|
|
4773
|
-
e.preventDefault();
|
|
4774
|
-
openLink();
|
|
4775
|
-
},
|
|
4776
|
-
children: url
|
|
4777
|
-
}
|
|
4778
|
-
),
|
|
4779
|
-
/* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-0.5 border-l pl-1 ml-1", children: [
|
|
4780
|
-
/* @__PURE__ */ jsx16(
|
|
4781
|
-
IconButton6,
|
|
4782
|
-
{
|
|
4783
|
-
type: "button",
|
|
4784
|
-
size: "sm",
|
|
4785
|
-
variant: "ghost",
|
|
4786
|
-
onClick: openLink,
|
|
4787
|
-
"aria-label": "Open link in new tab",
|
|
4788
|
-
children: /* @__PURE__ */ jsx16(ExternalLinkIcon, { className: "size-4" })
|
|
4789
|
-
}
|
|
4790
|
-
),
|
|
4791
|
-
/* @__PURE__ */ jsx16(
|
|
4792
|
-
IconButton6,
|
|
4793
|
-
{
|
|
4794
|
-
type: "button",
|
|
4795
|
-
size: "sm",
|
|
4796
|
-
variant: "ghost",
|
|
4797
|
-
onClick: handleEdit,
|
|
4798
|
-
"aria-label": "Edit link",
|
|
4799
|
-
children: /* @__PURE__ */ jsx16(EditIcon2, { className: "size-4" })
|
|
4800
|
-
}
|
|
4801
|
-
),
|
|
4802
|
-
/* @__PURE__ */ jsx16(
|
|
4803
|
-
IconButton6,
|
|
4804
|
-
{
|
|
4805
|
-
type: "button",
|
|
4806
|
-
size: "sm",
|
|
4807
|
-
variant: "ghost",
|
|
4808
|
-
onClick: removeLink,
|
|
4809
|
-
"aria-label": "Remove link",
|
|
4810
|
-
children: /* @__PURE__ */ jsx16(TrashIcon3, { className: "size-4" })
|
|
4811
|
-
}
|
|
4812
|
-
)
|
|
4813
|
-
] })
|
|
4814
|
-
] });
|
|
4815
|
-
};
|
|
4816
|
-
function LinkPopover({
|
|
4817
|
-
editor: providedEditor,
|
|
4818
|
-
hideWhenUnavailable = false,
|
|
4819
|
-
onSetLink,
|
|
4820
|
-
onOpenChange,
|
|
4821
|
-
autoOpenOnLinkActive = true,
|
|
4822
|
-
onClick,
|
|
4823
|
-
children,
|
|
4824
|
-
...buttonProps
|
|
4825
|
-
}) {
|
|
4826
|
-
const { editor } = useTiptapEditor3(providedEditor);
|
|
4827
|
-
const [isOpen, setIsOpen] = useState15(false);
|
|
4828
|
-
const {
|
|
4829
|
-
isVisible,
|
|
4830
|
-
canSet,
|
|
4831
|
-
isActive,
|
|
4832
|
-
url,
|
|
4833
|
-
setUrl,
|
|
4834
|
-
setLink,
|
|
4835
|
-
removeLink,
|
|
4836
|
-
openLink,
|
|
4837
|
-
label
|
|
4838
|
-
} = useLinkPopover({
|
|
4839
|
-
editor,
|
|
4840
|
-
hideWhenUnavailable,
|
|
4841
|
-
onSetLink
|
|
4842
|
-
});
|
|
4843
|
-
const handleOnOpenChange = useCallback15(
|
|
4844
|
-
(nextIsOpen) => {
|
|
4845
|
-
setIsOpen(nextIsOpen);
|
|
4846
|
-
onOpenChange == null ? void 0 : onOpenChange(nextIsOpen);
|
|
4847
|
-
},
|
|
4848
|
-
[onOpenChange]
|
|
4849
|
-
);
|
|
4850
|
-
const handleSetLink = useCallback15(() => {
|
|
4851
|
-
setLink();
|
|
4852
|
-
setIsOpen(false);
|
|
4853
|
-
}, [setLink]);
|
|
4854
|
-
const handleClick = useCallback15(
|
|
4855
|
-
(event) => {
|
|
4856
|
-
onClick == null ? void 0 : onClick(event);
|
|
4857
|
-
if (event.defaultPrevented) return;
|
|
4858
|
-
setIsOpen(!isOpen);
|
|
4859
|
-
},
|
|
4860
|
-
[onClick, isOpen]
|
|
4861
|
-
);
|
|
4862
|
-
useEffect16(() => {
|
|
4863
|
-
if (autoOpenOnLinkActive && isActive) {
|
|
4864
|
-
setIsOpen(true);
|
|
4865
|
-
}
|
|
4866
|
-
}, [autoOpenOnLinkActive, isActive]);
|
|
4867
|
-
if (!isVisible) {
|
|
4868
|
-
return null;
|
|
4869
|
-
}
|
|
4870
|
-
return /* @__PURE__ */ jsxs12(
|
|
4871
|
-
Popover.Root,
|
|
4872
|
-
{
|
|
4873
|
-
open: isOpen,
|
|
4874
|
-
onOpenChange: handleOnOpenChange,
|
|
4875
|
-
spacing: "dense",
|
|
4876
|
-
children: [
|
|
4877
|
-
/* @__PURE__ */ jsx16(
|
|
4878
|
-
Popover.Trigger,
|
|
4879
|
-
{
|
|
4880
|
-
render: /* @__PURE__ */ jsx16(
|
|
4881
|
-
LinkButton,
|
|
4882
|
-
{
|
|
4883
|
-
"data-disabled": !canSet,
|
|
4884
|
-
disabled: !canSet,
|
|
4885
|
-
"data-active-state": isActive ? "on" : "off",
|
|
4886
|
-
"aria-label": label,
|
|
4887
|
-
"aria-pressed": isActive,
|
|
4888
|
-
onClick: handleClick,
|
|
4889
|
-
...buttonProps
|
|
4890
|
-
}
|
|
4891
|
-
)
|
|
4892
|
-
}
|
|
4893
|
-
),
|
|
4894
|
-
/* @__PURE__ */ jsx16(Popover.Content, { children: /* @__PURE__ */ jsx16(
|
|
4895
|
-
LinkMain,
|
|
4896
|
-
{
|
|
4897
|
-
url,
|
|
4898
|
-
setUrl,
|
|
4899
|
-
setLink: handleSetLink,
|
|
4900
|
-
removeLink,
|
|
4901
|
-
openLink,
|
|
4902
|
-
isActive,
|
|
4903
|
-
onSave: () => setIsOpen(false)
|
|
4904
|
-
}
|
|
4905
|
-
) })
|
|
4906
|
-
]
|
|
4907
|
-
}
|
|
4908
|
-
);
|
|
4909
|
-
}
|
|
4910
|
-
LinkButton.displayName = "LinkButton";
|
|
4911
|
-
|
|
4912
|
-
// src/ui/mark-button/index.tsx
|
|
4913
|
-
import {
|
|
4914
|
-
isMarkInSchema as isMarkInSchema2,
|
|
4915
|
-
isNodeTypeSelected,
|
|
4916
|
-
useTiptapEditor as useTiptapEditor4
|
|
4917
|
-
} from "@kopexa/editor-utils";
|
|
4918
|
-
import {
|
|
4919
|
-
BoldIcon,
|
|
4920
|
-
CodeIcon,
|
|
4921
|
-
ItalicIcon,
|
|
4922
|
-
StrikeIcon,
|
|
4923
|
-
SubscriptIcon,
|
|
4924
|
-
SuperscriptIcon,
|
|
4925
|
-
UnderlineIcon
|
|
4926
|
-
} from "@kopexa/icons";
|
|
4927
|
-
import { ToolbarButton as ToolbarButton2 } from "@kopexa/toolbar";
|
|
4928
|
-
import { isNodeSelection as isNodeSelection2 } from "@tiptap/react";
|
|
4929
|
-
import { useCallback as useCallback16, useMemo as useMemo13 } from "react";
|
|
4930
|
-
import { useIntl as useIntl9 } from "react-intl";
|
|
4456
|
+
import { useHotkeys } from "react-hotkeys-hook";
|
|
4457
|
+
import { useIntl as useIntl9 } from "react-intl";
|
|
4931
4458
|
|
|
4932
4459
|
// src/ui/messages.ts
|
|
4933
4460
|
import { defineMessages as defineMessages7 } from "react-intl";
|
|
@@ -5345,545 +4872,593 @@ var messages7 = defineMessages7({
|
|
|
5345
4872
|
}
|
|
5346
4873
|
});
|
|
5347
4874
|
|
|
5348
|
-
// src/ui/
|
|
5349
|
-
|
|
5350
|
-
var
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
var
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
|
|
5367
|
-
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5381
|
-
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
5387
|
-
|
|
5388
|
-
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
}
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
superscript: messages7.superscript,
|
|
5408
|
-
subscript: messages7.subscript
|
|
5409
|
-
};
|
|
5410
|
-
function getTranslatedMarkName(type, intl) {
|
|
5411
|
-
return intl.formatMessage(markMessages[type]);
|
|
4875
|
+
// src/ui/color-highlight-button/use-color-highlight.ts
|
|
4876
|
+
var COLOR_HIGHLIGHT_SHORTCUT_KEY = "mod+shift+h";
|
|
4877
|
+
var HIGHLIGHT_COLORS = [
|
|
4878
|
+
{
|
|
4879
|
+
label: "Default background",
|
|
4880
|
+
value: "var(--tt-bg-color)",
|
|
4881
|
+
border: "var(--tt-bg-color-contrast)"
|
|
4882
|
+
},
|
|
4883
|
+
{
|
|
4884
|
+
label: "Gray background",
|
|
4885
|
+
value: "var(--tt-color-highlight-gray)",
|
|
4886
|
+
border: "var(--tt-color-highlight-gray-contrast)"
|
|
4887
|
+
},
|
|
4888
|
+
{
|
|
4889
|
+
label: "Brown background",
|
|
4890
|
+
value: "var(--tt-color-highlight-brown)",
|
|
4891
|
+
border: "var(--tt-color-highlight-brown-contrast)"
|
|
4892
|
+
},
|
|
4893
|
+
{
|
|
4894
|
+
label: "Orange background",
|
|
4895
|
+
value: "var(--tt-color-highlight-orange)",
|
|
4896
|
+
border: "var(--tt-color-highlight-orange-contrast)"
|
|
4897
|
+
},
|
|
4898
|
+
{
|
|
4899
|
+
label: "Yellow background",
|
|
4900
|
+
value: "var(--tt-color-highlight-yellow)",
|
|
4901
|
+
border: "var(--tt-color-highlight-yellow-contrast)"
|
|
4902
|
+
},
|
|
4903
|
+
{
|
|
4904
|
+
label: "Green background",
|
|
4905
|
+
value: "var(--tt-color-highlight-green)",
|
|
4906
|
+
border: "var(--tt-color-highlight-green-contrast)"
|
|
4907
|
+
},
|
|
4908
|
+
{
|
|
4909
|
+
label: "Blue background",
|
|
4910
|
+
value: "var(--tt-color-highlight-blue)",
|
|
4911
|
+
border: "var(--tt-color-highlight-blue-contrast)"
|
|
4912
|
+
},
|
|
4913
|
+
{
|
|
4914
|
+
label: "Purple background",
|
|
4915
|
+
value: "var(--tt-color-highlight-purple)",
|
|
4916
|
+
border: "var(--tt-color-highlight-purple-contrast)"
|
|
4917
|
+
},
|
|
4918
|
+
{
|
|
4919
|
+
label: "Pink background",
|
|
4920
|
+
value: "var(--tt-color-highlight-pink)",
|
|
4921
|
+
border: "var(--tt-color-highlight-pink-contrast)"
|
|
4922
|
+
},
|
|
4923
|
+
{
|
|
4924
|
+
label: "Red background",
|
|
4925
|
+
value: "var(--tt-color-highlight-red)",
|
|
4926
|
+
border: "var(--tt-color-highlight-red-contrast)"
|
|
4927
|
+
}
|
|
4928
|
+
];
|
|
4929
|
+
function pickHighlightColorsByValue(values) {
|
|
4930
|
+
const colorMap = new Map(
|
|
4931
|
+
HIGHLIGHT_COLORS.map((color) => [color.value, color])
|
|
4932
|
+
);
|
|
4933
|
+
return values.map((value) => colorMap.get(value)).filter((color) => !!color);
|
|
5412
4934
|
}
|
|
5413
|
-
function
|
|
4935
|
+
function canColorHighlight(editor) {
|
|
4936
|
+
if (!editor || !editor.isEditable) return false;
|
|
4937
|
+
if (!isMarkInSchema("highlight", editor) || isNodeTypeSelected(editor, ["image"]))
|
|
4938
|
+
return false;
|
|
4939
|
+
return editor.can().setMark("highlight");
|
|
4940
|
+
}
|
|
4941
|
+
function isColorHighlightActive(editor, highlightColor) {
|
|
4942
|
+
if (!editor || !editor.isEditable) return false;
|
|
4943
|
+
return highlightColor ? editor.isActive("highlight", { color: highlightColor }) : editor.isActive("highlight");
|
|
4944
|
+
}
|
|
4945
|
+
function removeHighlight(editor) {
|
|
4946
|
+
if (!editor || !editor.isEditable) return false;
|
|
4947
|
+
if (!canColorHighlight(editor)) return false;
|
|
4948
|
+
return editor.chain().focus().unsetMark("highlight").run();
|
|
4949
|
+
}
|
|
4950
|
+
function shouldShowButton(props) {
|
|
4951
|
+
const { editor, hideWhenUnavailable } = props;
|
|
4952
|
+
if (!editor || !editor.isEditable) return false;
|
|
4953
|
+
if (!isMarkInSchema("highlight", editor)) return false;
|
|
4954
|
+
if (hideWhenUnavailable && !editor.isActive("code")) {
|
|
4955
|
+
return canColorHighlight(editor);
|
|
4956
|
+
}
|
|
4957
|
+
return true;
|
|
4958
|
+
}
|
|
4959
|
+
function useColorHighlight(config) {
|
|
4960
|
+
const {
|
|
4961
|
+
editor: providedEditor,
|
|
4962
|
+
label,
|
|
4963
|
+
highlightColor,
|
|
4964
|
+
hideWhenUnavailable = false,
|
|
4965
|
+
onApplied
|
|
4966
|
+
} = config;
|
|
5414
4967
|
const intl = useIntl9();
|
|
5415
|
-
const
|
|
5416
|
-
const
|
|
5417
|
-
const
|
|
5418
|
-
const
|
|
5419
|
-
const
|
|
5420
|
-
|
|
4968
|
+
const { editor } = useTiptapEditor2(providedEditor);
|
|
4969
|
+
const isMobile = useIsMobile();
|
|
4970
|
+
const [isVisible, setIsVisible] = React7.useState(true);
|
|
4971
|
+
const canColorHighlightState = canColorHighlight(editor);
|
|
4972
|
+
const isActive = isColorHighlightActive(editor, highlightColor);
|
|
4973
|
+
React7.useEffect(() => {
|
|
4974
|
+
if (!editor) return;
|
|
4975
|
+
const handleSelectionUpdate = () => {
|
|
4976
|
+
setIsVisible(shouldShowButton({ editor, hideWhenUnavailable }));
|
|
4977
|
+
};
|
|
4978
|
+
handleSelectionUpdate();
|
|
4979
|
+
editor.on("selectionUpdate", handleSelectionUpdate);
|
|
4980
|
+
return () => {
|
|
4981
|
+
editor.off("selectionUpdate", handleSelectionUpdate);
|
|
4982
|
+
};
|
|
4983
|
+
}, [editor, hideWhenUnavailable]);
|
|
4984
|
+
const handleColorHighlight = React7.useCallback(() => {
|
|
4985
|
+
if (!editor || !canColorHighlightState || !highlightColor || !label)
|
|
4986
|
+
return false;
|
|
4987
|
+
if (editor.state.storedMarks) {
|
|
4988
|
+
const highlightMarkType = editor.schema.marks.highlight;
|
|
4989
|
+
if (highlightMarkType) {
|
|
4990
|
+
editor.view.dispatch(
|
|
4991
|
+
editor.state.tr.removeStoredMark(highlightMarkType)
|
|
4992
|
+
);
|
|
4993
|
+
}
|
|
4994
|
+
}
|
|
4995
|
+
setTimeout(() => {
|
|
4996
|
+
const success = editor.chain().focus().toggleMark("highlight", { color: highlightColor }).run();
|
|
4997
|
+
if (success) {
|
|
4998
|
+
onApplied == null ? void 0 : onApplied({ color: highlightColor, label });
|
|
4999
|
+
}
|
|
5000
|
+
return success;
|
|
5001
|
+
}, 0);
|
|
5002
|
+
}, [canColorHighlightState, highlightColor, editor, label, onApplied]);
|
|
5003
|
+
const handleRemoveHighlight = React7.useCallback(() => {
|
|
5004
|
+
const success = removeHighlight(editor);
|
|
5005
|
+
if (success) {
|
|
5006
|
+
onApplied == null ? void 0 : onApplied({ color: "", label: "Remove highlight" });
|
|
5007
|
+
}
|
|
5008
|
+
return success;
|
|
5009
|
+
}, [editor, onApplied]);
|
|
5010
|
+
useHotkeys(
|
|
5011
|
+
COLOR_HIGHLIGHT_SHORTCUT_KEY,
|
|
5012
|
+
(event) => {
|
|
5013
|
+
event.preventDefault();
|
|
5014
|
+
handleColorHighlight();
|
|
5015
|
+
},
|
|
5016
|
+
{
|
|
5017
|
+
enabled: isVisible && canColorHighlightState,
|
|
5018
|
+
enableOnContentEditable: !isMobile,
|
|
5019
|
+
enableOnFormTags: true
|
|
5020
|
+
}
|
|
5021
|
+
);
|
|
5421
5022
|
return {
|
|
5422
|
-
|
|
5423
|
-
isDisabled,
|
|
5023
|
+
isVisible,
|
|
5424
5024
|
isActive,
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5025
|
+
handleColorHighlight,
|
|
5026
|
+
handleRemoveHighlight,
|
|
5027
|
+
canColorHighlight: canColorHighlightState,
|
|
5028
|
+
label: label || intl.formatMessage(messages7.highlight_color),
|
|
5029
|
+
shortcutKeys: COLOR_HIGHLIGHT_SHORTCUT_KEY,
|
|
5030
|
+
Icon: HighlighterIcon
|
|
5428
5031
|
};
|
|
5429
5032
|
}
|
|
5430
|
-
|
|
5033
|
+
|
|
5034
|
+
// src/ui/color-highlight-button/color-highlight-button.tsx
|
|
5035
|
+
import { Fragment as Fragment3, jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
5036
|
+
var ColorHighlightButton = ({
|
|
5431
5037
|
editor: providedEditor,
|
|
5432
|
-
|
|
5038
|
+
highlightColor,
|
|
5433
5039
|
text,
|
|
5434
5040
|
hideWhenUnavailable = false,
|
|
5435
|
-
|
|
5436
|
-
|
|
5041
|
+
onApplied,
|
|
5042
|
+
showShortcut = false,
|
|
5437
5043
|
onClick,
|
|
5438
5044
|
children,
|
|
5045
|
+
style,
|
|
5046
|
+
className,
|
|
5439
5047
|
...buttonProps
|
|
5440
5048
|
}) => {
|
|
5441
|
-
const { editor } =
|
|
5049
|
+
const { editor } = useTiptapEditor3(providedEditor);
|
|
5442
5050
|
const {
|
|
5443
|
-
|
|
5444
|
-
|
|
5051
|
+
isVisible,
|
|
5052
|
+
canColorHighlight: canColorHighlight2,
|
|
5445
5053
|
isActive,
|
|
5446
|
-
|
|
5447
|
-
|
|
5448
|
-
|
|
5449
|
-
} =
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
|
|
5054
|
+
handleColorHighlight,
|
|
5055
|
+
label,
|
|
5056
|
+
shortcutKeys
|
|
5057
|
+
} = useColorHighlight({
|
|
5058
|
+
editor,
|
|
5059
|
+
highlightColor,
|
|
5060
|
+
label: text || `Toggle highlight (${highlightColor})`,
|
|
5061
|
+
hideWhenUnavailable,
|
|
5062
|
+
onApplied
|
|
5063
|
+
});
|
|
5064
|
+
const handleClick = useCallback15(
|
|
5065
|
+
(event) => {
|
|
5066
|
+
onClick == null ? void 0 : onClick(event);
|
|
5067
|
+
if (event.defaultPrevented) return;
|
|
5068
|
+
handleColorHighlight();
|
|
5456
5069
|
},
|
|
5457
|
-
[
|
|
5070
|
+
[handleColorHighlight, onClick]
|
|
5458
5071
|
);
|
|
5459
|
-
const
|
|
5460
|
-
|
|
5461
|
-
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
if (!show || !editor || !editor.isEditable) {
|
|
5072
|
+
const buttonStyle = useMemo13(
|
|
5073
|
+
() => ({
|
|
5074
|
+
...style,
|
|
5075
|
+
"--highlight-color": highlightColor
|
|
5076
|
+
}),
|
|
5077
|
+
[highlightColor, style]
|
|
5078
|
+
);
|
|
5079
|
+
if (!isVisible) {
|
|
5468
5080
|
return null;
|
|
5469
5081
|
}
|
|
5470
|
-
|
|
5471
|
-
|
|
5082
|
+
const styles = colorHighlightButton();
|
|
5083
|
+
return /* @__PURE__ */ jsxs12(
|
|
5084
|
+
ToolbarButton,
|
|
5472
5085
|
{
|
|
5473
5086
|
type: "button",
|
|
5474
|
-
|
|
5475
|
-
disabled:
|
|
5087
|
+
disabled: !canColorHighlight2,
|
|
5088
|
+
"data-disabled": !canColorHighlight2,
|
|
5476
5089
|
variant: "ghost",
|
|
5477
5090
|
color: "default",
|
|
5478
5091
|
"data-active-state": isActive ? "on" : "off",
|
|
5479
|
-
"data-disabled": isDisabled,
|
|
5480
5092
|
tabIndex: -1,
|
|
5481
|
-
"aria-label":
|
|
5093
|
+
"aria-label": label,
|
|
5094
|
+
shortcutKeys,
|
|
5482
5095
|
"aria-pressed": isActive,
|
|
5483
|
-
title: formattedName,
|
|
5484
|
-
shortcutKeys: shortcutKey,
|
|
5485
5096
|
onClick: handleClick,
|
|
5097
|
+
style: buttonStyle,
|
|
5098
|
+
className: styles.button({ className }),
|
|
5486
5099
|
isIconOnly: true,
|
|
5487
5100
|
...buttonProps,
|
|
5488
|
-
children:
|
|
5101
|
+
children: [
|
|
5102
|
+
/* @__PURE__ */ jsx16(
|
|
5103
|
+
"span",
|
|
5104
|
+
{
|
|
5105
|
+
"data-active-state": isActive ? "on" : "off",
|
|
5106
|
+
className: styles.mark()
|
|
5107
|
+
}
|
|
5108
|
+
),
|
|
5109
|
+
children || /* @__PURE__ */ jsxs12(Fragment3, { children: [
|
|
5110
|
+
/* @__PURE__ */ jsx16(
|
|
5111
|
+
"span",
|
|
5112
|
+
{
|
|
5113
|
+
style: { "--highlight-color": highlightColor }
|
|
5114
|
+
}
|
|
5115
|
+
),
|
|
5116
|
+
text
|
|
5117
|
+
] })
|
|
5118
|
+
]
|
|
5489
5119
|
}
|
|
5490
5120
|
);
|
|
5491
5121
|
};
|
|
5492
5122
|
|
|
5493
|
-
// src/ui/
|
|
5494
|
-
import {
|
|
5495
|
-
|
|
5496
|
-
|
|
5497
|
-
|
|
5123
|
+
// src/ui/color-highlight-popover/color-highlight-popover.tsx
|
|
5124
|
+
import { IconButton as IconButton6 } from "@kopexa/button";
|
|
5125
|
+
import { useTiptapEditor as useTiptapEditor4 } from "@kopexa/editor-utils";
|
|
5126
|
+
import { BanIcon, HighlighterIcon as HighlighterIcon2 } from "@kopexa/icons";
|
|
5127
|
+
import { Popover } from "@kopexa/popover";
|
|
5128
|
+
import { ToolbarSeparator } from "@kopexa/toolbar";
|
|
5129
|
+
import { useMemo as useMemo14, useRef as useRef9, useState as useState15 } from "react";
|
|
5130
|
+
import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
5131
|
+
var ColorHighlightPopoverButton = ({
|
|
5132
|
+
className,
|
|
5133
|
+
children,
|
|
5134
|
+
...props
|
|
5135
|
+
}) => /* @__PURE__ */ jsx17(
|
|
5136
|
+
IconButton6,
|
|
5137
|
+
{
|
|
5138
|
+
type: "button",
|
|
5139
|
+
className,
|
|
5140
|
+
variant: "ghost",
|
|
5141
|
+
color: "default",
|
|
5142
|
+
tabIndex: -1,
|
|
5143
|
+
"aria-label": "Highlight text",
|
|
5144
|
+
tooltip: "Highlight",
|
|
5145
|
+
isIconOnly: !children,
|
|
5146
|
+
...props,
|
|
5147
|
+
children: children != null ? children : /* @__PURE__ */ jsx17(HighlighterIcon2, {})
|
|
5498
5148
|
}
|
|
5499
|
-
|
|
5500
|
-
|
|
5501
|
-
|
|
5502
|
-
|
|
5503
|
-
|
|
5504
|
-
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
|
|
5508
|
-
|
|
5509
|
-
|
|
5510
|
-
|
|
5511
|
-
|
|
5512
|
-
|
|
5513
|
-
|
|
5514
|
-
|
|
5515
|
-
placement: "top",
|
|
5516
|
-
offset: 8
|
|
5517
|
-
},
|
|
5518
|
-
className: "rounded-lg border bg-background shadow-md",
|
|
5519
|
-
children: /* @__PURE__ */ jsxs13(Toolbar, { radius: "md", border: "none", className: "p-1", children: [
|
|
5520
|
-
/* @__PURE__ */ jsxs13(ToolbarGroup, { children: [
|
|
5521
|
-
/* @__PURE__ */ jsx18(MarkButton, { type: "bold" }),
|
|
5522
|
-
/* @__PURE__ */ jsx18(MarkButton, { type: "italic" }),
|
|
5523
|
-
/* @__PURE__ */ jsx18(MarkButton, { type: "strike" }),
|
|
5524
|
-
/* @__PURE__ */ jsx18(MarkButton, { type: "code" })
|
|
5525
|
-
] }),
|
|
5526
|
-
/* @__PURE__ */ jsx18(ToolbarSeparator, {}),
|
|
5527
|
-
/* @__PURE__ */ jsx18(ToolbarGroup, { children: /* @__PURE__ */ jsx18(LinkPopover, { autoOpenOnLinkActive: false }) })
|
|
5528
|
-
] })
|
|
5529
|
-
}
|
|
5149
|
+
);
|
|
5150
|
+
function ColorHighlightPopoverContent({
|
|
5151
|
+
editor,
|
|
5152
|
+
colors = pickHighlightColorsByValue([
|
|
5153
|
+
"var(--tt-color-highlight-green)",
|
|
5154
|
+
"var(--tt-color-highlight-blue)",
|
|
5155
|
+
"var(--tt-color-highlight-red)",
|
|
5156
|
+
"var(--tt-color-highlight-purple)",
|
|
5157
|
+
"var(--tt-color-highlight-yellow)"
|
|
5158
|
+
])
|
|
5159
|
+
}) {
|
|
5160
|
+
const { handleRemoveHighlight } = useColorHighlight({ editor });
|
|
5161
|
+
const containerRef = useRef9(null);
|
|
5162
|
+
const menuItems = useMemo14(
|
|
5163
|
+
() => [...colors, { label: "Remove highlight", value: "none" }],
|
|
5164
|
+
[colors]
|
|
5530
5165
|
);
|
|
5166
|
+
const { selectedIndex } = useMenuNavigation({
|
|
5167
|
+
containerRef,
|
|
5168
|
+
items: menuItems,
|
|
5169
|
+
orientation: "both",
|
|
5170
|
+
onSelect: (item) => {
|
|
5171
|
+
if (!containerRef.current) return false;
|
|
5172
|
+
const highlightedElement = containerRef.current.querySelector(
|
|
5173
|
+
'[data-highlighted="true"]'
|
|
5174
|
+
);
|
|
5175
|
+
if (highlightedElement) highlightedElement.click();
|
|
5176
|
+
if (item.value === "none") handleRemoveHighlight();
|
|
5177
|
+
},
|
|
5178
|
+
autoSelectFirstItem: false
|
|
5179
|
+
});
|
|
5180
|
+
return /* @__PURE__ */ jsxs13("div", { ref: containerRef, className: "flex gap-1 items-center", children: [
|
|
5181
|
+
/* @__PURE__ */ jsx17(
|
|
5182
|
+
"div",
|
|
5183
|
+
{
|
|
5184
|
+
className: "flex items-center gap-1 outline-none",
|
|
5185
|
+
"data-orientation": "horizontal",
|
|
5186
|
+
children: colors.map((color, index) => /* @__PURE__ */ jsx17(
|
|
5187
|
+
ColorHighlightButton,
|
|
5188
|
+
{
|
|
5189
|
+
editor,
|
|
5190
|
+
highlightColor: color.value,
|
|
5191
|
+
"aria-label": `${color.label} highlight color`,
|
|
5192
|
+
tabIndex: index === selectedIndex ? 0 : -1,
|
|
5193
|
+
"data-highlighted": selectedIndex === index
|
|
5194
|
+
},
|
|
5195
|
+
color.value
|
|
5196
|
+
))
|
|
5197
|
+
}
|
|
5198
|
+
),
|
|
5199
|
+
/* @__PURE__ */ jsx17(ToolbarSeparator, { orientation: "vertical" }),
|
|
5200
|
+
/* @__PURE__ */ jsx17("div", { className: "tiptap-button-group", children: /* @__PURE__ */ jsx17(
|
|
5201
|
+
IconButton6,
|
|
5202
|
+
{
|
|
5203
|
+
onClick: handleRemoveHighlight,
|
|
5204
|
+
"aria-label": "Remove highlight",
|
|
5205
|
+
tabIndex: selectedIndex === colors.length ? 0 : -1,
|
|
5206
|
+
type: "button",
|
|
5207
|
+
role: "menuitem",
|
|
5208
|
+
variant: "ghost",
|
|
5209
|
+
color: "default",
|
|
5210
|
+
"data-highlighted": selectedIndex === colors.length,
|
|
5211
|
+
children: /* @__PURE__ */ jsx17(BanIcon, {})
|
|
5212
|
+
}
|
|
5213
|
+
) })
|
|
5214
|
+
] });
|
|
5215
|
+
}
|
|
5216
|
+
function ColorHighlightPopover({
|
|
5217
|
+
editor: providedEditor,
|
|
5218
|
+
colors = pickHighlightColorsByValue([
|
|
5219
|
+
"var(--tt-color-highlight-green)",
|
|
5220
|
+
"var(--tt-color-highlight-blue)",
|
|
5221
|
+
"var(--tt-color-highlight-red)",
|
|
5222
|
+
"var(--tt-color-highlight-purple)",
|
|
5223
|
+
"var(--tt-color-highlight-yellow)"
|
|
5224
|
+
]),
|
|
5225
|
+
hideWhenUnavailable = false,
|
|
5226
|
+
onApplied,
|
|
5227
|
+
...props
|
|
5228
|
+
}) {
|
|
5229
|
+
const { editor } = useTiptapEditor4(providedEditor);
|
|
5230
|
+
const [isOpen, setIsOpen] = useState15(false);
|
|
5231
|
+
const { isVisible, canColorHighlight: canColorHighlight2, isActive, label } = useColorHighlight({
|
|
5232
|
+
editor,
|
|
5233
|
+
hideWhenUnavailable,
|
|
5234
|
+
onApplied
|
|
5235
|
+
});
|
|
5236
|
+
if (!isVisible) return null;
|
|
5237
|
+
return /* @__PURE__ */ jsxs13(Popover.Root, { open: isOpen, onOpenChange: setIsOpen, spacing: "dense", children: [
|
|
5238
|
+
/* @__PURE__ */ jsx17(
|
|
5239
|
+
Popover.Trigger,
|
|
5240
|
+
{
|
|
5241
|
+
render: /* @__PURE__ */ jsx17(
|
|
5242
|
+
ColorHighlightPopoverButton,
|
|
5243
|
+
{
|
|
5244
|
+
disabled: !canColorHighlight2,
|
|
5245
|
+
"data-disabled": !canColorHighlight2,
|
|
5246
|
+
"data-active-state": isActive ? "on" : "off",
|
|
5247
|
+
"aria-pressed": isActive,
|
|
5248
|
+
"aria-label": label,
|
|
5249
|
+
title: label,
|
|
5250
|
+
...props
|
|
5251
|
+
}
|
|
5252
|
+
)
|
|
5253
|
+
}
|
|
5254
|
+
),
|
|
5255
|
+
/* @__PURE__ */ jsx17(Popover.Content, { "aria-label": "Highlight colors", children: /* @__PURE__ */ jsx17(ColorHighlightPopoverContent, { editor, colors }) })
|
|
5256
|
+
] });
|
|
5531
5257
|
}
|
|
5532
5258
|
|
|
5533
|
-
// src/ui/
|
|
5534
|
-
import {
|
|
5535
|
-
import
|
|
5259
|
+
// src/ui/link-popover/link-popover.tsx
|
|
5260
|
+
import { IconButton as IconButton7 } from "@kopexa/button";
|
|
5261
|
+
import { useTiptapEditor as useTiptapEditor6 } from "@kopexa/editor-utils";
|
|
5262
|
+
import {
|
|
5263
|
+
CheckIcon,
|
|
5264
|
+
EditIcon as EditIcon2,
|
|
5265
|
+
ExternalLinkIcon,
|
|
5266
|
+
LinkIcon as LinkIcon2,
|
|
5267
|
+
TrashIcon as TrashIcon3
|
|
5268
|
+
} from "@kopexa/icons";
|
|
5269
|
+
import { Input as Input3 } from "@kopexa/input";
|
|
5270
|
+
import { Popover as Popover2 } from "@kopexa/popover";
|
|
5271
|
+
import { ToolbarButton as ToolbarButton2 } from "@kopexa/toolbar";
|
|
5272
|
+
import { useCallback as useCallback17, useEffect as useEffect17, useState as useState17 } from "react";
|
|
5536
5273
|
|
|
5537
|
-
// src/
|
|
5538
|
-
import {
|
|
5539
|
-
import {
|
|
5274
|
+
// src/ui/link-popover/use-link-popover.ts
|
|
5275
|
+
import { isMarkInSchema as isMarkInSchema2, useTiptapEditor as useTiptapEditor5 } from "@kopexa/editor-utils";
|
|
5276
|
+
import { LinkIcon } from "@kopexa/icons";
|
|
5540
5277
|
import * as React8 from "react";
|
|
5541
|
-
var HIDE_FLOATING_META = "hideFloatingToolbar";
|
|
5542
|
-
var selectNodeAndHideFloating = (editor, pos) => {
|
|
5543
|
-
if (!editor) return;
|
|
5544
|
-
const { state, view } = editor;
|
|
5545
|
-
view.dispatch(
|
|
5546
|
-
state.tr.setSelection(NodeSelection.create(state.doc, pos)).setMeta(HIDE_FLOATING_META, true)
|
|
5547
|
-
);
|
|
5548
|
-
};
|
|
5549
5278
|
|
|
5550
|
-
// src/
|
|
5551
|
-
|
|
5552
|
-
|
|
5553
|
-
|
|
5554
|
-
|
|
5555
|
-
|
|
5556
|
-
|
|
5557
|
-
|
|
5558
|
-
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
|
|
5562
|
-
|
|
5563
|
-
|
|
5564
|
-
const attributeName = (_c = (_b = (_a = getEditorExtension(editor, "uniqueID")) == null ? void 0 : _a.options) == null ? void 0 : _b.attributeName) != null ? _c : "data-id";
|
|
5565
|
-
let position = null;
|
|
5566
|
-
editor.state.doc.descendants((node, pos) => {
|
|
5567
|
-
var _a2;
|
|
5568
|
-
if (((_a2 = node.attrs) == null ? void 0 : _a2[attributeName]) === id) {
|
|
5569
|
-
position = pos;
|
|
5570
|
-
return false;
|
|
5571
|
-
}
|
|
5572
|
-
return true;
|
|
5573
|
-
});
|
|
5574
|
-
if (position === null) return false;
|
|
5575
|
-
selectNodeAndHideFloating(editor, position);
|
|
5576
|
-
setTimeout(() => {
|
|
5577
|
-
let dom = null;
|
|
5578
|
-
if (typeof position === "number") {
|
|
5579
|
-
dom = editor.view.nodeDOM(position);
|
|
5580
|
-
}
|
|
5581
|
-
if (dom) {
|
|
5582
|
-
dom.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
5583
|
-
}
|
|
5584
|
-
}, 0);
|
|
5585
|
-
return true;
|
|
5586
|
-
},
|
|
5587
|
-
[editor]
|
|
5588
|
-
);
|
|
5589
|
-
const handleScroll = React9.useCallback(
|
|
5590
|
-
(delay = 0) => {
|
|
5591
|
-
var _a;
|
|
5592
|
-
const hash = (_a = window.location.hash) == null ? void 0 : _a.substring(1);
|
|
5593
|
-
if (!hash) return;
|
|
5594
|
-
setTimeout(() => {
|
|
5595
|
-
if (scrollToNode(hash)) {
|
|
5596
|
-
onTargetFound(hash);
|
|
5597
|
-
} else {
|
|
5598
|
-
onTargetNotFound(hash);
|
|
5599
|
-
}
|
|
5600
|
-
}, delay);
|
|
5601
|
-
},
|
|
5602
|
-
[scrollToNode, onTargetFound, onTargetNotFound]
|
|
5603
|
-
);
|
|
5604
|
-
React9.useEffect(() => {
|
|
5605
|
-
var _a, _b;
|
|
5606
|
-
if (!editor) return;
|
|
5607
|
-
const provider = (_b = (_a = editor.extensionManager.extensions.find(
|
|
5608
|
-
(ext) => ext.name === "collaborationCaret"
|
|
5609
|
-
)) == null ? void 0 : _a.options) == null ? void 0 : _b.provider;
|
|
5610
|
-
if (provider == null ? void 0 : provider.on) {
|
|
5611
|
-
const syncHandler = () => handleScroll(500);
|
|
5612
|
-
provider.on("synced", syncHandler);
|
|
5613
|
-
return () => {
|
|
5614
|
-
var _a2;
|
|
5615
|
-
(_a2 = provider.off) == null ? void 0 : _a2.call(provider, "synced", syncHandler);
|
|
5616
|
-
};
|
|
5617
|
-
} else {
|
|
5618
|
-
handleScroll(500);
|
|
5279
|
+
// src/utils/index.ts
|
|
5280
|
+
var MAX_FILE_SIZE = 5 * 1024 * 1024;
|
|
5281
|
+
var handleImageUpload = async (file, onProgress, abortSignal) => {
|
|
5282
|
+
if (!file) {
|
|
5283
|
+
throw new Error("No file provided");
|
|
5284
|
+
}
|
|
5285
|
+
if (file.size > MAX_FILE_SIZE) {
|
|
5286
|
+
throw new Error(
|
|
5287
|
+
`File size exceeds maximum allowed (${MAX_FILE_SIZE / (1024 * 1024)}MB)`
|
|
5288
|
+
);
|
|
5289
|
+
}
|
|
5290
|
+
for (let progress = 0; progress <= 100; progress += 10) {
|
|
5291
|
+
if (abortSignal == null ? void 0 : abortSignal.aborted) {
|
|
5292
|
+
throw new Error("Upload cancelled");
|
|
5619
5293
|
}
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
return (
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
|
|
5634
|
-
}
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
import { IconButton as IconButton7 } from "@kopexa/button";
|
|
5638
|
-
import { EditIcon as EditIcon3, ExternalLinkIcon as ExternalLinkIcon2, TrashIcon as TrashIcon4 } from "@kopexa/icons";
|
|
5639
|
-
import { Input as Input4 } from "@kopexa/input";
|
|
5640
|
-
import { BubbleMenu as TiptapBubbleMenu2 } from "@tiptap/react/menus";
|
|
5641
|
-
import { useCallback as useCallback18, useEffect as useEffect19, useState as useState17 } from "react";
|
|
5642
|
-
import { useIntl as useIntl10 } from "react-intl";
|
|
5643
|
-
import { Fragment as Fragment3, jsx as jsx19, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
5644
|
-
function LinkBubble({ editor }) {
|
|
5645
|
-
const intl = useIntl10();
|
|
5646
|
-
const [isEditing, setIsEditing] = useState17(false);
|
|
5647
|
-
const [url, setUrl] = useState17("");
|
|
5648
|
-
const getCurrentUrl = useCallback18(() => {
|
|
5649
|
-
if (!editor) return "";
|
|
5650
|
-
const attrs = editor.getAttributes("link");
|
|
5651
|
-
return attrs.href || "";
|
|
5652
|
-
}, [editor]);
|
|
5653
|
-
useEffect19(() => {
|
|
5654
|
-
const isLinkActive2 = editor == null ? void 0 : editor.isActive("link");
|
|
5655
|
-
if (isLinkActive2) {
|
|
5656
|
-
setUrl(getCurrentUrl());
|
|
5657
|
-
setIsEditing(false);
|
|
5658
|
-
}
|
|
5659
|
-
}, [editor, getCurrentUrl]);
|
|
5660
|
-
const handleOpenLink = useCallback18(() => {
|
|
5661
|
-
const href = getCurrentUrl();
|
|
5662
|
-
if (href) {
|
|
5663
|
-
window.open(href, "_blank", "noopener,noreferrer");
|
|
5664
|
-
}
|
|
5665
|
-
}, [getCurrentUrl]);
|
|
5666
|
-
const handleRemoveLink = useCallback18(() => {
|
|
5667
|
-
editor == null ? void 0 : editor.chain().focus().unsetLink().run();
|
|
5668
|
-
}, [editor]);
|
|
5669
|
-
const handleEdit = useCallback18(() => {
|
|
5670
|
-
setUrl(getCurrentUrl());
|
|
5671
|
-
setIsEditing(true);
|
|
5672
|
-
}, [getCurrentUrl]);
|
|
5673
|
-
const handleSave = useCallback18(() => {
|
|
5674
|
-
if (url) {
|
|
5675
|
-
editor == null ? void 0 : editor.chain().focus().extendMarkRange("link").setLink({ href: url }).run();
|
|
5676
|
-
} else {
|
|
5677
|
-
editor == null ? void 0 : editor.chain().focus().unsetLink().run();
|
|
5294
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
5295
|
+
onProgress == null ? void 0 : onProgress({ progress });
|
|
5296
|
+
}
|
|
5297
|
+
return "/images/placeholder-image.png";
|
|
5298
|
+
};
|
|
5299
|
+
var convertFileToBase64 = (file, abortSignal) => {
|
|
5300
|
+
if (!file) {
|
|
5301
|
+
return Promise.reject(new Error("No file provided"));
|
|
5302
|
+
}
|
|
5303
|
+
return new Promise((resolve, reject) => {
|
|
5304
|
+
const reader = new FileReader();
|
|
5305
|
+
const abortHandler = () => {
|
|
5306
|
+
reader.abort();
|
|
5307
|
+
reject(new Error("Upload cancelled"));
|
|
5308
|
+
};
|
|
5309
|
+
if (abortSignal) {
|
|
5310
|
+
abortSignal.addEventListener("abort", abortHandler);
|
|
5678
5311
|
}
|
|
5679
|
-
|
|
5680
|
-
|
|
5681
|
-
|
|
5682
|
-
|
|
5683
|
-
if (
|
|
5684
|
-
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
|
|
5688
|
-
|
|
5689
|
-
|
|
5312
|
+
reader.onloadend = () => {
|
|
5313
|
+
if (abortSignal) {
|
|
5314
|
+
abortSignal.removeEventListener("abort", abortHandler);
|
|
5315
|
+
}
|
|
5316
|
+
if (typeof reader.result === "string") {
|
|
5317
|
+
resolve(reader.result);
|
|
5318
|
+
} else {
|
|
5319
|
+
reject(new Error("Failed to convert File to base64"));
|
|
5320
|
+
}
|
|
5321
|
+
};
|
|
5322
|
+
reader.onerror = (error) => reject(new Error(`File reading error: ${error}`));
|
|
5323
|
+
reader.readAsDataURL(file);
|
|
5324
|
+
});
|
|
5325
|
+
};
|
|
5326
|
+
var ATTR_WHITESPACE = (
|
|
5327
|
+
// eslint-disable-next-line no-control-regex
|
|
5328
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: we can do this yay
|
|
5329
|
+
/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g
|
|
5330
|
+
);
|
|
5331
|
+
function isAllowedUri(uri, protocols) {
|
|
5332
|
+
const allowedProtocols = [
|
|
5333
|
+
"http",
|
|
5334
|
+
"https",
|
|
5335
|
+
"ftp",
|
|
5336
|
+
"ftps",
|
|
5337
|
+
"mailto",
|
|
5338
|
+
"tel",
|
|
5339
|
+
"callto",
|
|
5340
|
+
"sms",
|
|
5341
|
+
"cid",
|
|
5342
|
+
"xmpp"
|
|
5343
|
+
];
|
|
5344
|
+
if (protocols) {
|
|
5345
|
+
for (const protocol of protocols) {
|
|
5346
|
+
const nextProtocol = typeof protocol === "string" ? protocol : protocol.scheme;
|
|
5347
|
+
if (nextProtocol) {
|
|
5348
|
+
allowedProtocols.push(nextProtocol);
|
|
5690
5349
|
}
|
|
5691
|
-
},
|
|
5692
|
-
[handleSave, getCurrentUrl]
|
|
5693
|
-
);
|
|
5694
|
-
if (!editor) {
|
|
5695
|
-
return null;
|
|
5696
|
-
}
|
|
5697
|
-
return /* @__PURE__ */ jsx19(
|
|
5698
|
-
TiptapBubbleMenu2,
|
|
5699
|
-
{
|
|
5700
|
-
editor,
|
|
5701
|
-
pluginKey: "linkBubbleMenu",
|
|
5702
|
-
shouldShow: ({ editor: e, view }) => {
|
|
5703
|
-
if (!view.hasFocus()) return false;
|
|
5704
|
-
return e.isActive("link") && e.isEditable;
|
|
5705
|
-
},
|
|
5706
|
-
options: {
|
|
5707
|
-
placement: "bottom-start",
|
|
5708
|
-
offset: 8
|
|
5709
|
-
},
|
|
5710
|
-
className: "rounded-lg border bg-background shadow-md",
|
|
5711
|
-
children: /* @__PURE__ */ jsx19("div", { className: "flex items-center gap-1 p-1.5 min-w-[280px]", children: isEditing ? /* @__PURE__ */ jsxs14(Fragment3, { children: [
|
|
5712
|
-
/* @__PURE__ */ jsx19(
|
|
5713
|
-
Input4,
|
|
5714
|
-
{
|
|
5715
|
-
type: "url",
|
|
5716
|
-
value: url,
|
|
5717
|
-
onChange: (e) => setUrl(e.target.value),
|
|
5718
|
-
onKeyDown: handleKeyDown,
|
|
5719
|
-
placeholder: intl.formatMessage(messages7.link_placeholder),
|
|
5720
|
-
className: "flex-1 h-8 text-sm",
|
|
5721
|
-
autoFocus: true
|
|
5722
|
-
}
|
|
5723
|
-
),
|
|
5724
|
-
/* @__PURE__ */ jsx19(
|
|
5725
|
-
IconButton7,
|
|
5726
|
-
{
|
|
5727
|
-
type: "button",
|
|
5728
|
-
size: "sm",
|
|
5729
|
-
variant: "ghost",
|
|
5730
|
-
onClick: handleSave,
|
|
5731
|
-
"aria-label": intl.formatMessage(messages7.link_save),
|
|
5732
|
-
children: /* @__PURE__ */ jsx19(EditIcon3, { className: "size-4" })
|
|
5733
|
-
}
|
|
5734
|
-
)
|
|
5735
|
-
] }) : /* @__PURE__ */ jsxs14(Fragment3, { children: [
|
|
5736
|
-
/* @__PURE__ */ jsx19(
|
|
5737
|
-
"a",
|
|
5738
|
-
{
|
|
5739
|
-
href: getCurrentUrl(),
|
|
5740
|
-
target: "_blank",
|
|
5741
|
-
rel: "noopener noreferrer",
|
|
5742
|
-
className: "flex-1 text-sm text-primary truncate max-w-[200px] hover:underline px-2",
|
|
5743
|
-
onClick: (e) => {
|
|
5744
|
-
e.preventDefault();
|
|
5745
|
-
handleOpenLink();
|
|
5746
|
-
},
|
|
5747
|
-
children: getCurrentUrl()
|
|
5748
|
-
}
|
|
5749
|
-
),
|
|
5750
|
-
/* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-0.5 border-l pl-1 ml-1", children: [
|
|
5751
|
-
/* @__PURE__ */ jsx19(
|
|
5752
|
-
IconButton7,
|
|
5753
|
-
{
|
|
5754
|
-
type: "button",
|
|
5755
|
-
size: "sm",
|
|
5756
|
-
variant: "ghost",
|
|
5757
|
-
onClick: handleOpenLink,
|
|
5758
|
-
"aria-label": intl.formatMessage(messages7.link_open),
|
|
5759
|
-
children: /* @__PURE__ */ jsx19(ExternalLinkIcon2, { className: "size-4" })
|
|
5760
|
-
}
|
|
5761
|
-
),
|
|
5762
|
-
/* @__PURE__ */ jsx19(
|
|
5763
|
-
IconButton7,
|
|
5764
|
-
{
|
|
5765
|
-
type: "button",
|
|
5766
|
-
size: "sm",
|
|
5767
|
-
variant: "ghost",
|
|
5768
|
-
onClick: handleEdit,
|
|
5769
|
-
"aria-label": intl.formatMessage(messages7.link_edit),
|
|
5770
|
-
children: /* @__PURE__ */ jsx19(EditIcon3, { className: "size-4" })
|
|
5771
|
-
}
|
|
5772
|
-
),
|
|
5773
|
-
/* @__PURE__ */ jsx19(
|
|
5774
|
-
IconButton7,
|
|
5775
|
-
{
|
|
5776
|
-
type: "button",
|
|
5777
|
-
size: "sm",
|
|
5778
|
-
variant: "ghost",
|
|
5779
|
-
onClick: handleRemoveLink,
|
|
5780
|
-
"aria-label": intl.formatMessage(messages7.link_remove),
|
|
5781
|
-
children: /* @__PURE__ */ jsx19(TrashIcon4, { className: "size-4" })
|
|
5782
|
-
}
|
|
5783
|
-
)
|
|
5784
|
-
] })
|
|
5785
|
-
] }) })
|
|
5786
5350
|
}
|
|
5351
|
+
}
|
|
5352
|
+
return !uri || uri.replace(ATTR_WHITESPACE, "").match(
|
|
5353
|
+
new RegExp(
|
|
5354
|
+
// eslint-disable-next-line no-useless-escape
|
|
5355
|
+
`^(?:(?:${allowedProtocols.join("|")}):|[^a-z]|[a-z0-9+.-]+(?:[^a-z+.-:]|$))`,
|
|
5356
|
+
"i"
|
|
5357
|
+
)
|
|
5787
5358
|
);
|
|
5788
5359
|
}
|
|
5789
|
-
|
|
5790
|
-
// src/ui/slash-dropdown-menu/slash-dropdown-menu.tsx
|
|
5791
|
-
import { Button as Button6 } from "@kopexa/button";
|
|
5792
|
-
import { getElementOverflowPosition as getElementOverflowPosition2 } from "@kopexa/editor-utils";
|
|
5793
|
-
import { Separator } from "@kopexa/separator";
|
|
5794
|
-
import { slashDropdownMenu as slashDropdownMenu2 } from "@kopexa/theme";
|
|
5795
|
-
import * as React11 from "react";
|
|
5796
|
-
|
|
5797
|
-
// src/ui/slash-dropdown-menu/use-slash-dropdown-menu.ts
|
|
5798
|
-
import {
|
|
5799
|
-
findSelectionPosition,
|
|
5800
|
-
hasContentAbove,
|
|
5801
|
-
isExtensionAvailable,
|
|
5802
|
-
isNodeInSchema as isNodeInSchema2
|
|
5803
|
-
} from "@kopexa/editor-utils";
|
|
5804
|
-
import {
|
|
5805
|
-
AiSparklesIcon,
|
|
5806
|
-
AlertIcon as AlertIcon2,
|
|
5807
|
-
BlockquoteIcon,
|
|
5808
|
-
CodeBlockIcon,
|
|
5809
|
-
ControlsIcon,
|
|
5810
|
-
HeadingOneIcon,
|
|
5811
|
-
HeadingThreeIcon,
|
|
5812
|
-
HeadingTwoIcon,
|
|
5813
|
-
ImageIcon as ImageIcon2,
|
|
5814
|
-
InfoIcon as InfoIcon2,
|
|
5815
|
-
ListIcon as ListIcon2,
|
|
5816
|
-
ListOrderedIcon,
|
|
5817
|
-
ListTodoIcon,
|
|
5818
|
-
MinusIcon,
|
|
5819
|
-
TableIcon as TableIcon2,
|
|
5820
|
-
TableOfContentsIcon,
|
|
5821
|
-
TypeIcon
|
|
5822
|
-
} from "@kopexa/icons";
|
|
5823
|
-
import * as React10 from "react";
|
|
5824
|
-
import { useIntl as useIntl12 } from "react-intl";
|
|
5825
|
-
|
|
5826
|
-
// src/ui/table-button/use-table.ts
|
|
5827
|
-
import {
|
|
5828
|
-
isNodeInSchema,
|
|
5829
|
-
isNodeTypeSelected as isNodeTypeSelected2,
|
|
5830
|
-
useTiptapEditor as useTiptapEditor6
|
|
5831
|
-
} from "@kopexa/editor-utils";
|
|
5832
|
-
import { TableIcon } from "@kopexa/icons";
|
|
5833
|
-
import { isNodeSelection as isNodeSelection4 } from "@tiptap/react";
|
|
5834
|
-
import { useCallback as useCallback19, useEffect as useEffect20, useState as useState18 } from "react";
|
|
5835
|
-
import { useIntl as useIntl11 } from "react-intl";
|
|
5836
|
-
function canToggle(editor) {
|
|
5837
|
-
if (!editor || !editor.isEditable) return false;
|
|
5838
|
-
if (!isNodeInSchema("table", editor) || isNodeTypeSelected2(editor, ["image"])) {
|
|
5839
|
-
return false;
|
|
5840
|
-
}
|
|
5360
|
+
function sanitizeUrl(inputUrl, baseUrl, protocols) {
|
|
5841
5361
|
try {
|
|
5842
|
-
|
|
5362
|
+
const url = new URL(inputUrl, baseUrl);
|
|
5363
|
+
if (isAllowedUri(url.href, protocols)) {
|
|
5364
|
+
return url.href;
|
|
5365
|
+
}
|
|
5843
5366
|
} catch {
|
|
5844
|
-
return false;
|
|
5845
5367
|
}
|
|
5368
|
+
return "#";
|
|
5846
5369
|
}
|
|
5847
|
-
|
|
5848
|
-
|
|
5370
|
+
|
|
5371
|
+
// src/ui/link-popover/use-link-popover.ts
|
|
5372
|
+
function canSetLink(editor) {
|
|
5849
5373
|
if (!editor || !editor.isEditable) return false;
|
|
5850
|
-
|
|
5851
|
-
try {
|
|
5852
|
-
return editor.chain().focus().insertTable({
|
|
5853
|
-
rows: (config == null ? void 0 : config.rows) || 3,
|
|
5854
|
-
cols: (config == null ? void 0 : config.cols) || 3,
|
|
5855
|
-
withHeaderRow: (_a = config == null ? void 0 : config.withHeaderRow) != null ? _a : true
|
|
5856
|
-
}).run();
|
|
5857
|
-
} catch {
|
|
5858
|
-
return false;
|
|
5859
|
-
}
|
|
5374
|
+
return editor.can().setMark("link");
|
|
5860
5375
|
}
|
|
5861
|
-
function
|
|
5862
|
-
const { editor, hideWhenUnavailable } = props;
|
|
5376
|
+
function isLinkActive(editor) {
|
|
5863
5377
|
if (!editor || !editor.isEditable) return false;
|
|
5864
|
-
|
|
5865
|
-
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5378
|
+
return editor.isActive("link");
|
|
5379
|
+
}
|
|
5380
|
+
function shouldShowLinkButton(props) {
|
|
5381
|
+
const { editor, hideWhenUnavailable } = props;
|
|
5382
|
+
const linkInSchema = isMarkInSchema2("link", editor);
|
|
5383
|
+
if (!linkInSchema || !editor) {
|
|
5384
|
+
return false;
|
|
5385
|
+
}
|
|
5386
|
+
if (hideWhenUnavailable && !editor.isActive("code")) {
|
|
5387
|
+
return canSetLink(editor);
|
|
5869
5388
|
}
|
|
5870
5389
|
return true;
|
|
5871
5390
|
}
|
|
5872
|
-
function
|
|
5873
|
-
const {
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
|
|
5878
|
-
|
|
5879
|
-
|
|
5880
|
-
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
|
|
5391
|
+
function useLinkHandler(props) {
|
|
5392
|
+
const { editor, onSetLink } = props;
|
|
5393
|
+
const [url, setUrl] = React8.useState(null);
|
|
5394
|
+
React8.useEffect(() => {
|
|
5395
|
+
if (!editor) return;
|
|
5396
|
+
const { href } = editor.getAttributes("link");
|
|
5397
|
+
if (isLinkActive(editor) && url === null) {
|
|
5398
|
+
setUrl(href || "");
|
|
5399
|
+
}
|
|
5400
|
+
}, [editor, url]);
|
|
5401
|
+
React8.useEffect(() => {
|
|
5402
|
+
if (!editor) return;
|
|
5403
|
+
const updateLinkState = () => {
|
|
5404
|
+
const { href } = editor.getAttributes("link");
|
|
5405
|
+
setUrl(href || "");
|
|
5406
|
+
};
|
|
5407
|
+
editor.on("selectionUpdate", updateLinkState);
|
|
5408
|
+
return () => {
|
|
5409
|
+
editor.off("selectionUpdate", updateLinkState);
|
|
5410
|
+
};
|
|
5411
|
+
}, [editor]);
|
|
5412
|
+
const setLink = React8.useCallback(() => {
|
|
5413
|
+
if (!url || !editor) return;
|
|
5414
|
+
const { selection } = editor.state;
|
|
5415
|
+
const isEmpty = selection.empty;
|
|
5416
|
+
let chain = editor.chain().focus();
|
|
5417
|
+
chain = chain.extendMarkRange("link").setLink({ href: url });
|
|
5418
|
+
if (isEmpty) {
|
|
5419
|
+
chain = chain.insertContent({ type: "text", text: url });
|
|
5420
|
+
}
|
|
5421
|
+
chain.run();
|
|
5422
|
+
setUrl(null);
|
|
5423
|
+
onSetLink == null ? void 0 : onSetLink();
|
|
5424
|
+
}, [editor, onSetLink, url]);
|
|
5425
|
+
const removeLink = React8.useCallback(() => {
|
|
5426
|
+
if (!editor) return;
|
|
5427
|
+
editor.chain().focus().extendMarkRange("link").unsetLink().setMeta("preventAutolink", true).run();
|
|
5428
|
+
setUrl("");
|
|
5429
|
+
}, [editor]);
|
|
5430
|
+
const openLink = React8.useCallback(
|
|
5431
|
+
(target = "_blank", features = "noopener,noreferrer") => {
|
|
5432
|
+
if (!url) return;
|
|
5433
|
+
const safeUrl = sanitizeUrl(url, window.location.href);
|
|
5434
|
+
if (safeUrl !== "#") {
|
|
5435
|
+
window.open(safeUrl, target, features);
|
|
5436
|
+
}
|
|
5437
|
+
},
|
|
5438
|
+
[url]
|
|
5439
|
+
);
|
|
5440
|
+
return {
|
|
5441
|
+
url: url || "",
|
|
5442
|
+
setUrl,
|
|
5443
|
+
setLink,
|
|
5444
|
+
removeLink,
|
|
5445
|
+
openLink
|
|
5446
|
+
};
|
|
5447
|
+
}
|
|
5448
|
+
function useLinkState(props) {
|
|
5449
|
+
const { editor, hideWhenUnavailable = false } = props;
|
|
5450
|
+
const canSet = canSetLink(editor);
|
|
5451
|
+
const isActive = isLinkActive(editor);
|
|
5452
|
+
const [isVisible, setIsVisible] = React8.useState(false);
|
|
5453
|
+
React8.useEffect(() => {
|
|
5884
5454
|
if (!editor) return;
|
|
5885
5455
|
const handleSelectionUpdate = () => {
|
|
5886
|
-
setIsVisible(
|
|
5456
|
+
setIsVisible(
|
|
5457
|
+
shouldShowLinkButton({
|
|
5458
|
+
editor,
|
|
5459
|
+
hideWhenUnavailable
|
|
5460
|
+
})
|
|
5461
|
+
);
|
|
5887
5462
|
};
|
|
5888
5463
|
handleSelectionUpdate();
|
|
5889
5464
|
editor.on("selectionUpdate", handleSelectionUpdate);
|
|
@@ -5891,1078 +5466,1526 @@ function useTableBlock(config) {
|
|
|
5891
5466
|
editor.off("selectionUpdate", handleSelectionUpdate);
|
|
5892
5467
|
};
|
|
5893
5468
|
}, [editor, hideWhenUnavailable]);
|
|
5894
|
-
const handleToggle = useCallback19(() => {
|
|
5895
|
-
if (!editor) return false;
|
|
5896
|
-
const success = toggleTable(editor);
|
|
5897
|
-
if (success) {
|
|
5898
|
-
onToggled == null ? void 0 : onToggled();
|
|
5899
|
-
}
|
|
5900
|
-
return success;
|
|
5901
|
-
}, [editor, onToggled]);
|
|
5902
5469
|
return {
|
|
5903
5470
|
isVisible,
|
|
5471
|
+
canSet,
|
|
5472
|
+
isActive
|
|
5473
|
+
};
|
|
5474
|
+
}
|
|
5475
|
+
function useLinkPopover(config) {
|
|
5476
|
+
const {
|
|
5477
|
+
editor: providedEditor,
|
|
5478
|
+
hideWhenUnavailable = false,
|
|
5479
|
+
onSetLink
|
|
5480
|
+
} = config || {};
|
|
5481
|
+
const { editor } = useTiptapEditor5(providedEditor);
|
|
5482
|
+
const { isVisible, canSet, isActive } = useLinkState({
|
|
5483
|
+
editor,
|
|
5484
|
+
hideWhenUnavailable
|
|
5485
|
+
});
|
|
5486
|
+
const linkHandler = useLinkHandler({
|
|
5487
|
+
editor,
|
|
5488
|
+
onSetLink
|
|
5489
|
+
});
|
|
5490
|
+
return {
|
|
5491
|
+
isVisible,
|
|
5492
|
+
canSet,
|
|
5904
5493
|
isActive,
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
// shortcutKeys: CODE_BLOCK_SHORTCUT_KEY,
|
|
5909
|
-
Icon: TableIcon
|
|
5494
|
+
label: "Link",
|
|
5495
|
+
Icon: LinkIcon,
|
|
5496
|
+
...linkHandler
|
|
5910
5497
|
};
|
|
5911
5498
|
}
|
|
5912
5499
|
|
|
5913
|
-
// src/ui/
|
|
5914
|
-
|
|
5915
|
-
|
|
5916
|
-
|
|
5917
|
-
|
|
5918
|
-
|
|
5919
|
-
|
|
5920
|
-
|
|
5921
|
-
|
|
5922
|
-
|
|
5923
|
-
|
|
5924
|
-
|
|
5925
|
-
title:
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
|
|
5945
|
-
|
|
5946
|
-
|
|
5947
|
-
|
|
5948
|
-
|
|
5949
|
-
|
|
5950
|
-
|
|
5951
|
-
|
|
5952
|
-
|
|
5953
|
-
|
|
5954
|
-
title: formatMessage(messages7.slash_heading_3),
|
|
5955
|
-
subtext: formatMessage(messages7.slash_heading_3_subtext),
|
|
5956
|
-
keywords: ["h3", "heading3", "subheading"],
|
|
5957
|
-
badge: HeadingThreeIcon,
|
|
5958
|
-
group: formatMessage(messages7.group_style)
|
|
5959
|
-
},
|
|
5960
|
-
bullet_list: {
|
|
5961
|
-
title: formatMessage(messages7.slash_bullet_list),
|
|
5962
|
-
subtext: formatMessage(messages7.slash_bullet_list_subtext),
|
|
5963
|
-
keywords: ["ul", "li", "list", "bulletlist", "bullet list"],
|
|
5964
|
-
badge: ListIcon2,
|
|
5965
|
-
group: formatMessage(messages7.group_style)
|
|
5966
|
-
},
|
|
5967
|
-
ordered_list: {
|
|
5968
|
-
title: formatMessage(messages7.slash_ordered_list),
|
|
5969
|
-
subtext: formatMessage(messages7.slash_ordered_list_subtext),
|
|
5970
|
-
keywords: ["ol", "li", "list", "numberedlist", "numbered list"],
|
|
5971
|
-
badge: ListOrderedIcon,
|
|
5972
|
-
group: formatMessage(messages7.group_style)
|
|
5973
|
-
},
|
|
5974
|
-
task_list: {
|
|
5975
|
-
title: formatMessage(messages7.slash_task_list),
|
|
5976
|
-
subtext: formatMessage(messages7.slash_task_list_subtext),
|
|
5977
|
-
keywords: ["tasklist", "task list", "todo", "checklist"],
|
|
5978
|
-
badge: ListTodoIcon,
|
|
5979
|
-
group: formatMessage(messages7.group_style)
|
|
5980
|
-
},
|
|
5981
|
-
quote: {
|
|
5982
|
-
title: formatMessage(messages7.slash_blockquote),
|
|
5983
|
-
subtext: formatMessage(messages7.slash_blockquote_subtext),
|
|
5984
|
-
keywords: ["quote", "blockquote"],
|
|
5985
|
-
badge: BlockquoteIcon,
|
|
5986
|
-
group: formatMessage(messages7.group_style)
|
|
5987
|
-
},
|
|
5988
|
-
code_block: {
|
|
5989
|
-
title: formatMessage(messages7.slash_code_block),
|
|
5990
|
-
subtext: formatMessage(messages7.slash_code_block_subtext),
|
|
5991
|
-
keywords: ["code", "pre"],
|
|
5992
|
-
badge: CodeBlockIcon,
|
|
5993
|
-
group: formatMessage(messages7.group_style)
|
|
5994
|
-
},
|
|
5995
|
-
// Insert
|
|
5996
|
-
control: {
|
|
5997
|
-
title: formatMessage(messages7.slash_control),
|
|
5998
|
-
subtext: formatMessage(messages7.slash_control_subtext),
|
|
5999
|
-
keywords: ["control"],
|
|
6000
|
-
badge: ControlsIcon,
|
|
6001
|
-
group: formatMessage(messages7.group_insert)
|
|
6002
|
-
},
|
|
6003
|
-
divider: {
|
|
6004
|
-
title: formatMessage(messages7.slash_separator),
|
|
6005
|
-
subtext: formatMessage(messages7.slash_separator_subtext),
|
|
6006
|
-
keywords: ["hr", "horizontalRule", "line", "separator"],
|
|
6007
|
-
badge: MinusIcon,
|
|
6008
|
-
group: formatMessage(messages7.group_insert)
|
|
6009
|
-
},
|
|
6010
|
-
table: {
|
|
6011
|
-
title: formatMessage(messages7.slash_table),
|
|
6012
|
-
subtext: formatMessage(messages7.slash_table_subtext),
|
|
6013
|
-
keywords: ["table", "grid", "spreadsheet"],
|
|
6014
|
-
badge: TableIcon2,
|
|
6015
|
-
group: formatMessage(messages7.group_insert)
|
|
6016
|
-
},
|
|
6017
|
-
table_of_contents: {
|
|
6018
|
-
title: formatMessage(messages7.slash_toc),
|
|
6019
|
-
subtext: formatMessage(messages7.slash_toc_subtext),
|
|
6020
|
-
keywords: ["toc", "table of contents", "index", "navigation", "headings"],
|
|
6021
|
-
badge: TableOfContentsIcon,
|
|
6022
|
-
group: formatMessage(messages7.group_insert)
|
|
6023
|
-
},
|
|
6024
|
-
callout: {
|
|
6025
|
-
title: formatMessage(messages7.slash_callout),
|
|
6026
|
-
subtext: formatMessage(messages7.slash_callout_subtext),
|
|
6027
|
-
keywords: ["callout", "info", "warning", "alert", "note", "tip"],
|
|
6028
|
-
badge: InfoIcon2,
|
|
6029
|
-
group: formatMessage(messages7.group_insert)
|
|
6030
|
-
},
|
|
6031
|
-
callout_warning: {
|
|
6032
|
-
title: formatMessage(messages7.slash_warning),
|
|
6033
|
-
subtext: formatMessage(messages7.slash_warning_subtext),
|
|
6034
|
-
keywords: ["warning", "caution", "attention"],
|
|
6035
|
-
badge: AlertIcon2,
|
|
6036
|
-
group: formatMessage(messages7.group_insert)
|
|
6037
|
-
},
|
|
6038
|
-
math: {
|
|
6039
|
-
title: formatMessage(messages7.slash_formula),
|
|
6040
|
-
subtext: formatMessage(messages7.slash_formula_subtext),
|
|
6041
|
-
keywords: ["math", "latex", "formula", "equation", "katex"],
|
|
6042
|
-
badge: TypeIcon,
|
|
6043
|
-
group: formatMessage(messages7.group_insert)
|
|
6044
|
-
},
|
|
6045
|
-
// Upload
|
|
6046
|
-
image: {
|
|
6047
|
-
title: formatMessage(messages7.slash_image),
|
|
6048
|
-
subtext: formatMessage(messages7.slash_image_subtext),
|
|
6049
|
-
keywords: [
|
|
6050
|
-
"image",
|
|
6051
|
-
"imageUpload",
|
|
6052
|
-
"upload",
|
|
6053
|
-
"img",
|
|
6054
|
-
"picture",
|
|
6055
|
-
"media",
|
|
6056
|
-
"url"
|
|
6057
|
-
],
|
|
6058
|
-
badge: ImageIcon2,
|
|
6059
|
-
group: formatMessage(messages7.group_upload)
|
|
5500
|
+
// src/ui/link-popover/link-popover.tsx
|
|
5501
|
+
import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
5502
|
+
var LinkButton = ({ className, children, ...props }) => {
|
|
5503
|
+
return /* @__PURE__ */ jsx18(
|
|
5504
|
+
ToolbarButton2,
|
|
5505
|
+
{
|
|
5506
|
+
type: "button",
|
|
5507
|
+
className,
|
|
5508
|
+
variant: "ghost",
|
|
5509
|
+
color: "default",
|
|
5510
|
+
tabIndex: -1,
|
|
5511
|
+
"aria-label": "Link",
|
|
5512
|
+
title: "Link",
|
|
5513
|
+
isIconOnly: !children,
|
|
5514
|
+
...props,
|
|
5515
|
+
children: children || /* @__PURE__ */ jsx18(LinkIcon2, {})
|
|
5516
|
+
}
|
|
5517
|
+
);
|
|
5518
|
+
};
|
|
5519
|
+
var LinkMain = ({
|
|
5520
|
+
url,
|
|
5521
|
+
setUrl,
|
|
5522
|
+
setLink,
|
|
5523
|
+
removeLink,
|
|
5524
|
+
openLink,
|
|
5525
|
+
isActive,
|
|
5526
|
+
onSave
|
|
5527
|
+
}) => {
|
|
5528
|
+
const [isEditing, setIsEditing] = useState17(!isActive || !url);
|
|
5529
|
+
useEffect17(() => {
|
|
5530
|
+
setIsEditing(!isActive || !url);
|
|
5531
|
+
}, [isActive, url]);
|
|
5532
|
+
const handleKeyDown = (event) => {
|
|
5533
|
+
if (event.key === "Enter") {
|
|
5534
|
+
event.preventDefault();
|
|
5535
|
+
setLink();
|
|
5536
|
+
setIsEditing(false);
|
|
5537
|
+
onSave == null ? void 0 : onSave();
|
|
5538
|
+
} else if (event.key === "Escape") {
|
|
5539
|
+
event.preventDefault();
|
|
5540
|
+
setIsEditing(false);
|
|
6060
5541
|
}
|
|
6061
5542
|
};
|
|
6062
|
-
|
|
6063
|
-
|
|
6064
|
-
|
|
6065
|
-
|
|
6066
|
-
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
|
|
6071
|
-
|
|
6072
|
-
|
|
6073
|
-
|
|
6074
|
-
|
|
6075
|
-
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
|
|
6079
|
-
|
|
5543
|
+
const handleSave = () => {
|
|
5544
|
+
setLink();
|
|
5545
|
+
setIsEditing(false);
|
|
5546
|
+
onSave == null ? void 0 : onSave();
|
|
5547
|
+
};
|
|
5548
|
+
const handleEdit = () => {
|
|
5549
|
+
setIsEditing(true);
|
|
5550
|
+
};
|
|
5551
|
+
if (isEditing) {
|
|
5552
|
+
return /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-1 min-w-[280px]", children: [
|
|
5553
|
+
/* @__PURE__ */ jsx18(
|
|
5554
|
+
Input3,
|
|
5555
|
+
{
|
|
5556
|
+
type: "url",
|
|
5557
|
+
placeholder: "Enter URL...",
|
|
5558
|
+
value: url,
|
|
5559
|
+
onChange: (e) => setUrl(e.target.value),
|
|
5560
|
+
onKeyDown: handleKeyDown,
|
|
5561
|
+
autoComplete: "off",
|
|
5562
|
+
autoCorrect: "off",
|
|
5563
|
+
autoCapitalize: "off",
|
|
5564
|
+
className: "flex-1 h-8 text-sm",
|
|
5565
|
+
autoFocus: true
|
|
6080
5566
|
}
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
|
|
6087
|
-
|
|
6088
|
-
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
|
|
6092
|
-
text: prompt
|
|
6093
|
-
}).run();
|
|
6094
|
-
});
|
|
6095
|
-
}
|
|
6096
|
-
},
|
|
6097
|
-
ai_ask_button: {
|
|
6098
|
-
check: (editor) => isExtensionAvailable(editor, ["ai", "aiAdvanced"]),
|
|
6099
|
-
action: ({ editor }) => {
|
|
6100
|
-
const editorChain = editor.chain().focus();
|
|
6101
|
-
const nodeSelectionPosition = findSelectionPosition({ editor });
|
|
6102
|
-
if (nodeSelectionPosition !== null) {
|
|
6103
|
-
editorChain.setNodeSelection(nodeSelectionPosition);
|
|
5567
|
+
),
|
|
5568
|
+
/* @__PURE__ */ jsx18(
|
|
5569
|
+
IconButton7,
|
|
5570
|
+
{
|
|
5571
|
+
type: "button",
|
|
5572
|
+
size: "sm",
|
|
5573
|
+
variant: "ghost",
|
|
5574
|
+
onClick: handleSave,
|
|
5575
|
+
"aria-label": "Save link",
|
|
5576
|
+
disabled: !url,
|
|
5577
|
+
children: /* @__PURE__ */ jsx18(CheckIcon, { className: "size-4" })
|
|
6104
5578
|
}
|
|
6105
|
-
|
|
6106
|
-
|
|
6107
|
-
|
|
6108
|
-
|
|
6109
|
-
|
|
6110
|
-
|
|
6111
|
-
|
|
6112
|
-
|
|
6113
|
-
|
|
6114
|
-
|
|
6115
|
-
|
|
6116
|
-
|
|
6117
|
-
|
|
6118
|
-
|
|
6119
|
-
|
|
6120
|
-
|
|
6121
|
-
},
|
|
6122
|
-
heading_2: {
|
|
6123
|
-
check: (editor) => isNodeInSchema2("heading", editor),
|
|
6124
|
-
action: ({ editor }) => {
|
|
6125
|
-
editor.chain().focus().toggleHeading({ level: 2 }).run();
|
|
6126
|
-
}
|
|
6127
|
-
},
|
|
6128
|
-
heading_3: {
|
|
6129
|
-
check: (editor) => isNodeInSchema2("heading", editor),
|
|
6130
|
-
action: ({ editor }) => {
|
|
6131
|
-
editor.chain().focus().toggleHeading({ level: 3 }).run();
|
|
6132
|
-
}
|
|
6133
|
-
},
|
|
6134
|
-
bullet_list: {
|
|
6135
|
-
check: (editor) => isNodeInSchema2("bulletList", editor),
|
|
6136
|
-
action: ({ editor }) => {
|
|
6137
|
-
editor.chain().focus().toggleBulletList().run();
|
|
6138
|
-
}
|
|
6139
|
-
},
|
|
6140
|
-
ordered_list: {
|
|
6141
|
-
check: (editor) => isNodeInSchema2("orderedList", editor),
|
|
6142
|
-
action: ({ editor }) => {
|
|
6143
|
-
editor.chain().focus().toggleOrderedList().run();
|
|
6144
|
-
}
|
|
6145
|
-
},
|
|
6146
|
-
task_list: {
|
|
6147
|
-
check: (editor) => isNodeInSchema2("taskList", editor),
|
|
6148
|
-
action: ({ editor }) => {
|
|
6149
|
-
editor.chain().focus().toggleTaskList().run();
|
|
5579
|
+
)
|
|
5580
|
+
] });
|
|
5581
|
+
}
|
|
5582
|
+
return /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-1 min-w-[280px]", children: [
|
|
5583
|
+
/* @__PURE__ */ jsx18(
|
|
5584
|
+
"a",
|
|
5585
|
+
{
|
|
5586
|
+
href: url,
|
|
5587
|
+
target: "_blank",
|
|
5588
|
+
rel: "noopener noreferrer",
|
|
5589
|
+
className: "flex-1 text-sm text-primary truncate max-w-[200px] hover:underline px-2",
|
|
5590
|
+
onClick: (e) => {
|
|
5591
|
+
e.preventDefault();
|
|
5592
|
+
openLink();
|
|
5593
|
+
},
|
|
5594
|
+
children: url
|
|
6150
5595
|
}
|
|
5596
|
+
),
|
|
5597
|
+
/* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-0.5 border-l pl-1 ml-1", children: [
|
|
5598
|
+
/* @__PURE__ */ jsx18(
|
|
5599
|
+
IconButton7,
|
|
5600
|
+
{
|
|
5601
|
+
type: "button",
|
|
5602
|
+
size: "sm",
|
|
5603
|
+
variant: "ghost",
|
|
5604
|
+
onClick: openLink,
|
|
5605
|
+
"aria-label": "Open link in new tab",
|
|
5606
|
+
children: /* @__PURE__ */ jsx18(ExternalLinkIcon, { className: "size-4" })
|
|
5607
|
+
}
|
|
5608
|
+
),
|
|
5609
|
+
/* @__PURE__ */ jsx18(
|
|
5610
|
+
IconButton7,
|
|
5611
|
+
{
|
|
5612
|
+
type: "button",
|
|
5613
|
+
size: "sm",
|
|
5614
|
+
variant: "ghost",
|
|
5615
|
+
onClick: handleEdit,
|
|
5616
|
+
"aria-label": "Edit link",
|
|
5617
|
+
children: /* @__PURE__ */ jsx18(EditIcon2, { className: "size-4" })
|
|
5618
|
+
}
|
|
5619
|
+
),
|
|
5620
|
+
/* @__PURE__ */ jsx18(
|
|
5621
|
+
IconButton7,
|
|
5622
|
+
{
|
|
5623
|
+
type: "button",
|
|
5624
|
+
size: "sm",
|
|
5625
|
+
variant: "ghost",
|
|
5626
|
+
onClick: removeLink,
|
|
5627
|
+
"aria-label": "Remove link",
|
|
5628
|
+
children: /* @__PURE__ */ jsx18(TrashIcon3, { className: "size-4" })
|
|
5629
|
+
}
|
|
5630
|
+
)
|
|
5631
|
+
] })
|
|
5632
|
+
] });
|
|
5633
|
+
};
|
|
5634
|
+
function LinkPopover({
|
|
5635
|
+
editor: providedEditor,
|
|
5636
|
+
hideWhenUnavailable = false,
|
|
5637
|
+
onSetLink,
|
|
5638
|
+
onOpenChange,
|
|
5639
|
+
autoOpenOnLinkActive = true,
|
|
5640
|
+
onClick,
|
|
5641
|
+
children,
|
|
5642
|
+
...buttonProps
|
|
5643
|
+
}) {
|
|
5644
|
+
const { editor } = useTiptapEditor6(providedEditor);
|
|
5645
|
+
const [isOpen, setIsOpen] = useState17(false);
|
|
5646
|
+
const {
|
|
5647
|
+
isVisible,
|
|
5648
|
+
canSet,
|
|
5649
|
+
isActive,
|
|
5650
|
+
url,
|
|
5651
|
+
setUrl,
|
|
5652
|
+
setLink,
|
|
5653
|
+
removeLink,
|
|
5654
|
+
openLink,
|
|
5655
|
+
label
|
|
5656
|
+
} = useLinkPopover({
|
|
5657
|
+
editor,
|
|
5658
|
+
hideWhenUnavailable,
|
|
5659
|
+
onSetLink
|
|
5660
|
+
});
|
|
5661
|
+
const handleOnOpenChange = useCallback17(
|
|
5662
|
+
(nextIsOpen) => {
|
|
5663
|
+
setIsOpen(nextIsOpen);
|
|
5664
|
+
onOpenChange == null ? void 0 : onOpenChange(nextIsOpen);
|
|
6151
5665
|
},
|
|
6152
|
-
|
|
6153
|
-
|
|
6154
|
-
|
|
6155
|
-
|
|
6156
|
-
|
|
5666
|
+
[onOpenChange]
|
|
5667
|
+
);
|
|
5668
|
+
const handleSetLink = useCallback17(() => {
|
|
5669
|
+
setLink();
|
|
5670
|
+
setIsOpen(false);
|
|
5671
|
+
}, [setLink]);
|
|
5672
|
+
const handleClick = useCallback17(
|
|
5673
|
+
(event) => {
|
|
5674
|
+
onClick == null ? void 0 : onClick(event);
|
|
5675
|
+
if (event.defaultPrevented) return;
|
|
5676
|
+
setIsOpen(!isOpen);
|
|
6157
5677
|
},
|
|
6158
|
-
|
|
6159
|
-
|
|
6160
|
-
|
|
6161
|
-
|
|
6162
|
-
|
|
6163
|
-
},
|
|
6164
|
-
// Insert
|
|
6165
|
-
// mention: {
|
|
6166
|
-
// check: (editor: Editor) =>
|
|
6167
|
-
// isExtensionAvailable(editor, ["mention", "mentionAdvanced"]),
|
|
6168
|
-
// action: ({ editor }: { editor: Editor }) => addMentionTrigger(editor),
|
|
6169
|
-
// },
|
|
6170
|
-
// emoji: {
|
|
6171
|
-
// check: (editor: Editor) =>
|
|
6172
|
-
// isExtensionAvailable(editor, ["emoji", "emojiPicker"]),
|
|
6173
|
-
// action: ({ editor }: { editor: Editor }) => addEmojiTrigger(editor),
|
|
6174
|
-
// },
|
|
6175
|
-
divider: {
|
|
6176
|
-
check: (editor) => isNodeInSchema2("horizontalRule", editor),
|
|
6177
|
-
action: ({ editor }) => {
|
|
6178
|
-
editor.chain().focus().setHorizontalRule().run();
|
|
6179
|
-
}
|
|
6180
|
-
},
|
|
6181
|
-
table: {
|
|
6182
|
-
check: (editor) => isNodeInSchema2("table", editor),
|
|
6183
|
-
action: ({ editor }) => toggleTable(editor, { rows: 3, cols: 3, withHeaderRow: true })
|
|
6184
|
-
},
|
|
6185
|
-
control: {
|
|
6186
|
-
check: (editor) => isNodeInSchema2("controlBlock", editor),
|
|
6187
|
-
action: ({ editor }) => {
|
|
6188
|
-
try {
|
|
6189
|
-
return editor.chain().focus().insertControlBlock().run();
|
|
6190
|
-
} catch (e) {
|
|
6191
|
-
console.error(e);
|
|
6192
|
-
}
|
|
6193
|
-
}
|
|
6194
|
-
},
|
|
6195
|
-
table_of_contents: {
|
|
6196
|
-
check: (editor) => isNodeInSchema2("tableOfContentsNode", editor),
|
|
6197
|
-
action: ({ editor }) => {
|
|
6198
|
-
editor.chain().focus().insertTableOfContents().run();
|
|
6199
|
-
}
|
|
6200
|
-
},
|
|
6201
|
-
callout: {
|
|
6202
|
-
check: (editor) => isNodeInSchema2("calloutNode", editor),
|
|
6203
|
-
action: ({ editor }) => {
|
|
6204
|
-
editor.chain().focus().insertCallout("info").run();
|
|
6205
|
-
}
|
|
6206
|
-
},
|
|
6207
|
-
callout_warning: {
|
|
6208
|
-
check: (editor) => isNodeInSchema2("calloutNode", editor),
|
|
6209
|
-
action: ({ editor }) => {
|
|
6210
|
-
editor.chain().focus().insertCallout("warning").run();
|
|
6211
|
-
}
|
|
6212
|
-
},
|
|
6213
|
-
math: {
|
|
6214
|
-
check: (editor) => isNodeInSchema2("mathBlock", editor),
|
|
6215
|
-
action: ({ editor }) => {
|
|
6216
|
-
editor.chain().focus().insertMathBlock().run();
|
|
6217
|
-
}
|
|
6218
|
-
},
|
|
6219
|
-
// Upload
|
|
6220
|
-
image: {
|
|
6221
|
-
check: (editor) => isNodeInSchema2("imageUpload", editor),
|
|
6222
|
-
action: ({ editor }) => {
|
|
6223
|
-
editor.chain().focus().setImageUpload().run();
|
|
6224
|
-
}
|
|
5678
|
+
[onClick, isOpen]
|
|
5679
|
+
);
|
|
5680
|
+
useEffect17(() => {
|
|
5681
|
+
if (autoOpenOnLinkActive && isActive) {
|
|
5682
|
+
setIsOpen(true);
|
|
6225
5683
|
}
|
|
6226
|
-
};
|
|
6227
|
-
|
|
6228
|
-
|
|
6229
|
-
if (!showGroups) {
|
|
6230
|
-
return items.map((item) => ({ ...item, group: "" }));
|
|
5684
|
+
}, [autoOpenOnLinkActive, isActive]);
|
|
5685
|
+
if (!isVisible) {
|
|
5686
|
+
return null;
|
|
6231
5687
|
}
|
|
6232
|
-
|
|
6233
|
-
|
|
6234
|
-
|
|
6235
|
-
|
|
6236
|
-
|
|
6237
|
-
|
|
6238
|
-
|
|
6239
|
-
|
|
6240
|
-
|
|
6241
|
-
|
|
6242
|
-
|
|
6243
|
-
|
|
6244
|
-
|
|
6245
|
-
|
|
6246
|
-
|
|
6247
|
-
|
|
6248
|
-
|
|
6249
|
-
|
|
6250
|
-
|
|
6251
|
-
|
|
6252
|
-
|
|
6253
|
-
|
|
6254
|
-
"ordered_list",
|
|
6255
|
-
"task_list",
|
|
6256
|
-
"quote",
|
|
6257
|
-
"code_block",
|
|
6258
|
-
"control",
|
|
6259
|
-
"divider",
|
|
6260
|
-
"table",
|
|
6261
|
-
"table_of_contents",
|
|
6262
|
-
"callout",
|
|
6263
|
-
"callout_warning",
|
|
6264
|
-
"math",
|
|
6265
|
-
"image"
|
|
6266
|
-
];
|
|
6267
|
-
function useSlashDropdownMenu(config) {
|
|
6268
|
-
const intl = useIntl12();
|
|
6269
|
-
const getSlashMenuItems = React10.useCallback(
|
|
6270
|
-
(editor) => {
|
|
6271
|
-
const items = [];
|
|
6272
|
-
const texts = createSlashMenuTexts(intl.formatMessage);
|
|
6273
|
-
const enabledItems = (config == null ? void 0 : config.enabledItems) || ALL_SLASH_MENU_ITEMS;
|
|
6274
|
-
const showGroups = (config == null ? void 0 : config.showGroups) !== false;
|
|
6275
|
-
const itemImplementations = getItemImplementations();
|
|
6276
|
-
enabledItems.forEach((itemType) => {
|
|
6277
|
-
var _a;
|
|
6278
|
-
const itemImpl = itemImplementations[itemType];
|
|
6279
|
-
const itemText = texts[itemType];
|
|
6280
|
-
if (itemImpl && itemText && itemImpl.check(editor)) {
|
|
6281
|
-
const item = {
|
|
6282
|
-
onSelect: ({ editor: editor2 }) => itemImpl.action({ editor: editor2 }),
|
|
6283
|
-
...itemText
|
|
6284
|
-
};
|
|
6285
|
-
if ((_a = config == null ? void 0 : config.itemGroups) == null ? void 0 : _a[itemType]) {
|
|
6286
|
-
item.group = config.itemGroups[itemType];
|
|
6287
|
-
} else if (!showGroups) {
|
|
6288
|
-
item.group = "";
|
|
5688
|
+
return /* @__PURE__ */ jsxs14(
|
|
5689
|
+
Popover2.Root,
|
|
5690
|
+
{
|
|
5691
|
+
open: isOpen,
|
|
5692
|
+
onOpenChange: handleOnOpenChange,
|
|
5693
|
+
spacing: "dense",
|
|
5694
|
+
children: [
|
|
5695
|
+
/* @__PURE__ */ jsx18(
|
|
5696
|
+
Popover2.Trigger,
|
|
5697
|
+
{
|
|
5698
|
+
render: /* @__PURE__ */ jsx18(
|
|
5699
|
+
LinkButton,
|
|
5700
|
+
{
|
|
5701
|
+
"data-disabled": !canSet,
|
|
5702
|
+
disabled: !canSet,
|
|
5703
|
+
"data-active-state": isActive ? "on" : "off",
|
|
5704
|
+
"aria-label": label,
|
|
5705
|
+
"aria-pressed": isActive,
|
|
5706
|
+
onClick: handleClick,
|
|
5707
|
+
...buttonProps
|
|
5708
|
+
}
|
|
5709
|
+
)
|
|
6289
5710
|
}
|
|
6290
|
-
|
|
6291
|
-
|
|
6292
|
-
|
|
6293
|
-
|
|
6294
|
-
|
|
6295
|
-
|
|
6296
|
-
|
|
6297
|
-
|
|
6298
|
-
|
|
5711
|
+
),
|
|
5712
|
+
/* @__PURE__ */ jsx18(Popover2.Content, { children: /* @__PURE__ */ jsx18(
|
|
5713
|
+
LinkMain,
|
|
5714
|
+
{
|
|
5715
|
+
url,
|
|
5716
|
+
setUrl,
|
|
5717
|
+
setLink: handleSetLink,
|
|
5718
|
+
removeLink,
|
|
5719
|
+
openLink,
|
|
5720
|
+
isActive,
|
|
5721
|
+
onSave: () => setIsOpen(false)
|
|
5722
|
+
}
|
|
5723
|
+
) })
|
|
5724
|
+
]
|
|
5725
|
+
}
|
|
6299
5726
|
);
|
|
6300
|
-
return {
|
|
6301
|
-
getSlashMenuItems,
|
|
6302
|
-
config
|
|
6303
|
-
};
|
|
6304
5727
|
}
|
|
5728
|
+
LinkButton.displayName = "LinkButton";
|
|
6305
5729
|
|
|
6306
|
-
// src/ui/
|
|
6307
|
-
import {
|
|
6308
|
-
|
|
6309
|
-
|
|
6310
|
-
|
|
6311
|
-
|
|
6312
|
-
|
|
6313
|
-
|
|
6314
|
-
|
|
6315
|
-
|
|
6316
|
-
|
|
6317
|
-
|
|
6318
|
-
|
|
6319
|
-
|
|
6320
|
-
|
|
6321
|
-
|
|
6322
|
-
|
|
6323
|
-
|
|
6324
|
-
};
|
|
6325
|
-
|
|
6326
|
-
|
|
6327
|
-
|
|
6328
|
-
|
|
6329
|
-
|
|
6330
|
-
|
|
6331
|
-
|
|
6332
|
-
|
|
6333
|
-
|
|
6334
|
-
if (overflow === "top") {
|
|
6335
|
-
itemRef.current.scrollIntoView(true);
|
|
6336
|
-
} else if (overflow === "bottom") {
|
|
6337
|
-
itemRef.current.scrollIntoView(false);
|
|
6338
|
-
}
|
|
6339
|
-
}, [isSelected]);
|
|
6340
|
-
const BadgeIcon = item.badge;
|
|
6341
|
-
return /* @__PURE__ */ jsx20(
|
|
6342
|
-
Button6,
|
|
6343
|
-
{
|
|
6344
|
-
ref: itemRef,
|
|
6345
|
-
variant: "ghost",
|
|
6346
|
-
color: "default",
|
|
6347
|
-
startContent: BadgeIcon && /* @__PURE__ */ jsx20(BadgeIcon, {}),
|
|
6348
|
-
"data-active-state": isSelected ? "on" : "off",
|
|
6349
|
-
onClick: onSelect,
|
|
6350
|
-
fullWidth: true,
|
|
6351
|
-
spacing: "start",
|
|
6352
|
-
children: item.title
|
|
6353
|
-
}
|
|
6354
|
-
);
|
|
5730
|
+
// src/ui/mark-button/index.tsx
|
|
5731
|
+
import {
|
|
5732
|
+
isMarkInSchema as isMarkInSchema3,
|
|
5733
|
+
isNodeTypeSelected as isNodeTypeSelected2,
|
|
5734
|
+
useTiptapEditor as useTiptapEditor7
|
|
5735
|
+
} from "@kopexa/editor-utils";
|
|
5736
|
+
import {
|
|
5737
|
+
BoldIcon,
|
|
5738
|
+
CodeIcon,
|
|
5739
|
+
ItalicIcon,
|
|
5740
|
+
StrikeIcon,
|
|
5741
|
+
SubscriptIcon,
|
|
5742
|
+
SuperscriptIcon,
|
|
5743
|
+
UnderlineIcon
|
|
5744
|
+
} from "@kopexa/icons";
|
|
5745
|
+
import { ToolbarButton as ToolbarButton3 } from "@kopexa/toolbar";
|
|
5746
|
+
import { isNodeSelection as isNodeSelection2 } from "@tiptap/react";
|
|
5747
|
+
import { useCallback as useCallback18, useMemo as useMemo15 } from "react";
|
|
5748
|
+
import { useIntl as useIntl10 } from "react-intl";
|
|
5749
|
+
import { jsx as jsx19 } from "react/jsx-runtime";
|
|
5750
|
+
var markIcons = {
|
|
5751
|
+
bold: BoldIcon,
|
|
5752
|
+
italic: ItalicIcon,
|
|
5753
|
+
underline: UnderlineIcon,
|
|
5754
|
+
strike: StrikeIcon,
|
|
5755
|
+
code: CodeIcon,
|
|
5756
|
+
superscript: SuperscriptIcon,
|
|
5757
|
+
subscript: SubscriptIcon
|
|
6355
5758
|
};
|
|
6356
|
-
var
|
|
6357
|
-
|
|
6358
|
-
|
|
6359
|
-
|
|
6360
|
-
|
|
6361
|
-
|
|
6362
|
-
|
|
6363
|
-
|
|
6364
|
-
|
|
6365
|
-
|
|
6366
|
-
|
|
6367
|
-
|
|
6368
|
-
|
|
6369
|
-
|
|
6370
|
-
|
|
6371
|
-
|
|
6372
|
-
|
|
6373
|
-
|
|
6374
|
-
|
|
6375
|
-
|
|
6376
|
-
|
|
6377
|
-
|
|
6378
|
-
|
|
6379
|
-
|
|
6380
|
-
|
|
5759
|
+
var markShortcutKeys = {
|
|
5760
|
+
bold: "mod+b",
|
|
5761
|
+
italic: "mod+i",
|
|
5762
|
+
underline: "mod+u",
|
|
5763
|
+
strike: "mod+shift+s",
|
|
5764
|
+
code: "mod+e",
|
|
5765
|
+
superscript: "mod+.",
|
|
5766
|
+
subscript: "mod+,"
|
|
5767
|
+
};
|
|
5768
|
+
function canToggleMark(editor, type) {
|
|
5769
|
+
if (!editor || !editor.isEditable) return false;
|
|
5770
|
+
if (!isMarkInSchema3(type, editor) || isNodeTypeSelected2(editor, ["image"]))
|
|
5771
|
+
return false;
|
|
5772
|
+
return editor.can().toggleMark(type);
|
|
5773
|
+
}
|
|
5774
|
+
function isMarkActive(editor, type) {
|
|
5775
|
+
if (!editor) return false;
|
|
5776
|
+
return editor.isActive(type);
|
|
5777
|
+
}
|
|
5778
|
+
function toggleMark(editor, type) {
|
|
5779
|
+
if (!editor) return;
|
|
5780
|
+
editor.chain().focus().toggleMark(type).run();
|
|
5781
|
+
}
|
|
5782
|
+
function isMarkButtonDisabled(editor, type, userDisabled = false) {
|
|
5783
|
+
if (!editor) return true;
|
|
5784
|
+
if (userDisabled) return true;
|
|
5785
|
+
if (editor.isActive("codeBlock")) return true;
|
|
5786
|
+
if (!canToggleMark(editor, type)) return true;
|
|
5787
|
+
return false;
|
|
5788
|
+
}
|
|
5789
|
+
function shouldShowMarkButton(params) {
|
|
5790
|
+
const { editor, type, hideWhenUnavailable, markInSchema } = params;
|
|
5791
|
+
if (!markInSchema || !editor) {
|
|
5792
|
+
return false;
|
|
5793
|
+
}
|
|
5794
|
+
if (hideWhenUnavailable) {
|
|
5795
|
+
if (isNodeSelection2(editor.state.selection) || !canToggleMark(editor, type)) {
|
|
5796
|
+
return false;
|
|
6381
5797
|
}
|
|
6382
|
-
|
|
6383
|
-
|
|
6384
|
-
|
|
6385
|
-
|
|
6386
|
-
|
|
6387
|
-
|
|
6388
|
-
|
|
6389
|
-
|
|
6390
|
-
|
|
6391
|
-
|
|
6392
|
-
|
|
6393
|
-
|
|
6394
|
-
|
|
6395
|
-
|
|
6396
|
-
|
|
6397
|
-
|
|
6398
|
-
|
|
6399
|
-
|
|
6400
|
-
|
|
6401
|
-
|
|
6402
|
-
|
|
6403
|
-
|
|
6404
|
-
|
|
6405
|
-
|
|
6406
|
-
|
|
6407
|
-
|
|
6408
|
-
|
|
6409
|
-
|
|
6410
|
-
|
|
6411
|
-
|
|
6412
|
-
|
|
6413
|
-
|
|
6414
|
-
|
|
6415
|
-
|
|
6416
|
-
|
|
6417
|
-
|
|
6418
|
-
|
|
6419
|
-
|
|
6420
|
-
|
|
6421
|
-
|
|
6422
|
-
|
|
6423
|
-
|
|
6424
|
-
|
|
6425
|
-
|
|
6426
|
-
|
|
6427
|
-
|
|
6428
|
-
|
|
6429
|
-
|
|
6430
|
-
|
|
5798
|
+
}
|
|
5799
|
+
return true;
|
|
5800
|
+
}
|
|
5801
|
+
var markMessages = {
|
|
5802
|
+
bold: messages7.bold,
|
|
5803
|
+
italic: messages7.italic,
|
|
5804
|
+
underline: messages7.underline,
|
|
5805
|
+
strike: messages7.strikethrough,
|
|
5806
|
+
code: messages7.code,
|
|
5807
|
+
superscript: messages7.superscript,
|
|
5808
|
+
subscript: messages7.subscript
|
|
5809
|
+
};
|
|
5810
|
+
function getTranslatedMarkName(type, intl) {
|
|
5811
|
+
return intl.formatMessage(markMessages[type]);
|
|
5812
|
+
}
|
|
5813
|
+
function useMarkState(editor, type, disabled = false) {
|
|
5814
|
+
const intl = useIntl10();
|
|
5815
|
+
const markInSchema = isMarkInSchema3(type, editor);
|
|
5816
|
+
const isDisabled = isMarkButtonDisabled(editor, type, disabled);
|
|
5817
|
+
const isActive = isMarkActive(editor, type);
|
|
5818
|
+
const Icon = markIcons[type];
|
|
5819
|
+
const shortcutKey = markShortcutKeys[type];
|
|
5820
|
+
const formattedName = getTranslatedMarkName(type, intl);
|
|
5821
|
+
return {
|
|
5822
|
+
markInSchema,
|
|
5823
|
+
isDisabled,
|
|
5824
|
+
isActive,
|
|
5825
|
+
Icon,
|
|
5826
|
+
shortcutKey,
|
|
5827
|
+
formattedName
|
|
5828
|
+
};
|
|
5829
|
+
}
|
|
5830
|
+
var MarkButton = ({
|
|
5831
|
+
editor: providedEditor,
|
|
5832
|
+
type,
|
|
5833
|
+
text,
|
|
5834
|
+
hideWhenUnavailable = false,
|
|
5835
|
+
className = "",
|
|
5836
|
+
disabled,
|
|
5837
|
+
onClick,
|
|
5838
|
+
children,
|
|
5839
|
+
...buttonProps
|
|
5840
|
+
}) => {
|
|
5841
|
+
const { editor } = useTiptapEditor7(providedEditor);
|
|
5842
|
+
const {
|
|
5843
|
+
markInSchema,
|
|
5844
|
+
isDisabled,
|
|
5845
|
+
isActive,
|
|
5846
|
+
Icon,
|
|
5847
|
+
shortcutKey,
|
|
5848
|
+
formattedName
|
|
5849
|
+
} = useMarkState(editor, type, disabled);
|
|
5850
|
+
const handleClick = useCallback18(
|
|
5851
|
+
(e) => {
|
|
5852
|
+
onClick == null ? void 0 : onClick(e);
|
|
5853
|
+
if (!e.defaultPrevented && !isDisabled && editor) {
|
|
5854
|
+
toggleMark(editor, type);
|
|
6431
5855
|
}
|
|
5856
|
+
},
|
|
5857
|
+
[onClick, isDisabled, editor, type]
|
|
5858
|
+
);
|
|
5859
|
+
const show = useMemo15(() => {
|
|
5860
|
+
return shouldShowMarkButton({
|
|
5861
|
+
editor,
|
|
5862
|
+
type,
|
|
5863
|
+
hideWhenUnavailable,
|
|
5864
|
+
markInSchema
|
|
6432
5865
|
});
|
|
6433
|
-
|
|
6434
|
-
|
|
6435
|
-
if (!renderedItems.length) {
|
|
5866
|
+
}, [editor, type, hideWhenUnavailable, markInSchema]);
|
|
5867
|
+
if (!show || !editor || !editor.isEditable) {
|
|
6436
5868
|
return null;
|
|
6437
5869
|
}
|
|
6438
|
-
return /* @__PURE__ */
|
|
6439
|
-
|
|
5870
|
+
return /* @__PURE__ */ jsx19(
|
|
5871
|
+
ToolbarButton3,
|
|
6440
5872
|
{
|
|
6441
|
-
|
|
6442
|
-
|
|
6443
|
-
|
|
6444
|
-
|
|
6445
|
-
|
|
5873
|
+
type: "button",
|
|
5874
|
+
className: className.trim(),
|
|
5875
|
+
disabled: isDisabled,
|
|
5876
|
+
variant: "ghost",
|
|
5877
|
+
color: "default",
|
|
5878
|
+
"data-active-state": isActive ? "on" : "off",
|
|
5879
|
+
"data-disabled": isDisabled,
|
|
5880
|
+
tabIndex: -1,
|
|
5881
|
+
"aria-label": formattedName,
|
|
5882
|
+
"aria-pressed": isActive,
|
|
5883
|
+
title: formattedName,
|
|
5884
|
+
shortcutKeys: shortcutKey,
|
|
5885
|
+
onClick: handleClick,
|
|
5886
|
+
isIconOnly: true,
|
|
5887
|
+
...buttonProps,
|
|
5888
|
+
children: /* @__PURE__ */ jsx19(Icon, {})
|
|
6446
5889
|
}
|
|
6447
5890
|
);
|
|
6448
5891
|
};
|
|
6449
5892
|
|
|
6450
|
-
// src/
|
|
6451
|
-
import {
|
|
6452
|
-
|
|
6453
|
-
|
|
6454
|
-
|
|
6455
|
-
|
|
6456
|
-
|
|
6457
|
-
|
|
6458
|
-
|
|
6459
|
-
|
|
6460
|
-
|
|
6461
|
-
|
|
6462
|
-
|
|
6463
|
-
|
|
6464
|
-
|
|
6465
|
-
|
|
6466
|
-
|
|
6467
|
-
|
|
6468
|
-
|
|
6469
|
-
function useWindowSize() {
|
|
6470
|
-
const [windowSize, setWindowSize] = React12.useState({
|
|
6471
|
-
width: 0,
|
|
6472
|
-
height: 0,
|
|
6473
|
-
offsetTop: 0
|
|
6474
|
-
});
|
|
6475
|
-
React12.useEffect(() => {
|
|
6476
|
-
handleResize();
|
|
6477
|
-
function handleResize() {
|
|
6478
|
-
if (typeof window === "undefined") return;
|
|
6479
|
-
const vp = window.visualViewport;
|
|
6480
|
-
if (!vp) return;
|
|
6481
|
-
const { width = 0, height = 0, offsetTop = 0 } = vp;
|
|
6482
|
-
setWindowSize((state) => {
|
|
6483
|
-
if (width === state.width && height === state.height && offsetTop === state.offsetTop) {
|
|
6484
|
-
return state;
|
|
6485
|
-
}
|
|
6486
|
-
return { width, height, offsetTop };
|
|
6487
|
-
});
|
|
6488
|
-
}
|
|
6489
|
-
const visualViewport = window.visualViewport;
|
|
6490
|
-
if (visualViewport) {
|
|
6491
|
-
visualViewport.addEventListener("resize", handleResize);
|
|
6492
|
-
visualViewport.addEventListener("scroll", handleResize);
|
|
5893
|
+
// src/ui/bubble-menu/index.tsx
|
|
5894
|
+
import { jsx as jsx20, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
5895
|
+
function useResolvedHighlightColors(editor) {
|
|
5896
|
+
var _a;
|
|
5897
|
+
if (!((_a = editor == null ? void 0 : editor.view) == null ? void 0 : _a.dom)) return void 0;
|
|
5898
|
+
const editorEl = editor.view.dom;
|
|
5899
|
+
const computed = getComputedStyle(editorEl);
|
|
5900
|
+
return HIGHLIGHT_COLORS.filter((c) => c.value !== "var(--tt-bg-color)").map(
|
|
5901
|
+
(color) => {
|
|
5902
|
+
const resolveVar = (v) => {
|
|
5903
|
+
const match = v.match(/^var\((.+)\)$/);
|
|
5904
|
+
if (!match) return v;
|
|
5905
|
+
return computed.getPropertyValue(match[1]).trim() || v;
|
|
5906
|
+
};
|
|
5907
|
+
return {
|
|
5908
|
+
...color,
|
|
5909
|
+
value: resolveVar(color.value),
|
|
5910
|
+
border: resolveVar(color.border)
|
|
5911
|
+
};
|
|
6493
5912
|
}
|
|
6494
|
-
|
|
6495
|
-
if (visualViewport) {
|
|
6496
|
-
visualViewport.removeEventListener("resize", handleResize);
|
|
6497
|
-
visualViewport.removeEventListener("scroll", handleResize);
|
|
6498
|
-
}
|
|
6499
|
-
};
|
|
6500
|
-
}, []);
|
|
6501
|
-
return windowSize;
|
|
5913
|
+
);
|
|
6502
5914
|
}
|
|
6503
|
-
|
|
6504
|
-
|
|
6505
|
-
|
|
6506
|
-
|
|
6507
|
-
|
|
6508
|
-
|
|
6509
|
-
|
|
6510
|
-
|
|
6511
|
-
|
|
6512
|
-
|
|
6513
|
-
|
|
6514
|
-
|
|
6515
|
-
|
|
6516
|
-
|
|
6517
|
-
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
|
|
6525
|
-
|
|
6526
|
-
|
|
6527
|
-
|
|
6528
|
-
|
|
6529
|
-
|
|
6530
|
-
|
|
6531
|
-
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
|
|
6535
|
-
|
|
6536
|
-
|
|
6537
|
-
|
|
6538
|
-
|
|
6539
|
-
|
|
6540
|
-
|
|
6541
|
-
|
|
6542
|
-
|
|
6543
|
-
|
|
6544
|
-
if (windowHeight < rect.height) {
|
|
6545
|
-
if (cursorCoords) {
|
|
6546
|
-
const availableSpace = windowHeight - cursorCoords.top - overlayHeight > 0;
|
|
6547
|
-
if (!availableSpace) {
|
|
6548
|
-
const targetScrollY = (
|
|
6549
|
-
// TODO: Needed?
|
|
6550
|
-
// window.scrollY + (cursorCoords.top - windowHeight / 2)
|
|
6551
|
-
cursorCoords.top - windowHeight / 2
|
|
6552
|
-
);
|
|
6553
|
-
window.scrollTo({
|
|
6554
|
-
top: targetScrollY,
|
|
6555
|
-
behavior: "smooth"
|
|
6556
|
-
});
|
|
6557
|
-
}
|
|
6558
|
-
}
|
|
6559
|
-
}
|
|
6560
|
-
};
|
|
6561
|
-
ensureCursorVisibility();
|
|
6562
|
-
}, [editor, overlayHeight, windowHeight, rect.height]);
|
|
6563
|
-
return rect;
|
|
5915
|
+
function BubbleMenu({ editor }) {
|
|
5916
|
+
const resolvedColors = useResolvedHighlightColors(editor);
|
|
5917
|
+
if (!editor) {
|
|
5918
|
+
return null;
|
|
5919
|
+
}
|
|
5920
|
+
return /* @__PURE__ */ jsx20(
|
|
5921
|
+
TiptapBubbleMenu,
|
|
5922
|
+
{
|
|
5923
|
+
editor,
|
|
5924
|
+
shouldShow: ({ editor: e, state, view }) => {
|
|
5925
|
+
const { selection } = state;
|
|
5926
|
+
const { empty } = selection;
|
|
5927
|
+
if (!view.hasFocus()) return false;
|
|
5928
|
+
if (empty) return false;
|
|
5929
|
+
if (e.isActive("codeBlock")) return false;
|
|
5930
|
+
if (e.isActive("link")) return false;
|
|
5931
|
+
if (e.isActive("variable")) return false;
|
|
5932
|
+
if (!e.isEditable) return false;
|
|
5933
|
+
return true;
|
|
5934
|
+
},
|
|
5935
|
+
options: {
|
|
5936
|
+
placement: "top",
|
|
5937
|
+
offset: 8
|
|
5938
|
+
},
|
|
5939
|
+
className: "rounded-lg border bg-background shadow-md",
|
|
5940
|
+
children: /* @__PURE__ */ jsxs15(Toolbar, { radius: "md", border: "none", className: "p-1", children: [
|
|
5941
|
+
/* @__PURE__ */ jsxs15(ToolbarGroup, { children: [
|
|
5942
|
+
/* @__PURE__ */ jsx20(MarkButton, { type: "bold" }),
|
|
5943
|
+
/* @__PURE__ */ jsx20(MarkButton, { type: "italic" }),
|
|
5944
|
+
/* @__PURE__ */ jsx20(MarkButton, { type: "underline" }),
|
|
5945
|
+
/* @__PURE__ */ jsx20(MarkButton, { type: "strike" }),
|
|
5946
|
+
/* @__PURE__ */ jsx20(MarkButton, { type: "code" })
|
|
5947
|
+
] }),
|
|
5948
|
+
/* @__PURE__ */ jsx20(ToolbarSeparator2, {}),
|
|
5949
|
+
/* @__PURE__ */ jsxs15(ToolbarGroup, { children: [
|
|
5950
|
+
/* @__PURE__ */ jsx20(ColorHighlightPopover, { colors: resolvedColors }),
|
|
5951
|
+
/* @__PURE__ */ jsx20(LinkPopover, { autoOpenOnLinkActive: false })
|
|
5952
|
+
] })
|
|
5953
|
+
] })
|
|
5954
|
+
}
|
|
5955
|
+
);
|
|
6564
5956
|
}
|
|
6565
5957
|
|
|
6566
|
-
// src/ui/
|
|
6567
|
-
import {
|
|
6568
|
-
import
|
|
6569
|
-
import { BanIcon, HighlighterIcon as HighlighterIcon2 } from "@kopexa/icons";
|
|
6570
|
-
import { Popover as Popover2 } from "@kopexa/popover";
|
|
6571
|
-
import { ToolbarSeparator as ToolbarSeparator2 } from "@kopexa/toolbar";
|
|
6572
|
-
import { useMemo as useMemo16, useRef as useRef11, useState as useState22 } from "react";
|
|
6573
|
-
|
|
6574
|
-
// src/ui/color-highlight-button/color-highlight-button.tsx
|
|
6575
|
-
import { useTiptapEditor as useTiptapEditor8 } from "@kopexa/editor-utils";
|
|
6576
|
-
import { colorHighlightButton } from "@kopexa/theme";
|
|
6577
|
-
import { ToolbarButton as ToolbarButton3 } from "@kopexa/toolbar";
|
|
6578
|
-
import { useCallback as useCallback23, useMemo as useMemo15 } from "react";
|
|
5958
|
+
// src/ui/copy-anchor-link-button/use-scroll-to-hash.ts
|
|
5959
|
+
import { getEditorExtension, useTiptapEditor as useTiptapEditor8 } from "@kopexa/editor-utils";
|
|
5960
|
+
import * as React10 from "react";
|
|
6579
5961
|
|
|
6580
|
-
// src/
|
|
6581
|
-
import {
|
|
6582
|
-
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
|
|
6589
|
-
|
|
6590
|
-
import { useIntl as useIntl13 } from "react-intl";
|
|
6591
|
-
var COLOR_HIGHLIGHT_SHORTCUT_KEY = "mod+shift+h";
|
|
6592
|
-
var HIGHLIGHT_COLORS = [
|
|
6593
|
-
{
|
|
6594
|
-
label: "Default background",
|
|
6595
|
-
value: "var(--tt-bg-color)",
|
|
6596
|
-
border: "var(--tt-bg-color-contrast)"
|
|
6597
|
-
},
|
|
6598
|
-
{
|
|
6599
|
-
label: "Gray background",
|
|
6600
|
-
value: "var(--tt-color-highlight-gray)",
|
|
6601
|
-
border: "var(--tt-color-highlight-gray-contrast)"
|
|
6602
|
-
},
|
|
6603
|
-
{
|
|
6604
|
-
label: "Brown background",
|
|
6605
|
-
value: "var(--tt-color-highlight-brown)",
|
|
6606
|
-
border: "var(--tt-color-highlight-brown-contrast)"
|
|
6607
|
-
},
|
|
6608
|
-
{
|
|
6609
|
-
label: "Orange background",
|
|
6610
|
-
value: "var(--tt-color-highlight-orange)",
|
|
6611
|
-
border: "var(--tt-color-highlight-orange-contrast)"
|
|
6612
|
-
},
|
|
6613
|
-
{
|
|
6614
|
-
label: "Yellow background",
|
|
6615
|
-
value: "var(--tt-color-highlight-yellow)",
|
|
6616
|
-
border: "var(--tt-color-highlight-yellow-contrast)"
|
|
6617
|
-
},
|
|
6618
|
-
{
|
|
6619
|
-
label: "Green background",
|
|
6620
|
-
value: "var(--tt-color-highlight-green)",
|
|
6621
|
-
border: "var(--tt-color-highlight-green-contrast)"
|
|
6622
|
-
},
|
|
6623
|
-
{
|
|
6624
|
-
label: "Blue background",
|
|
6625
|
-
value: "var(--tt-color-highlight-blue)",
|
|
6626
|
-
border: "var(--tt-color-highlight-blue-contrast)"
|
|
6627
|
-
},
|
|
6628
|
-
{
|
|
6629
|
-
label: "Purple background",
|
|
6630
|
-
value: "var(--tt-color-highlight-purple)",
|
|
6631
|
-
border: "var(--tt-color-highlight-purple-contrast)"
|
|
6632
|
-
},
|
|
6633
|
-
{
|
|
6634
|
-
label: "Pink background",
|
|
6635
|
-
value: "var(--tt-color-highlight-pink)",
|
|
6636
|
-
border: "var(--tt-color-highlight-pink-contrast)"
|
|
6637
|
-
},
|
|
6638
|
-
{
|
|
6639
|
-
label: "Red background",
|
|
6640
|
-
value: "var(--tt-color-highlight-red)",
|
|
6641
|
-
border: "var(--tt-color-highlight-red-contrast)"
|
|
6642
|
-
}
|
|
6643
|
-
];
|
|
6644
|
-
function pickHighlightColorsByValue(values) {
|
|
6645
|
-
const colorMap = new Map(
|
|
6646
|
-
HIGHLIGHT_COLORS.map((color) => [color.value, color])
|
|
5962
|
+
// src/hooks/use-floating-toolbar-visibility.ts
|
|
5963
|
+
import { NodeSelection } from "@tiptap/pm/state";
|
|
5964
|
+
import { isNodeSelection as isNodeSelection3 } from "@tiptap/react";
|
|
5965
|
+
import * as React9 from "react";
|
|
5966
|
+
var HIDE_FLOATING_META = "hideFloatingToolbar";
|
|
5967
|
+
var selectNodeAndHideFloating = (editor, pos) => {
|
|
5968
|
+
if (!editor) return;
|
|
5969
|
+
const { state, view } = editor;
|
|
5970
|
+
view.dispatch(
|
|
5971
|
+
state.tr.setSelection(NodeSelection.create(state.doc, pos)).setMeta(HIDE_FLOATING_META, true)
|
|
6647
5972
|
);
|
|
6648
|
-
|
|
6649
|
-
|
|
6650
|
-
|
|
6651
|
-
|
|
6652
|
-
if (!isMarkInSchema3("highlight", editor) || isNodeTypeSelected3(editor, ["image"]))
|
|
6653
|
-
return false;
|
|
6654
|
-
return editor.can().setMark("highlight");
|
|
6655
|
-
}
|
|
6656
|
-
function isColorHighlightActive(editor, highlightColor) {
|
|
6657
|
-
if (!editor || !editor.isEditable) return false;
|
|
6658
|
-
return highlightColor ? editor.isActive("highlight", { color: highlightColor }) : editor.isActive("highlight");
|
|
6659
|
-
}
|
|
6660
|
-
function removeHighlight(editor) {
|
|
6661
|
-
if (!editor || !editor.isEditable) return false;
|
|
6662
|
-
if (!canColorHighlight(editor)) return false;
|
|
6663
|
-
return editor.chain().focus().unsetMark("highlight").run();
|
|
6664
|
-
}
|
|
6665
|
-
function shouldShowButton2(props) {
|
|
6666
|
-
const { editor, hideWhenUnavailable } = props;
|
|
6667
|
-
if (!editor || !editor.isEditable) return false;
|
|
6668
|
-
if (!isMarkInSchema3("highlight", editor)) return false;
|
|
6669
|
-
if (hideWhenUnavailable && !editor.isActive("code")) {
|
|
6670
|
-
return canColorHighlight(editor);
|
|
6671
|
-
}
|
|
6672
|
-
return true;
|
|
6673
|
-
}
|
|
6674
|
-
function useColorHighlight(config) {
|
|
5973
|
+
};
|
|
5974
|
+
|
|
5975
|
+
// src/ui/copy-anchor-link-button/use-scroll-to-hash.ts
|
|
5976
|
+
function useScrollToHash(config = {}) {
|
|
6675
5977
|
const {
|
|
6676
5978
|
editor: providedEditor,
|
|
6677
|
-
|
|
6678
|
-
|
|
6679
|
-
|
|
6680
|
-
|
|
5979
|
+
onTargetFound = () => {
|
|
5980
|
+
},
|
|
5981
|
+
onTargetNotFound = () => {
|
|
5982
|
+
}
|
|
6681
5983
|
} = config;
|
|
6682
|
-
const
|
|
6683
|
-
const
|
|
6684
|
-
|
|
6685
|
-
|
|
6686
|
-
|
|
6687
|
-
|
|
6688
|
-
|
|
5984
|
+
const { editor } = useTiptapEditor8(providedEditor);
|
|
5985
|
+
const scrollToNode = React10.useCallback(
|
|
5986
|
+
(id) => {
|
|
5987
|
+
var _a, _b, _c;
|
|
5988
|
+
if (!editor) return false;
|
|
5989
|
+
const attributeName = (_c = (_b = (_a = getEditorExtension(editor, "uniqueID")) == null ? void 0 : _a.options) == null ? void 0 : _b.attributeName) != null ? _c : "data-id";
|
|
5990
|
+
let position = null;
|
|
5991
|
+
editor.state.doc.descendants((node, pos) => {
|
|
5992
|
+
var _a2;
|
|
5993
|
+
if (((_a2 = node.attrs) == null ? void 0 : _a2[attributeName]) === id) {
|
|
5994
|
+
position = pos;
|
|
5995
|
+
return false;
|
|
5996
|
+
}
|
|
5997
|
+
return true;
|
|
5998
|
+
});
|
|
5999
|
+
if (position === null) return false;
|
|
6000
|
+
selectNodeAndHideFloating(editor, position);
|
|
6001
|
+
setTimeout(() => {
|
|
6002
|
+
let dom = null;
|
|
6003
|
+
if (typeof position === "number") {
|
|
6004
|
+
dom = editor.view.nodeDOM(position);
|
|
6005
|
+
}
|
|
6006
|
+
if (dom) {
|
|
6007
|
+
dom.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
6008
|
+
}
|
|
6009
|
+
}, 0);
|
|
6010
|
+
return true;
|
|
6011
|
+
},
|
|
6012
|
+
[editor]
|
|
6013
|
+
);
|
|
6014
|
+
const handleScroll = React10.useCallback(
|
|
6015
|
+
(delay = 0) => {
|
|
6016
|
+
var _a;
|
|
6017
|
+
const hash = (_a = window.location.hash) == null ? void 0 : _a.substring(1);
|
|
6018
|
+
if (!hash) return;
|
|
6019
|
+
setTimeout(() => {
|
|
6020
|
+
if (scrollToNode(hash)) {
|
|
6021
|
+
onTargetFound(hash);
|
|
6022
|
+
} else {
|
|
6023
|
+
onTargetNotFound(hash);
|
|
6024
|
+
}
|
|
6025
|
+
}, delay);
|
|
6026
|
+
},
|
|
6027
|
+
[scrollToNode, onTargetFound, onTargetNotFound]
|
|
6028
|
+
);
|
|
6029
|
+
React10.useEffect(() => {
|
|
6030
|
+
var _a, _b;
|
|
6689
6031
|
if (!editor) return;
|
|
6690
|
-
const
|
|
6691
|
-
|
|
6692
|
-
|
|
6032
|
+
const provider = (_b = (_a = editor.extensionManager.extensions.find(
|
|
6033
|
+
(ext) => ext.name === "collaborationCaret"
|
|
6034
|
+
)) == null ? void 0 : _a.options) == null ? void 0 : _b.provider;
|
|
6035
|
+
if (provider == null ? void 0 : provider.on) {
|
|
6036
|
+
const syncHandler = () => handleScroll(500);
|
|
6037
|
+
provider.on("synced", syncHandler);
|
|
6038
|
+
return () => {
|
|
6039
|
+
var _a2;
|
|
6040
|
+
(_a2 = provider.off) == null ? void 0 : _a2.call(provider, "synced", syncHandler);
|
|
6041
|
+
};
|
|
6042
|
+
} else {
|
|
6043
|
+
handleScroll(500);
|
|
6044
|
+
}
|
|
6045
|
+
}, [editor, handleScroll]);
|
|
6046
|
+
React10.useEffect(() => {
|
|
6047
|
+
const immediateScroll = () => handleScroll();
|
|
6048
|
+
const delayedScroll = () => handleScroll(500);
|
|
6049
|
+
window.addEventListener("hashchange", immediateScroll);
|
|
6050
|
+
window.addEventListener("pageshow", delayedScroll);
|
|
6051
|
+
window.addEventListener("popstate", immediateScroll);
|
|
6052
|
+
return () => {
|
|
6053
|
+
window.removeEventListener("hashchange", immediateScroll);
|
|
6054
|
+
window.removeEventListener("pageshow", delayedScroll);
|
|
6055
|
+
window.removeEventListener("popstate", immediateScroll);
|
|
6056
|
+
};
|
|
6057
|
+
}, [handleScroll]);
|
|
6058
|
+
return { scrollToHash: scrollToNode };
|
|
6059
|
+
}
|
|
6060
|
+
|
|
6061
|
+
// src/ui/link-bubble/index.tsx
|
|
6062
|
+
import { IconButton as IconButton8 } from "@kopexa/button";
|
|
6063
|
+
import { EditIcon as EditIcon3, ExternalLinkIcon as ExternalLinkIcon2, TrashIcon as TrashIcon4 } from "@kopexa/icons";
|
|
6064
|
+
import { Input as Input4 } from "@kopexa/input";
|
|
6065
|
+
import { BubbleMenu as TiptapBubbleMenu2 } from "@tiptap/react/menus";
|
|
6066
|
+
import { useCallback as useCallback20, useEffect as useEffect20, useState as useState19 } from "react";
|
|
6067
|
+
import { useIntl as useIntl11 } from "react-intl";
|
|
6068
|
+
import { Fragment as Fragment4, jsx as jsx21, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
6069
|
+
function LinkBubble({ editor }) {
|
|
6070
|
+
const intl = useIntl11();
|
|
6071
|
+
const [isEditing, setIsEditing] = useState19(false);
|
|
6072
|
+
const [url, setUrl] = useState19("");
|
|
6073
|
+
const getCurrentUrl = useCallback20(() => {
|
|
6074
|
+
if (!editor) return "";
|
|
6075
|
+
const attrs = editor.getAttributes("link");
|
|
6076
|
+
return attrs.href || "";
|
|
6077
|
+
}, [editor]);
|
|
6078
|
+
useEffect20(() => {
|
|
6079
|
+
const isLinkActive2 = editor == null ? void 0 : editor.isActive("link");
|
|
6080
|
+
if (isLinkActive2) {
|
|
6081
|
+
setUrl(getCurrentUrl());
|
|
6082
|
+
setIsEditing(false);
|
|
6083
|
+
}
|
|
6084
|
+
}, [editor, getCurrentUrl]);
|
|
6085
|
+
const handleOpenLink = useCallback20(() => {
|
|
6086
|
+
const href = getCurrentUrl();
|
|
6087
|
+
if (href) {
|
|
6088
|
+
window.open(href, "_blank", "noopener,noreferrer");
|
|
6089
|
+
}
|
|
6090
|
+
}, [getCurrentUrl]);
|
|
6091
|
+
const handleRemoveLink = useCallback20(() => {
|
|
6092
|
+
editor == null ? void 0 : editor.chain().focus().unsetLink().run();
|
|
6093
|
+
}, [editor]);
|
|
6094
|
+
const handleEdit = useCallback20(() => {
|
|
6095
|
+
setUrl(getCurrentUrl());
|
|
6096
|
+
setIsEditing(true);
|
|
6097
|
+
}, [getCurrentUrl]);
|
|
6098
|
+
const handleSave = useCallback20(() => {
|
|
6099
|
+
if (url) {
|
|
6100
|
+
editor == null ? void 0 : editor.chain().focus().extendMarkRange("link").setLink({ href: url }).run();
|
|
6101
|
+
} else {
|
|
6102
|
+
editor == null ? void 0 : editor.chain().focus().unsetLink().run();
|
|
6103
|
+
}
|
|
6104
|
+
setIsEditing(false);
|
|
6105
|
+
}, [editor, url]);
|
|
6106
|
+
const handleKeyDown = useCallback20(
|
|
6107
|
+
(e) => {
|
|
6108
|
+
if (e.key === "Enter") {
|
|
6109
|
+
e.preventDefault();
|
|
6110
|
+
handleSave();
|
|
6111
|
+
} else if (e.key === "Escape") {
|
|
6112
|
+
e.preventDefault();
|
|
6113
|
+
setIsEditing(false);
|
|
6114
|
+
setUrl(getCurrentUrl());
|
|
6115
|
+
}
|
|
6116
|
+
},
|
|
6117
|
+
[handleSave, getCurrentUrl]
|
|
6118
|
+
);
|
|
6119
|
+
if (!editor) {
|
|
6120
|
+
return null;
|
|
6121
|
+
}
|
|
6122
|
+
return /* @__PURE__ */ jsx21(
|
|
6123
|
+
TiptapBubbleMenu2,
|
|
6124
|
+
{
|
|
6125
|
+
editor,
|
|
6126
|
+
pluginKey: "linkBubbleMenu",
|
|
6127
|
+
shouldShow: ({ editor: e, view }) => {
|
|
6128
|
+
if (!view.hasFocus()) return false;
|
|
6129
|
+
return e.isActive("link") && e.isEditable;
|
|
6130
|
+
},
|
|
6131
|
+
options: {
|
|
6132
|
+
placement: "bottom-start",
|
|
6133
|
+
offset: 8
|
|
6134
|
+
},
|
|
6135
|
+
className: "rounded-lg border bg-background shadow-md",
|
|
6136
|
+
children: /* @__PURE__ */ jsx21("div", { className: "flex items-center gap-1 p-1.5 min-w-[280px]", children: isEditing ? /* @__PURE__ */ jsxs16(Fragment4, { children: [
|
|
6137
|
+
/* @__PURE__ */ jsx21(
|
|
6138
|
+
Input4,
|
|
6139
|
+
{
|
|
6140
|
+
type: "url",
|
|
6141
|
+
value: url,
|
|
6142
|
+
onChange: (e) => setUrl(e.target.value),
|
|
6143
|
+
onKeyDown: handleKeyDown,
|
|
6144
|
+
placeholder: intl.formatMessage(messages7.link_placeholder),
|
|
6145
|
+
className: "flex-1 h-8 text-sm",
|
|
6146
|
+
autoFocus: true
|
|
6147
|
+
}
|
|
6148
|
+
),
|
|
6149
|
+
/* @__PURE__ */ jsx21(
|
|
6150
|
+
IconButton8,
|
|
6151
|
+
{
|
|
6152
|
+
type: "button",
|
|
6153
|
+
size: "sm",
|
|
6154
|
+
variant: "ghost",
|
|
6155
|
+
onClick: handleSave,
|
|
6156
|
+
"aria-label": intl.formatMessage(messages7.link_save),
|
|
6157
|
+
children: /* @__PURE__ */ jsx21(EditIcon3, { className: "size-4" })
|
|
6158
|
+
}
|
|
6159
|
+
)
|
|
6160
|
+
] }) : /* @__PURE__ */ jsxs16(Fragment4, { children: [
|
|
6161
|
+
/* @__PURE__ */ jsx21(
|
|
6162
|
+
"a",
|
|
6163
|
+
{
|
|
6164
|
+
href: getCurrentUrl(),
|
|
6165
|
+
target: "_blank",
|
|
6166
|
+
rel: "noopener noreferrer",
|
|
6167
|
+
className: "flex-1 text-sm text-primary truncate max-w-[200px] hover:underline px-2",
|
|
6168
|
+
onClick: (e) => {
|
|
6169
|
+
e.preventDefault();
|
|
6170
|
+
handleOpenLink();
|
|
6171
|
+
},
|
|
6172
|
+
children: getCurrentUrl()
|
|
6173
|
+
}
|
|
6174
|
+
),
|
|
6175
|
+
/* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-0.5 border-l pl-1 ml-1", children: [
|
|
6176
|
+
/* @__PURE__ */ jsx21(
|
|
6177
|
+
IconButton8,
|
|
6178
|
+
{
|
|
6179
|
+
type: "button",
|
|
6180
|
+
size: "sm",
|
|
6181
|
+
variant: "ghost",
|
|
6182
|
+
onClick: handleOpenLink,
|
|
6183
|
+
"aria-label": intl.formatMessage(messages7.link_open),
|
|
6184
|
+
children: /* @__PURE__ */ jsx21(ExternalLinkIcon2, { className: "size-4" })
|
|
6185
|
+
}
|
|
6186
|
+
),
|
|
6187
|
+
/* @__PURE__ */ jsx21(
|
|
6188
|
+
IconButton8,
|
|
6189
|
+
{
|
|
6190
|
+
type: "button",
|
|
6191
|
+
size: "sm",
|
|
6192
|
+
variant: "ghost",
|
|
6193
|
+
onClick: handleEdit,
|
|
6194
|
+
"aria-label": intl.formatMessage(messages7.link_edit),
|
|
6195
|
+
children: /* @__PURE__ */ jsx21(EditIcon3, { className: "size-4" })
|
|
6196
|
+
}
|
|
6197
|
+
),
|
|
6198
|
+
/* @__PURE__ */ jsx21(
|
|
6199
|
+
IconButton8,
|
|
6200
|
+
{
|
|
6201
|
+
type: "button",
|
|
6202
|
+
size: "sm",
|
|
6203
|
+
variant: "ghost",
|
|
6204
|
+
onClick: handleRemoveLink,
|
|
6205
|
+
"aria-label": intl.formatMessage(messages7.link_remove),
|
|
6206
|
+
children: /* @__PURE__ */ jsx21(TrashIcon4, { className: "size-4" })
|
|
6207
|
+
}
|
|
6208
|
+
)
|
|
6209
|
+
] })
|
|
6210
|
+
] }) })
|
|
6211
|
+
}
|
|
6212
|
+
);
|
|
6213
|
+
}
|
|
6214
|
+
|
|
6215
|
+
// src/ui/slash-dropdown-menu/slash-dropdown-menu.tsx
|
|
6216
|
+
import { Button as Button6 } from "@kopexa/button";
|
|
6217
|
+
import { getElementOverflowPosition as getElementOverflowPosition2 } from "@kopexa/editor-utils";
|
|
6218
|
+
import { Separator } from "@kopexa/separator";
|
|
6219
|
+
import { slashDropdownMenu as slashDropdownMenu2 } from "@kopexa/theme";
|
|
6220
|
+
import * as React12 from "react";
|
|
6221
|
+
|
|
6222
|
+
// src/ui/slash-dropdown-menu/use-slash-dropdown-menu.ts
|
|
6223
|
+
import {
|
|
6224
|
+
findSelectionPosition,
|
|
6225
|
+
hasContentAbove,
|
|
6226
|
+
isExtensionAvailable,
|
|
6227
|
+
isNodeInSchema as isNodeInSchema2
|
|
6228
|
+
} from "@kopexa/editor-utils";
|
|
6229
|
+
import {
|
|
6230
|
+
AiSparklesIcon,
|
|
6231
|
+
AlertIcon as AlertIcon2,
|
|
6232
|
+
BlockquoteIcon,
|
|
6233
|
+
CodeBlockIcon,
|
|
6234
|
+
ControlsIcon,
|
|
6235
|
+
HeadingOneIcon,
|
|
6236
|
+
HeadingThreeIcon,
|
|
6237
|
+
HeadingTwoIcon,
|
|
6238
|
+
ImageIcon as ImageIcon2,
|
|
6239
|
+
InfoIcon as InfoIcon2,
|
|
6240
|
+
ListIcon as ListIcon2,
|
|
6241
|
+
ListOrderedIcon,
|
|
6242
|
+
ListTodoIcon,
|
|
6243
|
+
MinusIcon,
|
|
6244
|
+
TableIcon as TableIcon2,
|
|
6245
|
+
TableOfContentsIcon,
|
|
6246
|
+
TypeIcon
|
|
6247
|
+
} from "@kopexa/icons";
|
|
6248
|
+
import * as React11 from "react";
|
|
6249
|
+
import { useIntl as useIntl13 } from "react-intl";
|
|
6250
|
+
|
|
6251
|
+
// src/ui/table-button/use-table.ts
|
|
6252
|
+
import {
|
|
6253
|
+
isNodeInSchema,
|
|
6254
|
+
isNodeTypeSelected as isNodeTypeSelected3,
|
|
6255
|
+
useTiptapEditor as useTiptapEditor9
|
|
6256
|
+
} from "@kopexa/editor-utils";
|
|
6257
|
+
import { TableIcon } from "@kopexa/icons";
|
|
6258
|
+
import { isNodeSelection as isNodeSelection4 } from "@tiptap/react";
|
|
6259
|
+
import { useCallback as useCallback21, useEffect as useEffect21, useState as useState20 } from "react";
|
|
6260
|
+
import { useIntl as useIntl12 } from "react-intl";
|
|
6261
|
+
function canToggle(editor) {
|
|
6262
|
+
if (!editor || !editor.isEditable) return false;
|
|
6263
|
+
if (!isNodeInSchema("table", editor) || isNodeTypeSelected3(editor, ["image"])) {
|
|
6264
|
+
return false;
|
|
6265
|
+
}
|
|
6266
|
+
try {
|
|
6267
|
+
return editor.can().insertTable({ rows: 3, cols: 3, withHeaderRow: true });
|
|
6268
|
+
} catch {
|
|
6269
|
+
return false;
|
|
6270
|
+
}
|
|
6271
|
+
}
|
|
6272
|
+
function toggleTable(editor, config) {
|
|
6273
|
+
var _a;
|
|
6274
|
+
if (!editor || !editor.isEditable) return false;
|
|
6275
|
+
if (!canToggle(editor)) return false;
|
|
6276
|
+
try {
|
|
6277
|
+
return editor.chain().focus().insertTable({
|
|
6278
|
+
rows: (config == null ? void 0 : config.rows) || 3,
|
|
6279
|
+
cols: (config == null ? void 0 : config.cols) || 3,
|
|
6280
|
+
withHeaderRow: (_a = config == null ? void 0 : config.withHeaderRow) != null ? _a : true
|
|
6281
|
+
}).run();
|
|
6282
|
+
} catch {
|
|
6283
|
+
return false;
|
|
6284
|
+
}
|
|
6285
|
+
}
|
|
6286
|
+
function shouldShowButton2(props) {
|
|
6287
|
+
const { editor, hideWhenUnavailable } = props;
|
|
6288
|
+
if (!editor || !editor.isEditable) return false;
|
|
6289
|
+
if (!isNodeInSchema("table", editor)) return false;
|
|
6290
|
+
if (hideWhenUnavailable) {
|
|
6291
|
+
if (isNodeSelection4(editor.state.selection) || !canToggle) {
|
|
6292
|
+
return false;
|
|
6293
|
+
}
|
|
6294
|
+
}
|
|
6295
|
+
return true;
|
|
6296
|
+
}
|
|
6297
|
+
function useTableBlock(config) {
|
|
6298
|
+
const {
|
|
6299
|
+
editor: providedEditor,
|
|
6300
|
+
hideWhenUnavailable = false,
|
|
6301
|
+
onToggled
|
|
6302
|
+
} = config || {};
|
|
6303
|
+
const intl = useIntl12();
|
|
6304
|
+
const { editor } = useTiptapEditor9(providedEditor);
|
|
6305
|
+
const [isVisible, setIsVisible] = useState20(true);
|
|
6306
|
+
const canToggleState = canToggle(editor);
|
|
6307
|
+
const isActive = (editor == null ? void 0 : editor.isActive("table")) || false;
|
|
6308
|
+
useEffect21(() => {
|
|
6309
|
+
if (!editor) return;
|
|
6310
|
+
const handleSelectionUpdate = () => {
|
|
6311
|
+
setIsVisible(shouldShowButton2({ editor, hideWhenUnavailable }));
|
|
6312
|
+
};
|
|
6693
6313
|
handleSelectionUpdate();
|
|
6694
6314
|
editor.on("selectionUpdate", handleSelectionUpdate);
|
|
6695
6315
|
return () => {
|
|
6696
6316
|
editor.off("selectionUpdate", handleSelectionUpdate);
|
|
6697
6317
|
};
|
|
6698
6318
|
}, [editor, hideWhenUnavailable]);
|
|
6699
|
-
const
|
|
6700
|
-
if (!editor
|
|
6701
|
-
|
|
6702
|
-
if (
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
6706
|
-
|
|
6707
|
-
|
|
6319
|
+
const handleToggle = useCallback21(() => {
|
|
6320
|
+
if (!editor) return false;
|
|
6321
|
+
const success = toggleTable(editor);
|
|
6322
|
+
if (success) {
|
|
6323
|
+
onToggled == null ? void 0 : onToggled();
|
|
6324
|
+
}
|
|
6325
|
+
return success;
|
|
6326
|
+
}, [editor, onToggled]);
|
|
6327
|
+
return {
|
|
6328
|
+
isVisible,
|
|
6329
|
+
isActive,
|
|
6330
|
+
handleToggle,
|
|
6331
|
+
canToggle: canToggleState,
|
|
6332
|
+
label: intl.formatMessage(messages7.table_insert),
|
|
6333
|
+
// shortcutKeys: CODE_BLOCK_SHORTCUT_KEY,
|
|
6334
|
+
Icon: TableIcon
|
|
6335
|
+
};
|
|
6336
|
+
}
|
|
6337
|
+
|
|
6338
|
+
// src/ui/slash-dropdown-menu/use-slash-dropdown-menu.ts
|
|
6339
|
+
function createSlashMenuTexts(formatMessage) {
|
|
6340
|
+
return {
|
|
6341
|
+
// AI
|
|
6342
|
+
continue_writing: {
|
|
6343
|
+
title: formatMessage(messages7.slash_continue_writing),
|
|
6344
|
+
subtext: formatMessage(messages7.slash_continue_writing_subtext),
|
|
6345
|
+
keywords: ["continue", "write", "continue writing", "ai"],
|
|
6346
|
+
badge: AiSparklesIcon,
|
|
6347
|
+
group: formatMessage(messages7.group_ai)
|
|
6348
|
+
},
|
|
6349
|
+
ai_ask_button: {
|
|
6350
|
+
title: formatMessage(messages7.slash_ask_ai),
|
|
6351
|
+
subtext: formatMessage(messages7.slash_ask_ai_subtext),
|
|
6352
|
+
keywords: ["ai", "ask", "generate"],
|
|
6353
|
+
badge: AiSparklesIcon,
|
|
6354
|
+
group: formatMessage(messages7.group_ai)
|
|
6355
|
+
},
|
|
6356
|
+
// Style
|
|
6357
|
+
text: {
|
|
6358
|
+
title: formatMessage(messages7.slash_text),
|
|
6359
|
+
subtext: formatMessage(messages7.slash_text_subtext),
|
|
6360
|
+
keywords: ["p", "paragraph", "text"],
|
|
6361
|
+
badge: TypeIcon,
|
|
6362
|
+
group: formatMessage(messages7.group_style)
|
|
6363
|
+
},
|
|
6364
|
+
heading_1: {
|
|
6365
|
+
title: formatMessage(messages7.slash_heading_1),
|
|
6366
|
+
subtext: formatMessage(messages7.slash_heading_1_subtext),
|
|
6367
|
+
keywords: ["h", "heading1", "h1"],
|
|
6368
|
+
badge: HeadingOneIcon,
|
|
6369
|
+
group: formatMessage(messages7.group_style)
|
|
6370
|
+
},
|
|
6371
|
+
heading_2: {
|
|
6372
|
+
title: formatMessage(messages7.slash_heading_2),
|
|
6373
|
+
subtext: formatMessage(messages7.slash_heading_2_subtext),
|
|
6374
|
+
keywords: ["h2", "heading2", "subheading"],
|
|
6375
|
+
badge: HeadingTwoIcon,
|
|
6376
|
+
group: formatMessage(messages7.group_style)
|
|
6377
|
+
},
|
|
6378
|
+
heading_3: {
|
|
6379
|
+
title: formatMessage(messages7.slash_heading_3),
|
|
6380
|
+
subtext: formatMessage(messages7.slash_heading_3_subtext),
|
|
6381
|
+
keywords: ["h3", "heading3", "subheading"],
|
|
6382
|
+
badge: HeadingThreeIcon,
|
|
6383
|
+
group: formatMessage(messages7.group_style)
|
|
6384
|
+
},
|
|
6385
|
+
bullet_list: {
|
|
6386
|
+
title: formatMessage(messages7.slash_bullet_list),
|
|
6387
|
+
subtext: formatMessage(messages7.slash_bullet_list_subtext),
|
|
6388
|
+
keywords: ["ul", "li", "list", "bulletlist", "bullet list"],
|
|
6389
|
+
badge: ListIcon2,
|
|
6390
|
+
group: formatMessage(messages7.group_style)
|
|
6391
|
+
},
|
|
6392
|
+
ordered_list: {
|
|
6393
|
+
title: formatMessage(messages7.slash_ordered_list),
|
|
6394
|
+
subtext: formatMessage(messages7.slash_ordered_list_subtext),
|
|
6395
|
+
keywords: ["ol", "li", "list", "numberedlist", "numbered list"],
|
|
6396
|
+
badge: ListOrderedIcon,
|
|
6397
|
+
group: formatMessage(messages7.group_style)
|
|
6398
|
+
},
|
|
6399
|
+
task_list: {
|
|
6400
|
+
title: formatMessage(messages7.slash_task_list),
|
|
6401
|
+
subtext: formatMessage(messages7.slash_task_list_subtext),
|
|
6402
|
+
keywords: ["tasklist", "task list", "todo", "checklist"],
|
|
6403
|
+
badge: ListTodoIcon,
|
|
6404
|
+
group: formatMessage(messages7.group_style)
|
|
6405
|
+
},
|
|
6406
|
+
quote: {
|
|
6407
|
+
title: formatMessage(messages7.slash_blockquote),
|
|
6408
|
+
subtext: formatMessage(messages7.slash_blockquote_subtext),
|
|
6409
|
+
keywords: ["quote", "blockquote"],
|
|
6410
|
+
badge: BlockquoteIcon,
|
|
6411
|
+
group: formatMessage(messages7.group_style)
|
|
6412
|
+
},
|
|
6413
|
+
code_block: {
|
|
6414
|
+
title: formatMessage(messages7.slash_code_block),
|
|
6415
|
+
subtext: formatMessage(messages7.slash_code_block_subtext),
|
|
6416
|
+
keywords: ["code", "pre"],
|
|
6417
|
+
badge: CodeBlockIcon,
|
|
6418
|
+
group: formatMessage(messages7.group_style)
|
|
6419
|
+
},
|
|
6420
|
+
// Insert
|
|
6421
|
+
control: {
|
|
6422
|
+
title: formatMessage(messages7.slash_control),
|
|
6423
|
+
subtext: formatMessage(messages7.slash_control_subtext),
|
|
6424
|
+
keywords: ["control"],
|
|
6425
|
+
badge: ControlsIcon,
|
|
6426
|
+
group: formatMessage(messages7.group_insert)
|
|
6427
|
+
},
|
|
6428
|
+
divider: {
|
|
6429
|
+
title: formatMessage(messages7.slash_separator),
|
|
6430
|
+
subtext: formatMessage(messages7.slash_separator_subtext),
|
|
6431
|
+
keywords: ["hr", "horizontalRule", "line", "separator"],
|
|
6432
|
+
badge: MinusIcon,
|
|
6433
|
+
group: formatMessage(messages7.group_insert)
|
|
6434
|
+
},
|
|
6435
|
+
table: {
|
|
6436
|
+
title: formatMessage(messages7.slash_table),
|
|
6437
|
+
subtext: formatMessage(messages7.slash_table_subtext),
|
|
6438
|
+
keywords: ["table", "grid", "spreadsheet"],
|
|
6439
|
+
badge: TableIcon2,
|
|
6440
|
+
group: formatMessage(messages7.group_insert)
|
|
6441
|
+
},
|
|
6442
|
+
table_of_contents: {
|
|
6443
|
+
title: formatMessage(messages7.slash_toc),
|
|
6444
|
+
subtext: formatMessage(messages7.slash_toc_subtext),
|
|
6445
|
+
keywords: ["toc", "table of contents", "index", "navigation", "headings"],
|
|
6446
|
+
badge: TableOfContentsIcon,
|
|
6447
|
+
group: formatMessage(messages7.group_insert)
|
|
6448
|
+
},
|
|
6449
|
+
callout: {
|
|
6450
|
+
title: formatMessage(messages7.slash_callout),
|
|
6451
|
+
subtext: formatMessage(messages7.slash_callout_subtext),
|
|
6452
|
+
keywords: ["callout", "info", "warning", "alert", "note", "tip"],
|
|
6453
|
+
badge: InfoIcon2,
|
|
6454
|
+
group: formatMessage(messages7.group_insert)
|
|
6455
|
+
},
|
|
6456
|
+
callout_warning: {
|
|
6457
|
+
title: formatMessage(messages7.slash_warning),
|
|
6458
|
+
subtext: formatMessage(messages7.slash_warning_subtext),
|
|
6459
|
+
keywords: ["warning", "caution", "attention"],
|
|
6460
|
+
badge: AlertIcon2,
|
|
6461
|
+
group: formatMessage(messages7.group_insert)
|
|
6462
|
+
},
|
|
6463
|
+
math: {
|
|
6464
|
+
title: formatMessage(messages7.slash_formula),
|
|
6465
|
+
subtext: formatMessage(messages7.slash_formula_subtext),
|
|
6466
|
+
keywords: ["math", "latex", "formula", "equation", "katex"],
|
|
6467
|
+
badge: TypeIcon,
|
|
6468
|
+
group: formatMessage(messages7.group_insert)
|
|
6469
|
+
},
|
|
6470
|
+
// Upload
|
|
6471
|
+
image: {
|
|
6472
|
+
title: formatMessage(messages7.slash_image),
|
|
6473
|
+
subtext: formatMessage(messages7.slash_image_subtext),
|
|
6474
|
+
keywords: [
|
|
6475
|
+
"image",
|
|
6476
|
+
"imageUpload",
|
|
6477
|
+
"upload",
|
|
6478
|
+
"img",
|
|
6479
|
+
"picture",
|
|
6480
|
+
"media",
|
|
6481
|
+
"url"
|
|
6482
|
+
],
|
|
6483
|
+
badge: ImageIcon2,
|
|
6484
|
+
group: formatMessage(messages7.group_upload)
|
|
6485
|
+
}
|
|
6486
|
+
};
|
|
6487
|
+
}
|
|
6488
|
+
var getItemImplementations = () => {
|
|
6489
|
+
return {
|
|
6490
|
+
// AI
|
|
6491
|
+
continue_writing: {
|
|
6492
|
+
check: (editor) => {
|
|
6493
|
+
const { hasContent } = hasContentAbove(editor);
|
|
6494
|
+
const extensionsReady = isExtensionAvailable(editor, [
|
|
6495
|
+
"ai",
|
|
6496
|
+
"aiAdvanced"
|
|
6497
|
+
]);
|
|
6498
|
+
return extensionsReady && hasContent;
|
|
6499
|
+
},
|
|
6500
|
+
action: ({ editor }) => {
|
|
6501
|
+
const editorChain = editor.chain().focus();
|
|
6502
|
+
const nodeSelectionPosition = findSelectionPosition({ editor });
|
|
6503
|
+
if (nodeSelectionPosition !== null) {
|
|
6504
|
+
editorChain.setNodeSelection(nodeSelectionPosition);
|
|
6505
|
+
}
|
|
6506
|
+
editorChain.run();
|
|
6507
|
+
editor.chain().focus().aiGenerationShow().run();
|
|
6508
|
+
requestAnimationFrame(() => {
|
|
6509
|
+
const { hasContent, content } = hasContentAbove(editor);
|
|
6510
|
+
const snippet = content.length > 500 ? `...${content.slice(-500)}` : content;
|
|
6511
|
+
const prompt = hasContent ? `Context: ${snippet}
|
|
6512
|
+
|
|
6513
|
+
Continue writing from where the text above ends. Write ONLY ONE SENTENCE. DONT REPEAT THE TEXT.` : "Start writing a new paragraph. Write ONLY ONE SENTENCE.";
|
|
6514
|
+
editor.chain().focus().aiTextPrompt({
|
|
6515
|
+
stream: true,
|
|
6516
|
+
format: "rich-text",
|
|
6517
|
+
text: prompt
|
|
6518
|
+
}).run();
|
|
6519
|
+
});
|
|
6520
|
+
}
|
|
6521
|
+
},
|
|
6522
|
+
ai_ask_button: {
|
|
6523
|
+
check: (editor) => isExtensionAvailable(editor, ["ai", "aiAdvanced"]),
|
|
6524
|
+
action: ({ editor }) => {
|
|
6525
|
+
const editorChain = editor.chain().focus();
|
|
6526
|
+
const nodeSelectionPosition = findSelectionPosition({ editor });
|
|
6527
|
+
if (nodeSelectionPosition !== null) {
|
|
6528
|
+
editorChain.setNodeSelection(nodeSelectionPosition);
|
|
6529
|
+
}
|
|
6530
|
+
editorChain.run();
|
|
6531
|
+
editor.chain().focus().aiGenerationShow().run();
|
|
6532
|
+
}
|
|
6533
|
+
},
|
|
6534
|
+
// Style
|
|
6535
|
+
text: {
|
|
6536
|
+
check: (editor) => isNodeInSchema2("paragraph", editor),
|
|
6537
|
+
action: ({ editor }) => {
|
|
6538
|
+
editor.chain().focus().setParagraph().run();
|
|
6539
|
+
}
|
|
6540
|
+
},
|
|
6541
|
+
heading_1: {
|
|
6542
|
+
check: (editor) => isNodeInSchema2("heading", editor),
|
|
6543
|
+
action: ({ editor }) => {
|
|
6544
|
+
editor.chain().focus().toggleHeading({ level: 1 }).run();
|
|
6545
|
+
}
|
|
6546
|
+
},
|
|
6547
|
+
heading_2: {
|
|
6548
|
+
check: (editor) => isNodeInSchema2("heading", editor),
|
|
6549
|
+
action: ({ editor }) => {
|
|
6550
|
+
editor.chain().focus().toggleHeading({ level: 2 }).run();
|
|
6551
|
+
}
|
|
6552
|
+
},
|
|
6553
|
+
heading_3: {
|
|
6554
|
+
check: (editor) => isNodeInSchema2("heading", editor),
|
|
6555
|
+
action: ({ editor }) => {
|
|
6556
|
+
editor.chain().focus().toggleHeading({ level: 3 }).run();
|
|
6557
|
+
}
|
|
6558
|
+
},
|
|
6559
|
+
bullet_list: {
|
|
6560
|
+
check: (editor) => isNodeInSchema2("bulletList", editor),
|
|
6561
|
+
action: ({ editor }) => {
|
|
6562
|
+
editor.chain().focus().toggleBulletList().run();
|
|
6563
|
+
}
|
|
6564
|
+
},
|
|
6565
|
+
ordered_list: {
|
|
6566
|
+
check: (editor) => isNodeInSchema2("orderedList", editor),
|
|
6567
|
+
action: ({ editor }) => {
|
|
6568
|
+
editor.chain().focus().toggleOrderedList().run();
|
|
6569
|
+
}
|
|
6570
|
+
},
|
|
6571
|
+
task_list: {
|
|
6572
|
+
check: (editor) => isNodeInSchema2("taskList", editor),
|
|
6573
|
+
action: ({ editor }) => {
|
|
6574
|
+
editor.chain().focus().toggleTaskList().run();
|
|
6575
|
+
}
|
|
6576
|
+
},
|
|
6577
|
+
quote: {
|
|
6578
|
+
check: (editor) => isNodeInSchema2("blockquote", editor),
|
|
6579
|
+
action: ({ editor }) => {
|
|
6580
|
+
editor.chain().focus().toggleBlockquote().run();
|
|
6581
|
+
}
|
|
6582
|
+
},
|
|
6583
|
+
code_block: {
|
|
6584
|
+
check: (editor) => isNodeInSchema2("codeBlock", editor),
|
|
6585
|
+
action: ({ editor }) => {
|
|
6586
|
+
editor.chain().focus().toggleNode("codeBlock", "paragraph").run();
|
|
6587
|
+
}
|
|
6588
|
+
},
|
|
6589
|
+
// Insert
|
|
6590
|
+
// mention: {
|
|
6591
|
+
// check: (editor: Editor) =>
|
|
6592
|
+
// isExtensionAvailable(editor, ["mention", "mentionAdvanced"]),
|
|
6593
|
+
// action: ({ editor }: { editor: Editor }) => addMentionTrigger(editor),
|
|
6594
|
+
// },
|
|
6595
|
+
// emoji: {
|
|
6596
|
+
// check: (editor: Editor) =>
|
|
6597
|
+
// isExtensionAvailable(editor, ["emoji", "emojiPicker"]),
|
|
6598
|
+
// action: ({ editor }: { editor: Editor }) => addEmojiTrigger(editor),
|
|
6599
|
+
// },
|
|
6600
|
+
divider: {
|
|
6601
|
+
check: (editor) => isNodeInSchema2("horizontalRule", editor),
|
|
6602
|
+
action: ({ editor }) => {
|
|
6603
|
+
editor.chain().focus().setHorizontalRule().run();
|
|
6604
|
+
}
|
|
6605
|
+
},
|
|
6606
|
+
table: {
|
|
6607
|
+
check: (editor) => isNodeInSchema2("table", editor),
|
|
6608
|
+
action: ({ editor }) => toggleTable(editor, { rows: 3, cols: 3, withHeaderRow: true })
|
|
6609
|
+
},
|
|
6610
|
+
control: {
|
|
6611
|
+
check: (editor) => isNodeInSchema2("controlBlock", editor),
|
|
6612
|
+
action: ({ editor }) => {
|
|
6613
|
+
try {
|
|
6614
|
+
return editor.chain().focus().insertControlBlock().run();
|
|
6615
|
+
} catch (e) {
|
|
6616
|
+
console.error(e);
|
|
6617
|
+
}
|
|
6618
|
+
}
|
|
6619
|
+
},
|
|
6620
|
+
table_of_contents: {
|
|
6621
|
+
check: (editor) => isNodeInSchema2("tableOfContentsNode", editor),
|
|
6622
|
+
action: ({ editor }) => {
|
|
6623
|
+
editor.chain().focus().insertTableOfContents().run();
|
|
6624
|
+
}
|
|
6625
|
+
},
|
|
6626
|
+
callout: {
|
|
6627
|
+
check: (editor) => isNodeInSchema2("calloutNode", editor),
|
|
6628
|
+
action: ({ editor }) => {
|
|
6629
|
+
editor.chain().focus().insertCallout("info").run();
|
|
6630
|
+
}
|
|
6631
|
+
},
|
|
6632
|
+
callout_warning: {
|
|
6633
|
+
check: (editor) => isNodeInSchema2("calloutNode", editor),
|
|
6634
|
+
action: ({ editor }) => {
|
|
6635
|
+
editor.chain().focus().insertCallout("warning").run();
|
|
6636
|
+
}
|
|
6637
|
+
},
|
|
6638
|
+
math: {
|
|
6639
|
+
check: (editor) => isNodeInSchema2("mathBlock", editor),
|
|
6640
|
+
action: ({ editor }) => {
|
|
6641
|
+
editor.chain().focus().insertMathBlock().run();
|
|
6642
|
+
}
|
|
6643
|
+
},
|
|
6644
|
+
// Upload
|
|
6645
|
+
image: {
|
|
6646
|
+
check: (editor) => isNodeInSchema2("imageUpload", editor),
|
|
6647
|
+
action: ({ editor }) => {
|
|
6648
|
+
editor.chain().focus().setImageUpload().run();
|
|
6708
6649
|
}
|
|
6709
6650
|
}
|
|
6710
|
-
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
|
|
6714
|
-
|
|
6715
|
-
|
|
6716
|
-
|
|
6717
|
-
|
|
6718
|
-
|
|
6719
|
-
|
|
6720
|
-
|
|
6721
|
-
onApplied == null ? void 0 : onApplied({ color: "", label: "Remove highlight" });
|
|
6651
|
+
};
|
|
6652
|
+
};
|
|
6653
|
+
function organizeItemsByGroups(items, showGroups) {
|
|
6654
|
+
if (!showGroups) {
|
|
6655
|
+
return items.map((item) => ({ ...item, group: "" }));
|
|
6656
|
+
}
|
|
6657
|
+
const groups = {};
|
|
6658
|
+
items.forEach((item) => {
|
|
6659
|
+
const groupLabel = item.group || "";
|
|
6660
|
+
if (!groups[groupLabel]) {
|
|
6661
|
+
groups[groupLabel] = [];
|
|
6722
6662
|
}
|
|
6723
|
-
|
|
6724
|
-
}
|
|
6725
|
-
|
|
6726
|
-
|
|
6727
|
-
(
|
|
6728
|
-
|
|
6729
|
-
|
|
6663
|
+
groups[groupLabel].push(item);
|
|
6664
|
+
});
|
|
6665
|
+
const organizedItems = [];
|
|
6666
|
+
Object.entries(groups).forEach(([, groupItems]) => {
|
|
6667
|
+
organizedItems.push(...groupItems);
|
|
6668
|
+
});
|
|
6669
|
+
return organizedItems;
|
|
6670
|
+
}
|
|
6671
|
+
var ALL_SLASH_MENU_ITEMS = [
|
|
6672
|
+
"continue_writing",
|
|
6673
|
+
"ai_ask_button",
|
|
6674
|
+
"text",
|
|
6675
|
+
"heading_1",
|
|
6676
|
+
"heading_2",
|
|
6677
|
+
"heading_3",
|
|
6678
|
+
"bullet_list",
|
|
6679
|
+
"ordered_list",
|
|
6680
|
+
"task_list",
|
|
6681
|
+
"quote",
|
|
6682
|
+
"code_block",
|
|
6683
|
+
"control",
|
|
6684
|
+
"divider",
|
|
6685
|
+
"table",
|
|
6686
|
+
"table_of_contents",
|
|
6687
|
+
"callout",
|
|
6688
|
+
"callout_warning",
|
|
6689
|
+
"math",
|
|
6690
|
+
"image"
|
|
6691
|
+
];
|
|
6692
|
+
function useSlashDropdownMenu(config) {
|
|
6693
|
+
const intl = useIntl13();
|
|
6694
|
+
const getSlashMenuItems = React11.useCallback(
|
|
6695
|
+
(editor) => {
|
|
6696
|
+
const items = [];
|
|
6697
|
+
const texts = createSlashMenuTexts(intl.formatMessage);
|
|
6698
|
+
const enabledItems = (config == null ? void 0 : config.enabledItems) || ALL_SLASH_MENU_ITEMS;
|
|
6699
|
+
const showGroups = (config == null ? void 0 : config.showGroups) !== false;
|
|
6700
|
+
const itemImplementations = getItemImplementations();
|
|
6701
|
+
enabledItems.forEach((itemType) => {
|
|
6702
|
+
var _a;
|
|
6703
|
+
const itemImpl = itemImplementations[itemType];
|
|
6704
|
+
const itemText = texts[itemType];
|
|
6705
|
+
if (itemImpl && itemText && itemImpl.check(editor)) {
|
|
6706
|
+
const item = {
|
|
6707
|
+
onSelect: ({ editor: editor2 }) => itemImpl.action({ editor: editor2 }),
|
|
6708
|
+
...itemText
|
|
6709
|
+
};
|
|
6710
|
+
if ((_a = config == null ? void 0 : config.itemGroups) == null ? void 0 : _a[itemType]) {
|
|
6711
|
+
item.group = config.itemGroups[itemType];
|
|
6712
|
+
} else if (!showGroups) {
|
|
6713
|
+
item.group = "";
|
|
6714
|
+
}
|
|
6715
|
+
items.push(item);
|
|
6716
|
+
}
|
|
6717
|
+
});
|
|
6718
|
+
if (config == null ? void 0 : config.customItems) {
|
|
6719
|
+
items.push(...config.customItems);
|
|
6720
|
+
}
|
|
6721
|
+
return organizeItemsByGroups(items, showGroups);
|
|
6730
6722
|
},
|
|
6731
|
-
|
|
6732
|
-
enabled: isVisible && canColorHighlightState,
|
|
6733
|
-
enableOnContentEditable: !isMobile,
|
|
6734
|
-
enableOnFormTags: true
|
|
6735
|
-
}
|
|
6723
|
+
[config, intl]
|
|
6736
6724
|
);
|
|
6737
6725
|
return {
|
|
6738
|
-
|
|
6739
|
-
|
|
6740
|
-
handleColorHighlight,
|
|
6741
|
-
handleRemoveHighlight,
|
|
6742
|
-
canColorHighlight: canColorHighlightState,
|
|
6743
|
-
label: label || intl.formatMessage(messages7.highlight_color),
|
|
6744
|
-
shortcutKeys: COLOR_HIGHLIGHT_SHORTCUT_KEY,
|
|
6745
|
-
Icon: HighlighterIcon
|
|
6726
|
+
getSlashMenuItems,
|
|
6727
|
+
config
|
|
6746
6728
|
};
|
|
6747
6729
|
}
|
|
6748
6730
|
|
|
6749
|
-
// src/ui/
|
|
6750
|
-
import {
|
|
6751
|
-
var
|
|
6752
|
-
|
|
6753
|
-
|
|
6754
|
-
|
|
6755
|
-
|
|
6756
|
-
|
|
6757
|
-
|
|
6758
|
-
|
|
6759
|
-
|
|
6760
|
-
|
|
6761
|
-
|
|
6762
|
-
|
|
6763
|
-
|
|
6764
|
-
|
|
6765
|
-
|
|
6766
|
-
isVisible,
|
|
6767
|
-
canColorHighlight: canColorHighlight2,
|
|
6768
|
-
isActive,
|
|
6769
|
-
handleColorHighlight,
|
|
6770
|
-
label,
|
|
6771
|
-
shortcutKeys
|
|
6772
|
-
} = useColorHighlight({
|
|
6773
|
-
editor,
|
|
6774
|
-
highlightColor,
|
|
6775
|
-
label: text || `Toggle highlight (${highlightColor})`,
|
|
6776
|
-
hideWhenUnavailable,
|
|
6777
|
-
onApplied
|
|
6778
|
-
});
|
|
6779
|
-
const handleClick = useCallback23(
|
|
6780
|
-
(event) => {
|
|
6781
|
-
onClick == null ? void 0 : onClick(event);
|
|
6782
|
-
if (event.defaultPrevented) return;
|
|
6783
|
-
handleColorHighlight();
|
|
6784
|
-
},
|
|
6785
|
-
[handleColorHighlight, onClick]
|
|
6731
|
+
// src/ui/slash-dropdown-menu/slash-dropdown-menu.tsx
|
|
6732
|
+
import { jsx as jsx22, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
6733
|
+
var SlashDropdownMenu = (props) => {
|
|
6734
|
+
const { config, ...restProps } = props;
|
|
6735
|
+
const { getSlashMenuItems } = useSlashDropdownMenu(config);
|
|
6736
|
+
return /* @__PURE__ */ jsx22(
|
|
6737
|
+
SuggestionMenu,
|
|
6738
|
+
{
|
|
6739
|
+
char: "/",
|
|
6740
|
+
pluginKey: "slashDropdownMenu",
|
|
6741
|
+
decorationClass: "tiptap-slash-decoration",
|
|
6742
|
+
decorationContent: "Filter...",
|
|
6743
|
+
selector: "tiptap-slash-dropdown-menu",
|
|
6744
|
+
items: ({ query, editor }) => filterSuggestionItems(getSlashMenuItems(editor), query),
|
|
6745
|
+
...restProps,
|
|
6746
|
+
children: (props2) => /* @__PURE__ */ jsx22(List, { ...props2, config })
|
|
6747
|
+
}
|
|
6786
6748
|
);
|
|
6787
|
-
|
|
6788
|
-
|
|
6789
|
-
|
|
6790
|
-
|
|
6791
|
-
|
|
6792
|
-
|
|
6749
|
+
};
|
|
6750
|
+
var Item = (props) => {
|
|
6751
|
+
const { item, isSelected, onSelect } = props;
|
|
6752
|
+
const itemRef = React12.useRef(null);
|
|
6753
|
+
React12.useEffect(() => {
|
|
6754
|
+
const selector = document.querySelector(
|
|
6755
|
+
'[data-selector="tiptap-slash-dropdown-menu"]'
|
|
6756
|
+
);
|
|
6757
|
+
if (!itemRef.current || !isSelected || !selector) return;
|
|
6758
|
+
const overflow = getElementOverflowPosition2(itemRef.current, selector);
|
|
6759
|
+
if (overflow === "top") {
|
|
6760
|
+
itemRef.current.scrollIntoView(true);
|
|
6761
|
+
} else if (overflow === "bottom") {
|
|
6762
|
+
itemRef.current.scrollIntoView(false);
|
|
6763
|
+
}
|
|
6764
|
+
}, [isSelected]);
|
|
6765
|
+
const BadgeIcon = item.badge;
|
|
6766
|
+
return /* @__PURE__ */ jsx22(
|
|
6767
|
+
Button6,
|
|
6768
|
+
{
|
|
6769
|
+
ref: itemRef,
|
|
6770
|
+
variant: "ghost",
|
|
6771
|
+
color: "default",
|
|
6772
|
+
startContent: BadgeIcon && /* @__PURE__ */ jsx22(BadgeIcon, {}),
|
|
6773
|
+
"data-active-state": isSelected ? "on" : "off",
|
|
6774
|
+
onClick: onSelect,
|
|
6775
|
+
fullWidth: true,
|
|
6776
|
+
spacing: "start",
|
|
6777
|
+
children: item.title
|
|
6778
|
+
}
|
|
6793
6779
|
);
|
|
6794
|
-
|
|
6780
|
+
};
|
|
6781
|
+
var List = ({
|
|
6782
|
+
items,
|
|
6783
|
+
selectedIndex,
|
|
6784
|
+
onSelect,
|
|
6785
|
+
config
|
|
6786
|
+
}) => {
|
|
6787
|
+
const styles = slashDropdownMenu2();
|
|
6788
|
+
const renderedItems = React12.useMemo(() => {
|
|
6789
|
+
const rendered = [];
|
|
6790
|
+
const showGroups = (config == null ? void 0 : config.showGroups) !== false;
|
|
6791
|
+
if (!showGroups) {
|
|
6792
|
+
items.forEach((item, index) => {
|
|
6793
|
+
rendered.push(
|
|
6794
|
+
/* @__PURE__ */ jsx22(
|
|
6795
|
+
Item,
|
|
6796
|
+
{
|
|
6797
|
+
item,
|
|
6798
|
+
isSelected: index === selectedIndex,
|
|
6799
|
+
onSelect: () => onSelect(item)
|
|
6800
|
+
},
|
|
6801
|
+
`item-${index}-${item.title}`
|
|
6802
|
+
)
|
|
6803
|
+
);
|
|
6804
|
+
});
|
|
6805
|
+
return rendered;
|
|
6806
|
+
}
|
|
6807
|
+
const groups = {};
|
|
6808
|
+
items.forEach((item, index) => {
|
|
6809
|
+
const groupLabel = item.group || "";
|
|
6810
|
+
if (!groups[groupLabel]) {
|
|
6811
|
+
groups[groupLabel] = { items: [], indices: [] };
|
|
6812
|
+
}
|
|
6813
|
+
groups[groupLabel].items.push(item);
|
|
6814
|
+
groups[groupLabel].indices.push(index);
|
|
6815
|
+
});
|
|
6816
|
+
Object.entries(groups).forEach(([groupLabel, groupData], groupIndex) => {
|
|
6817
|
+
if (groupIndex > 0) {
|
|
6818
|
+
rendered.push(
|
|
6819
|
+
/* @__PURE__ */ jsx22(
|
|
6820
|
+
Separator,
|
|
6821
|
+
{
|
|
6822
|
+
orientation: "horizontal"
|
|
6823
|
+
},
|
|
6824
|
+
`separator-${groupIndex.toString()}`
|
|
6825
|
+
)
|
|
6826
|
+
);
|
|
6827
|
+
}
|
|
6828
|
+
const groupItems = groupData.items.map((item, itemIndex) => {
|
|
6829
|
+
const originalIndex = groupData.indices[itemIndex];
|
|
6830
|
+
return /* @__PURE__ */ jsx22(
|
|
6831
|
+
Item,
|
|
6832
|
+
{
|
|
6833
|
+
item,
|
|
6834
|
+
isSelected: originalIndex === selectedIndex,
|
|
6835
|
+
onSelect: () => onSelect(item)
|
|
6836
|
+
},
|
|
6837
|
+
`item-${originalIndex}-${item.title}`
|
|
6838
|
+
);
|
|
6839
|
+
});
|
|
6840
|
+
if (groupLabel) {
|
|
6841
|
+
rendered.push(
|
|
6842
|
+
/* @__PURE__ */ jsxs17(
|
|
6843
|
+
"div",
|
|
6844
|
+
{
|
|
6845
|
+
className: styles.cardItemGroup(),
|
|
6846
|
+
children: [
|
|
6847
|
+
/* @__PURE__ */ jsx22("div", { className: styles.cardGroupLabel(), children: groupLabel }),
|
|
6848
|
+
/* @__PURE__ */ jsx22("div", { className: styles.cardGroup(), children: groupItems })
|
|
6849
|
+
]
|
|
6850
|
+
},
|
|
6851
|
+
`group-${groupIndex}-${groupLabel}`
|
|
6852
|
+
)
|
|
6853
|
+
);
|
|
6854
|
+
} else {
|
|
6855
|
+
rendered.push(...groupItems);
|
|
6856
|
+
}
|
|
6857
|
+
});
|
|
6858
|
+
return rendered;
|
|
6859
|
+
}, [items, selectedIndex, onSelect, config == null ? void 0 : config.showGroups, styles]);
|
|
6860
|
+
if (!renderedItems.length) {
|
|
6795
6861
|
return null;
|
|
6796
6862
|
}
|
|
6797
|
-
|
|
6798
|
-
|
|
6799
|
-
ToolbarButton3,
|
|
6863
|
+
return /* @__PURE__ */ jsx22(
|
|
6864
|
+
"div",
|
|
6800
6865
|
{
|
|
6801
|
-
|
|
6802
|
-
|
|
6803
|
-
|
|
6804
|
-
|
|
6805
|
-
|
|
6806
|
-
"data-active-state": isActive ? "on" : "off",
|
|
6807
|
-
tabIndex: -1,
|
|
6808
|
-
"aria-label": label,
|
|
6809
|
-
shortcutKeys,
|
|
6810
|
-
"aria-pressed": isActive,
|
|
6811
|
-
onClick: handleClick,
|
|
6812
|
-
style: buttonStyle,
|
|
6813
|
-
className: styles.button({ className }),
|
|
6814
|
-
isIconOnly: true,
|
|
6815
|
-
...buttonProps,
|
|
6816
|
-
children: [
|
|
6817
|
-
/* @__PURE__ */ jsx21(
|
|
6818
|
-
"span",
|
|
6819
|
-
{
|
|
6820
|
-
"data-active-state": isActive ? "on" : "off",
|
|
6821
|
-
className: styles.mark()
|
|
6822
|
-
}
|
|
6823
|
-
),
|
|
6824
|
-
children || /* @__PURE__ */ jsxs16(Fragment4, { children: [
|
|
6825
|
-
/* @__PURE__ */ jsx21(
|
|
6826
|
-
"span",
|
|
6827
|
-
{
|
|
6828
|
-
style: { "--highlight-color": highlightColor }
|
|
6829
|
-
}
|
|
6830
|
-
),
|
|
6831
|
-
text
|
|
6832
|
-
] })
|
|
6833
|
-
]
|
|
6866
|
+
className: styles.card(),
|
|
6867
|
+
style: {
|
|
6868
|
+
maxHeight: "var(--suggestion-menu-max-height)"
|
|
6869
|
+
},
|
|
6870
|
+
children: /* @__PURE__ */ jsx22("div", { className: styles.body(), children: renderedItems })
|
|
6834
6871
|
}
|
|
6835
6872
|
);
|
|
6836
6873
|
};
|
|
6837
6874
|
|
|
6838
|
-
// src/
|
|
6839
|
-
import {
|
|
6840
|
-
|
|
6841
|
-
|
|
6842
|
-
|
|
6843
|
-
|
|
6844
|
-
|
|
6845
|
-
|
|
6846
|
-
|
|
6847
|
-
|
|
6848
|
-
|
|
6849
|
-
|
|
6850
|
-
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
|
|
6854
|
-
|
|
6855
|
-
|
|
6856
|
-
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
"var(--tt-color-highlight-green)",
|
|
6863
|
-
"var(--tt-color-highlight-blue)",
|
|
6864
|
-
"var(--tt-color-highlight-red)",
|
|
6865
|
-
"var(--tt-color-highlight-purple)",
|
|
6866
|
-
"var(--tt-color-highlight-yellow)"
|
|
6867
|
-
])
|
|
6868
|
-
}) {
|
|
6869
|
-
const { handleRemoveHighlight } = useColorHighlight({ editor });
|
|
6870
|
-
const containerRef = useRef11(null);
|
|
6871
|
-
const menuItems = useMemo16(
|
|
6872
|
-
() => [...colors, { label: "Remove highlight", value: "none" }],
|
|
6873
|
-
[colors]
|
|
6874
|
-
);
|
|
6875
|
-
const { selectedIndex } = useMenuNavigation({
|
|
6876
|
-
containerRef,
|
|
6877
|
-
items: menuItems,
|
|
6878
|
-
orientation: "both",
|
|
6879
|
-
onSelect: (item) => {
|
|
6880
|
-
if (!containerRef.current) return false;
|
|
6881
|
-
const highlightedElement = containerRef.current.querySelector(
|
|
6882
|
-
'[data-highlighted="true"]'
|
|
6883
|
-
);
|
|
6884
|
-
if (highlightedElement) highlightedElement.click();
|
|
6885
|
-
if (item.value === "none") handleRemoveHighlight();
|
|
6886
|
-
},
|
|
6887
|
-
autoSelectFirstItem: false
|
|
6875
|
+
// src/presets/basic/editor-header.tsx
|
|
6876
|
+
import { useTiptapEditor as useTiptapEditor26 } from "@kopexa/editor-utils";
|
|
6877
|
+
import { MoreVerticalIcon } from "@kopexa/icons";
|
|
6878
|
+
import { Popover as Popover3 } from "@kopexa/popover";
|
|
6879
|
+
import {
|
|
6880
|
+
Toolbar as Toolbar2,
|
|
6881
|
+
ToolbarButton as ToolbarButton8,
|
|
6882
|
+
ToolbarGroup as ToolbarGroup2,
|
|
6883
|
+
ToolbarSeparator as ToolbarSeparator3
|
|
6884
|
+
} from "@kopexa/toolbar";
|
|
6885
|
+
import { useIsMobile as useIsMobile3 } from "@kopexa/use-is-mobile";
|
|
6886
|
+
import { useEffect as useEffect31, useRef as useRef12, useState as useState30 } from "react";
|
|
6887
|
+
import { useIntl as useIntl18 } from "react-intl";
|
|
6888
|
+
|
|
6889
|
+
// src/hooks/use-cursor-visibility.ts
|
|
6890
|
+
import * as React14 from "react";
|
|
6891
|
+
|
|
6892
|
+
// src/hooks/use-window-size.ts
|
|
6893
|
+
import * as React13 from "react";
|
|
6894
|
+
function useWindowSize() {
|
|
6895
|
+
const [windowSize, setWindowSize] = React13.useState({
|
|
6896
|
+
width: 0,
|
|
6897
|
+
height: 0,
|
|
6898
|
+
offsetTop: 0
|
|
6888
6899
|
});
|
|
6889
|
-
|
|
6890
|
-
|
|
6891
|
-
|
|
6892
|
-
|
|
6893
|
-
|
|
6894
|
-
|
|
6895
|
-
|
|
6896
|
-
|
|
6897
|
-
|
|
6898
|
-
|
|
6899
|
-
|
|
6900
|
-
|
|
6901
|
-
|
|
6902
|
-
|
|
6903
|
-
|
|
6904
|
-
|
|
6905
|
-
|
|
6906
|
-
|
|
6907
|
-
|
|
6908
|
-
|
|
6909
|
-
|
|
6910
|
-
|
|
6911
|
-
|
|
6912
|
-
onClick: handleRemoveHighlight,
|
|
6913
|
-
"aria-label": "Remove highlight",
|
|
6914
|
-
tabIndex: selectedIndex === colors.length ? 0 : -1,
|
|
6915
|
-
type: "button",
|
|
6916
|
-
role: "menuitem",
|
|
6917
|
-
variant: "ghost",
|
|
6918
|
-
color: "default",
|
|
6919
|
-
"data-highlighted": selectedIndex === colors.length,
|
|
6920
|
-
children: /* @__PURE__ */ jsx22(BanIcon, {})
|
|
6900
|
+
React13.useEffect(() => {
|
|
6901
|
+
handleResize();
|
|
6902
|
+
function handleResize() {
|
|
6903
|
+
if (typeof window === "undefined") return;
|
|
6904
|
+
const vp = window.visualViewport;
|
|
6905
|
+
if (!vp) return;
|
|
6906
|
+
const { width = 0, height = 0, offsetTop = 0 } = vp;
|
|
6907
|
+
setWindowSize((state) => {
|
|
6908
|
+
if (width === state.width && height === state.height && offsetTop === state.offsetTop) {
|
|
6909
|
+
return state;
|
|
6910
|
+
}
|
|
6911
|
+
return { width, height, offsetTop };
|
|
6912
|
+
});
|
|
6913
|
+
}
|
|
6914
|
+
const visualViewport = window.visualViewport;
|
|
6915
|
+
if (visualViewport) {
|
|
6916
|
+
visualViewport.addEventListener("resize", handleResize);
|
|
6917
|
+
visualViewport.addEventListener("scroll", handleResize);
|
|
6918
|
+
}
|
|
6919
|
+
return () => {
|
|
6920
|
+
if (visualViewport) {
|
|
6921
|
+
visualViewport.removeEventListener("resize", handleResize);
|
|
6922
|
+
visualViewport.removeEventListener("scroll", handleResize);
|
|
6921
6923
|
}
|
|
6922
|
-
|
|
6923
|
-
]
|
|
6924
|
+
};
|
|
6925
|
+
}, []);
|
|
6926
|
+
return windowSize;
|
|
6924
6927
|
}
|
|
6925
|
-
|
|
6926
|
-
|
|
6927
|
-
|
|
6928
|
-
|
|
6929
|
-
|
|
6930
|
-
|
|
6931
|
-
"var(--tt-color-highlight-purple)",
|
|
6932
|
-
"var(--tt-color-highlight-yellow)"
|
|
6933
|
-
]),
|
|
6934
|
-
hideWhenUnavailable = false,
|
|
6935
|
-
onApplied,
|
|
6936
|
-
...props
|
|
6928
|
+
|
|
6929
|
+
// src/hooks/use-cursor-visibility.ts
|
|
6930
|
+
function useCursorVisibility({
|
|
6931
|
+
editor,
|
|
6932
|
+
overlayHeight = 0,
|
|
6933
|
+
elementRef = null
|
|
6937
6934
|
}) {
|
|
6938
|
-
const {
|
|
6939
|
-
const [
|
|
6940
|
-
|
|
6941
|
-
|
|
6942
|
-
|
|
6943
|
-
|
|
6935
|
+
const { height: windowHeight } = useWindowSize();
|
|
6936
|
+
const [rect, setRect] = React14.useState({
|
|
6937
|
+
x: 0,
|
|
6938
|
+
y: 0,
|
|
6939
|
+
width: 0,
|
|
6940
|
+
height: 0
|
|
6944
6941
|
});
|
|
6945
|
-
|
|
6946
|
-
|
|
6947
|
-
|
|
6948
|
-
|
|
6949
|
-
|
|
6950
|
-
|
|
6951
|
-
|
|
6952
|
-
|
|
6953
|
-
|
|
6954
|
-
|
|
6955
|
-
|
|
6956
|
-
|
|
6957
|
-
|
|
6958
|
-
|
|
6959
|
-
|
|
6942
|
+
const updateRect = React14.useCallback(() => {
|
|
6943
|
+
var _a;
|
|
6944
|
+
const element = (_a = elementRef == null ? void 0 : elementRef.current) != null ? _a : document.body;
|
|
6945
|
+
const { x, y, width, height } = element.getBoundingClientRect();
|
|
6946
|
+
setRect({ x, y, width, height });
|
|
6947
|
+
}, [elementRef]);
|
|
6948
|
+
React14.useEffect(() => {
|
|
6949
|
+
var _a;
|
|
6950
|
+
const element = (_a = elementRef == null ? void 0 : elementRef.current) != null ? _a : document.body;
|
|
6951
|
+
updateRect();
|
|
6952
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
6953
|
+
window.requestAnimationFrame(updateRect);
|
|
6954
|
+
});
|
|
6955
|
+
resizeObserver.observe(element);
|
|
6956
|
+
window.addEventListener("scroll", updateRect, { passive: true });
|
|
6957
|
+
return () => {
|
|
6958
|
+
resizeObserver.disconnect();
|
|
6959
|
+
window.removeEventListener("scroll", updateRect);
|
|
6960
|
+
};
|
|
6961
|
+
}, [elementRef, updateRect]);
|
|
6962
|
+
React14.useEffect(() => {
|
|
6963
|
+
const ensureCursorVisibility = () => {
|
|
6964
|
+
if (!editor) return;
|
|
6965
|
+
const { state, view } = editor;
|
|
6966
|
+
if (!view.hasFocus()) return;
|
|
6967
|
+
const { from } = state.selection;
|
|
6968
|
+
const cursorCoords = view.coordsAtPos(from);
|
|
6969
|
+
if (windowHeight < rect.height) {
|
|
6970
|
+
if (cursorCoords) {
|
|
6971
|
+
const availableSpace = windowHeight - cursorCoords.top - overlayHeight > 0;
|
|
6972
|
+
if (!availableSpace) {
|
|
6973
|
+
const targetScrollY = (
|
|
6974
|
+
// TODO: Needed?
|
|
6975
|
+
// window.scrollY + (cursorCoords.top - windowHeight / 2)
|
|
6976
|
+
cursorCoords.top - windowHeight / 2
|
|
6977
|
+
);
|
|
6978
|
+
window.scrollTo({
|
|
6979
|
+
top: targetScrollY,
|
|
6980
|
+
behavior: "smooth"
|
|
6981
|
+
});
|
|
6960
6982
|
}
|
|
6961
|
-
|
|
6983
|
+
}
|
|
6962
6984
|
}
|
|
6963
|
-
|
|
6964
|
-
|
|
6965
|
-
]
|
|
6985
|
+
};
|
|
6986
|
+
ensureCursorVisibility();
|
|
6987
|
+
}, [editor, overlayHeight, windowHeight, rect.height]);
|
|
6988
|
+
return rect;
|
|
6966
6989
|
}
|
|
6967
6990
|
|
|
6968
6991
|
// src/ui/list-dropdown-menu/index.tsx
|