@olddevs/ui 4.5.1 → 4.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 (224) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/dist/editor/RichTextEditor.d.ts +2 -2
  3. package/dist/editor/RichTextEditor.d.ts.map +1 -1
  4. package/dist/editor/RichTextEditor.js +3 -0
  5. package/dist/editor/RichTextEditor.js.map +1 -1
  6. package/dist/editor/core/blockTypes.d.ts +2 -8
  7. package/dist/editor/core/blockTypes.d.ts.map +1 -1
  8. package/dist/editor/core/blockTypes.js +11 -54
  9. package/dist/editor/core/blockTypes.js.map +1 -1
  10. package/dist/editor/core/containers.d.ts.map +1 -1
  11. package/dist/editor/core/containers.js +1 -0
  12. package/dist/editor/core/containers.js.map +1 -1
  13. package/dist/editor/core/customMarkdown.js +1 -1
  14. package/dist/editor/core/customMarkdown.js.map +1 -1
  15. package/dist/editor/core/customTool.d.ts +6 -6
  16. package/dist/editor/core/customTool.d.ts.map +1 -1
  17. package/dist/editor/core/customToolEngine.d.ts +2 -2
  18. package/dist/editor/core/extensions.d.ts +7 -2
  19. package/dist/editor/core/extensions.d.ts.map +1 -1
  20. package/dist/editor/core/extensions.js +96 -64
  21. package/dist/editor/core/extensions.js.map +1 -1
  22. package/dist/editor/core/fieldNotice.d.ts +1 -1
  23. package/dist/editor/core/insertItems.d.ts +5 -28
  24. package/dist/editor/core/insertItems.d.ts.map +1 -1
  25. package/dist/editor/core/insertItems.js +3 -94
  26. package/dist/editor/core/insertItems.js.map +1 -1
  27. package/dist/editor/core/insertMenuTool.d.ts +13 -0
  28. package/dist/editor/core/insertMenuTool.d.ts.map +1 -0
  29. package/dist/editor/core/insertMenuTool.js +20 -0
  30. package/dist/editor/core/insertMenuTool.js.map +1 -0
  31. package/dist/editor/core/schema.d.ts +20 -5
  32. package/dist/editor/core/schema.d.ts.map +1 -1
  33. package/dist/editor/core/schema.js +18 -3
  34. package/dist/editor/core/schema.js.map +1 -1
  35. package/dist/editor/core/shortcuts.d.ts +56 -5
  36. package/dist/editor/core/shortcuts.d.ts.map +1 -1
  37. package/dist/editor/core/shortcuts.js +91 -156
  38. package/dist/editor/core/shortcuts.js.map +1 -1
  39. package/dist/editor/core/skeleton.d.ts +1 -1
  40. package/dist/editor/core/skeleton.d.ts.map +1 -1
  41. package/dist/editor/core/skeleton.js +1 -0
  42. package/dist/editor/core/skeleton.js.map +1 -1
  43. package/dist/editor/core/toolButtons.d.ts +3 -3
  44. package/dist/editor/core/toolDefinition.d.ts +3 -3
  45. package/dist/editor/core/toolDescriptor.d.ts +49 -30
  46. package/dist/editor/core/toolDescriptor.d.ts.map +1 -1
  47. package/dist/editor/core/toolDescriptor.js +2 -5
  48. package/dist/editor/core/toolDescriptor.js.map +1 -1
  49. package/dist/editor/core/toolLoader.d.ts +1 -1
  50. package/dist/editor/core/toolLoader.d.ts.map +1 -1
  51. package/dist/editor/core/toolOptions.d.ts +19 -2
  52. package/dist/editor/core/toolOptions.d.ts.map +1 -1
  53. package/dist/editor/core/toolOptions.js.map +1 -1
  54. package/dist/editor/core/tools.d.ts +10 -2
  55. package/dist/editor/core/tools.d.ts.map +1 -1
  56. package/dist/editor/core/tools.js +1 -0
  57. package/dist/editor/core/tools.js.map +1 -1
  58. package/dist/editor/features/builtins.d.ts +6 -8
  59. package/dist/editor/features/builtins.d.ts.map +1 -1
  60. package/dist/editor/features/builtins.js +48 -21
  61. package/dist/editor/features/builtins.js.map +1 -1
  62. package/dist/editor/features/code/codeBlockTool.d.ts +5 -6
  63. package/dist/editor/features/code/codeBlockTool.d.ts.map +1 -1
  64. package/dist/editor/features/code/codeBlockTool.js +10 -1
  65. package/dist/editor/features/code/codeBlockTool.js.map +1 -1
  66. package/dist/editor/features/color/colorTools.d.ts +14 -0
  67. package/dist/editor/features/color/colorTools.d.ts.map +1 -0
  68. package/dist/editor/features/color/colorTools.js +39 -0
  69. package/dist/editor/features/color/colorTools.js.map +1 -0
  70. package/dist/editor/features/details/details.d.ts +68 -0
  71. package/dist/editor/features/details/details.d.ts.map +1 -0
  72. package/dist/editor/features/details/details.js +237 -0
  73. package/dist/editor/features/details/details.js.map +1 -0
  74. package/dist/editor/features/details/detailsMarkdown.d.ts +17 -0
  75. package/dist/editor/features/details/detailsMarkdown.d.ts.map +1 -0
  76. package/dist/editor/features/details/detailsMarkdown.js +84 -0
  77. package/dist/editor/features/details/detailsMarkdown.js.map +1 -0
  78. package/dist/editor/features/details/detailsTool.d.ts +16 -0
  79. package/dist/editor/features/details/detailsTool.d.ts.map +1 -0
  80. package/dist/editor/features/details/detailsTool.js +39 -0
  81. package/dist/editor/features/details/detailsTool.js.map +1 -0
  82. package/dist/editor/features/emoji/emojiTool.d.ts +1 -1
  83. package/dist/editor/features/heading/headingTool.d.ts +18 -0
  84. package/dist/editor/features/heading/headingTool.d.ts.map +1 -0
  85. package/dist/editor/features/heading/headingTool.js +59 -0
  86. package/dist/editor/features/heading/headingTool.js.map +1 -0
  87. package/dist/editor/features/history/historyTool.d.ts +15 -0
  88. package/dist/editor/features/history/historyTool.d.ts.map +1 -0
  89. package/dist/editor/features/history/historyTool.js +28 -0
  90. package/dist/editor/features/history/historyTool.js.map +1 -0
  91. package/dist/editor/features/image/imageTool.d.ts +19 -0
  92. package/dist/editor/features/image/imageTool.d.ts.map +1 -0
  93. package/dist/editor/features/image/imageTool.js +28 -0
  94. package/dist/editor/features/image/imageTool.js.map +1 -0
  95. package/dist/editor/features/layout/layoutTool.d.ts +11 -0
  96. package/dist/editor/features/layout/layoutTool.d.ts.map +1 -0
  97. package/dist/editor/features/layout/layoutTool.js +20 -0
  98. package/dist/editor/features/layout/layoutTool.js.map +1 -0
  99. package/dist/editor/features/lists/listTools.d.ts +15 -0
  100. package/dist/editor/features/lists/listTools.d.ts.map +1 -0
  101. package/dist/editor/features/lists/listTools.js +78 -0
  102. package/dist/editor/features/lists/listTools.js.map +1 -0
  103. package/dist/editor/features/marks/markTools.d.ts +16 -0
  104. package/dist/editor/features/marks/markTools.d.ts.map +1 -0
  105. package/dist/editor/features/marks/markTools.js +43 -0
  106. package/dist/editor/features/marks/markTools.js.map +1 -0
  107. package/dist/editor/features/search/searchTool.d.ts +1 -1
  108. package/dist/editor/features/table/tableTool.d.ts +3 -3
  109. package/dist/editor/features/wrapper/wrapperTool.d.ts +11 -0
  110. package/dist/editor/features/wrapper/wrapperTool.d.ts.map +1 -0
  111. package/dist/editor/features/wrapper/wrapperTool.js +32 -0
  112. package/dist/editor/features/wrapper/wrapperTool.js.map +1 -0
  113. package/dist/editor/index.d.ts +1 -1
  114. package/dist/editor/index.d.ts.map +1 -1
  115. package/dist/editor/index.js.map +1 -1
  116. package/dist/editor/schema-entry.d.ts +1 -1
  117. package/dist/editor/styles/prose.d.ts.map +1 -1
  118. package/dist/editor/styles/prose.js +26 -0
  119. package/dist/editor/styles/prose.js.map +1 -1
  120. package/dist/editor/ui/EditorToolbar.d.ts.map +1 -1
  121. package/dist/editor/ui/EditorToolbar.js +5 -38
  122. package/dist/editor/ui/EditorToolbar.js.map +1 -1
  123. package/dist/editor/ui/SelectionBubble.d.ts.map +1 -1
  124. package/dist/editor/ui/SelectionBubble.js +1 -1
  125. package/dist/editor/ui/SelectionBubble.js.map +1 -1
  126. package/dist/editor/ui/ToolButtonView.d.ts.map +1 -1
  127. package/dist/editor/ui/ToolButtonView.js +1 -1
  128. package/dist/editor/ui/ToolButtonView.js.map +1 -1
  129. package/dist/i18n/defaults.d.ts.map +1 -1
  130. package/dist/i18n/defaults.js +3 -0
  131. package/dist/i18n/defaults.js.map +1 -1
  132. package/dist/i18n/messages.d.ts +16 -1
  133. package/dist/i18n/messages.d.ts.map +1 -1
  134. package/dist/icons.d.ts +8 -1
  135. package/dist/icons.d.ts.map +1 -1
  136. package/dist/icons.js +1 -1
  137. package/dist/icons.js.map +1 -1
  138. package/dist/locales/ru.d.ts.map +1 -1
  139. package/dist/locales/ru.js +3 -0
  140. package/dist/locales/ru.js.map +1 -1
  141. package/docs/api/editor-extension.en.md +68 -68
  142. package/docs/api/editor-extension.md +55 -55
  143. package/docs/api/messages.en.md +4 -1
  144. package/docs/api/messages.md +3 -0
  145. package/docs/api/migration-notes.en.md +11 -11
  146. package/docs/api/migration-notes.md +2 -2
  147. package/docs/api/utilities.en.md +6 -6
  148. package/docs/components/actions/split-button.en.md +9 -9
  149. package/docs/components/content/data-display.en.md +1 -1
  150. package/docs/components/content/layout-primitives.en.md +3 -3
  151. package/docs/components/content/rich-text-toc.en.md +1 -1
  152. package/docs/components/content/rich-text-view.en.md +23 -17
  153. package/docs/components/content/rich-text-view.md +17 -11
  154. package/docs/components/content/virtual-grid.en.md +2 -2
  155. package/docs/components/date-time/date-picker.en.md +1 -1
  156. package/docs/components/inputs/rich-text-editor.en.md +283 -230
  157. package/docs/components/inputs/rich-text-editor.md +139 -86
  158. package/docs/components/navigation/mega-menu.en.md +10 -10
  159. package/docs/components/overlays/menu.en.md +2 -2
  160. package/docs/components/overlays/shortcuts-help.en.md +1 -1
  161. package/docs/components/table/data-table.en.md +3 -3
  162. package/docs/components/table/filter-bar.en.md +1 -1
  163. package/docs/components/table/toolbar.en.md +1 -1
  164. package/docs/conventions.en.md +2 -2
  165. package/docs/conventions.md +1 -1
  166. package/docs/getting-started.en.md +9 -9
  167. package/docs/getting-started.md +8 -8
  168. package/docs/glossary.en.md +193 -133
  169. package/docs/glossary.md +191 -132
  170. package/docs/icons.en.md +12 -9
  171. package/docs/icons.md +3 -0
  172. package/docs/palette.en.md +3 -3
  173. package/docs/theming.en.md +6 -6
  174. package/package.json +2 -1
  175. package/src/editor/RichTextEditor.tsx +9 -6
  176. package/src/editor/core/atomText.ts +2 -2
  177. package/src/editor/core/blockTypes.ts +19 -83
  178. package/src/editor/core/containers.ts +4 -2
  179. package/src/editor/core/customMarkdown.ts +2 -2
  180. package/src/editor/core/customTool.ts +6 -6
  181. package/src/editor/core/customToolEngine.ts +9 -9
  182. package/src/editor/core/extensions.ts +165 -110
  183. package/src/editor/core/fieldNotice.ts +1 -1
  184. package/src/editor/core/handle.ts +2 -2
  185. package/src/editor/core/insertItems.ts +12 -158
  186. package/src/editor/core/insertMenuTool.ts +32 -0
  187. package/src/editor/core/jsonDocument.ts +1 -1
  188. package/src/editor/core/schema.ts +42 -7
  189. package/src/editor/core/shortcuts.ts +116 -177
  190. package/src/editor/core/skeleton.ts +4 -3
  191. package/src/editor/core/toolButtons.ts +3 -3
  192. package/src/editor/core/toolDefinition.ts +4 -4
  193. package/src/editor/core/toolDescriptor.ts +65 -52
  194. package/src/editor/core/toolLoader.ts +3 -3
  195. package/src/editor/core/toolOptions.ts +21 -2
  196. package/src/editor/core/tools.ts +10 -1
  197. package/src/editor/core/viewIsland.ts +1 -1
  198. package/src/editor/features/builtins.ts +76 -41
  199. package/src/editor/features/code/codeBlockTool.ts +16 -8
  200. package/src/editor/features/color/colorTools.ts +68 -0
  201. package/src/editor/features/details/details.ts +389 -0
  202. package/src/editor/features/details/detailsMarkdown.ts +158 -0
  203. package/src/editor/features/details/detailsTool.ts +69 -0
  204. package/src/editor/features/emoji/emojiTool.tsx +1 -1
  205. package/src/editor/features/heading/headingTool.ts +96 -0
  206. package/src/editor/features/history/historyTool.ts +42 -0
  207. package/src/editor/features/image/imageTool.ts +49 -0
  208. package/src/editor/features/layout/layoutTool.ts +40 -0
  209. package/src/editor/features/lists/listTools.ts +112 -0
  210. package/src/editor/features/marks/markTools.ts +78 -0
  211. package/src/editor/features/search/searchTool.ts +1 -1
  212. package/src/editor/features/table/tableTool.ts +3 -3
  213. package/src/editor/features/toc/tocTool.ts +1 -1
  214. package/src/editor/features/wrapper/wrapperTool.ts +52 -0
  215. package/src/editor/index.ts +1 -0
  216. package/src/editor/schema-entry.ts +1 -1
  217. package/src/editor/styles/prose.ts +49 -0
  218. package/src/editor/ui/EditorToolbar.tsx +7 -50
  219. package/src/editor/ui/SelectionBubble.tsx +2 -5
  220. package/src/editor/ui/ToolButtonView.tsx +2 -1
  221. package/src/i18n/defaults.ts +3 -0
  222. package/src/i18n/messages.ts +14 -1
  223. package/src/icons.ts +7 -0
  224. package/src/locales/ru.ts +3 -0
@@ -0,0 +1,96 @@
1
+ "use client";
2
+
3
+ import {
4
+ Heading1Icon,
5
+ Heading2Icon,
6
+ Heading3Icon,
7
+ Heading4Icon,
8
+ HeadingIcon,
9
+ } from "../../../icons.js";
10
+ import { turnIntoTypes } from "../../core/blockTypes.js";
11
+ import type { InsertItem } from "../../core/insertItems.js";
12
+ import { SHORTCUT_KEYS, type ShortcutCommand } from "../../core/shortcuts.js";
13
+ import type { ToolDescriptor } from "../../core/toolDescriptor.js";
14
+
15
+ const LEVELS = [1, 2, 3, 4] as const;
16
+
17
+ /** Иконки «¶»: в отличие от Меню вставки (одна `HeadingIcon`), там уровень виден на самой иконке. */
18
+ const BLOCK_TYPE_ICONS = [Heading1Icon, Heading2Icon, Heading3Icon, Heading4Icon] as const;
19
+
20
+ /**
21
+ * Пункты заголовков: Меню вставки и «¶» — одни и те же пункты «Превратить в».
22
+ * H1 стоит под `heading` и есть только вместе с `heading1`: у `heading1` своего
23
+ * места в порядке поверхностей нет (`INSERT_ORDER`, `BLOCK_TYPE_ORDER`), поэтому
24
+ * его пункт живёт здесь и скрывается по `requires`.
25
+ */
26
+ const HEADING_ITEMS: readonly InsertItem[] = LEVELS.map((level): InsertItem => ({
27
+ id: `heading${level}`,
28
+ section: "turnInto",
29
+ label: (m) => m.heading({ level }),
30
+ icon: HeadingIcon,
31
+ aliases: [`h${level}`],
32
+ markdown: "#".repeat(level),
33
+ keys: SHORTCUT_KEYS[`h${level}`],
34
+ requires: level === 1 ? "heading1" : undefined,
35
+ run: (chain) => chain.setHeading({ level }),
36
+ }));
37
+
38
+ /**
39
+ * Заголовки на описании Инструмента (ADR-0058, п. 10).
40
+ *
41
+ * Узел и его клавиши даёт StarterKit, поэтому в описании — доля StarterKit, а
42
+ * не `engine`. H1 — только с `heading1`; без него `#` и `<h1>` остаются
43
+ * прежними: правило ввода и разбор HTML строятся по этим уровням. Меню «¶»
44
+ * (`BlockTypeMenu`) — виджет кита и описания не касается.
45
+ */
46
+ export const HEADING_TOOL: ToolDescriptor = {
47
+ name: "heading",
48
+ starterKit: (context) => ({
49
+ heading: { levels: context.has("heading1") ? [1, 2, 3, 4] : [2, 3, 4] },
50
+ }),
51
+ insertItems: HEADING_ITEMS,
52
+ blockTypes: turnIntoTypes("heading", HEADING_ITEMS).map((item, index) => ({
53
+ ...item,
54
+ icon: BLOCK_TYPE_ICONS[index]!,
55
+ alsoTool: item.requires,
56
+ })),
57
+ // Клавиши вешает расширение Tiptap; строки справочника — подписи к ним.
58
+ // Подпись — как у пункта «¶» («Текст»), а не «Абзац» Меню вставки: справочник
59
+ // называет команду тем словом, которым её выбирают на Панели.
60
+ shortcuts: [
61
+ {
62
+ id: "paragraph",
63
+ group: "blockType",
64
+ tool: "heading",
65
+ keys: SHORTCUT_KEYS.paragraph,
66
+ label: (m) => m.textBlock,
67
+ },
68
+ ...([2, 3, 4] as const).map((level): ShortcutCommand => ({
69
+ id: `h${level}`,
70
+ group: "blockType",
71
+ tool: "heading",
72
+ keys: SHORTCUT_KEYS[`h${level}`],
73
+ label: (m) => m.heading({ level }),
74
+ })),
75
+ ],
76
+ };
77
+
78
+ /**
79
+ * `heading1` — надстройка над `heading`: своего узла у неё нет, она добавляет
80
+ * только H1 (уровни заголовка спрашивают `has("heading1")`, пункты H1 лежат в
81
+ * описании `heading`). В описании — строка справочника `⌘⌥1`, которая есть
82
+ * лишь вместе с `heading`.
83
+ */
84
+ export const HEADING1_TOOL: ToolDescriptor = {
85
+ name: "heading1",
86
+ shortcuts: [
87
+ {
88
+ id: "h1",
89
+ group: "blockType",
90
+ tool: "heading1",
91
+ requires: "heading",
92
+ keys: SHORTCUT_KEYS.h1,
93
+ label: (m) => m.heading({ level: 1 }),
94
+ },
95
+ ],
96
+ };
@@ -0,0 +1,42 @@
1
+ "use client";
2
+
3
+ import { RedoIcon, UndoIcon } from "../../../icons.js";
4
+ import { SHORTCUT_KEYS } from "../../core/shortcuts.js";
5
+ import type { ToolDescriptor } from "../../core/toolDescriptor.js";
6
+
7
+ /**
8
+ * Отмена и повтор на описании Инструмента.
9
+ *
10
+ * Расширение — доля StarterKit (`undoRedo`): `{}` включает его умолчание так же,
11
+ * как прежний `undefined`. Клавиши `Mod+Z` и `Mod+Shift+Z` вешает оно, а не
12
+ * кнопки: поэтому у кнопок `keys` (подпись для подсказки, `aria-keyshortcuts` и
13
+ * справочника), а не `shortcut` — тот привязал бы клавишу второй раз. Строки
14
+ * справочника — те же кнопки с группой «История» (`shortcutGroup`), отдельных
15
+ * `shortcuts` у Инструмента нет: иначе строка встала бы дважды.
16
+ *
17
+ * Место кнопок — последняя группа Панели (`slot("history")`, `EditorToolbar.tsx`).
18
+ */
19
+ export const HISTORY_TOOL: ToolDescriptor = {
20
+ name: "history",
21
+ starterKit: () => ({ undoRedo: {} }),
22
+ buttons: [
23
+ {
24
+ id: "undo",
25
+ label: ({ messages }) => messages.undo,
26
+ icon: UndoIcon,
27
+ keys: SHORTCUT_KEYS.undo,
28
+ shortcutGroup: "history",
29
+ run: (editor) => void editor.chain().focus().undo().run(),
30
+ can: (editor) => editor.can().undo(),
31
+ },
32
+ {
33
+ id: "redo",
34
+ label: ({ messages }) => messages.redo,
35
+ icon: RedoIcon,
36
+ keys: SHORTCUT_KEYS.redo,
37
+ shortcutGroup: "history",
38
+ run: (editor) => void editor.chain().focus().redo().run(),
39
+ can: (editor) => editor.can().redo(),
40
+ },
41
+ ],
42
+ };
@@ -0,0 +1,49 @@
1
+ "use client";
2
+
3
+ import { ImageIcon } from "../../../icons.js";
4
+ import type { ToolDescriptor } from "../../core/toolDescriptor.js";
5
+ import { RichImage } from "./image.js";
6
+
7
+ /**
8
+ * Картинка на описании Инструмента (ADR-0058, п. 10).
9
+ *
10
+ * Картинка — `figure` с подписью (`image.ts`). Без Инструмента `<img>` из
11
+ * вставки и `value` отбрасывается, а файлы из буфера не перехватываются:
12
+ * расширения нет в списке, пока нет моста `image` (его даёт `RichTextEditor`
13
+ * только с Инструментом).
14
+ *
15
+ * Картинке нужен адрес, и ни кнопка, ни пункт его не знают: оба готовят пустой
16
+ * абзац и открывают поповер картинки (`ImagePopover`), команда `openImageEditor`
17
+ * — из `image.ts`. Картинка из поповера встаёт на место этого абзаца. Закрытый
18
+ * без адреса поповер оставляет пустой абзац — как Блок, который передумали
19
+ * писать. Сам поповер и панель выделенной картинки (`ImageToolbar`) — виджеты
20
+ * кита, у описания их нет. Форма Блока для Скелета не нужна: его Схеме мост
21
+ * картинки подставлен заглушкой (`skeleton.ts`), и расширение то же.
22
+ */
23
+ export const IMAGE_TOOL: ToolDescriptor = {
24
+ name: "image",
25
+ engine: (context) => (context.image ? [RichImage.configure(context.image)] : []),
26
+ buttons: [
27
+ {
28
+ id: "image",
29
+ label: ({ messages }) => messages.image,
30
+ icon: ImageIcon,
31
+ // Как у ссылки: кнопка открывает поповер, а не вставляет сама.
32
+ run: (editor) => editor.commands.openImageEditor(),
33
+ can: (editor) => editor.can().openImageEditor(),
34
+ // `aria-pressed` — выделена картинка или каретка в её подписи: поповер
35
+ // тогда правит её, а не вставляет новую.
36
+ isActive: (editor) => editor.isActive("image"),
37
+ },
38
+ ],
39
+ insertItems: [
40
+ {
41
+ id: "image",
42
+ section: "insert",
43
+ label: (m) => m.image,
44
+ icon: ImageIcon,
45
+ aliases: ["img", "picture", "photo", "![]()"],
46
+ run: (chain) => chain.openImageEditor(),
47
+ },
48
+ ],
49
+ };
@@ -0,0 +1,40 @@
1
+ "use client";
2
+
3
+ import { ColumnLayoutIcon } from "../../../icons.js";
4
+ import type { ToolDescriptor } from "../../core/toolDescriptor.js";
5
+ import { DEFAULT_LAYOUT } from "./columnLayouts.js";
6
+ import { LAYOUT_NODE, layoutExtensions } from "./layout.js";
7
+
8
+ /**
9
+ * Раскладка на описании Инструмента: узлы колонок и их клавиатура
10
+ * (`layout.ts`), пункт Меню вставки и место в модели содержимого Документа.
11
+ *
12
+ * Варианты раскладки («Добавить › Раскладка ›», «Формат ›», «Раскладка ›» колонки)
13
+ * — меню-виджеты Ручки Блока: своего Инструмента у них нет, и они остаются в
14
+ * `BlockHandle.tsx`, показ — по `has("layout")`.
15
+ */
16
+ export const LAYOUT_TOOL: ToolDescriptor = {
17
+ name: "layout",
18
+ // Без инструмента `div[data-block]` из вставки и `value` не разбирается, и Блоки
19
+ // колонок встают в Документ подряд. Обёртка в колонке — только при `wrapper`.
20
+ engine: (context) => layoutExtensions({ wrapper: context.has("wrapper") }),
21
+ // Раскладка не в группе `block`: принять её может только Документ, назвав поимённо.
22
+ documentContent: [LAYOUT_NODE],
23
+ /*
24
+ Раскладка — пунктом без подменю: Меню вставки — плоский список
25
+ с фильтром по `/`, и подменю в нём не живут. Вариант по умолчанию — две
26
+ колонки, остальные — в «Добавить › Раскладка ›» и «Формат ›» ручки Блока.
27
+ Встаёт она только на верхний уровень: из колонки — под своей Раскладкой
28
+ (`insertLayout`).
29
+ */
30
+ insertItems: [
31
+ {
32
+ id: "layout",
33
+ section: "insert",
34
+ label: (m) => m.layout,
35
+ icon: ColumnLayoutIcon,
36
+ aliases: ["columns", "cols"],
37
+ run: (chain) => chain.insertLayout(DEFAULT_LAYOUT),
38
+ },
39
+ ],
40
+ };
@@ -0,0 +1,112 @@
1
+ "use client";
2
+
3
+ import { TaskItem, TaskList } from "@tiptap/extension-list";
4
+ import { ListIcon, OrderedListIcon, TaskListIcon } from "../../../icons.js";
5
+ import { turnIntoTypes } from "../../core/blockTypes.js";
6
+ import type { InsertItem } from "../../core/insertItems.js";
7
+ import { SHORTCUT_KEYS, shortcutKeys, type ShortcutCommand } from "../../core/shortcuts.js";
8
+ import type { ToolDescriptor } from "../../core/toolDescriptor.js";
9
+
10
+ /**
11
+ * Три вида списка на описаниях Инструментов: маркированный, нумерованный и
12
+ * чек-лист.
13
+ *
14
+ * Пункт один на три поверхности: Меню вставки, «Формат ›» ручки Блока и меню
15
+ * «Списки ▾» читают те же слова, иконку и сочетание (`turnIntoTypes`). Виджет
16
+ * «Списки ▾» (`ListMenu`) и кнопки отступа остаются у кита, показ — по
17
+ * `has(name)`; строки отступа `indentListItem` и `outdentListItem` — общие для
18
+ * трёх списков и лежат в `SHORTCUTS`. Пункт списка (`ListItem`) и клавиши
19
+ * `ListKeymap` — в ядре, они есть в любом наборе.
20
+ *
21
+ */
22
+
23
+ /** Строка справочника: клавишу вешает расширение Tiptap, строка — подпись к ней. */
24
+ function shortcutOf(id: "bulletList" | "orderedList" | "taskList"): ShortcutCommand {
25
+ return {
26
+ id,
27
+ group: "lists",
28
+ tool: id,
29
+ keys: SHORTCUT_KEYS[id],
30
+ label: (m) => m[id],
31
+ };
32
+ }
33
+
34
+ const BULLET_LIST_ITEMS: readonly InsertItem[] = [
35
+ {
36
+ id: "bulletList",
37
+ section: "turnInto",
38
+ label: (m) => m.bulletList,
39
+ icon: ListIcon,
40
+ aliases: ["ul", "-"],
41
+ markdown: "-",
42
+ keys: shortcutKeys("bulletList"),
43
+ run: (chain) => chain.toggleBulletList(),
44
+ },
45
+ ];
46
+
47
+ const ORDERED_LIST_ITEMS: readonly InsertItem[] = [
48
+ {
49
+ id: "orderedList",
50
+ section: "turnInto",
51
+ label: (m) => m.orderedList,
52
+ icon: OrderedListIcon,
53
+ aliases: ["ol", "1."],
54
+ markdown: "1.",
55
+ keys: shortcutKeys("orderedList"),
56
+ run: (chain) => chain.toggleOrderedList(),
57
+ },
58
+ ];
59
+
60
+ const TASK_LIST_ITEMS: readonly InsertItem[] = [
61
+ {
62
+ id: "taskList",
63
+ section: "turnInto",
64
+ label: (m) => m.taskList,
65
+ icon: TaskListIcon,
66
+ aliases: ["todo", "[]"],
67
+ markdown: "[ ]",
68
+ keys: shortcutKeys("taskList"),
69
+ run: (chain) => chain.toggleTaskList(),
70
+ },
71
+ ];
72
+
73
+ /**
74
+ * Маркированный список. Расширение — доля StarterKit: `{}` включает его
75
+ * умолчание так же, как прежний `undefined`.
76
+ */
77
+ export const BULLET_LIST_TOOL: ToolDescriptor = {
78
+ name: "bulletList",
79
+ starterKit: () => ({ bulletList: {} }),
80
+ insertItems: BULLET_LIST_ITEMS,
81
+ blockTypes: turnIntoTypes("bulletList", BULLET_LIST_ITEMS),
82
+ shortcuts: [shortcutOf("bulletList")],
83
+ };
84
+
85
+ /** Нумерованный список: `start` и `1.` в Markdown — умолчания StarterKit. */
86
+ export const ORDERED_LIST_TOOL: ToolDescriptor = {
87
+ name: "orderedList",
88
+ starterKit: () => ({ orderedList: {} }),
89
+ insertItems: ORDERED_LIST_ITEMS,
90
+ blockTypes: turnIntoTypes("orderedList", ORDERED_LIST_ITEMS),
91
+ shortcuts: [shortcutOf("orderedList")],
92
+ };
93
+
94
+ /**
95
+ * Чек-лист. Подпись флажка пункта — из каталога, читается при каждой
96
+ * перерисовке пункта (`context.taskCheckbox`), поэтому смена локали не
97
+ * пересоздаёт движок.
98
+ */
99
+ export const TASK_LIST_TOOL: ToolDescriptor = {
100
+ name: "taskList",
101
+ engine: (context) => [
102
+ TaskList,
103
+ TaskItem.configure({
104
+ // Вложенные чек-листы — как вложенные обычные списки: Tab и Shift+Tab.
105
+ nested: true,
106
+ a11y: { checkboxLabel: (node) => context.taskCheckbox(node.textContent) },
107
+ }),
108
+ ],
109
+ insertItems: TASK_LIST_ITEMS,
110
+ blockTypes: turnIntoTypes("taskList", TASK_LIST_ITEMS),
111
+ shortcuts: [shortcutOf("taskList")],
112
+ };
@@ -0,0 +1,78 @@
1
+ "use client";
2
+
3
+ import { Subscript } from "@tiptap/extension-subscript";
4
+ import { Superscript } from "@tiptap/extension-superscript";
5
+ import { SHORTCUT_KEYS, type ShortcutCommand } from "../../core/shortcuts.js";
6
+ import type { ToolDescriptor } from "../../core/toolDescriptor.js";
7
+
8
+ /**
9
+ * Инлайн-Метки на описаниях Инструментов: жирный, курсив, подчёркнутый,
10
+ * зачёркнутый, код, верхний и нижний индекс.
11
+ *
12
+ * Клавишу и markdown-шорткат вешает само расширение Tiptap, а строка справочника
13
+ * — подпись к ней (`SHORTCUT_KEYS`). Виджеты остаются у кита, показ по
14
+ * `has(name)`: ряд Меток B·I·U (`rowMarks.ts`), «…» (`MORE_MARKS` в
15
+ * `FormatMenus.tsx`) и места Меток в Меню выделения.
16
+ *
17
+ * ПОРЯДОК МЕТОК в сохранённом HTML решает StarterKit: ранг и вложенность Меток
18
+ * зависят от порядка его расширений, а не от порядка доль. Поэтому пять Меток
19
+ * из StarterKit — его доли (`starterKit`), которые сливаются в один
20
+ * `StarterKit.configure` (`extensions.ts`); индексы подключаются отдельными
21
+ * расширениями на местах `ENGINE_ORDER`.
22
+ */
23
+
24
+ /** Строка справочника Метки: подпись из каталога под тем же id. */
25
+ function shortcutOf(
26
+ id: "bold" | "italic" | "strike" | "underline" | "code" | "superscript" | "subscript"
27
+ ): ShortcutCommand {
28
+ return { id, group: "marks", tool: id, keys: SHORTCUT_KEYS[id], label: (m) => m[id] };
29
+ }
30
+
31
+ /** Жирный: доля StarterKit, `{}` включает его умолчание. */
32
+ export const BOLD_TOOL: ToolDescriptor = {
33
+ name: "bold",
34
+ starterKit: () => ({ bold: {} }),
35
+ shortcuts: [shortcutOf("bold")],
36
+ };
37
+
38
+ /** Курсив: доля StarterKit. */
39
+ export const ITALIC_TOOL: ToolDescriptor = {
40
+ name: "italic",
41
+ starterKit: () => ({ italic: {} }),
42
+ shortcuts: [shortcutOf("italic")],
43
+ };
44
+
45
+ /** Подчёркнутый: доля StarterKit. */
46
+ export const UNDERLINE_TOOL: ToolDescriptor = {
47
+ name: "underline",
48
+ starterKit: () => ({ underline: {} }),
49
+ shortcuts: [shortcutOf("underline")],
50
+ };
51
+
52
+ /** Зачёркнутый: доля StarterKit. */
53
+ export const STRIKE_TOOL: ToolDescriptor = {
54
+ name: "strike",
55
+ starterKit: () => ({ strike: {} }),
56
+ shortcuts: [shortcutOf("strike")],
57
+ };
58
+
59
+ /** Код в строке: доля StarterKit. */
60
+ export const CODE_TOOL: ToolDescriptor = {
61
+ name: "code",
62
+ starterKit: () => ({ code: {} }),
63
+ shortcuts: [shortcutOf("code")],
64
+ };
65
+
66
+ /** Верхний индекс: отдельное расширение Tiptap, место в `ENGINE_ORDER` — первое. */
67
+ export const SUPERSCRIPT_TOOL: ToolDescriptor = {
68
+ name: "superscript",
69
+ engine: () => [Superscript],
70
+ shortcuts: [shortcutOf("superscript")],
71
+ };
72
+
73
+ /** Нижний индекс: отдельное расширение Tiptap. */
74
+ export const SUBSCRIPT_TOOL: ToolDescriptor = {
75
+ name: "subscript",
76
+ engine: () => [Subscript],
77
+ shortcuts: [shortcutOf("subscript")],
78
+ };
@@ -7,7 +7,7 @@ import type { SearchToolOptions } from "../../core/toolOptions.js";
7
7
  import { SearchExtension } from "./searchPlugin.js";
8
8
 
9
9
  /**
10
- * Поиск и замена на описании Инструмента. Часть для движка —
10
+ * Поиск и замена на описании Инструмента. Extensions —
11
11
  * плагин состояния и подсветки с сочетаниями `Mod+F` и `Mod+Alt+F`; панель
12
12
  * держит `RichTextEditor` и открывает её мостом `openSearch`. Кнопка «Поиск» —
13
13
  * виджет фиксированной Панели у правого края (`EditorToolbar.tsx`), как
@@ -55,11 +55,11 @@ const TABLE_SHAPE = [
55
55
 
56
56
  /**
57
57
  * Таблица на описании Инструмента (ADR-0058, п. 11.1): её нет в
58
- * `basic`, а Часть для движка с `prosemirror-tables` тяжелее 10 кБ, поэтому она
59
- * грузится тем же загрузчиком, что у своего Инструмента. В том же чанке — меню
58
+ * `basic`, а Extensions с `prosemirror-tables` тяжелее 10 кБ, поэтому они
59
+ * грузятся тем же загрузчиком, что у своего Инструмента. В том же чанке — меню
60
60
  * «Таблица» и окно «Вставить таблицу»: код кита меряется и статическим графом.
61
61
  *
62
- * Переехали Часть для движка и пункт Меню вставки. Меню «Таблица» — виджет
62
+ * Переехали Extensions и пункт Меню вставки. Меню «Таблица» — виджет
63
63
  * Панели и остаётся её местом (`EditorToolbar.tsx`), только компонент берёт из
64
64
  * чанка.
65
65
  */
@@ -5,7 +5,7 @@ import type { TocToolCommands } from "../../core/toolCommands.js";
5
5
  import type { TocLevel, TocToolOptions } from "../../core/toolOptions.js";
6
6
 
7
7
  /**
8
- * Оглавление на описании Инструмента (ADR-0068). Части для движка и UI-записей
8
+ * Оглавление на описании Инструмента (ADR-0068). Extensions и UI-записей
9
9
  * у него нет: полосу и список рисует `RichTextEditor` в левом поле рамки
10
10
  * (`TocCore`, ленивым чанком), а описание несёт Опции — умолчания и проверки,
11
11
  * из которых `expandTools` собирает Опции Редактора.
@@ -0,0 +1,52 @@
1
+ "use client";
2
+
3
+ import { UnwrapIcon, WrapperIcon } from "../../../icons.js";
4
+ import type { ToolDescriptor } from "../../core/toolDescriptor.js";
5
+ import { unwrapWrapperAt, WRAPPER_EXTENSIONS, WRAPPER_NODE } from "./wrapper.js";
6
+
7
+ /**
8
+ * Обёртка на описании Инструмента: узел и выход из неё по Enter (`wrapper.ts`),
9
+ * пункт Меню вставки, Действие «Снять обёртку» и место в модели содержимого
10
+ * Документа.
11
+ *
12
+ * «Тон ›» — меню-виджет Ручки Блока: своего Инструмента у него нет, и он
13
+ * остаётся в `BlockHandle.tsx`, показ — по `has("wrapper")`.
14
+ */
15
+ export const WRAPPER_TOOL: ToolDescriptor = {
16
+ name: "wrapper",
17
+ // Без инструмента `div[data-block="wrapper"]` из вставки и `value` не
18
+ // разбирается, и её Блоки встают на её место подряд.
19
+ engine: () => WRAPPER_EXTENSIONS,
20
+ // Обёртка не в группе `block`: принять её может только Документ, назвав поимённо.
21
+ documentContent: [WRAPPER_NODE],
22
+ /*
23
+ Обёртка — пустая, с одним абзацем, каретка внутри. Место —
24
+ Документ или колонка: из Обёртки, списка, цитаты и ячейки она встаёт под
25
+ их Блоком (`insertWrapper`).
26
+ */
27
+ insertItems: [
28
+ {
29
+ id: "wrapper",
30
+ section: "insert",
31
+ label: (m) => m.wrapper,
32
+ icon: WrapperIcon,
33
+ aliases: ["group", "container", "div"],
34
+ run: (chain) => chain.insertWrapper(),
35
+ },
36
+ ],
37
+ blockActions: [
38
+ {
39
+ id: "unwrap",
40
+ label: (messages) => messages.unwrap,
41
+ icon: UnwrapIcon,
42
+ blocks: [WRAPPER_NODE],
43
+ // Одна правка: Блоки встают на место Обёртки, отмена — одна.
44
+ run: (editor, pos) =>
45
+ void editor
46
+ .chain()
47
+ .focus()
48
+ .command(({ tr }) => unwrapWrapperAt(tr, pos))
49
+ .run(),
50
+ },
51
+ ],
52
+ };
@@ -48,6 +48,7 @@ export type {
48
48
  } from "./core/toolDefinition.js";
49
49
  export type {
50
50
  ConfiguredTool,
51
+ DetailsToolOptions,
51
52
  RichTextTools,
52
53
  SearchToolOptions,
53
54
  TocLevel,
@@ -12,7 +12,7 @@
12
12
  *
13
13
  * Свои Инструменты (ADR-0058, п. 2.3) сюда приходят массивом `tools` в
14
14
  * `sanitizeRichText` и `richTextSchema`: читаются только правила Схемы из
15
- * описания (`customTool.ts`, тоже без движка), Часть для движка не трогается.
15
+ * описания (`customTool.ts`, тоже без движка), Extensions не трогаются.
16
16
  * Описание своего Инструмента с Карточкой у текста собирается и на сервере,
17
17
  * поэтому её помощник `markRange` (функция над состоянием, Tiptap — только
18
18
  * типом) отдаётся и здесь.
@@ -270,6 +270,53 @@ const WRAPPER_CLASS =
270
270
  "[&_[data-block=wrapper][data-tone=danger]]:bg-(--rte-tone-danger-background) " +
271
271
  "[&_[data-block=wrapper][data-tone=danger]]:border-(--rte-tone-danger-border)";
272
272
 
273
+ /**
274
+ * Сворачиваемый Блок (Details): в Просмотре — родной `details > summary` с
275
+ * `div[data-type=detailsContent]`, в Редакторе — `div[data-type=details]` с
276
+ * кнопкой раскрытия, заголовком `summary` и содержимым в обёртке (представление
277
+ * узла Tiptap). Рамка, отступы и ритм Блоков внутри — одни в обоих видах, чтобы
278
+ * читатель видел то, что набирал автор.
279
+ *
280
+ * ПРОСМОТР — БЕЗ JAVASCRIPT. Раскрывает и скрывает сам браузер, маркер
281
+ * `summary` — его, родной: он доступен скринридеру и с клавиатуры (Tab, Enter,
282
+ * Space) без единой строки кита. Свёрнутое содержимое прячет `details`, а не
283
+ * наше правило.
284
+ *
285
+ * РЕДАКТОР. Маркер `summary` снят (`list-none`): его заменяет кнопка слева,
286
+ * шеврон которой рисует CSS (`border-r` + `border-b`, повёрнутые на 45°), а
287
+ * раскрытость — класс `is-open` на `div`. Кнопка — настоящий `button` с именем
288
+ * из каталога и `aria-expanded`. Скрытое содержимое прячет атрибут `hidden`,
289
+ * который ставит само представление Tiptap.
290
+ */
291
+ const DETAILS_BOX =
292
+ "[&_:is(details,div[data-type=details])]:rounded-(--rte-radius) " +
293
+ "[&_:is(details,div[data-type=details])]:border [&_:is(details,div[data-type=details])]:border-(--rte-hr) " +
294
+ "[&_:is(details,div[data-type=details])]:px-3 [&_:is(details,div[data-type=details])]:py-2 " +
295
+ "[&_summary]:font-medium [&_details>summary]:cursor-pointer " +
296
+ "[&_div[data-type=detailsContent]>*:not(:last-child)]:mb-3 " +
297
+ "[&_div[data-type=detailsContent]>:is(h1,h2,h3,h4):not(:first-child)]:mt-6 " +
298
+ "[&_details[open]>summary]:mb-2 ";
299
+
300
+ const DETAILS_EDITOR =
301
+ "[&_div[data-type=details]]:flex [&_div[data-type=details]]:items-start " +
302
+ "[&_div[data-type=details]]:gap-1.5 " +
303
+ "[&_div[data-type=details]>div]:min-w-0 [&_div[data-type=details]>div]:flex-1 " +
304
+ "[&_div[data-type=details]_summary]:list-none " +
305
+ "[&_div[data-type=details].is-open>div>summary]:mb-2 " +
306
+ "[&_div[data-type=details]>button]:relative [&_div[data-type=details]>button]:mt-px " +
307
+ "[&_div[data-type=details]>button]:size-6 [&_div[data-type=details]>button]:shrink-0 " +
308
+ "[&_div[data-type=details]>button]:cursor-pointer [&_div[data-type=details]>button]:rounded-(--rte-radius) " +
309
+ "[&_div[data-type=details]>button]:text-(--rte-secondary) " +
310
+ "[&_div[data-type=details]>button]:before:absolute [&_div[data-type=details]>button]:before:left-1/2 " +
311
+ "[&_div[data-type=details]>button]:before:top-1/2 [&_div[data-type=details]>button]:before:size-1.5 " +
312
+ "[&_div[data-type=details]>button]:before:-translate-x-1/2 [&_div[data-type=details]>button]:before:-translate-y-1/2 " +
313
+ "[&_div[data-type=details]>button]:before:-rotate-45 [&_div[data-type=details]>button]:before:border-r-2 " +
314
+ "[&_div[data-type=details]>button]:before:border-b-2 [&_div[data-type=details]>button]:before:border-current " +
315
+ "[&_div[data-type=details]>button]:before:content-[''] " +
316
+ "[&_div[data-type=details].is-open>button]:before:rotate-45";
317
+
318
+ const DETAILS_CLASS = DETAILS_BOX + DETAILS_EDITOR;
319
+
273
320
  /**
274
321
  * Свой Блок потребителя (ADR-0058, п. 3.2): `div[data-block]` с
275
322
  * именем, которого нет у кита. Кит даёт ему ритм абзаца и больше ничего — вид
@@ -401,6 +448,8 @@ const PROSE_RULES =
401
448
  " " +
402
449
  WRAPPER_CLASS +
403
450
  " " +
451
+ DETAILS_CLASS +
452
+ " " +
404
453
  CUSTOM_BLOCK_CLASS;
405
454
 
406
455
  /**