@olddevs/ui 4.5.1 → 4.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. package/CHANGELOG.md +18 -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 +56 -56
  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 +3 -3
  146. package/docs/api/migration-notes.md +2 -2
  147. package/docs/components/content/rich-text-view.en.md +18 -12
  148. package/docs/components/content/rich-text-view.md +17 -11
  149. package/docs/components/inputs/rich-text-editor.en.md +140 -87
  150. package/docs/components/inputs/rich-text-editor.md +139 -86
  151. package/docs/conventions.en.md +2 -2
  152. package/docs/conventions.md +1 -1
  153. package/docs/getting-started.en.md +9 -9
  154. package/docs/getting-started.md +8 -8
  155. package/docs/glossary.en.md +80 -52
  156. package/docs/glossary.md +79 -51
  157. package/docs/icons.en.md +4 -1
  158. package/docs/icons.md +3 -0
  159. package/package.json +2 -1
  160. package/src/editor/RichTextEditor.tsx +9 -6
  161. package/src/editor/core/atomText.ts +2 -2
  162. package/src/editor/core/blockTypes.ts +19 -83
  163. package/src/editor/core/containers.ts +4 -2
  164. package/src/editor/core/customMarkdown.ts +2 -2
  165. package/src/editor/core/customTool.ts +6 -6
  166. package/src/editor/core/customToolEngine.ts +9 -9
  167. package/src/editor/core/extensions.ts +165 -110
  168. package/src/editor/core/fieldNotice.ts +1 -1
  169. package/src/editor/core/handle.ts +2 -2
  170. package/src/editor/core/insertItems.ts +12 -158
  171. package/src/editor/core/insertMenuTool.ts +32 -0
  172. package/src/editor/core/jsonDocument.ts +1 -1
  173. package/src/editor/core/schema.ts +42 -7
  174. package/src/editor/core/shortcuts.ts +116 -177
  175. package/src/editor/core/skeleton.ts +4 -3
  176. package/src/editor/core/toolButtons.ts +3 -3
  177. package/src/editor/core/toolDefinition.ts +4 -4
  178. package/src/editor/core/toolDescriptor.ts +65 -52
  179. package/src/editor/core/toolLoader.ts +3 -3
  180. package/src/editor/core/toolOptions.ts +21 -2
  181. package/src/editor/core/tools.ts +10 -1
  182. package/src/editor/core/viewIsland.ts +1 -1
  183. package/src/editor/features/builtins.ts +76 -41
  184. package/src/editor/features/code/codeBlockTool.ts +16 -8
  185. package/src/editor/features/color/colorTools.ts +68 -0
  186. package/src/editor/features/details/details.ts +389 -0
  187. package/src/editor/features/details/detailsMarkdown.ts +158 -0
  188. package/src/editor/features/details/detailsTool.ts +69 -0
  189. package/src/editor/features/emoji/emojiTool.tsx +1 -1
  190. package/src/editor/features/heading/headingTool.ts +96 -0
  191. package/src/editor/features/history/historyTool.ts +42 -0
  192. package/src/editor/features/image/imageTool.ts +49 -0
  193. package/src/editor/features/layout/layoutTool.ts +40 -0
  194. package/src/editor/features/lists/listTools.ts +112 -0
  195. package/src/editor/features/marks/markTools.ts +78 -0
  196. package/src/editor/features/search/searchTool.ts +1 -1
  197. package/src/editor/features/table/tableTool.ts +3 -3
  198. package/src/editor/features/toc/tocTool.ts +1 -1
  199. package/src/editor/features/wrapper/wrapperTool.ts +52 -0
  200. package/src/editor/index.ts +1 -0
  201. package/src/editor/schema-entry.ts +1 -1
  202. package/src/editor/styles/prose.ts +49 -0
  203. package/src/editor/ui/EditorToolbar.tsx +7 -50
  204. package/src/editor/ui/SelectionBubble.tsx +2 -5
  205. package/src/editor/ui/ToolButtonView.tsx +2 -1
  206. package/src/i18n/defaults.ts +3 -0
  207. package/src/i18n/messages.ts +14 -1
  208. package/src/icons.ts +7 -0
  209. package/src/locales/ru.ts +3 -0
@@ -38,7 +38,7 @@ declare module "@tiptap/core" {
38
38
  * история и `onChange` не затрагиваются. `error` уходит сама со следующей
39
39
  * Правкой автора, `status` остаётся, пока его не заменят или не снимут.
40
40
  *
41
- * Свой Инструмент зовёт её из Части для движка (`this.editor.commands`),
41
+ * Свой Инструмент зовёт её из Extensions (`this.editor.commands`),
42
42
  * экран — через `handle.editor`. Сообщение без текста — то же, что `null`.
43
43
  */
44
44
  showNotice: (notice: FieldNotice | null) => ReturnType;
@@ -138,8 +138,8 @@ const TAG = /<[^>]*>/g;
138
138
  const UNSEEN = /<!--[\s\S]*?-->|<(script|style|template)\b[\s\S]*?<\/\1\s*>/gi;
139
139
  /**
140
140
  * Листья Документа — содержимое и без текста: мягкий перенос, линия, картинка,
141
- * свой Блок. Свой Блок до движка — всегда содержимое: атом ли он, знает только
142
- * его Часть для движка, а атом без текста — тоже содержимое.
141
+ * свой Блок. Свой Блок до движка — всегда содержимое: атом ли он, знают только
142
+ * его Extensions, а атом без текста — тоже содержимое.
143
143
  */
144
144
  const LEAF = /<(?:br|hr|img)\b|\sdata-block\s*=\s*["']?[A-Za-z][A-Za-z0-9]*_/i;
145
145
  /** Таблица — содержимое и с пустыми ячейками (`emptiness.ts`). */
@@ -4,22 +4,9 @@ import type { ChainedCommands, Editor, Range } from "@tiptap/core";
4
4
  import { TextSelection, type Transaction } from "@tiptap/pm/state";
5
5
  import type { ComponentType } from "react";
6
6
  import type { UiMessages } from "../../i18n/messages.js";
7
- import {
8
- ColumnLayoutIcon,
9
- HeadingIcon,
10
- ImageIcon,
11
- ListIcon,
12
- OrderedListIcon,
13
- ParagraphIcon,
14
- TableIcon,
15
- TaskListIcon,
16
- WrapperIcon,
17
- type IconProps,
18
- } from "../../icons.js";
7
+ import { TableIcon, type IconProps } from "../../icons.js";
19
8
  import { DEFAULT_LOCALE } from "../../i18n/defaults.js";
20
9
  import { handleBlock } from "./blockCommands.js";
21
- import { DEFAULT_LAYOUT } from "../features/layout/columnLayouts.js";
22
- import { shortcutKeys } from "./shortcuts.js";
23
10
  import { projectRecords, type ToolList, type ToolPopover } from "./toolDescriptor.js";
24
11
  import type { Tool } from "./tools.js";
25
12
 
@@ -63,12 +50,12 @@ export interface InsertItem {
63
50
  keys?: readonly string[];
64
51
  /**
65
52
  * Сочетание, которое вешает кит (ADR-0058, п. 5.7): единственный источник
66
- * клавиши, подсказки и строки справочника. У встроенных нет — клавишу вешает
67
- * их Часть для движка, а подпись — `keys`.
53
+ * клавиши, подсказки и строки справочника. У встроенных нет — клавишу вешают
54
+ * их Extensions, а подпись — `keys`.
68
55
  */
69
56
  shortcut?: readonly string[];
70
57
  /**
71
- * Второй нужный инструмент сверх ключа реестра: Заголовок 1 стоит под
58
+ * Второй нужный инструмент сверх владельца пункта: Заголовок 1 стоит под
72
59
  * `heading`, но есть только вместе с `heading1`, как в меню «¶».
73
60
  */
74
61
  requires?: Tool;
@@ -80,136 +67,6 @@ export interface InsertItem {
80
67
  popover?: ToolPopover;
81
68
  }
82
69
 
83
- /**
84
- * Старый реестр пунктов Меню вставки по инструментам.
85
- *
86
- * Новый встроенный Инструмент сюда уже не пишется: его пункты лежат в его
87
- * описании (`insertItems`, `features/builtins.ts`), и меню получает их проекцией
88
- * списка описаний (`insertItemsFor`). Здесь — пункты Инструментов, ещё не
89
- * переехавших на описание (ADR-0058, п. 10), одной записью без правки меню:
90
- *
91
- * horizontalRule: [{ id: "horizontalRule", section: "insert",
92
- * label: (m) => m.horizontalRule, icon: DividerIcon,
93
- * run: (chain) => chain.setHorizontalRule() }],
94
- *
95
- * Ключ — `Tool`: выключенный инструмент убирает свои пункты и из `/`, и из «+»
96
- * сам, без проверок в меню. Порядок пунктов в разделе — `INSERT_ORDER`, а не
97
- * порядок записей. Подпись — из каталога (`editor.*`), иконка —
98
- * из `@olddevs/ui/icons`. Пункт, которому нужен свой интерфейс (поповер адреса
99
- * картинки), открывает его из `run` — `editor` для этого и передан.
100
- *
101
- * Абзац записан под самим `insertMenu`: это не инструмент, он есть в Схеме
102
- * всегда, и «Превратить в абзац» нужен, пока есть само меню.
103
- */
104
- export const INSERT_ITEMS: Partial<Record<Tool, readonly InsertItem[]>> = {
105
- insertMenu: [
106
- {
107
- id: "paragraph",
108
- section: "turnInto",
109
- label: (m) => m.paragraph,
110
- icon: ParagraphIcon,
111
- aliases: ["p", "text"],
112
- keys: shortcutKeys("paragraph"),
113
- run: (chain) => chain.setParagraph(),
114
- },
115
- ],
116
- heading: ([1, 2, 3, 4] as const).map((level): InsertItem => ({
117
- id: `heading${level}`,
118
- section: "turnInto",
119
- label: (m) => m.heading({ level }),
120
- icon: HeadingIcon,
121
- aliases: [`h${level}`],
122
- markdown: "#".repeat(level),
123
- keys: shortcutKeys(`h${level}`),
124
- requires: level === 1 ? "heading1" : undefined,
125
- run: (chain) => chain.setHeading({ level }),
126
- })),
127
- bulletList: [
128
- {
129
- id: "bulletList",
130
- section: "turnInto",
131
- label: (m) => m.bulletList,
132
- icon: ListIcon,
133
- aliases: ["ul", "-"],
134
- markdown: "-",
135
- keys: shortcutKeys("bulletList"),
136
- run: (chain) => chain.toggleBulletList(),
137
- },
138
- ],
139
- orderedList: [
140
- {
141
- id: "orderedList",
142
- section: "turnInto",
143
- label: (m) => m.orderedList,
144
- icon: OrderedListIcon,
145
- aliases: ["ol", "1."],
146
- markdown: "1.",
147
- keys: shortcutKeys("orderedList"),
148
- run: (chain) => chain.toggleOrderedList(),
149
- },
150
- ],
151
- taskList: [
152
- {
153
- id: "taskList",
154
- section: "turnInto",
155
- label: (m) => m.taskList,
156
- icon: TaskListIcon,
157
- aliases: ["todo", "[]"],
158
- markdown: "[ ]",
159
- keys: shortcutKeys("taskList"),
160
- run: (chain) => chain.toggleTaskList(),
161
- },
162
- ],
163
- /*
164
- Картинке нужен адрес, и пункт его не знает: он готовит пустой абзац (как
165
- любой пункт «Добавить») и открывает поповер картинки. Картинка из поповера
166
- встаёт на место этого абзаца. Закрытый без адреса поповер оставляет
167
- пустой абзац — как Блок, который передумали писать.
168
- */
169
- image: [
170
- {
171
- id: "image",
172
- section: "insert",
173
- label: (m) => m.image,
174
- icon: ImageIcon,
175
- aliases: ["img", "picture", "photo", "![]()"],
176
- run: (chain) => chain.openImageEditor(),
177
- },
178
- ],
179
- /*
180
- Раскладка — пунктом без подменю: Меню вставки — плоский список
181
- с фильтром по `/`, и подменю в нём не живут. Вариант по умолчанию — две
182
- колонки, остальные — в «Добавить › Раскладка ›» и «Формат ›» ручки Блока.
183
- Встаёт она только на верхний уровень: из колонки — под своей Раскладкой
184
- (`insertLayout`).
185
- */
186
- layout: [
187
- {
188
- id: "layout",
189
- section: "insert",
190
- label: (m) => m.layout,
191
- icon: ColumnLayoutIcon,
192
- aliases: ["columns", "cols"],
193
- run: (chain) => chain.insertLayout(DEFAULT_LAYOUT),
194
- },
195
- ],
196
- /*
197
- Обёртка — пустая, с одним абзацем, каретка внутри. Место —
198
- Документ или колонка: из Обёртки, списка, цитаты и ячейки она встаёт под
199
- их Блоком (`insertWrapper`).
200
- */
201
- wrapper: [
202
- {
203
- id: "wrapper",
204
- section: "insert",
205
- label: (m) => m.wrapper,
206
- icon: WrapperIcon,
207
- aliases: ["group", "container", "div"],
208
- run: (chain) => chain.insertWrapper(),
209
- },
210
- ],
211
- };
212
-
213
70
  /**
214
71
  * Пункт «Таблица» заданного размера: строка-заголовок всегда есть.
215
72
  *
@@ -271,8 +128,8 @@ export function insertItemHint(
271
128
 
272
129
  /**
273
130
  * Место встроенных Инструментов в Меню вставки — порядок их пунктов внутри
274
- * раздела. Держится отдельно от `INSERT_ITEMS`: Инструмент, переехавший на
275
- * описание (`toolDescriptor.ts`), уходит из реестра, а место остаётся здесь.
131
+ * раздела. Пункты живут в описаниях (`insertItems`), а место на поверхности —
132
+ * привилегия кита, поэтому оно здесь, а не в описании.
276
133
  */
277
134
  const INSERT_ORDER: readonly Tool[] = [
278
135
  "insertMenu",
@@ -287,25 +144,22 @@ const INSERT_ORDER: readonly Tool[] = [
287
144
  "image",
288
145
  "layout",
289
146
  "wrapper",
147
+ "details",
290
148
  ];
291
149
 
292
150
  /** Разделы по порядку показа. */
293
151
  export const INSERT_SECTIONS = ["insert", "turnInto"] as const;
294
152
 
295
153
  /**
296
- * Пункты для набора инструментов — проекция списка описаний: у переехавшего
297
- * Инструмента — из описания, у остальных — из `INSERT_ITEMS`, в порядке
298
- * `INSERT_ORDER`. Меню без пункта сверх
154
+ * Пункты для набора инструментов — проекция списка описаний (`insertItems`) в
155
+ * порядке `INSERT_ORDER`. Абзац — пункт описания `insertMenu`. Меню без пункта сверх
299
156
  * абзаца бессмысленно, и тогда список пуст: ни «+», ни `/` не появляются.
300
157
  */
301
158
  export function insertItemsFor(tools: ToolList): readonly InsertItem[] {
302
159
  if (!tools.has("insertMenu")) return [];
303
- const items = projectRecords(
304
- tools,
305
- INSERT_ORDER,
306
- (tool) => INSERT_ITEMS[tool],
307
- (descriptor) => descriptor.insertItems
308
- ).filter((item) => item.requires === undefined || tools.has(item.requires));
160
+ const items = projectRecords(tools, INSERT_ORDER, (descriptor) => descriptor.insertItems).filter(
161
+ (item) => item.requires === undefined || tools.has(item.requires)
162
+ );
309
163
  return items.length > 1 ? items : [];
310
164
  }
311
165
 
@@ -0,0 +1,32 @@
1
+ "use client";
2
+
3
+ import { ParagraphIcon } from "../../icons.js";
4
+ import { shortcutKeys } from "./shortcuts.js";
5
+ import { SlashCommand } from "./slashCommand.js";
6
+ import type { ToolDescriptor } from "./toolDescriptor.js";
7
+
8
+ /**
9
+ * Меню вставки (`/` в тексте; «+» на Панели и «Добавить ›» Ручки Блока — виджеты
10
+ * кита, они читают `tools.has("insertMenu")`).
11
+ *
12
+ * Своего Блока у Инструмента нет: это способ добраться до чужих Блоков. Extensions
13
+ * Инструмента — это `/`, и они есть, только когда `RichTextEditor` дал мост: пунктов
14
+ * для меню нет — моста нет, и `/` остаётся обычным символом. Абзац записан здесь:
15
+ * это не Инструмент, он есть в Схеме всегда, и «Превратить в абзац» нужен, пока
16
+ * есть само меню.
17
+ */
18
+ export const INSERT_MENU_TOOL: ToolDescriptor = {
19
+ name: "insertMenu",
20
+ engine: (context) => (context.slash ? [SlashCommand.configure({ bridge: context.slash })] : []),
21
+ insertItems: [
22
+ {
23
+ id: "paragraph",
24
+ section: "turnInto",
25
+ label: (m) => m.paragraph,
26
+ icon: ParagraphIcon,
27
+ aliases: ["p", "text"],
28
+ keys: shortcutKeys("paragraph"),
29
+ run: (chain) => chain.setParagraph(),
30
+ },
31
+ ],
32
+ };
@@ -80,7 +80,7 @@ export function documentFromJSON(editor: Editor, value: unknown): PmNode | null
80
80
  preserveWhitespace: "full",
81
81
  });
82
82
  } catch (error) {
83
- // Кривой файл — обычный отказ, но бросить может и Часть для движка своего
83
+ // Кривой файл — обычный отказ, но бросить могут и Extensions своего
84
84
  // Инструмента: причина уходит в консоль разработки, иначе её не найти.
85
85
  devError("RichTextEditor: a JSON document could not be built from its tree", error);
86
86
  return null;
@@ -63,6 +63,13 @@ export const RICH_TEXT_SCHEMA = {
63
63
  * Стоит на верхнем уровне и прямо в колонке Раскладки; внутри — Блоки, кроме
64
64
  * другой Обёртки и Раскладки.
65
65
  *
66
+ * `details` — сворачиваемый Блок: `details > summary` и следом
67
+ * `div[data-type="detailsContent"]` с Блоками; `open` — только у `details` и
68
+ * только пустым (`open=""`), так его пишет Редактор с Опцией `persist`.
69
+ * `summary` вне `details` и обёртка содержимого вне него снимаются —
70
+ * Блоки остаются подряд. Теги и атрибуты Блока открыты только в этой форме:
71
+ * `iframe` и ему подобное так не открывается.
72
+ *
66
73
  * Таблица — `table > tbody > tr > th|td`, без `thead`, `colgroup` и
67
74
  * `caption`: строка-заголовок — это обычная первая строка из `th`. На ячейке
68
75
  * бывают только слияние (`colspan`, `rowspan`) и выравнивание столбца
@@ -94,6 +101,8 @@ export const RICH_TEXT_SCHEMA = {
94
101
  "figure",
95
102
  "img",
96
103
  "figcaption",
104
+ "details",
105
+ "summary",
97
106
  ],
98
107
  /**
99
108
  * Метки и строчные узлы. `br` — мягкий перенос по Shift+Enter. `mark` —
@@ -112,9 +121,13 @@ export const RICH_TEXT_SCHEMA = {
112
121
  * Раскладка и её колонка: `data-block="layout"` и `data-block="column"`;
113
122
  * `data-layout` — вариант из `layouts`, только у Раскладки. Обёртка —
114
123
  * `data-block="wrapper"` и, если у неё есть тон, `data-tone` — имя из
115
- * `wrapperTones`, только у Обёртки. У `div` пункта чек-листа атрибутов нет.
124
+ * `wrapperTones`, только у Обёртки. Обёртка содержимого сворачиваемого
125
+ * Блока — `data-type="detailsContent"`, только прямо в `details`. У `div`
126
+ * пункта чек-листа атрибутов нет.
116
127
  */
117
- div: ["data-block", "data-layout", "data-tone"],
128
+ div: ["data-block", "data-layout", "data-tone", "data-type"],
129
+ /** Открытый сворачиваемый Блок: пустой `open`, только с Опцией `persist`. */
130
+ details: ["open"],
118
131
  /** Пункт чек-листа: `data-type="taskItem"` и `data-checked="true|false"`. */
119
132
  li: ["data-type", "data-checked"],
120
133
  /** Флажок пункта чек-листа: всегда `type="checkbox"`, `checked` — у отмеченного. */
@@ -369,8 +382,8 @@ function customAttributes(
369
382
  * набор отвергает ошибкой поля, а у сервера ошибок нет — и описание с именем
370
383
  * кита (`wrapper`, атрибут `tone`) не должно открыть обход правил кита.
371
384
  *
372
- * Читаются только данные описания (`blocks`, `marks`), Часть для движка не
373
- * трогается: модуль с описанием своего Инструмента импортируется на сервере,
385
+ * Читаются только данные описания (`blocks`, `marks`), Extensions не
386
+ * трогаются: модуль с описанием своего Инструмента импортируется на сервере,
374
387
  * и Tiptap здесь не нужен.
375
388
  */
376
389
  export function richTextSchema(tools: readonly ToolEntry[] = []): RichTextSchema {
@@ -584,6 +597,10 @@ export interface RichTextPurifier {
584
597
  * колонке Раскладки, не в другой Обёртке, списке, цитате и таблице; иначе
585
598
  * снимается так же, вместе с тоном; `data-tone` — имя из `wrapperTones` и
586
599
  * только на Обёртке;
600
+ * - сворачиваемый Блок — `details` с `summary` прямо в нём и обёрткой содержимого
601
+ * `div[data-type="detailsContent"]` тоже прямо в нём; `open` — только пустой и
602
+ * только на `details`. `summary` и обёртка вне `details` теряют тег, их Блоки
603
+ * остаются подряд;
587
604
  * - `colspan`, `rowspan` — на `th`/`td`, целое от 1 до `tableSpans`;
588
605
  * - ссылка — `href` по `protocols` (выражение конфига), `rel` всегда
589
606
  * `"noopener noreferrer"`, ссылка без адреса становится текстом;
@@ -648,7 +665,14 @@ const ATTRIBUTE_RULES: Record<string, (node: Element, value: string) => boolean>
648
665
  start: (node) => node.nodeName === "OL",
649
666
  "data-type": (node, value) =>
650
667
  (node.nodeName === "UL" && value === "taskList") ||
651
- (node.nodeName === "LI" && value === "taskItem"),
668
+ (node.nodeName === "LI" && value === "taskItem") ||
669
+ // Обёртка содержимого сворачиваемого Блока: только `div` прямо в `details`.
670
+ (node.nodeName === "DIV" &&
671
+ value === DETAILS_CONTENT &&
672
+ node.parentElement?.nodeName === "DETAILS"),
673
+ // Открытый Блок: `open=""`, как его пишет Редактор; значение `open="open"` из
674
+ // чужого HTML атрибут теряет, Блок остаётся закрытым.
675
+ open: (node, value) => node.nodeName === "DETAILS" && value === "",
652
676
  "data-checked": (node, value) =>
653
677
  node.nodeName === "LI" &&
654
678
  node.getAttribute("data-type") === "taskItem" &&
@@ -787,6 +811,9 @@ const PARENT_OF: Record<string, string> = {
787
811
  SPAN: `${TASK_ITEM} > label`,
788
812
  };
789
813
 
814
+ /** Значение `div[data-type]` обёртки содержимого сворачиваемого Блока (`details.ts`). */
815
+ const DETAILS_CONTENT = "detailsContent";
816
+
790
817
  /** Значения `div[data-block]`: Раскладка, её колонка и Обёртка. */
791
818
  const LAYOUT_BLOCK = "layout";
792
819
  const COLUMN_BLOCK = "column";
@@ -794,10 +821,10 @@ const WRAPPER_BLOCK = "wrapper";
794
821
 
795
822
  /**
796
823
  * Где Раскладки быть не может: в другой Раскладке (её колонке), в Обёртке, в
797
- * списке, цитате и ячейке. Редактор такую не отдаёт — у колонки и прочих контейнеров
824
+ * списке, цитате, ячейке и сворачиваемом Блоке. Редактор такую не отдаёт — у колонки и прочих контейнеров
798
825
  * в Схеме движка её нет, — и при загрузке разворачивает в Блоки подряд.
799
826
  */
800
- const NOT_A_LAYOUT_PLACE = "div[data-block], li, blockquote, th, td";
827
+ const NOT_A_LAYOUT_PLACE = "div[data-block], li, blockquote, th, td, details";
801
828
 
802
829
  /**
803
830
  * Раскладка, колонка и Обёртка: `true` — элемент обработан (оставлен или
@@ -868,6 +895,14 @@ function checkStructure(node: Element, schema: RichTextSchema): void {
868
895
  return;
869
896
  }
870
897
  if (node.nodeName === "SPAN" && coloured) return;
898
+ // Сворачиваемый Блок: `summary` — только прямо в `details`, обёртка
899
+ // содержимого с `data-type` — тоже (атрибут уже снят хуком, если она не там).
900
+ // Вне Блока они теряют обёртку, содержимое остаётся.
901
+ if (node.nodeName === "SUMMARY") {
902
+ if (node.parentElement?.nodeName !== "DETAILS") node.replaceWith(...node.childNodes);
903
+ return;
904
+ }
905
+ if (node.nodeName === "DIV" && node.getAttribute("data-type") === DETAILS_CONTENT) return;
871
906
  // Картинка — `img` с адресом прямо внутри `figure`, и только так: `figure`
872
907
  // без годной картинки разворачивается в содержимое, `img` вне `figure` или без
873
908
  // адреса удаляется, `figcaption` вне `figure` теряет обёртку.