@blocklet/editor 1.6.233 → 1.6.236

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.
Files changed (42) hide show
  1. package/lib/config.js +3 -11
  2. package/lib/ext/AIPlugin/index.js +1 -1
  3. package/lib/ext/Aide/context.d.ts +2 -4
  4. package/lib/ext/Aide/context.js +9 -57
  5. package/lib/ext/Alert/AlertNode.js +1 -1
  6. package/lib/ext/ContentLocale.d.ts +7 -0
  7. package/lib/ext/ContentLocale.js +15 -0
  8. package/lib/ext/FilePlugin/FileNode.d.ts +44 -0
  9. package/lib/ext/FilePlugin/FileNode.js +194 -0
  10. package/lib/ext/FilePlugin/FilePlugin.d.ts +7 -0
  11. package/lib/ext/FilePlugin/FilePlugin.js +27 -0
  12. package/lib/ext/FilePlugin/index.d.ts +2 -0
  13. package/lib/ext/FilePlugin/index.js +2 -0
  14. package/lib/ext/PagesKitComponent/PagesKitComponentNode.d.ts +31 -0
  15. package/lib/ext/PagesKitComponent/PagesKitComponentNode.js +96 -0
  16. package/lib/ext/PagesKitComponent/PagesKitComponentPlugin.d.ts +18 -0
  17. package/lib/ext/PagesKitComponent/PagesKitComponentPlugin.js +38 -0
  18. package/lib/ext/PagesKitComponent/PagesKitComponentRenderer.d.ts +9 -0
  19. package/lib/ext/PagesKitComponent/PagesKitComponentRenderer.js +75 -0
  20. package/lib/ext/PagesKitComponent/PropertyField.d.ts +13 -0
  21. package/lib/ext/PagesKitComponent/PropertyField.js +65 -0
  22. package/lib/ext/PagesKitComponent/utils.d.ts +25 -0
  23. package/lib/ext/PagesKitComponent/utils.js +16 -0
  24. package/lib/ext/PdfPlugin/PdfNode.d.ts +7 -7
  25. package/lib/ext/PdfPlugin/PdfNode.js +8 -6
  26. package/lib/ext/SubpageListingPlugin/SubpageListingComponent.js +1 -1
  27. package/lib/ext/VideoPlugin/VideoPlugin.d.ts +1 -1
  28. package/lib/ext/VideoPlugin/VideoPlugin.js +2 -3
  29. package/lib/ext/utils.d.ts +1 -0
  30. package/lib/ext/utils.js +3 -0
  31. package/lib/main/editor.js +3 -1
  32. package/lib/main/nodes/ImageComponent.js +3 -3
  33. package/lib/main/nodes/ImageNode.css +10 -3
  34. package/lib/main/nodes/PlaygroundNodes.js +4 -0
  35. package/lib/main/plugins/ComponentPickerPlugin/index.js +23 -5
  36. package/lib/main/plugins/ImagesPlugin/index.d.ts +1 -0
  37. package/lib/main/plugins/ImagesPlugin/index.js +3 -0
  38. package/lib/main/plugins/ToolbarPlugin/index.js +1 -5
  39. package/lib/main/themes/customTheme.js +1 -1
  40. package/lib/main/themes/defaultTheme.js +7 -5
  41. package/lib/main/ui/ImageResizer.js +2 -2
  42. package/package.json +6 -4
@@ -0,0 +1,75 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
3
+ import { Box, Button, Divider, IconButton, Popover, useMediaQuery } from '@mui/material';
4
+ import { useEffect, useState } from 'react';
5
+ import SettingsOutlinedIcon from '@mui/icons-material/SettingsOutlined';
6
+ import { CustomComponentRenderer, customComponentStates } from '@blocklet/pages-kit/components';
7
+ import { isBlockletRunning } from '../utils';
8
+ import { mode, isValidPropertyType, getLocalizedValue, getPropertyLabel } from './utils';
9
+ import { PropertyField } from './PropertyField';
10
+ import { useContentLocale } from '../ContentLocale';
11
+ export function PagesKitComponentRenderer({ id, name, properties, onPropertiesChange }) {
12
+ const locale = useContentLocale();
13
+ const [editor] = useLexicalComposerContext();
14
+ const isEditable = editor.isEditable();
15
+ const [loading, setLoading] = useState(true);
16
+ const [anchorEl, setAnchorEl] = useState(null);
17
+ const open = Boolean(anchorEl);
18
+ const upMd = useMediaQuery((theme) => theme.breakpoints.up('md'));
19
+ const [pendingProperties, setPendingProperties] = useState(properties || {});
20
+ const handleClick = (event) => {
21
+ setAnchorEl(event.currentTarget);
22
+ };
23
+ const handleCancel = () => {
24
+ setAnchorEl(null);
25
+ setPendingProperties(properties || {});
26
+ };
27
+ const handleSave = () => {
28
+ onPropertiesChange(pendingProperties);
29
+ setAnchorEl(null);
30
+ };
31
+ useEffect(() => {
32
+ const load = async () => {
33
+ const state = customComponentStates().getState();
34
+ await state.loadComponents({
35
+ mode,
36
+ locale,
37
+ instances: [{ id, component: { name } }],
38
+ });
39
+ setLoading(false);
40
+ };
41
+ if (isBlockletRunning('pages-kit')) {
42
+ load();
43
+ }
44
+ }, [id, name]);
45
+ const component = customComponentStates().getState()?.state?.components?.[id]?.component;
46
+ if (loading || !component) {
47
+ return null;
48
+ }
49
+ // 暂时仅支持基础类型的组件参数配置
50
+ const propDefinitions = Object.values(component.properties || {})
51
+ .map((x) => x.data)
52
+ .filter((x) => !!x.key && isValidPropertyType(x.type));
53
+ return (_jsxs(Box, { sx: { position: 'relative', ...(isEditable && { mt: 2 }) }, children: [_jsx(CustomComponentRenderer, { componentId: id, locale: locale, dev: { mode }, props: properties?.[locale] }), isEditable && !!propDefinitions.length && upMd && (_jsxs(_Fragment, { children: [_jsx(IconButton, { sx: { position: 'absolute', right: 0, top: -32 }, onClick: handleClick, children: _jsx(SettingsOutlinedIcon, {}) }), _jsx(Popover, { open: open, anchorEl: anchorEl, onClose: () => setAnchorEl(null), anchorOrigin: {
54
+ vertical: 'bottom',
55
+ horizontal: 'right',
56
+ }, transformOrigin: {
57
+ vertical: 'top',
58
+ horizontal: 'right',
59
+ }, children: _jsxs(Box, { sx: { width: 440, maxHeight: 500, p: 2 }, children: [_jsx(Box, { sx: { display: 'flex', alignItems: 'center' }, children: _jsxs(Box, { sx: {
60
+ display: 'flex',
61
+ alignItems: 'center',
62
+ gap: 0.5,
63
+ color: 'text.secondary',
64
+ fontSize: 14,
65
+ fontWeight: 600,
66
+ }, children: [_jsx(Box, { component: "span", className: "iconify", "data-icon": "tabler:settings-2", sx: { fontSize: 20 } }), _jsx("span", { children: "Properties" })] }) }), _jsx(Divider, { sx: { mt: 0.75, mb: 2 } }), propDefinitions.map((x) => {
67
+ // @ts-ignore
68
+ const multiline = !!x.multiline;
69
+ return (_jsx(Box, { sx: { mt: 2 }, children: _jsx(PropertyField, { label: getPropertyLabel({ locale, locales: x.locales }), type: x.type, value: pendingProperties[locale]?.[x.key] ||
70
+ getLocalizedValue({ key: 'defaultValue', locale, data: x.locales }), onChange: (v) => setPendingProperties({
71
+ ...pendingProperties,
72
+ [locale]: { ...pendingProperties[locale], [x.key]: v },
73
+ }), multiline: multiline }) }, x.key));
74
+ }), _jsxs(Box, { sx: { display: 'flex', justifyContent: 'flex-end', gap: 1, py: 2 }, children: [_jsx(Button, { color: "inherit", onClick: handleCancel, children: "Cancel" }), _jsx(Button, { color: "primary", variant: "contained", onClick: handleSave, children: "Save" })] })] }) })] }))] }));
75
+ }
@@ -0,0 +1,13 @@
1
+ import { type PropertyType } from './utils';
2
+ interface PropertyFieldProps {
3
+ type?: PropertyType;
4
+ multiline?: boolean;
5
+ label: string;
6
+ value: any;
7
+ onChange: (value: any) => void;
8
+ }
9
+ export declare function UploaderButton({ onChange }: {
10
+ onChange?: Function;
11
+ }): import("react/jsx-runtime").JSX.Element;
12
+ export declare function PropertyField({ type, multiline, label, value, onChange, ...rest }: PropertyFieldProps): import("react/jsx-runtime").JSX.Element;
13
+ export {};
@@ -0,0 +1,65 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { FormControlLabel, IconButton, InputAdornment, Switch, TextField } from '@mui/material';
3
+ import UploadIcon from '@mui/icons-material/Upload';
4
+ function getVideoSize(url) {
5
+ return new Promise((resolve, reject) => {
6
+ const video = document.createElement('video');
7
+ video.src = url;
8
+ video.onloadedmetadata = () => {
9
+ const { videoWidth: naturalWidth, videoHeight: naturalHeight } = video;
10
+ resolve({ naturalWidth, naturalHeight });
11
+ };
12
+ video.onerror = (e) => reject(e);
13
+ });
14
+ }
15
+ function getImageSize(url) {
16
+ return new Promise((resolve, reject) => {
17
+ const img = new Image();
18
+ img.src = url;
19
+ img.onload = () => {
20
+ const { naturalWidth, naturalHeight } = img;
21
+ resolve({ naturalWidth, naturalHeight });
22
+ };
23
+ img.onerror = (e) => reject(e);
24
+ });
25
+ }
26
+ export function UploaderButton({ onChange }) {
27
+ const handleOpen = () => {
28
+ // @ts-ignore
29
+ const uploader = uploaderRef?.current?.getUploader();
30
+ uploader?.open();
31
+ if (onChange) {
32
+ // rewrite default emitter
33
+ uploader.onceUploadSuccess((...args) => {
34
+ onChange(...args);
35
+ });
36
+ }
37
+ };
38
+ return (_jsx(IconButton, { size: "small", onClick: handleOpen, children: _jsx(UploadIcon, {}) }, "uploader-trigger"));
39
+ }
40
+ export function PropertyField({ type, multiline, label, value, onChange, ...rest }) {
41
+ if (type === 'boolean') {
42
+ return (_jsx(FormControlLabel, { sx: { display: 'inline-flex', m: 0 }, label: label, labelPlacement: "start", control: _jsx(Switch, { checked: value === true, onChange: (_, checked) => onChange?.(checked) }) }));
43
+ }
44
+ if (type === 'number') {
45
+ return (_jsx(TextField, { type: "number", size: "small", sx: { width: 1 }, label: label, value: value, onChange: (e) => onChange(e.target.value), ...rest }));
46
+ }
47
+ if (type === 'url') {
48
+ return (_jsx(TextField, { sx: { width: 1 }, size: "small", label: label, value: value.url, onChange: (e) => onChange(e.target.value), InputProps: {
49
+ endAdornment: (_jsx(InputAdornment, { position: "end", children: _jsx(UploaderButton, { onChange: async ({ response }) => {
50
+ const url = response?.data?.url || response?.data?.fileUrl;
51
+ let size;
52
+ if (url) {
53
+ size = await getImageSize(url)
54
+ .catch(() => getVideoSize(url))
55
+ .catch(() => undefined);
56
+ }
57
+ onChange?.({ url, width: size?.naturalWidth, height: size?.naturalHeight });
58
+ } }) })),
59
+ }, ...rest }));
60
+ }
61
+ return (_jsx(TextField, { sx: { width: 1 }, size: "small", label: label, value: value, onChange: (e) => onChange(e.target.value), ...((multiline || type === 'json') && {
62
+ multiline: true,
63
+ rows: 2,
64
+ }), ...rest }));
65
+ }
@@ -0,0 +1,25 @@
1
+ export declare const mode: string;
2
+ export declare const propertyTypes: readonly ["string", "number", "boolean", "json", "url"];
3
+ export type PropertyType = typeof propertyTypes[number];
4
+ export type Properties = {
5
+ [locale: string]: {
6
+ [key: string]: any;
7
+ };
8
+ };
9
+ export declare const isValidPropertyType: (type: string | null | undefined) => boolean;
10
+ type Locales = {
11
+ [locale: string]: {
12
+ name?: string;
13
+ defaultValue?: any;
14
+ };
15
+ };
16
+ export declare const getLocalizedValue: ({ key, locale, data }: {
17
+ key: string;
18
+ locale: string;
19
+ data?: Properties | undefined;
20
+ }) => any;
21
+ export declare const getPropertyLabel: ({ locale, locales }: {
22
+ locale: string;
23
+ locales?: Locales | undefined;
24
+ }) => string;
25
+ export {};
@@ -0,0 +1,16 @@
1
+ export const mode = process.env.NODE_ENV === 'development' ? 'draft' : 'production';
2
+ export const propertyTypes = ['string', 'number', 'boolean', 'json', 'url'];
3
+ export const isValidPropertyType = (type) => {
4
+ return !type || propertyTypes.includes(type);
5
+ };
6
+ // 依次尝试取值
7
+ // - `data[locale][key]`
8
+ // - `data.en[key]`
9
+ // - 遍历所有 locale 取第一个有值的 `data[locale][key]`
10
+ export const getLocalizedValue = ({ key, locale, data }) => {
11
+ data = data || {};
12
+ return data[locale]?.[key] || data.en?.[key] || Object.values(data).find((x) => x?.[key])?.[key] || '';
13
+ };
14
+ export const getPropertyLabel = ({ locale, locales }) => {
15
+ return getLocalizedValue({ key: 'name', locale, data: locales });
16
+ };
@@ -5,24 +5,24 @@ export declare const getAbsoluteUrl: (imageUrl: string) => any;
5
5
  export interface PdfPayload {
6
6
  key?: NodeKey;
7
7
  src: string;
8
- width?: number;
9
- height?: number;
8
+ width?: number | string;
9
+ height?: number | string;
10
10
  }
11
11
  export type SerializedPdfNode = Spread<{
12
- height?: number;
12
+ height?: number | string;
13
13
  src: string;
14
- width?: number;
14
+ width?: number | string;
15
15
  }, SerializedLexicalNode>;
16
16
  export declare class PdfNode extends DecoratorNode<JSX.Element> {
17
17
  __src: string;
18
- __width: 'inherit' | number;
19
- __height: 'inherit' | number;
18
+ __width: 'inherit' | number | string;
19
+ __height: 'inherit' | number | string;
20
20
  static getType(): string;
21
21
  static clone(node: PdfNode): PdfNode;
22
22
  static importJSON(serializedNode: SerializedPdfNode): PdfNode;
23
23
  exportDOM(): DOMExportOutput;
24
24
  static importDOM(): DOMConversionMap | null;
25
- constructor(src: string, width?: 'inherit' | number, height?: 'inherit' | number, key?: NodeKey);
25
+ constructor(src: string, width?: 'inherit' | number | string, height?: 'inherit' | number | string, key?: NodeKey);
26
26
  exportJSON(): SerializedPdfNode;
27
27
  setWidthAndHeight(width: 'inherit' | number, height: 'inherit' | number): void;
28
28
  createDOM(config: EditorConfig): HTMLElement;
@@ -23,9 +23,9 @@ export const getAbsoluteUrl = (imageUrl) => {
23
23
  return discussKit ? joinURL(window.location.origin, discussKit.mountPoint || '/', '/uploads', imageUrl) : imageUrl;
24
24
  };
25
25
  function convertPdfElement(domNode) {
26
- if (domNode instanceof HTMLImageElement) {
27
- const { src, width, height } = domNode;
28
- const node = $createPdfNode({ height, src, width });
26
+ if (domNode instanceof HTMLObjectElement) {
27
+ const { data, width, height } = domNode;
28
+ const node = $createPdfNode({ height, src: data, width });
29
29
  return { node };
30
30
  }
31
31
  return null;
@@ -50,15 +50,17 @@ export class PdfNode extends DecoratorNode {
50
50
  return node;
51
51
  }
52
52
  exportDOM() {
53
- const element = document.createElement('img');
54
- element.setAttribute('src', this.__src);
53
+ const element = document.createElement('object');
54
+ element.setAttribute('data', this.__src);
55
55
  element.setAttribute('width', this.__width.toString());
56
56
  element.setAttribute('height', this.__height.toString());
57
+ // set pdf content-type
58
+ element.setAttribute('type', 'application/pdf');
57
59
  return { element };
58
60
  }
59
61
  static importDOM() {
60
62
  return {
61
- img: (node) => ({
63
+ object: (node) => ({
62
64
  conversion: convertPdfElement,
63
65
  priority: 0,
64
66
  }),
@@ -50,7 +50,7 @@ export default function SubpageListingComponent({ docPostId, mode, onModeChange,
50
50
  if (loading) {
51
51
  return (_jsx(Box, { sx: { display: 'flex', justifyContent: 'center', alignItems: 'center', height: 200 }, children: _jsx(CircularProgress, {}) }));
52
52
  }
53
- return (_jsxs(Box, { sx: { display: 'flex', flexWrap: 'wrap', position: 'relative', mx: -1 }, children: [isEmpty && (_jsx(Box, { sx: { width: 1, px: 1 }, children: _jsx(Alert, { severity: "info", sx: {}, children: "No subpages" }) })), isEditable && (_jsxs(_Fragment, { children: [_jsx(IconButton, { sx: { position: 'absolute', right: 0, top: -32 }, onClick: handleClick, children: _jsx(SettingsOutlinedIcon, {}) }), _jsx(Popover, { open: open, anchorEl: anchorEl, onClose: () => setAnchorEl(null), anchorOrigin: {
53
+ return (_jsxs(Box, { sx: { display: 'flex', flexWrap: 'wrap', position: 'relative', mx: -1, ...(isEditable && { mt: 2 }) }, children: [isEmpty && (_jsx(Box, { sx: { width: 1, px: 1 }, children: _jsx(Alert, { severity: "info", sx: {}, children: "No subpages" }) })), isEditable && (_jsxs(_Fragment, { children: [_jsx(IconButton, { sx: { position: 'absolute', right: 0, top: -32 }, onClick: handleClick, children: _jsx(SettingsOutlinedIcon, {}) }), _jsx(Popover, { open: open, anchorEl: anchorEl, onClose: () => setAnchorEl(null), anchorOrigin: {
54
54
  vertical: 'bottom',
55
55
  horizontal: 'right',
56
56
  }, transformOrigin: {
@@ -4,4 +4,4 @@ import { VideoPayload } from './VideoNode';
4
4
  export type InsertVideoPayload = Readonly<VideoPayload>;
5
5
  export declare const INSERT_VIDEO_COMMAND: LexicalCommand<InsertVideoPayload>;
6
6
  export declare function VideoPlugin(): JSX.Element | null;
7
- export declare const isVideo: (filename: string) => boolean | "" | undefined;
7
+ export declare const isVideo: (src: string) => boolean;
@@ -21,7 +21,6 @@ export function VideoPlugin() {
21
21
  }, [editor]);
22
22
  return null;
23
23
  }
24
- export const isVideo = (filename) => {
25
- const ext = filename.split('.').pop();
26
- return ext && ['mp4', 'webm'].includes(ext);
24
+ export const isVideo = (src) => {
25
+ return /\.(mp4|avi|mov|wmv|flv|mkv|mpeg|mpg|webm|ogg|ogv|3gp)$/i.test(src);
27
26
  };
@@ -17,3 +17,4 @@ export declare const sleep: (ms: number) => Promise<unknown>;
17
17
  export declare const minDelay: <T>(promise: Promise<T>, ms: number) => Promise<T>;
18
18
  export declare function exhaustiveGuard(_value: never): never;
19
19
  export declare const isSameHost: (url: string | URL) => boolean;
20
+ export declare const isBlockletRunning: (name: string) => boolean;
package/lib/ext/utils.js CHANGED
@@ -86,3 +86,6 @@ export const isSameHost = (url) => {
86
86
  return false;
87
87
  }
88
88
  };
89
+ export const isBlockletRunning = (name) => {
90
+ return getBlockletMountPointInfo(name)?.status === 'running';
91
+ };
@@ -79,9 +79,11 @@ import { VideoPlugin } from '../ext/VideoPlugin';
79
79
  import { TemplatePlugin } from '../ext/TemplatePlugin';
80
80
  import { CustomOnChangePlugin } from '../ext/CustomOnChangePlugin';
81
81
  import { PdfPlugin } from '../ext/PdfPlugin';
82
+ import { FilePlugin } from '../ext/FilePlugin';
82
83
  import { BlurTextPlugin } from '../ext/BlurTextPlugin';
83
84
  import { useResponsiveTable } from './hooks/useResponsiveTable';
84
85
  import { useTranslationListener } from './hooks/useTranslationListener';
86
+ import { PagesKitComponentPlugin } from '../ext/PagesKitComponent/PagesKitComponentPlugin';
85
87
  export default function Editor({ children, placeholder, onChange, autoFocus = true, showToolbar = true, editorRef, onReady, enableHeadingsIdPlugin, }) {
86
88
  const [editor] = useLexicalComposerContext();
87
89
  const [editable, setEditable] = useState(false);
@@ -109,7 +111,7 @@ export default function Editor({ children, placeholder, onChange, autoFocus = tr
109
111
  console.warn('You need to configure uploader to use ImagesPlugin');
110
112
  }
111
113
  }, [hasNodes('image'), hasUploader]);
112
- return (_jsxs(_Fragment, { children: [isRichText && editable && showToolbar && _jsx(ToolbarPlugin, {}), isMaxLength && _jsx(MaxLengthPlugin, { maxLength: 30 }), hasNodes('image') && hasUploader && _jsx(DragDropPaste, {}), autoFocus && _jsx(AutoFocusPlugin, { defaultSelection: "rootEnd" }), _jsx(ClearEditorPlugin, {}), !!editable && _jsx(ComponentPickerPlugin, {}), _jsx(EmojiPickerPlugin, {}), hasNodes('link') && _jsx(AutoEmbedPlugin, {}), !!editable && _jsx(MentionsPlugin, {}), hasNodes('emoji') && _jsx(EmojisPlugin, {}), hasNodes('hashtag') && _jsx(HashtagPlugin, {}), hasNodes('keyword') && _jsx(KeywordsPlugin, {}), _jsx(SpeechToTextPlugin, {}), hasNodes('autolink') && _jsx(AutoLinkPlugin, {}), isRichText ? (_jsxs(_Fragment, { children: [_jsx(HistoryPlugin, { externalHistoryState: historyState }), _jsx(EditorContent, { ref: onRef, className: cx('be-content', editable && 'editable'), children: _jsx(RichTextPlugin, { contentEditable: _jsx(ContentEditable, { className: cx('be-editable', 'notranslate') }), placeholder: _jsx(Placeholder, { className: "be-placeholder", children: placeholder }), ErrorBoundary: LexicalErrorBoundary }) }), _jsx(MarkdownShortcutPlugin, {}), _jsx(TabIndentationPlugin, {}), hasNodes('code', 'code-highlight') && _jsx(CodeHighlightPlugin, {}), hasNodes('list', 'listitem') && _jsx(ListPlugin, {}), hasNodes('list', 'listitem') && _jsx(CheckListPlugin, {}), hasNodes('list', 'listitem') && _jsx(ListMaxIndentLevelPlugin, { maxDepth: 7 }), hasNodes('table', 'tablerow', 'tablecell') && editable && (_jsx(TablePlugin, { hasCellMerge: true, hasCellBackgroundColor: true })), hasNodes('table', 'tablerow', 'tablecell') && _jsx(TableCellResizer, {}), hasNodes('image') && hasUploader && _jsx(ImagesPlugin, {}), hasNodes('video') && _jsx(VideoPlugin, {}), hasNodes('link') && _jsx(LinkPlugin, {}), hasNodes('tweet') && _jsx(TwitterPlugin, {}), hasNodes('youtube') && _jsx(YouTubePlugin, {}), hasNodes('figma') && _jsx(FigmaPlugin, {}), _jsx(BilibiliPlugin, {}), _jsx(BlockletEmbedPlugin, {}), _jsx(PostLinkEmbedPlugin, {}), _jsx(BookmarkPlugin, {}), _jsx(AidePlugin, {}), editable && _jsx(CustomOnChangePlugin, { placeholder: placeholder }), editable && _jsx(TemplatePlugin, {}), !editable && _jsx(BlurTextPlugin, {}), hasNodes('pdf') && _jsx(PdfPlugin, {}), hasNodes('link') && _jsx(ClickableLinkPlugin, {}), hasNodes('horizontalrule') && _jsx(HorizontalRulePlugin, {}), hasNodes('equation') && _jsx(EquationsPlugin, {}), hasNodes('excalidraw') && _jsx(ExcalidrawPlugin, {}), hasNodes('alert') && _jsx(AlertPlugin, {}), _jsx(TabFocusPlugin, {}), hasNodes('collapsible-container', 'collapsible-content', 'collapsible-title') && _jsx(CollapsiblePlugin, {}), onChange && _jsx(OnChangePlugin, { onChange: onChange }), floatingAnchorElem && editable && (_jsxs(_Fragment, { children: [_jsx(DraggableBlockPlugin, { anchorElem: floatingAnchorElem }), hasNodes('code') && _jsx(CodeActionMenuPlugin, { anchorElem: floatingAnchorElem }), hasNodes('link') && _jsx(FloatingLinkEditorPlugin, { anchorElem: floatingAnchorElem }), hasNodes('table') && _jsx(TableCellActionMenuPlugin, { anchorElem: floatingAnchorElem, cellMerge: true }), _jsx(FloatingTextFormatToolbarPlugin, { anchorElem: floatingAnchorElem })] })), enableHeadingsIdPlugin && _jsx(HeadingsIdPlugin, {})] })) : (_jsxs(_Fragment, { children: [_jsx(PlainTextPlugin, { contentEditable: _jsx(ContentEditable, {}), placeholder: _jsx(Placeholder, { children: "placeholder" }), ErrorBoundary: LexicalErrorBoundary }), _jsx(HistoryPlugin, { externalHistoryState: historyState })] })), (isCharLimit || isCharLimitUtf8) && (_jsx(CharacterLimitPlugin, { charset: isCharLimit ? 'UTF-16' : 'UTF-8', maxLength: 5 })), isAutocomplete && hasNodes('autocomplete') && _jsx(AutocompletePlugin, {}), _jsx("div", { children: showTableOfContents && _jsx(TableOfContentsPlugin, {}) }), _jsx(SelectBlockPlugin, {}), _jsx(RemoveListPlugin, {}), _jsx(MarkdownHeadTextPlugin, {}), _jsx(AiImagePlugin, {}), editorRef && _jsx(EditorRefPlugin, { editorRef: editorRef }), onReady && _jsx(EditorReadyPlugin, { onReady: onReady }), children] }));
114
+ return (_jsxs(_Fragment, { children: [isRichText && editable && showToolbar && _jsx(ToolbarPlugin, {}), isMaxLength && _jsx(MaxLengthPlugin, { maxLength: 30 }), hasNodes('image') && hasUploader && _jsx(DragDropPaste, {}), autoFocus && _jsx(AutoFocusPlugin, { defaultSelection: "rootEnd" }), _jsx(ClearEditorPlugin, {}), !!editable && _jsx(ComponentPickerPlugin, {}), _jsx(EmojiPickerPlugin, {}), hasNodes('link') && _jsx(AutoEmbedPlugin, {}), !!editable && _jsx(MentionsPlugin, {}), hasNodes('emoji') && _jsx(EmojisPlugin, {}), hasNodes('hashtag') && _jsx(HashtagPlugin, {}), hasNodes('keyword') && _jsx(KeywordsPlugin, {}), _jsx(SpeechToTextPlugin, {}), hasNodes('autolink') && _jsx(AutoLinkPlugin, {}), isRichText ? (_jsxs(_Fragment, { children: [_jsx(HistoryPlugin, { externalHistoryState: historyState }), _jsx(EditorContent, { ref: onRef, className: cx('be-content', editable && 'editable'), children: _jsx(RichTextPlugin, { contentEditable: _jsx(ContentEditable, { className: cx('be-editable', 'notranslate') }), placeholder: _jsx(Placeholder, { className: "be-placeholder", children: placeholder }), ErrorBoundary: LexicalErrorBoundary }) }), _jsx(MarkdownShortcutPlugin, {}), _jsx(TabIndentationPlugin, {}), hasNodes('code', 'code-highlight') && _jsx(CodeHighlightPlugin, {}), hasNodes('list', 'listitem') && _jsx(ListPlugin, {}), hasNodes('list', 'listitem') && _jsx(CheckListPlugin, {}), hasNodes('list', 'listitem') && _jsx(ListMaxIndentLevelPlugin, { maxDepth: 7 }), hasNodes('table', 'tablerow', 'tablecell') && editable && (_jsx(TablePlugin, { hasCellMerge: true, hasCellBackgroundColor: true })), hasNodes('table', 'tablerow', 'tablecell') && _jsx(TableCellResizer, {}), hasNodes('image') && hasUploader && _jsx(ImagesPlugin, {}), hasNodes('video') && _jsx(VideoPlugin, {}), hasNodes('link') && _jsx(LinkPlugin, {}), hasNodes('tweet') && _jsx(TwitterPlugin, {}), hasNodes('youtube') && _jsx(YouTubePlugin, {}), hasNodes('figma') && _jsx(FigmaPlugin, {}), _jsx(BilibiliPlugin, {}), _jsx(BlockletEmbedPlugin, {}), _jsx(PostLinkEmbedPlugin, {}), _jsx(BookmarkPlugin, {}), _jsx(AidePlugin, {}), editable && _jsx(CustomOnChangePlugin, { placeholder: placeholder }), editable && _jsx(TemplatePlugin, {}), !editable && _jsx(BlurTextPlugin, {}), hasNodes('pdf') && _jsx(PdfPlugin, {}), hasNodes('file') && _jsx(FilePlugin, {}), hasNodes('link') && _jsx(ClickableLinkPlugin, {}), hasNodes('horizontalrule') && _jsx(HorizontalRulePlugin, {}), hasNodes('equation') && _jsx(EquationsPlugin, {}), hasNodes('excalidraw') && _jsx(ExcalidrawPlugin, {}), hasNodes('alert') && _jsx(AlertPlugin, {}), hasNodes('pages-kit-component') && _jsx(PagesKitComponentPlugin, {}), _jsx(TabFocusPlugin, {}), hasNodes('collapsible-container', 'collapsible-content', 'collapsible-title') && _jsx(CollapsiblePlugin, {}), onChange && _jsx(OnChangePlugin, { onChange: onChange }), floatingAnchorElem && editable && (_jsxs(_Fragment, { children: [_jsx(DraggableBlockPlugin, { anchorElem: floatingAnchorElem }), hasNodes('code') && _jsx(CodeActionMenuPlugin, { anchorElem: floatingAnchorElem }), hasNodes('link') && _jsx(FloatingLinkEditorPlugin, { anchorElem: floatingAnchorElem }), hasNodes('table') && _jsx(TableCellActionMenuPlugin, { anchorElem: floatingAnchorElem, cellMerge: true }), _jsx(FloatingTextFormatToolbarPlugin, { anchorElem: floatingAnchorElem })] })), enableHeadingsIdPlugin && _jsx(HeadingsIdPlugin, {})] })) : (_jsxs(_Fragment, { children: [_jsx(PlainTextPlugin, { contentEditable: _jsx(ContentEditable, {}), placeholder: _jsx(Placeholder, { children: "placeholder" }), ErrorBoundary: LexicalErrorBoundary }), _jsx(HistoryPlugin, { externalHistoryState: historyState })] })), (isCharLimit || isCharLimitUtf8) && (_jsx(CharacterLimitPlugin, { charset: isCharLimit ? 'UTF-16' : 'UTF-8', maxLength: 5 })), isAutocomplete && hasNodes('autocomplete') && _jsx(AutocompletePlugin, {}), _jsx("div", { children: showTableOfContents && _jsx(TableOfContentsPlugin, {}) }), _jsx(SelectBlockPlugin, {}), _jsx(RemoveListPlugin, {}), _jsx(MarkdownHeadTextPlugin, {}), _jsx(AiImagePlugin, {}), editorRef && _jsx(EditorRefPlugin, { editorRef: editorRef }), onReady && _jsx(EditorReadyPlugin, { onReady: onReady }), children] }));
113
115
  }
114
116
  const EditorContent = styled.div `
115
117
  position: relative;
@@ -159,11 +159,11 @@ export default function ImageComponent({ file, src, altText, nodeKey, width, hei
159
159
  return false;
160
160
  }, COMMAND_PRIORITY_LOW), editor.registerCommand(KEY_DELETE_COMMAND, onDelete, COMMAND_PRIORITY_LOW), editor.registerCommand(KEY_BACKSPACE_COMMAND, onDelete, COMMAND_PRIORITY_LOW), editor.registerCommand(KEY_ENTER_COMMAND, onEnter, COMMAND_PRIORITY_LOW), editor.registerCommand(KEY_ESCAPE_COMMAND, onEscape, COMMAND_PRIORITY_LOW));
161
161
  }, [clearSelection, editor, isResizing, isSelected, nodeKey, onDelete, onEnter, onEscape, setSelected]);
162
- const setShowCaption = () => {
162
+ const setShowCaption = (value) => {
163
163
  editor.update(() => {
164
164
  const node = $getNodeByKey(nodeKey);
165
165
  if ($isImageNode(node)) {
166
- node.setShowCaption(true);
166
+ node.setShowCaption(value);
167
167
  }
168
168
  });
169
169
  };
@@ -227,7 +227,7 @@ export default function ImageComponent({ file, src, altText, nodeKey, width, hei
227
227
  }
228
228
  }, [src, file, confirmText]);
229
229
  const placeholder = objectUrl ? (_jsx(ImageContainer, { draggable: draggable, success: !!src, loading: loading, error: error, confirmText: confirmText, uploadImage: uploadImage, placeholder: objectUrl, placeholderProps: { style: { width, height, maxWidth } } })) : null;
230
- return (_jsx(Suspense, { fallback: placeholder, children: _jsxs(_Fragment, { children: [src ? (_jsx(ImageContainer, { draggable: draggable, children: _jsx(LazyImage, { className: isFocused ? `focused ${$isNodeSelection(selection) ? 'draggable' : ''}` : null, src: src, altText: altText, imageRef: imageRef, width: width, height: height, maxWidth: maxWidth }) })) : (placeholder), showCaption && (_jsx("div", { className: "image-caption-container", children: _jsxs(LexicalNestedComposer, { initialEditor: caption, children: [_jsx(AutoFocusPlugin, {}), _jsx(MentionsPlugin, {}), _jsx(LinkPlugin, {}), _jsx(EmojisPlugin, {}), _jsx(HashtagPlugin, {}), _jsx(KeywordsPlugin, {}), _jsx(HistoryPlugin, { externalHistoryState: historyState }), _jsx(RichTextPlugin, { contentEditable: _jsx(ContentEditable, { className: "ImageNode__contentEditable" }), placeholder: _jsx(Placeholder, { className: "ImageNode__placeholder", children: "Enter a caption..." }), ErrorBoundary: LexicalErrorBoundary })] }) })), resizable && $isNodeSelection(selection) && isFocused && (_jsx(ImageResizer, { showCaption: showCaption, setShowCaption: setShowCaption, editor: editor, buttonRef: buttonRef, imageRef: imageRef,
230
+ return (_jsx(Suspense, { fallback: placeholder, children: _jsxs(_Fragment, { children: [src ? (_jsx(ImageContainer, { draggable: draggable, children: _jsx(LazyImage, { className: isFocused ? `focused ${$isNodeSelection(selection) ? 'draggable' : ''}` : null, src: src, altText: altText, imageRef: imageRef, width: width, height: height, maxWidth: maxWidth }) })) : (placeholder), showCaption && (_jsx("div", { className: "image-caption-container", children: _jsxs(LexicalNestedComposer, { initialEditor: caption, children: [_jsx(AutoFocusPlugin, {}), _jsx(MentionsPlugin, {}), _jsx(LinkPlugin, {}), _jsx(EmojisPlugin, {}), _jsx(HashtagPlugin, {}), _jsx(KeywordsPlugin, {}), _jsx(HistoryPlugin, { externalHistoryState: historyState }), _jsx(RichTextPlugin, { contentEditable: _jsx(ContentEditable, { className: "ImageNode__contentEditable" }), placeholder: editor.isEditable() ? (_jsx(Placeholder, { className: "ImageNode__placeholder", children: "Enter a caption..." })) : null, ErrorBoundary: LexicalErrorBoundary })] }) })), resizable && $isNodeSelection(selection) && isFocused && (_jsx(ImageResizer, { showCaption: showCaption, setShowCaption: setShowCaption, editor: editor, buttonRef: buttonRef, imageRef: imageRef,
231
231
  // maxWidth={maxWidth}
232
232
  onResizeStart: onResizeStart, onResizeEnd: onResizeEnd, captionsEnabled: captionsEnabled }))] }) }));
233
233
  }
@@ -19,10 +19,13 @@
19
19
  outline: 0px;
20
20
  padding: 10px;
21
21
  user-select: text;
22
- font-size: 12px;
22
+
23
23
  width: calc(100% - 20px);
24
24
  white-space: pre-wrap;
25
25
  word-break: break-word;
26
+ p {
27
+ font-size: 12px;
28
+ }
26
29
  }
27
30
 
28
31
  .ImageNode__placeholder {
@@ -31,12 +34,16 @@
31
34
  overflow: hidden;
32
35
  position: absolute;
33
36
  text-overflow: ellipsis;
34
- top: 10px;
35
- left: 10px;
37
+ top: 0;
38
+ bottom: 0;
39
+ margin: auto;
40
+ left: 0;
36
41
  user-select: none;
37
42
  white-space: nowrap;
38
43
  display: inline-block;
39
44
  pointer-events: none;
45
+ padding: 10px;
46
+ line-height: inherit;
40
47
  }
41
48
 
42
49
  .image-control-wrapper--resizing {
@@ -36,7 +36,9 @@ import { GithubNode } from '../../ext/nodes/GithubNode';
36
36
  import { AlertNode } from '../../ext/Alert/AlertNode';
37
37
  import { VideoNode } from '../../ext/VideoPlugin';
38
38
  import { PdfNode } from '../../ext/PdfPlugin';
39
+ import { FileNode } from '../../ext/FilePlugin';
39
40
  import { SubpageListingNode } from '../../ext/SubpageListingPlugin/SubpageListingNode';
41
+ import { PagesKitComponentNode } from '../../ext/PagesKitComponent/PagesKitComponentNode';
40
42
  const PlaygroundNodes = [
41
43
  HeadingNode,
42
44
  ListNode,
@@ -75,6 +77,8 @@ const PlaygroundNodes = [
75
77
  GithubNode,
76
78
  AlertNode,
77
79
  PdfNode,
80
+ FileNode,
78
81
  SubpageListingNode,
82
+ PagesKitComponentNode,
79
83
  ];
80
84
  export default PlaygroundNodes;
@@ -18,6 +18,7 @@ import { INSERT_TABLE_COMMAND } from '@lexical/table';
18
18
  import { $createParagraphNode, $getSelection, $isRangeSelection, FORMAT_ELEMENT_COMMAND, createCommand, $insertNodes, $createTextNode, COMMAND_PRIORITY_EDITOR, } from 'lexical';
19
19
  import { mergeRegister } from '@lexical/utils';
20
20
  import { useCallback, useEffect, useMemo, useState } from 'react';
21
+ import { Box } from '@mui/material';
21
22
  import { LexicalTypeaheadMenuPlugin, TypeaheadOption, useBasicTypeaheadTriggerMatch, } from '../LexicalTypeaheadMenuPlugin';
22
23
  import useHasNodes from '../../hooks/useHasNodes';
23
24
  import useModal from '../../hooks/useModal';
@@ -25,7 +26,7 @@ import { EmbedConfigs } from '../AutoEmbedPlugin';
25
26
  import { INSERT_COLLAPSIBLE_COMMAND } from '../CollapsiblePlugin';
26
27
  import { InsertEquationDialog } from '../EquationsPlugin';
27
28
  import { INSERT_EXCALIDRAW_COMMAND } from '../ExcalidrawPlugin';
28
- import { INSERT_IMAGE_COMMAND } from '../ImagesPlugin';
29
+ import { INSERT_IMAGE_COMMAND, isImage } from '../ImagesPlugin';
29
30
  import { InsertTableDialog } from '../TablePlugin';
30
31
  import { useEditorConfig } from '../../../config';
31
32
  import createPortal from '../../../components/createPortal';
@@ -34,7 +35,9 @@ import { $insertAlert } from '../../../ext/Alert/AlertPlugin';
34
35
  import { INSERT_VIDEO_COMMAND, isVideo } from '../../../ext/VideoPlugin';
35
36
  import { INSERT_TEMPLATE_COMMAND } from '../../../ext/TemplatePlugin';
36
37
  import { INSERT_PDF_COMMAND, isPdf } from '../../../ext/PdfPlugin';
38
+ import { INSERT_FILE_COMMAND } from '../../../ext/FilePlugin';
37
39
  import { $insertSubpageListing } from '../../../ext/SubpageListingPlugin/SubpageListingPlugin';
40
+ import { INSERT_PAGES_KIT_COMPONENT_COMMAND, usePagesKitComponents, } from '../../../ext/PagesKitComponent/PagesKitComponentPlugin';
38
41
  class ComponentPickerOption extends TypeaheadOption {
39
42
  // What shows up in the editor
40
43
  title;
@@ -68,6 +71,7 @@ export default function ComponentPickerMenuPlugin() {
68
71
  const [modal, showModal] = useModal();
69
72
  const [queryString, setQueryString] = useState(null);
70
73
  const config = useEditorConfig();
74
+ const { pagesKitComponents } = usePagesKitComponents();
71
75
  const hasUploader = !!config.uploader;
72
76
  const checkForTriggerMatch = useBasicTypeaheadTriggerMatch('/', {
73
77
  minLength: 0,
@@ -290,6 +294,13 @@ export default function ComponentPickerMenuPlugin() {
290
294
  keywords: ['subpage', 'doc', 'list'],
291
295
  onSelect: () => $insertSubpageListing(editor, 'children'),
292
296
  }),
297
+ ...pagesKitComponents
298
+ .filter((x) => !!x.name)
299
+ .map((x) => new ComponentPickerOption(`${x.name}`, {
300
+ icon: (_jsx(Box, { component: "span", sx: { display: 'inline-flex', width: 20, px: 0.25, mr: '1px', fontSize: 13 }, title: "Pages kit custom component", children: "\uD83E\uDDE9" })),
301
+ keywords: [x.name],
302
+ onSelect: () => editor.dispatchCommand(INSERT_PAGES_KIT_COMPONENT_COMMAND, { id: x.id, name: x.name }),
303
+ })),
293
304
  ].filter(Boolean);
294
305
  const dynamicOptions = getDynamicOptions();
295
306
  return queryString
@@ -365,20 +376,27 @@ export const uploadFile = (editor) => {
365
376
  if (file.source !== 'function-upload-file') {
366
377
  const { data } = response;
367
378
  if (data?.filename) {
368
- if (isVideo(data.filename)) {
379
+ if (isVideo(data.filename) || isVideo(data.originalname)) {
369
380
  editor.dispatchCommand(INSERT_VIDEO_COMMAND, {
370
381
  src: `/${data.filename}`,
371
382
  });
372
383
  }
373
- else if (isPdf(data.filename)) {
384
+ else if (isPdf(data.filename) || isPdf(data.originalname)) {
374
385
  editor.dispatchCommand(INSERT_PDF_COMMAND, {
375
386
  src: `/${data.filename}`,
376
387
  });
377
388
  }
378
- else {
389
+ else if (isImage(data.filename) || isImage(data.originalname)) {
379
390
  editor.dispatchCommand(INSERT_IMAGE_COMMAND, {
380
391
  src: `/${data.filename}`,
381
- altText: 'Image',
392
+ altText: data.originalname,
393
+ });
394
+ }
395
+ else {
396
+ editor.dispatchCommand(INSERT_FILE_COMMAND, {
397
+ src: `/${data.filename}`,
398
+ name: data.originalname,
399
+ mimetype: data.mimetype,
382
400
  });
383
401
  }
384
402
  }
@@ -12,3 +12,4 @@ declare global {
12
12
  rangeParent?: Node;
13
13
  }
14
14
  }
15
+ export declare const isImage: (src: string) => boolean;
@@ -31,3 +31,6 @@ export default function ImagesPlugin({ captionsEnabled }) {
31
31
  const TRANSPARENT_IMAGE = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
32
32
  const img = document.createElement('img');
33
33
  img.src = TRANSPARENT_IMAGE;
34
+ export const isImage = (src) => {
35
+ return /\.(jpg|jpeg|png|gif|bmp|svg|webp|ico|tiff|tif|raw)$/i.test(src);
36
+ };
@@ -25,8 +25,6 @@ import { sanitizeUrl } from '../../utils/sanitizeUrl';
25
25
  // import { InsertEquationDialog } from "../EquationsPlugin";
26
26
  // import { INSERT_EXCALIDRAW_COMMAND } from "../ExcalidrawPlugin";
27
27
  import { INSERT_IMAGE_COMMAND } from '../ImagesPlugin';
28
- import { AideToolbarButton } from '../../../ext/AIPlugin';
29
- import { AITranslateToolbarButton } from '../../../ext/AITranslationPlugin';
30
28
  import { uploadFile, INSERT_COMPONENT_COMMAND } from '../ComponentPickerPlugin';
31
29
  // import { InsertNewTableDialog, InsertTableDialog } from "../TablePlugin";
32
30
  const blockTypeToBlockName = {
@@ -390,7 +388,7 @@ export default function ToolbarPlugin() {
390
388
  if (data?.filename) {
391
389
  editor.dispatchCommand(INSERT_IMAGE_COMMAND, {
392
390
  src: `/${data?.filename}`,
393
- altText: 'Image',
391
+ altText: data?.originalname,
394
392
  });
395
393
  }
396
394
  }
@@ -463,8 +461,6 @@ export default function ToolbarPlugin() {
463
461
  if (menus.length === 0) {
464
462
  return null;
465
463
  }
466
- menus.push(_jsx(AideToolbarButton, {}, "aide"));
467
- menus.push(_jsx(AITranslateToolbarButton, {}, "ai-translate"));
468
464
  return (_jsxs(Toolbar, { className: "toolbar", children: [menus, modal] }));
469
465
  }
470
466
  const Toolbar = styled.div `
@@ -222,7 +222,7 @@ const createCustomTheme = (theme) => {
222
222
  padding: 0;
223
223
  p {
224
224
  color: ${palette.text.secondary};
225
- font-size: 0.875rem;
225
+ font-size: 12px;
226
226
  }
227
227
  }
228
228
  }
@@ -535,12 +535,12 @@ const image = css `
535
535
  .image-caption-container {
536
536
  display: block;
537
537
  position: absolute;
538
- bottom: 4px;
538
+ bottom: 8px;
539
539
  left: 0;
540
540
  right: 0;
541
541
  padding: 0;
542
542
  margin: 0;
543
- border-top: 1px solid #fff;
543
+
544
544
  background-color: rgba(255, 255, 255, 0.9);
545
545
  min-width: 100px;
546
546
  color: #000;
@@ -560,13 +560,15 @@ const image = css `
560
560
  .image-caption-button {
561
561
  display: block;
562
562
  position: absolute;
563
- bottom: 20px;
563
+ height: 40px;
564
+ bottom: 0;
565
+ top: 0;
564
566
  left: 0;
565
567
  right: 0;
566
568
  width: 30%;
567
569
  padding: 10px;
568
- margin: 0 auto;
569
- border: 1px solid rgba(255, 255, 255, 0.3);
570
+ margin: auto;
571
+ border: 2px solid rgba(255, 255, 255, 0.3);
570
572
  border-radius: 5px;
571
573
  background-color: rgba(0, 0, 0, 0.5);
572
574
  min-width: 100px;
@@ -136,9 +136,9 @@ export default function ImageResizer({ onResizeStart, onResizeEnd, buttonRef, im
136
136
  document.removeEventListener('pointerup', handlePointerUp);
137
137
  }
138
138
  };
139
- return (_jsxs("div", { ref: controlWrapperRef, children: [!showCaption && captionsEnabled && (_jsx("button", { className: "image-caption-button", ref: buttonRef, onClick: () => {
139
+ return (_jsxs("div", { ref: controlWrapperRef, children: [captionsEnabled && (_jsx("button", { className: "image-caption-button", ref: buttonRef, onClick: () => {
140
140
  setShowCaption(!showCaption);
141
- }, children: "Add Caption" })), _jsx("div", { className: "image-resizer image-resizer-n", onPointerDown: (event) => {
141
+ }, children: showCaption ? 'Remove Caption' : 'Add Caption' })), _jsx("div", { className: "image-resizer image-resizer-n", onPointerDown: (event) => {
142
142
  handlePointerDown(event, Direction.north);
143
143
  } }), _jsx("div", { className: "image-resizer image-resizer-ne", onPointerDown: (event) => {
144
144
  handlePointerDown(event, Direction.north | Direction.east);