@comitor/ui 3.5.0 → 3.6.1

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 (51) hide show
  1. package/README.md +4 -2
  2. package/dist/data-grid/data-grid-cell-variants.js +6 -1
  3. package/dist/data-grid/data-grid-cell-variants.js.map +1 -1
  4. package/dist/data-grid/types.d.ts +10 -0
  5. package/dist/data-grid/use-data-grid.d.ts +2 -2
  6. package/dist/data-grid/use-data-grid.js +30 -13
  7. package/dist/data-grid/use-data-grid.js.map +1 -1
  8. package/dist/editor/bubble-toolbar.d.ts +20 -1
  9. package/dist/editor/bubble-toolbar.js +12 -172
  10. package/dist/editor/bubble-toolbar.js.map +1 -1
  11. package/dist/editor/editor-core.d.ts +28 -0
  12. package/dist/editor/editor-core.js +354 -0
  13. package/dist/editor/editor-core.js.map +1 -0
  14. package/dist/editor/editor-types.d.ts +3 -0
  15. package/dist/editor/editor-types.js +1 -0
  16. package/dist/editor/editor-types.js.map +1 -0
  17. package/dist/editor/fixed-toolbar.d.ts +19 -0
  18. package/dist/editor/fixed-toolbar.js +106 -0
  19. package/dist/editor/fixed-toolbar.js.map +1 -0
  20. package/dist/editor/image-upload.d.ts +3 -57
  21. package/dist/editor/image-upload.js +3 -2
  22. package/dist/editor/image-upload.js.map +1 -1
  23. package/dist/editor/index.d.ts +3 -2
  24. package/dist/editor/index.js +4 -4
  25. package/dist/editor/index.js.map +1 -1
  26. package/dist/editor/markdown-editor-classes.js +6 -4
  27. package/dist/editor/markdown-editor-classes.js.map +1 -1
  28. package/dist/editor/markdown-editor-constants.d.ts +8 -2
  29. package/dist/editor/markdown-editor-constants.js.map +1 -1
  30. package/dist/editor/markdown-editor.d.ts +14 -91
  31. package/dist/editor/markdown-editor.js +23 -315
  32. package/dist/editor/markdown-editor.js.map +1 -1
  33. package/dist/editor/markdown-extensions.d.ts +40 -15
  34. package/dist/editor/markdown-extensions.js +143 -24
  35. package/dist/editor/markdown-extensions.js.map +1 -1
  36. package/dist/editor/rich-text-editor.d.ts +48 -0
  37. package/dist/editor/rich-text-editor.js +33 -0
  38. package/dist/editor/rich-text-editor.js.map +1 -0
  39. package/dist/editor/slash-commands.d.ts +4 -1
  40. package/dist/editor/slash-commands.js +7 -2
  41. package/dist/editor/slash-commands.js.map +1 -1
  42. package/dist/editor/toolbar-actions.d.ts +44 -0
  43. package/dist/editor/toolbar-actions.js +173 -0
  44. package/dist/editor/toolbar-actions.js.map +1 -0
  45. package/dist/editor/toolbar-button.d.ts +15 -0
  46. package/dist/editor/toolbar-button.js +47 -0
  47. package/dist/editor/toolbar-button.js.map +1 -0
  48. package/dist/editor-types-BMST7-VR.d.ts +171 -0
  49. package/dist/i18n/namespaces/markdown-editor.js +6 -0
  50. package/dist/i18n/namespaces/markdown-editor.js.map +1 -1
  51. package/package.json +1 -1
@@ -0,0 +1,44 @@
1
+ import { Editor } from '@tiptap/core';
2
+ import { LucideIcon } from 'lucide-react';
3
+ import { f as EditorFormat } from '../editor-types-BMST7-VR.js';
4
+ import { MarkdownEditorLabels } from './markdown-editor-constants.js';
5
+
6
+ /**
7
+ * Hành động của thanh công cụ — MỘT bảng cho cả thanh nổi (`bubble-toolbar.tsx`) lẫn thanh cố định
8
+ * (`fixed-toolbar.tsx`). Hai bảng riêng là hai nơi để lệch: nút In đậm ở thanh này có phím tắt, ở
9
+ * thanh kia thì không.
10
+ *
11
+ * File thuần (không `"use client"`): dữ liệu — icon là component, `run` là hàm. Chữ hiển thị không
12
+ * nằm ở đây; `label` là KHOÁ của namespace `markdownEditor`.
13
+ */
14
+
15
+ /** Khoá nhãn mang CHUỖI (loại `slashKeywords` và hàm `taskCheckbox`). */
16
+ type TextLabelKey = {
17
+ [K in keyof MarkdownEditorLabels]: MarkdownEditorLabels[K] extends string ? K : never;
18
+ }[keyof MarkdownEditorLabels];
19
+ interface ToolbarAction {
20
+ id: string;
21
+ icon: LucideIcon;
22
+ label: TextLabelKey;
23
+ /** Phím tắt cho tooltip — cú pháp của `KeyboardHint` (`mod+b`). Mặc định của Tiptap. */
24
+ keys?: string;
25
+ /**
26
+ * Chỉ có ở những định dạng này. Bỏ trống = có ở cả hai. Gạch chân chỉ ở HTML: markdown không có cú
27
+ * pháp cho nó (lý do ở `createSchemaExtensions`).
28
+ */
29
+ formats?: readonly EditorFormat[];
30
+ /** Có thì nút là nút BẬT/TẮT (`aria-pressed`); không có thì là nút lệnh. */
31
+ isActive?: (editor: Editor) => boolean;
32
+ /** `false` = nút bị khoá (hoàn tác khi chưa có gì để hoàn tác, chèn bảng khi đang ở trong bảng). */
33
+ canRun?: (editor: Editor) => boolean;
34
+ run: (editor: Editor) => void;
35
+ }
36
+ declare const HISTORY_ACTIONS: readonly ToolbarAction[];
37
+ declare const MARK_ACTIONS: readonly ToolbarAction[];
38
+ declare const BLOCK_ACTIONS: readonly ToolbarAction[];
39
+ /** Chèn — chỉ thanh cố định có (thanh nổi chỉ hiện khi đã bôi chọn chữ, không phải lúc để chèn khối). */
40
+ declare const INSERT_ACTIONS: readonly ToolbarAction[];
41
+ /** Các hành động có ở `format`. */
42
+ declare function actionsFor(actions: readonly ToolbarAction[], format: EditorFormat): ToolbarAction[];
43
+
44
+ export { BLOCK_ACTIONS, HISTORY_ACTIONS, INSERT_ACTIONS, MARK_ACTIONS, type TextLabelKey, type ToolbarAction, actionsFor };
@@ -0,0 +1,173 @@
1
+ import {
2
+ Bold,
3
+ Code,
4
+ Heading1,
5
+ Heading2,
6
+ Heading3,
7
+ Italic,
8
+ List,
9
+ ListChecks,
10
+ ListOrdered,
11
+ Minus,
12
+ Redo2,
13
+ SquareCode,
14
+ Strikethrough,
15
+ Table,
16
+ TextQuote,
17
+ Underline,
18
+ Undo2
19
+ } from "lucide-react";
20
+ const HISTORY_ACTIONS = [
21
+ {
22
+ id: "undo",
23
+ icon: Undo2,
24
+ label: "undo",
25
+ keys: "mod+z",
26
+ canRun: (editor) => editor.can().undo(),
27
+ run: (editor) => editor.chain().focus().undo().run()
28
+ },
29
+ {
30
+ id: "redo",
31
+ icon: Redo2,
32
+ label: "redo",
33
+ keys: "mod+shift+z",
34
+ canRun: (editor) => editor.can().redo(),
35
+ run: (editor) => editor.chain().focus().redo().run()
36
+ }
37
+ ];
38
+ const MARK_ACTIONS = [
39
+ {
40
+ id: "bold",
41
+ icon: Bold,
42
+ label: "bold",
43
+ keys: "mod+b",
44
+ isActive: (editor) => editor.isActive("bold"),
45
+ run: (editor) => editor.chain().focus().toggleBold().run()
46
+ },
47
+ {
48
+ id: "italic",
49
+ icon: Italic,
50
+ label: "italic",
51
+ keys: "mod+i",
52
+ isActive: (editor) => editor.isActive("italic"),
53
+ run: (editor) => editor.chain().focus().toggleItalic().run()
54
+ },
55
+ {
56
+ id: "underline",
57
+ icon: Underline,
58
+ label: "underline",
59
+ keys: "mod+u",
60
+ formats: ["html"],
61
+ isActive: (editor) => editor.isActive("underline"),
62
+ run: (editor) => editor.chain().focus().toggleUnderline().run()
63
+ },
64
+ {
65
+ id: "strike",
66
+ icon: Strikethrough,
67
+ label: "strike",
68
+ keys: "mod+shift+s",
69
+ isActive: (editor) => editor.isActive("strike"),
70
+ run: (editor) => editor.chain().focus().toggleStrike().run()
71
+ },
72
+ {
73
+ id: "code",
74
+ icon: Code,
75
+ label: "code",
76
+ keys: "mod+e",
77
+ isActive: (editor) => editor.isActive("code"),
78
+ run: (editor) => editor.chain().focus().toggleCode().run()
79
+ }
80
+ ];
81
+ const BLOCK_ACTIONS = [
82
+ {
83
+ id: "heading1",
84
+ icon: Heading1,
85
+ label: "heading1",
86
+ keys: "mod+alt+1",
87
+ isActive: (editor) => editor.isActive("heading", { level: 1 }),
88
+ run: (editor) => editor.chain().focus().toggleHeading({ level: 1 }).run()
89
+ },
90
+ {
91
+ id: "heading2",
92
+ icon: Heading2,
93
+ label: "heading2",
94
+ keys: "mod+alt+2",
95
+ isActive: (editor) => editor.isActive("heading", { level: 2 }),
96
+ run: (editor) => editor.chain().focus().toggleHeading({ level: 2 }).run()
97
+ },
98
+ {
99
+ id: "heading3",
100
+ icon: Heading3,
101
+ label: "heading3",
102
+ keys: "mod+alt+3",
103
+ isActive: (editor) => editor.isActive("heading", { level: 3 }),
104
+ run: (editor) => editor.chain().focus().toggleHeading({ level: 3 }).run()
105
+ },
106
+ {
107
+ id: "bulletList",
108
+ icon: List,
109
+ label: "bulletList",
110
+ keys: "mod+shift+8",
111
+ isActive: (editor) => editor.isActive("bulletList"),
112
+ run: (editor) => editor.chain().focus().toggleBulletList().run()
113
+ },
114
+ {
115
+ id: "orderedList",
116
+ icon: ListOrdered,
117
+ label: "orderedList",
118
+ keys: "mod+shift+7",
119
+ isActive: (editor) => editor.isActive("orderedList"),
120
+ run: (editor) => editor.chain().focus().toggleOrderedList().run()
121
+ },
122
+ {
123
+ id: "taskList",
124
+ icon: ListChecks,
125
+ label: "taskList",
126
+ keys: "mod+shift+9",
127
+ isActive: (editor) => editor.isActive("taskList"),
128
+ run: (editor) => editor.chain().focus().toggleTaskList().run()
129
+ },
130
+ {
131
+ id: "blockquote",
132
+ icon: TextQuote,
133
+ label: "blockquote",
134
+ keys: "mod+shift+b",
135
+ isActive: (editor) => editor.isActive("blockquote"),
136
+ run: (editor) => editor.chain().focus().toggleBlockquote().run()
137
+ },
138
+ {
139
+ id: "codeBlock",
140
+ icon: SquareCode,
141
+ label: "codeBlock",
142
+ keys: "mod+alt+c",
143
+ isActive: (editor) => editor.isActive("codeBlock"),
144
+ run: (editor) => editor.chain().focus().toggleCodeBlock().run()
145
+ }
146
+ ];
147
+ const INSERT_ACTIONS = [
148
+ {
149
+ id: "table",
150
+ icon: Table,
151
+ label: "table",
152
+ // Không lồng bảng trong bảng: GFM không có, và một bảng trong ô bảng hiếm khi là điều người soạn muốn.
153
+ canRun: (editor) => !editor.isActive("table"),
154
+ run: (editor) => editor.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run()
155
+ },
156
+ {
157
+ id: "divider",
158
+ icon: Minus,
159
+ label: "divider",
160
+ run: (editor) => editor.chain().focus().setHorizontalRule().run()
161
+ }
162
+ ];
163
+ function actionsFor(actions, format) {
164
+ return actions.filter((action) => !action.formats || action.formats.includes(format));
165
+ }
166
+ export {
167
+ BLOCK_ACTIONS,
168
+ HISTORY_ACTIONS,
169
+ INSERT_ACTIONS,
170
+ MARK_ACTIONS,
171
+ actionsFor
172
+ };
173
+ //# sourceMappingURL=toolbar-actions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/editor/toolbar-actions.ts"],"sourcesContent":["/**\n * Hành động của thanh công cụ — MỘT bảng cho cả thanh nổi (`bubble-toolbar.tsx`) lẫn thanh cố định\n * (`fixed-toolbar.tsx`). Hai bảng riêng là hai nơi để lệch: nút In đậm ở thanh này có phím tắt, ở\n * thanh kia thì không.\n *\n * File thuần (không `\"use client\"`): dữ liệu — icon là component, `run` là hàm. Chữ hiển thị không\n * nằm ở đây; `label` là KHOÁ của namespace `markdownEditor`.\n */\n\nimport type { Editor } from \"@tiptap/core\";\nimport {\n Bold,\n Code,\n Heading1,\n Heading2,\n Heading3,\n Italic,\n List,\n ListChecks,\n ListOrdered,\n type LucideIcon,\n Minus,\n Redo2,\n SquareCode,\n Strikethrough,\n Table,\n TextQuote,\n Underline,\n Undo2\n} from \"lucide-react\";\nimport type { EditorFormat } from \"./editor-types.js\";\nimport type { MarkdownEditorLabels } from \"./markdown-editor-constants.js\";\n\n/** Khoá nhãn mang CHUỖI (loại `slashKeywords` và hàm `taskCheckbox`). */\nexport type TextLabelKey = {\n [K in keyof MarkdownEditorLabels]: MarkdownEditorLabels[K] extends string ? K : never;\n}[keyof MarkdownEditorLabels];\n\nexport interface ToolbarAction {\n id: string;\n icon: LucideIcon;\n label: TextLabelKey;\n /** Phím tắt cho tooltip — cú pháp của `KeyboardHint` (`mod+b`). Mặc định của Tiptap. */\n keys?: string;\n /**\n * Chỉ có ở những định dạng này. Bỏ trống = có ở cả hai. Gạch chân chỉ ở HTML: markdown không có cú\n * pháp cho nó (lý do ở `createSchemaExtensions`).\n */\n formats?: readonly EditorFormat[];\n /** Có thì nút là nút BẬT/TẮT (`aria-pressed`); không có thì là nút lệnh. */\n isActive?: (editor: Editor) => boolean;\n /** `false` = nút bị khoá (hoàn tác khi chưa có gì để hoàn tác, chèn bảng khi đang ở trong bảng). */\n canRun?: (editor: Editor) => boolean;\n run: (editor: Editor) => void;\n}\n\nexport const HISTORY_ACTIONS: readonly ToolbarAction[] = [\n {\n id: \"undo\",\n icon: Undo2,\n label: \"undo\",\n keys: \"mod+z\",\n canRun: (editor) => editor.can().undo(),\n run: (editor) => editor.chain().focus().undo().run()\n },\n {\n id: \"redo\",\n icon: Redo2,\n label: \"redo\",\n keys: \"mod+shift+z\",\n canRun: (editor) => editor.can().redo(),\n run: (editor) => editor.chain().focus().redo().run()\n }\n];\n\nexport const MARK_ACTIONS: readonly ToolbarAction[] = [\n {\n id: \"bold\",\n icon: Bold,\n label: \"bold\",\n keys: \"mod+b\",\n isActive: (editor) => editor.isActive(\"bold\"),\n run: (editor) => editor.chain().focus().toggleBold().run()\n },\n {\n id: \"italic\",\n icon: Italic,\n label: \"italic\",\n keys: \"mod+i\",\n isActive: (editor) => editor.isActive(\"italic\"),\n run: (editor) => editor.chain().focus().toggleItalic().run()\n },\n {\n id: \"underline\",\n icon: Underline,\n label: \"underline\",\n keys: \"mod+u\",\n formats: [\"html\"],\n isActive: (editor) => editor.isActive(\"underline\"),\n run: (editor) => editor.chain().focus().toggleUnderline().run()\n },\n {\n id: \"strike\",\n icon: Strikethrough,\n label: \"strike\",\n keys: \"mod+shift+s\",\n isActive: (editor) => editor.isActive(\"strike\"),\n run: (editor) => editor.chain().focus().toggleStrike().run()\n },\n {\n id: \"code\",\n icon: Code,\n label: \"code\",\n keys: \"mod+e\",\n isActive: (editor) => editor.isActive(\"code\"),\n run: (editor) => editor.chain().focus().toggleCode().run()\n }\n];\n\n/*\n * Đổi khối. Thanh nổi ẩn nhóm này ở màn hẹp, và ở trong bảng của trình soạn MARKDOWN (ô bảng GFM chỉ\n * chứa một đoạn văn — xem `ComitorTableCell`); ô bảng HTML chứa được khối nên nhóm vẫn hiện.\n */\nexport const BLOCK_ACTIONS: readonly ToolbarAction[] = [\n {\n id: \"heading1\",\n icon: Heading1,\n label: \"heading1\",\n keys: \"mod+alt+1\",\n isActive: (editor) => editor.isActive(\"heading\", { level: 1 }),\n run: (editor) => editor.chain().focus().toggleHeading({ level: 1 }).run()\n },\n {\n id: \"heading2\",\n icon: Heading2,\n label: \"heading2\",\n keys: \"mod+alt+2\",\n isActive: (editor) => editor.isActive(\"heading\", { level: 2 }),\n run: (editor) => editor.chain().focus().toggleHeading({ level: 2 }).run()\n },\n {\n id: \"heading3\",\n icon: Heading3,\n label: \"heading3\",\n keys: \"mod+alt+3\",\n isActive: (editor) => editor.isActive(\"heading\", { level: 3 }),\n run: (editor) => editor.chain().focus().toggleHeading({ level: 3 }).run()\n },\n {\n id: \"bulletList\",\n icon: List,\n label: \"bulletList\",\n keys: \"mod+shift+8\",\n isActive: (editor) => editor.isActive(\"bulletList\"),\n run: (editor) => editor.chain().focus().toggleBulletList().run()\n },\n {\n id: \"orderedList\",\n icon: ListOrdered,\n label: \"orderedList\",\n keys: \"mod+shift+7\",\n isActive: (editor) => editor.isActive(\"orderedList\"),\n run: (editor) => editor.chain().focus().toggleOrderedList().run()\n },\n {\n id: \"taskList\",\n icon: ListChecks,\n label: \"taskList\",\n keys: \"mod+shift+9\",\n isActive: (editor) => editor.isActive(\"taskList\"),\n run: (editor) => editor.chain().focus().toggleTaskList().run()\n },\n {\n id: \"blockquote\",\n icon: TextQuote,\n label: \"blockquote\",\n keys: \"mod+shift+b\",\n isActive: (editor) => editor.isActive(\"blockquote\"),\n run: (editor) => editor.chain().focus().toggleBlockquote().run()\n },\n {\n id: \"codeBlock\",\n icon: SquareCode,\n label: \"codeBlock\",\n keys: \"mod+alt+c\",\n isActive: (editor) => editor.isActive(\"codeBlock\"),\n run: (editor) => editor.chain().focus().toggleCodeBlock().run()\n }\n];\n\n/** Chèn — chỉ thanh cố định có (thanh nổi chỉ hiện khi đã bôi chọn chữ, không phải lúc để chèn khối). */\nexport const INSERT_ACTIONS: readonly ToolbarAction[] = [\n {\n id: \"table\",\n icon: Table,\n label: \"table\",\n // Không lồng bảng trong bảng: GFM không có, và một bảng trong ô bảng hiếm khi là điều người soạn muốn.\n canRun: (editor) => !editor.isActive(\"table\"),\n run: (editor) => editor.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run()\n },\n {\n id: \"divider\",\n icon: Minus,\n label: \"divider\",\n run: (editor) => editor.chain().focus().setHorizontalRule().run()\n }\n];\n\n/** Các hành động có ở `format`. */\nexport function actionsFor(actions: readonly ToolbarAction[], format: EditorFormat): ToolbarAction[] {\n return actions.filter((action) => !action.formats || action.formats.includes(format));\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,15 @@
1
+ import * as React from 'react';
2
+ import { LucideIcon } from 'lucide-react';
3
+
4
+ interface ToolbarButtonProps {
5
+ icon: LucideIcon;
6
+ label: string;
7
+ keys?: string;
8
+ pressed?: boolean;
9
+ disabled?: boolean;
10
+ onClick: () => void;
11
+ className?: string;
12
+ }
13
+ declare function ToolbarButton({ icon: Icon, label, keys, pressed, disabled, onClick, className }: ToolbarButtonProps): React.JSX.Element;
14
+
15
+ export { ToolbarButton, type ToolbarButtonProps };
@@ -0,0 +1,47 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { KeyboardHint } from "../components/composite/keyboard-hint.js";
4
+ import { Toggle } from "../components/ui/toggle.js";
5
+ import { Tooltip, TooltipContent, TooltipTrigger } from "../components/ui/tooltip.js";
6
+ import { cn } from "../lib/cn.js";
7
+ const BUTTON_CLASS = "h-7 min-w-7 px-1.5";
8
+ function ToolbarButton({ icon: Icon, label, keys, pressed, disabled, onClick, className }) {
9
+ const button = pressed === void 0 ? /* @__PURE__ */ jsx(
10
+ "button",
11
+ {
12
+ type: "button",
13
+ "aria-label": label,
14
+ disabled,
15
+ onMouseDown: (event) => event.preventDefault(),
16
+ onClick,
17
+ className: cn(
18
+ "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors duration-150 ease-out hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0",
19
+ BUTTON_CLASS,
20
+ className
21
+ ),
22
+ children: /* @__PURE__ */ jsx(Icon, { "aria-hidden": "true" })
23
+ }
24
+ ) : /* @__PURE__ */ jsx(
25
+ Toggle,
26
+ {
27
+ "aria-label": label,
28
+ pressed,
29
+ disabled,
30
+ onPressedChange: onClick,
31
+ onMouseDown: (event) => event.preventDefault(),
32
+ className: cn(BUTTON_CLASS, className),
33
+ children: /* @__PURE__ */ jsx(Icon, { "aria-hidden": "true" })
34
+ }
35
+ );
36
+ return /* @__PURE__ */ jsxs(Tooltip, { delayDuration: 400, children: [
37
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: disabled ? /* @__PURE__ */ jsx("span", { className: "inline-flex", children: button }) : button }),
38
+ /* @__PURE__ */ jsxs(TooltipContent, { side: "top", sideOffset: 6, className: "flex items-center gap-2", children: [
39
+ label,
40
+ keys && /* @__PURE__ */ jsx(KeyboardHint, { keys, compact: true })
41
+ ] })
42
+ ] });
43
+ }
44
+ export {
45
+ ToolbarButton
46
+ };
47
+ //# sourceMappingURL=toolbar-button.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/editor/toolbar-button.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * Nút của thanh công cụ — dùng chung cho thanh nổi và thanh cố định.\n *\n * Nhỏ hơn `Toggle size=\"sm\"` một bậc (28px): mười mấy nút phải nằm gọn trên một dòng ở màn laptop.\n * Mousedown bị chặn để focus Ở LẠI vùng soạn — mất focus là mất vùng chọn mà nút sắp áp định dạng lên.\n * Nút có `pressed` là nút BẬT/TẮT (`Toggle`, `aria-pressed`); không có là nút lệnh.\n */\n\nimport type { LucideIcon } from \"lucide-react\";\nimport { KeyboardHint } from \"../components/composite/keyboard-hint.js\";\nimport { Toggle } from \"../components/ui/toggle.js\";\nimport { Tooltip, TooltipContent, TooltipTrigger } from \"../components/ui/tooltip.js\";\nimport { cn } from \"../lib/cn.js\";\n\nconst BUTTON_CLASS = \"h-7 min-w-7 px-1.5\";\n\nexport interface ToolbarButtonProps {\n icon: LucideIcon;\n label: string;\n keys?: string;\n pressed?: boolean;\n disabled?: boolean;\n onClick: () => void;\n className?: string;\n}\n\nexport function ToolbarButton({ icon: Icon, label, keys, pressed, disabled, onClick, className }: ToolbarButtonProps) {\n const button =\n pressed === undefined ? (\n <button\n type=\"button\"\n aria-label={label}\n disabled={disabled}\n onMouseDown={(event) => event.preventDefault()}\n onClick={onClick}\n className={cn(\n \"inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors duration-150 ease-out hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0\",\n BUTTON_CLASS,\n className\n )}\n >\n <Icon aria-hidden=\"true\" />\n </button>\n ) : (\n <Toggle\n aria-label={label}\n pressed={pressed}\n disabled={disabled}\n onPressedChange={onClick}\n onMouseDown={(event) => event.preventDefault()}\n className={cn(BUTTON_CLASS, className)}\n >\n <Icon aria-hidden=\"true\" />\n </Toggle>\n );\n\n return (\n <Tooltip delayDuration={400}>\n {/*\n Nút đang khoá không phát sự kiện chuột, nên tooltip của nó neo vào một `span` bọc ngoài. Nút\n bình thường thì CHÍNH nó là trigger — để tooltip cũng hiện khi focus bằng bàn phím.\n */}\n <TooltipTrigger asChild>{disabled ? <span className=\"inline-flex\">{button}</span> : button}</TooltipTrigger>\n <TooltipContent side=\"top\" sideOffset={6} className=\"flex items-center gap-2\">\n {label}\n {keys && <KeyboardHint keys={keys} compact />}\n </TooltipContent>\n </Tooltip>\n );\n}\n"],"mappings":";AA2CQ,cAsBF,YAtBE;AAhCR,SAAS,oBAAoB;AAC7B,SAAS,cAAc;AACvB,SAAS,SAAS,gBAAgB,sBAAsB;AACxD,SAAS,UAAU;AAEnB,MAAM,eAAe;AAYd,SAAS,cAAc,EAAE,MAAM,MAAM,OAAO,MAAM,SAAS,UAAU,SAAS,UAAU,GAAuB;AACpH,QAAM,SACJ,YAAY,SACV;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,cAAY;AAAA,MACZ;AAAA,MACA,aAAa,CAAC,UAAU,MAAM,eAAe;AAAA,MAC7C;AAAA,MACA,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MAEA,8BAAC,QAAK,eAAY,QAAO;AAAA;AAAA,EAC3B,IAEA;AAAA,IAAC;AAAA;AAAA,MACC,cAAY;AAAA,MACZ;AAAA,MACA;AAAA,MACA,iBAAiB;AAAA,MACjB,aAAa,CAAC,UAAU,MAAM,eAAe;AAAA,MAC7C,WAAW,GAAG,cAAc,SAAS;AAAA,MAErC,8BAAC,QAAK,eAAY,QAAO;AAAA;AAAA,EAC3B;AAGJ,SACE,qBAAC,WAAQ,eAAe,KAKtB;AAAA,wBAAC,kBAAe,SAAO,MAAE,qBAAW,oBAAC,UAAK,WAAU,eAAe,kBAAO,IAAU,QAAO;AAAA,IAC3F,qBAAC,kBAAe,MAAK,OAAM,YAAY,GAAG,WAAU,2BACjD;AAAA;AAAA,MACA,QAAQ,oBAAC,gBAAa,MAAY,SAAO,MAAC;AAAA,OAC7C;AAAA,KACF;AAEJ;","names":[]}
@@ -0,0 +1,171 @@
1
+ import { Extension, Editor, AnyExtension } from '@tiptap/core';
2
+ import { MarkdownEditorLabels } from './editor/markdown-editor-constants.js';
3
+
4
+ /**
5
+ * Ảnh dán / kéo thả / chọn từ menu `/` → tải qua `onImageUpload` của app → chèn vào tài liệu.
6
+ *
7
+ * Gói KHÔNG biết kho ảnh ở đâu: bucket của module Comitor là riêng tư, URL hiện được là URL đã ký
8
+ * — nên đường tải là của app (route của app ký và ghi), gói chỉ trao `File` và nhận lại `src` để
9
+ * LƯU. Cùng nguyên tắc với `/uploader` (`XHRUpload` về endpoint của app).
10
+ *
11
+ * ── ẢNH ĐANG TẢI LÀ DECORATION, KHÔNG PHẢI NODE ────────────────────────────────────────────────
12
+ * Trong lúc tải, ảnh hiện NGAY bằng `blob:` của chính tệp — nhưng dưới dạng một widget decoration
13
+ * (khuôn "upload" của ProseMirror), không phải một node trong tài liệu. Node ảnh thật chỉ được chèn
14
+ * MỘT lần, khi app đã trả `src`.
15
+ *
16
+ * Bản đầu chèn một node ảnh TẠM rồi đổi `src` khi xong (giao dịch đổi không vào lịch sử). Nó qua
17
+ * mọi phép thử lúc gõ, và hỏng ở lịch sử hoàn tác: ⌘Z gỡ ảnh, rồi ⌘⇧Z làm lại ĐÚNG bước chèn node
18
+ * tạm — `src` là `blob:` đã thu hồi, cờ "đang tải" còn nguyên: một ảnh vỡ kẹt mãi, và vì cờ đó
19
+ * nên nó cũng không bao giờ vào markdown. Với decoration thì tài liệu không bao giờ chứa thứ gì tạm:
20
+ * lịch sử chỉ có một bước, "chèn ảnh thật".
21
+ */
22
+
23
+ /** Thứ `onImageUpload` trả về: `src` để LƯU, hoặc kèm `alt`/`title`. */
24
+ type MarkdownEditorImageUploadResult = string | {
25
+ src: string;
26
+ alt?: string;
27
+ title?: string;
28
+ };
29
+ interface ImageUploadHandlers {
30
+ upload?: (file: File) => Promise<MarkdownEditorImageUploadResult>;
31
+ onError: (error: unknown, file: File) => void;
32
+ /** Đổi `src` lưu → `src` hiện (`resolveImageSrc`), để tải trước ảnh thật trước khi chèn. */
33
+ resolveSrc?: (src: string) => string;
34
+ /** Nhãn trợ năng của ảnh đang tải (`role="img"` của widget). */
35
+ uploadingLabel: string;
36
+ /** Định dạng lưu — ô bảng markdown không nhận ảnh (xem `blockInsertPosition`). Mặc định `markdown`. */
37
+ format?: EditorFormat;
38
+ }
39
+ /** Ảnh đang tải của MỘT trình soạn — `MarkdownEditorHandle.isUploading()` đọc nó. */
40
+ type PendingUploads = Set<string>;
41
+ /** Tệp ảnh trong một lần dán/thả. Tệp khác (PDF, zip…) bỏ qua: trình soạn chỉ nhúng ảnh. */
42
+ declare function imageFilesFrom(data: DataTransfer | null): File[];
43
+ /**
44
+ * Hiện widget cho từng tệp và tải chúng. Xong tệp nào thì gỡ widget và chèn node ảnh thật vào ĐÚNG
45
+ * chỗ widget đang đứng (vị trí đã đi theo mọi chỉnh sửa trong lúc chờ), trong CÙNG một giao dịch —
46
+ * một bước hoàn tác. Không dời con trỏ: người dùng có thể đang gõ ở chỗ khác.
47
+ */
48
+ declare function insertImageFiles(editor: Editor, files: readonly File[], position: number | undefined, handlers: ImageUploadHandlers, pending: PendingUploads): void;
49
+ /**
50
+ * Bắt ảnh khi DÁN và khi THẢ, và giữ các widget đang tải. Không có `onImageUpload` thì plugin đứng
51
+ * ngoài (trả `false`) — dán/thả đi đường mặc định của Tiptap, tức ảnh dạng tệp không được nhận
52
+ * (không có chỗ nào để lưu nó).
53
+ *
54
+ * Kéo thả NỘI BỘ (di chuyển một ảnh đã có trong tài liệu, `moved === true`) thì không đụng tới.
55
+ */
56
+ declare function createImageUploadExtension(handlers: {
57
+ readonly current: ImageUploadHandlers;
58
+ }, pending: PendingUploads): Extension;
59
+
60
+ /**
61
+ * Kiểu dùng CHUNG cho hai trình soạn của entry — `MarkdownEditor` (lưu markdown) và `RichTextEditor`
62
+ * (lưu HTML). Chỉ kiểu, không mã chạy.
63
+ *
64
+ * Tên `MarkdownEditor…` của các kiểu nhắc tên / biến thể là tên đã publish ở `3.5.0`, trước khi có
65
+ * trình soạn thứ hai. Giữ nguyên (đổi tên là breaking) — cả hai trình soạn dùng chung chúng.
66
+ */
67
+
68
+ /** Định dạng LƯU — thứ `value`/`onChange` mang. Quyết định cả lược đồ (xem `createSchemaExtensions`). */
69
+ type EditorFormat = "markdown" | "html";
70
+ /**
71
+ * Thanh công cụ:
72
+ * · `bubble` — kiểu Linear: không chiếm chỗ, định dạng hiện ngay chỗ bôi chọn;
73
+ * · `fixed` — thanh dính đầu khung soạn, mọi thao tác luôn thấy được: kiểu CMS, cho người soạn
74
+ * không thuộc phím tắt hay cú pháp gõ tắt. Ở chế độ này bong bóng chỉ còn phần LIÊN KẾT.
75
+ */
76
+ type EditorToolbarMode = "bubble" | "fixed";
77
+ /** Một người trong danh sách `@`. */
78
+ interface MarkdownEditorMentionItem {
79
+ /** Định danh LƯU vào nội dung — với module Comitor là id người dùng của Account */
80
+ id: string;
81
+ /** Tên hiển thị lúc chọn — cũng là ảnh chụp tên ghi kèm trong nội dung */
82
+ label: string;
83
+ /** Dòng phụ mờ bên phải (email, chức danh) — cũng được dùng để lọc */
84
+ description?: string;
85
+ avatarUrl?: string | null;
86
+ }
87
+ /**
88
+ * Nguồn danh sách `@`:
89
+ * · MẢNG — gói tự lọc theo tên/dòng phụ, không dấu (cùng luật `Combobox`), tối đa 50 người;
90
+ * · HÀM — app tự tìm (gọi API danh bạ…). `signal` bị huỷ khi người dùng gõ tiếp; gói chờ 150ms
91
+ * sau phím cuối mới gọi. (Kiểu nguồn xét LÚC MOUNT — độ trễ là tuỳ chọn dựng của plugin.)
92
+ */
93
+ type MarkdownEditorMentionSource = readonly MarkdownEditorMentionItem[] | ((query: string, options: {
94
+ signal: AbortSignal;
95
+ }) => readonly MarkdownEditorMentionItem[] | Promise<readonly MarkdownEditorMentionItem[]>);
96
+ interface MarkdownEditorMentions {
97
+ items: MarkdownEditorMentionSource;
98
+ /**
99
+ * Tên SỐNG theo id, dùng lúc VẼ. Nội dung giữ tên chụp lúc nhắc; người đó đổi tên ở Account thì
100
+ * hàm này cho tên mới mà không phải sửa nội dung. `undefined` → rơi về tên chụp.
101
+ */
102
+ resolveLabel?: (id: string) => string | undefined;
103
+ }
104
+ type MarkdownEditorVariant = "outline" | "ghost";
105
+ /** Phần điều khiển chung của hai handle — mỗi trình soạn thêm cặp đọc/ghi theo định dạng của nó. */
106
+ interface EditorHandleBase {
107
+ focus: (position?: "start" | "end") => void;
108
+ /** Xoá trắng — vd sau khi gửi bình luận. Có gọi `onChange("")`. */
109
+ clear: () => void;
110
+ isEmpty: () => boolean;
111
+ /**
112
+ * Còn ảnh đang tải lên không. Ảnh chưa tải xong KHÔNG có trong giá trị — nút Gửi nên chờ (hoặc
113
+ * khoá) khi hàm này trả `true`, nếu không nội dung đi mà thiếu ảnh.
114
+ */
115
+ isUploading: () => boolean;
116
+ /** Bản Tiptap bên dưới — lối thoát cho nhu cầu gói chưa phủ. `null` trước lần render đầu ở client. */
117
+ readonly editor: Editor | null;
118
+ }
119
+ /** Prop CHUNG của hai trình soạn — mọi thứ trừ cặp giá trị (`value`/`defaultValue`/`onChange`) và `ref`. */
120
+ interface EditorBaseProps {
121
+ /**
122
+ * ⌘/Ctrl+Enter (ngoài danh sách việc — ở đó phím này tick mục). Cho ô bình luận; không truyền thì
123
+ * phím giữ nghĩa mặc định.
124
+ */
125
+ onSubmit?: () => void;
126
+ onFocus?: () => void;
127
+ onBlur?: () => void;
128
+ /** Chữ mờ khi rỗng. Mặc định theo `ComitorLocaleProvider`. */
129
+ placeholder?: string;
130
+ /** `false` = chế độ ĐỌC: không con trỏ, không menu, liên kết bấm mở được. Đổi lúc chạy được. */
131
+ editable?: boolean;
132
+ autoFocus?: boolean;
133
+ /**
134
+ * `outline` (mặc định) — khung như `Textarea`, cho biểu mẫu và ô bình luận.
135
+ * `ghost` — không khung, không đệm: tài liệu nằm thẳng trên trang, kiểu mô tả việc của Linear.
136
+ */
137
+ variant?: MarkdownEditorVariant;
138
+ /** Bật `@`. Không truyền thì gõ `@` là chữ thường — nhưng nhắc tên CÓ SẴN trong nội dung vẫn hiện đúng. */
139
+ mentions?: MarkdownEditorMentions;
140
+ /**
141
+ * Bật ảnh: dán, kéo thả, mục "Ảnh" của menu `/` (và nút ảnh của thanh cố định). Nhận `File`, trả
142
+ * `src` để LƯU — khoá trong kho, hoặc một route ổn định của app; KHÔNG trả URL đã ký có hạn (xem
143
+ * `resolveImageSrc`).
144
+ */
145
+ onImageUpload?: (file: File) => Promise<MarkdownEditorImageUploadResult>;
146
+ /** Lỗi tải ảnh. Mặc định: toast `imageUploadFailed` (cần `Toaster` ở gốc app). */
147
+ onImageUploadError?: (error: unknown, file: File) => void;
148
+ /** Đổi `src` ĐÃ LƯU thành địa chỉ HIỂN THỊ (vd URL đã ký) lúc vẽ. Giá trị lưu vẫn giữ `src` gốc. */
149
+ resolveImageSrc?: (src: string) => string;
150
+ /**
151
+ * Extension Tiptap thêm vào (vd node `KEY-n` của module). Chỉ đọc LÚC MOUNT — đổi danh sách là
152
+ * phải dựng lại trình soạn (mất lịch sử hoàn tác), nên muốn đổi thì đổi `key` của component.
153
+ * Phải cùng phiên bản `@tiptap/*` với gói.
154
+ */
155
+ extensions?: readonly AnyExtension[];
156
+ labels?: Partial<MarkdownEditorLabels>;
157
+ /** Có `name` thì kèm một `<input type="hidden">` mang giá trị — để trình soạn nằm trong `<form>` thật. */
158
+ name?: string;
159
+ /** Đặt lên vùng soạn. Có `<label for={id}>` trong trang thì gói tự nối nó làm tên trợ năng. */
160
+ id?: string;
161
+ "aria-label"?: string;
162
+ "aria-labelledby"?: string;
163
+ "aria-describedby"?: string;
164
+ "aria-invalid"?: boolean | "true" | "false";
165
+ "aria-required"?: boolean | "true" | "false";
166
+ className?: string;
167
+ /** Lớp thêm cho vùng soạn (phần tử ProseMirror) — vd `min-h-40`, `max-h-96 overflow-y-auto`. */
168
+ contentClassName?: string;
169
+ }
170
+
171
+ export { type EditorHandleBase as E, type ImageUploadHandlers as I, type MarkdownEditorImageUploadResult as M, type PendingUploads as P, type EditorToolbarMode as a, type MarkdownEditorMentionItem as b, type MarkdownEditorMentionSource as c, type MarkdownEditorMentions as d, type MarkdownEditorVariant as e, type EditorFormat as f, type EditorBaseProps as g, createImageUploadExtension as h, imageFilesFrom as i, insertImageFiles as j };
@@ -4,9 +4,12 @@ const markdownEditorEn = {
4
4
  toolbar: "Formatting",
5
5
  bold: "Bold",
6
6
  italic: "Italic",
7
+ underline: "Underline",
7
8
  strike: "Strikethrough",
8
9
  code: "Inline code",
9
10
  link: "Link",
11
+ undo: "Undo",
12
+ redo: "Redo",
10
13
  text: "Text",
11
14
  heading1: "Heading 1",
12
15
  heading2: "Heading 2",
@@ -65,9 +68,12 @@ const markdownEditorVi = {
65
68
  toolbar: "\u0110\u1ECBnh d\u1EA1ng",
66
69
  bold: "In \u0111\u1EADm",
67
70
  italic: "In nghi\xEAng",
71
+ underline: "G\u1EA1ch ch\xE2n",
68
72
  strike: "G\u1EA1ch ngang",
69
73
  code: "M\xE3 n\u1ED9i d\xF2ng",
70
74
  link: "Li\xEAn k\u1EBFt",
75
+ undo: "Ho\xE0n t\xE1c",
76
+ redo: "L\xE0m l\u1EA1i",
71
77
  text: "V\u0103n b\u1EA3n",
72
78
  heading1: "Ti\xEAu \u0111\u1EC1 1",
73
79
  heading2: "Ti\xEAu \u0111\u1EC1 2",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/i18n/namespaces/markdown-editor.ts"],"sourcesContent":["import type { MarkdownEditorLabels } from \"../../editor/markdown-editor-constants.js\";\n\n/*\n * Chữ của `MarkdownEditor` (`@comitor/ui/editor`). Hình dạng + chú thích từng khoá:\n * `MarkdownEditorLabels`.\n *\n * ⚠ Import ở dòng đầu là `import type` — bị xoá lúc biên dịch. Bảng chữ là tầng của entry CHÍNH,\n * còn trình soạn kéo Tiptap (peer tuỳ chọn); một import CHẠY ĐƯỢC từ đây sang `src/editor/` là kéo\n * Tiptap vào mọi app chỉ dùng `Button`.\n *\n * Tên khối giữ đúng chữ quen thuộc của các trình soạn markdown; bí danh máy (`h1`, `ul`, `todo`…)\n * không nằm ở đây mà ở `slash-commands.ts`, vì chúng không đổi theo ngôn ngữ.\n */\nexport const markdownEditorEn: MarkdownEditorLabels = {\n placeholder: \"Write something, or type “/” for blocks…\",\n editorLabel: \"Editor\",\n\n toolbar: \"Formatting\",\n bold: \"Bold\",\n italic: \"Italic\",\n strike: \"Strikethrough\",\n code: \"Inline code\",\n link: \"Link\",\n\n text: \"Text\",\n heading1: \"Heading 1\",\n heading2: \"Heading 2\",\n heading3: \"Heading 3\",\n bulletList: \"Bulleted list\",\n orderedList: \"Numbered list\",\n taskList: \"Checklist\",\n blockquote: \"Quote\",\n codeBlock: \"Code block\",\n table: \"Table\",\n divider: \"Divider\",\n image: \"Image\",\n tableAddRow: \"Add row below\",\n tableAddColumn: \"Add column right\",\n tableDeleteRow: \"Delete row\",\n tableDeleteColumn: \"Delete column\",\n tableDelete: \"Delete table\",\n\n slashMenu: \"Insert block\",\n slashEmpty: \"No matching blocks\",\n slashKeywords: {\n text: \"paragraph plain\",\n heading1: \"title big\",\n heading2: \"subtitle medium\",\n heading3: \"small section\",\n bulletList: \"unordered bullets\",\n orderedList: \"ordered numbers\",\n taskList: \"checkbox todo tasks\",\n blockquote: \"citation callout\",\n codeBlock: \"snippet programming\",\n table: \"columns rows cells\",\n divider: \"separator rule line\",\n image: \"picture photo upload\",\n tableAddRow: \"insert row\",\n tableAddColumn: \"insert column\",\n tableDeleteRow: \"remove row\",\n tableDeleteColumn: \"remove column\",\n tableDelete: \"remove table\"\n },\n\n linkInput: \"Link address\",\n linkPlaceholder: \"Paste or type a link…\",\n linkApply: \"Apply link\",\n linkEdit: \"Edit link\",\n linkOpen: \"Open link\",\n linkRemove: \"Remove link\",\n linkInvalid: \"Enter a web, email or phone link\",\n\n mentionMenu: \"People\",\n mentionEmpty: \"No people found\",\n mentionLoading: \"Searching…\",\n\n imageUploading: \"Uploading image…\",\n imageUploadFailed: \"Couldn’t upload the image\",\n\n taskCheckbox: (text) => (text ? `Task: ${text}` : \"Empty task\")\n};\n\nexport const markdownEditorVi: MarkdownEditorLabels = {\n placeholder: \"Viết nội dung, hoặc gõ “/” để chèn khối…\",\n editorLabel: \"Trình soạn thảo\",\n\n toolbar: \"Định dạng\",\n bold: \"In đậm\",\n italic: \"In nghiêng\",\n strike: \"Gạch ngang\",\n code: \"Mã nội dòng\",\n link: \"Liên kết\",\n\n text: \"Văn bản\",\n heading1: \"Tiêu đề 1\",\n heading2: \"Tiêu đề 2\",\n heading3: \"Tiêu đề 3\",\n bulletList: \"Danh sách dấu chấm\",\n orderedList: \"Danh sách đánh số\",\n taskList: \"Danh sách việc\",\n blockquote: \"Trích dẫn\",\n codeBlock: \"Khối mã\",\n table: \"Bảng\",\n divider: \"Đường kẻ ngang\",\n image: \"Ảnh\",\n tableAddRow: \"Thêm hàng bên dưới\",\n tableAddColumn: \"Thêm cột bên phải\",\n tableDeleteRow: \"Xoá hàng\",\n tableDeleteColumn: \"Xoá cột\",\n tableDelete: \"Xoá bảng\",\n\n slashMenu: \"Chèn khối\",\n slashEmpty: \"Không có khối phù hợp\",\n slashKeywords: {\n text: \"đoạn văn chữ thường\",\n heading1: \"đề mục lớn\",\n heading2: \"đề mục vừa\",\n heading3: \"đề mục nhỏ\",\n bulletList: \"gạch đầu dòng liệt kê\",\n orderedList: \"liệt kê số thứ tự\",\n taskList: \"việc cần làm ô tick\",\n blockquote: \"dẫn lời\",\n codeBlock: \"mã nguồn đoạn mã\",\n table: \"lưới cột hàng\",\n divider: \"phân cách vạch kẻ\",\n image: \"hình ảnh tải lên\",\n tableAddRow: \"chèn hàng dòng\",\n tableAddColumn: \"chèn cột\",\n tableDeleteRow: \"bỏ hàng dòng\",\n tableDeleteColumn: \"bỏ cột\",\n tableDelete: \"bỏ bảng\"\n },\n\n linkInput: \"Địa chỉ liên kết\",\n linkPlaceholder: \"Dán hoặc nhập liên kết…\",\n linkApply: \"Áp dụng liên kết\",\n linkEdit: \"Sửa liên kết\",\n linkOpen: \"Mở liên kết\",\n linkRemove: \"Gỡ liên kết\",\n linkInvalid: \"Nhập liên kết web, email hoặc số điện thoại\",\n\n mentionMenu: \"Thành viên\",\n mentionEmpty: \"Không tìm thấy thành viên\",\n mentionLoading: \"Đang tìm…\",\n\n imageUploading: \"Đang tải ảnh lên…\",\n imageUploadFailed: \"Không tải được ảnh lên\",\n\n taskCheckbox: (text) => (text ? `Việc: ${text}` : \"Việc chưa có nội dung\")\n};\n"],"mappings":"AAaO,MAAM,mBAAyC;AAAA,EACpD,aAAa;AAAA,EACb,aAAa;AAAA,EAEb,SAAS;AAAA,EACT,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,MAAM;AAAA,EAEN,MAAM;AAAA,EACN,UAAU;AAAA,EACV,UAAU;AAAA,EACV,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,OAAO;AAAA,EACP,SAAS;AAAA,EACT,OAAO;AAAA,EACP,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,gBAAgB;AAAA,EAChB,mBAAmB;AAAA,EACnB,aAAa;AAAA,EAEb,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,eAAe;AAAA,IACb,MAAM;AAAA,IACN,UAAU;AAAA,IACV,UAAU;AAAA,IACV,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,WAAW;AAAA,IACX,OAAO;AAAA,IACP,SAAS;AAAA,IACT,OAAO;AAAA,IACP,aAAa;AAAA,IACb,gBAAgB;AAAA,IAChB,gBAAgB;AAAA,IAChB,mBAAmB;AAAA,IACnB,aAAa;AAAA,EACf;AAAA,EAEA,WAAW;AAAA,EACX,iBAAiB;AAAA,EACjB,WAAW;AAAA,EACX,UAAU;AAAA,EACV,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,aAAa;AAAA,EAEb,aAAa;AAAA,EACb,cAAc;AAAA,EACd,gBAAgB;AAAA,EAEhB,gBAAgB;AAAA,EAChB,mBAAmB;AAAA,EAEnB,cAAc,CAAC,SAAU,OAAO,SAAS,IAAI,KAAK;AACpD;AAEO,MAAM,mBAAyC;AAAA,EACpD,aAAa;AAAA,EACb,aAAa;AAAA,EAEb,SAAS;AAAA,EACT,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,MAAM;AAAA,EAEN,MAAM;AAAA,EACN,UAAU;AAAA,EACV,UAAU;AAAA,EACV,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,OAAO;AAAA,EACP,SAAS;AAAA,EACT,OAAO;AAAA,EACP,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,gBAAgB;AAAA,EAChB,mBAAmB;AAAA,EACnB,aAAa;AAAA,EAEb,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,eAAe;AAAA,IACb,MAAM;AAAA,IACN,UAAU;AAAA,IACV,UAAU;AAAA,IACV,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,WAAW;AAAA,IACX,OAAO;AAAA,IACP,SAAS;AAAA,IACT,OAAO;AAAA,IACP,aAAa;AAAA,IACb,gBAAgB;AAAA,IAChB,gBAAgB;AAAA,IAChB,mBAAmB;AAAA,IACnB,aAAa;AAAA,EACf;AAAA,EAEA,WAAW;AAAA,EACX,iBAAiB;AAAA,EACjB,WAAW;AAAA,EACX,UAAU;AAAA,EACV,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,aAAa;AAAA,EAEb,aAAa;AAAA,EACb,cAAc;AAAA,EACd,gBAAgB;AAAA,EAEhB,gBAAgB;AAAA,EAChB,mBAAmB;AAAA,EAEnB,cAAc,CAAC,SAAU,OAAO,cAAS,IAAI,KAAK;AACpD;","names":[]}
1
+ {"version":3,"sources":["../../../src/i18n/namespaces/markdown-editor.ts"],"sourcesContent":["import type { MarkdownEditorLabels } from \"../../editor/markdown-editor-constants.js\";\n\n/*\n * Chữ của `MarkdownEditor` và `RichTextEditor` (`@comitor/ui/editor`) — một namespace cho cả hai.\n * Hình dạng + chú thích từng khoá: `MarkdownEditorLabels`.\n *\n * ⚠ Import ở dòng đầu là `import type` — bị xoá lúc biên dịch. Bảng chữ là tầng của entry CHÍNH,\n * còn trình soạn kéo Tiptap (peer tuỳ chọn); một import CHẠY ĐƯỢC từ đây sang `src/editor/` là kéo\n * Tiptap vào mọi app chỉ dùng `Button`.\n *\n * Tên khối giữ đúng chữ quen thuộc của các trình soạn markdown; bí danh máy (`h1`, `ul`, `todo`…)\n * không nằm ở đây mà ở `slash-commands.ts`, vì chúng không đổi theo ngôn ngữ.\n */\nexport const markdownEditorEn: MarkdownEditorLabels = {\n placeholder: \"Write something, or type “/” for blocks…\",\n editorLabel: \"Editor\",\n\n toolbar: \"Formatting\",\n bold: \"Bold\",\n italic: \"Italic\",\n underline: \"Underline\",\n strike: \"Strikethrough\",\n code: \"Inline code\",\n link: \"Link\",\n undo: \"Undo\",\n redo: \"Redo\",\n\n text: \"Text\",\n heading1: \"Heading 1\",\n heading2: \"Heading 2\",\n heading3: \"Heading 3\",\n bulletList: \"Bulleted list\",\n orderedList: \"Numbered list\",\n taskList: \"Checklist\",\n blockquote: \"Quote\",\n codeBlock: \"Code block\",\n table: \"Table\",\n divider: \"Divider\",\n image: \"Image\",\n tableAddRow: \"Add row below\",\n tableAddColumn: \"Add column right\",\n tableDeleteRow: \"Delete row\",\n tableDeleteColumn: \"Delete column\",\n tableDelete: \"Delete table\",\n\n slashMenu: \"Insert block\",\n slashEmpty: \"No matching blocks\",\n slashKeywords: {\n text: \"paragraph plain\",\n heading1: \"title big\",\n heading2: \"subtitle medium\",\n heading3: \"small section\",\n bulletList: \"unordered bullets\",\n orderedList: \"ordered numbers\",\n taskList: \"checkbox todo tasks\",\n blockquote: \"citation callout\",\n codeBlock: \"snippet programming\",\n table: \"columns rows cells\",\n divider: \"separator rule line\",\n image: \"picture photo upload\",\n tableAddRow: \"insert row\",\n tableAddColumn: \"insert column\",\n tableDeleteRow: \"remove row\",\n tableDeleteColumn: \"remove column\",\n tableDelete: \"remove table\"\n },\n\n linkInput: \"Link address\",\n linkPlaceholder: \"Paste or type a link…\",\n linkApply: \"Apply link\",\n linkEdit: \"Edit link\",\n linkOpen: \"Open link\",\n linkRemove: \"Remove link\",\n linkInvalid: \"Enter a web, email or phone link\",\n\n mentionMenu: \"People\",\n mentionEmpty: \"No people found\",\n mentionLoading: \"Searching…\",\n\n imageUploading: \"Uploading image…\",\n imageUploadFailed: \"Couldn’t upload the image\",\n\n taskCheckbox: (text) => (text ? `Task: ${text}` : \"Empty task\")\n};\n\nexport const markdownEditorVi: MarkdownEditorLabels = {\n placeholder: \"Viết nội dung, hoặc gõ “/” để chèn khối…\",\n editorLabel: \"Trình soạn thảo\",\n\n toolbar: \"Định dạng\",\n bold: \"In đậm\",\n italic: \"In nghiêng\",\n underline: \"Gạch chân\",\n strike: \"Gạch ngang\",\n code: \"Mã nội dòng\",\n link: \"Liên kết\",\n undo: \"Hoàn tác\",\n redo: \"Làm lại\",\n\n text: \"Văn bản\",\n heading1: \"Tiêu đề 1\",\n heading2: \"Tiêu đề 2\",\n heading3: \"Tiêu đề 3\",\n bulletList: \"Danh sách dấu chấm\",\n orderedList: \"Danh sách đánh số\",\n taskList: \"Danh sách việc\",\n blockquote: \"Trích dẫn\",\n codeBlock: \"Khối mã\",\n table: \"Bảng\",\n divider: \"Đường kẻ ngang\",\n image: \"Ảnh\",\n tableAddRow: \"Thêm hàng bên dưới\",\n tableAddColumn: \"Thêm cột bên phải\",\n tableDeleteRow: \"Xoá hàng\",\n tableDeleteColumn: \"Xoá cột\",\n tableDelete: \"Xoá bảng\",\n\n slashMenu: \"Chèn khối\",\n slashEmpty: \"Không có khối phù hợp\",\n slashKeywords: {\n text: \"đoạn văn chữ thường\",\n heading1: \"đề mục lớn\",\n heading2: \"đề mục vừa\",\n heading3: \"đề mục nhỏ\",\n bulletList: \"gạch đầu dòng liệt kê\",\n orderedList: \"liệt kê số thứ tự\",\n taskList: \"việc cần làm ô tick\",\n blockquote: \"dẫn lời\",\n codeBlock: \"mã nguồn đoạn mã\",\n table: \"lưới cột hàng\",\n divider: \"phân cách vạch kẻ\",\n image: \"hình ảnh tải lên\",\n tableAddRow: \"chèn hàng dòng\",\n tableAddColumn: \"chèn cột\",\n tableDeleteRow: \"bỏ hàng dòng\",\n tableDeleteColumn: \"bỏ cột\",\n tableDelete: \"bỏ bảng\"\n },\n\n linkInput: \"Địa chỉ liên kết\",\n linkPlaceholder: \"Dán hoặc nhập liên kết…\",\n linkApply: \"Áp dụng liên kết\",\n linkEdit: \"Sửa liên kết\",\n linkOpen: \"Mở liên kết\",\n linkRemove: \"Gỡ liên kết\",\n linkInvalid: \"Nhập liên kết web, email hoặc số điện thoại\",\n\n mentionMenu: \"Thành viên\",\n mentionEmpty: \"Không tìm thấy thành viên\",\n mentionLoading: \"Đang tìm…\",\n\n imageUploading: \"Đang tải ảnh lên…\",\n imageUploadFailed: \"Không tải được ảnh lên\",\n\n taskCheckbox: (text) => (text ? `Việc: ${text}` : \"Việc chưa có nội dung\")\n};\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@comitor/ui",
3
- "version": "3.5.0",
3
+ "version": "3.6.1",
4
4
  "description": "Comitor design system — Shadcn/UI tùy biến theo brand Comitor + component dùng chung cho hệ sinh thái SaaS (Tasks, Chat, CRM, HR…)",
5
5
  "license": "MIT",
6
6
  "type": "module",