@olddevs/ui 4.5.0 → 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 (213) hide show
  1. package/CHANGELOG.md +24 -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/toc/RichTextToc.d.ts.map +1 -1
  110. package/dist/editor/features/toc/RichTextToc.js +9 -2
  111. package/dist/editor/features/toc/RichTextToc.js.map +1 -1
  112. package/dist/editor/features/wrapper/wrapperTool.d.ts +11 -0
  113. package/dist/editor/features/wrapper/wrapperTool.d.ts.map +1 -0
  114. package/dist/editor/features/wrapper/wrapperTool.js +32 -0
  115. package/dist/editor/features/wrapper/wrapperTool.js.map +1 -0
  116. package/dist/editor/index.d.ts +1 -1
  117. package/dist/editor/index.d.ts.map +1 -1
  118. package/dist/editor/index.js.map +1 -1
  119. package/dist/editor/schema-entry.d.ts +1 -1
  120. package/dist/editor/styles/prose.d.ts.map +1 -1
  121. package/dist/editor/styles/prose.js +26 -0
  122. package/dist/editor/styles/prose.js.map +1 -1
  123. package/dist/editor/ui/EditorToolbar.d.ts.map +1 -1
  124. package/dist/editor/ui/EditorToolbar.js +5 -38
  125. package/dist/editor/ui/EditorToolbar.js.map +1 -1
  126. package/dist/editor/ui/SelectionBubble.d.ts.map +1 -1
  127. package/dist/editor/ui/SelectionBubble.js +1 -1
  128. package/dist/editor/ui/SelectionBubble.js.map +1 -1
  129. package/dist/editor/ui/ToolButtonView.d.ts.map +1 -1
  130. package/dist/editor/ui/ToolButtonView.js +1 -1
  131. package/dist/editor/ui/ToolButtonView.js.map +1 -1
  132. package/dist/i18n/defaults.d.ts.map +1 -1
  133. package/dist/i18n/defaults.js +3 -0
  134. package/dist/i18n/defaults.js.map +1 -1
  135. package/dist/i18n/messages.d.ts +16 -1
  136. package/dist/i18n/messages.d.ts.map +1 -1
  137. package/dist/icons.d.ts +8 -1
  138. package/dist/icons.d.ts.map +1 -1
  139. package/dist/icons.js +1 -1
  140. package/dist/icons.js.map +1 -1
  141. package/dist/locales/ru.d.ts.map +1 -1
  142. package/dist/locales/ru.js +3 -0
  143. package/dist/locales/ru.js.map +1 -1
  144. package/docs/api/editor-extension.en.md +56 -56
  145. package/docs/api/editor-extension.md +55 -55
  146. package/docs/api/messages.en.md +4 -1
  147. package/docs/api/messages.md +3 -0
  148. package/docs/api/migration-notes.en.md +3 -3
  149. package/docs/api/migration-notes.md +2 -2
  150. package/docs/components/content/rich-text-view.en.md +18 -12
  151. package/docs/components/content/rich-text-view.md +17 -11
  152. package/docs/components/inputs/rich-text-editor.en.md +140 -87
  153. package/docs/components/inputs/rich-text-editor.md +139 -86
  154. package/docs/conventions.en.md +2 -2
  155. package/docs/conventions.md +1 -1
  156. package/docs/getting-started.en.md +9 -9
  157. package/docs/getting-started.md +8 -8
  158. package/docs/glossary.en.md +80 -52
  159. package/docs/glossary.md +79 -51
  160. package/docs/icons.en.md +4 -1
  161. package/docs/icons.md +3 -0
  162. package/package.json +2 -1
  163. package/src/editor/RichTextEditor.tsx +9 -6
  164. package/src/editor/core/atomText.ts +2 -2
  165. package/src/editor/core/blockTypes.ts +19 -83
  166. package/src/editor/core/containers.ts +4 -2
  167. package/src/editor/core/customMarkdown.ts +2 -2
  168. package/src/editor/core/customTool.ts +6 -6
  169. package/src/editor/core/customToolEngine.ts +9 -9
  170. package/src/editor/core/extensions.ts +165 -110
  171. package/src/editor/core/fieldNotice.ts +1 -1
  172. package/src/editor/core/handle.ts +2 -2
  173. package/src/editor/core/insertItems.ts +12 -158
  174. package/src/editor/core/insertMenuTool.ts +32 -0
  175. package/src/editor/core/jsonDocument.ts +1 -1
  176. package/src/editor/core/schema.ts +42 -7
  177. package/src/editor/core/shortcuts.ts +116 -177
  178. package/src/editor/core/skeleton.ts +4 -3
  179. package/src/editor/core/toolButtons.ts +3 -3
  180. package/src/editor/core/toolDefinition.ts +4 -4
  181. package/src/editor/core/toolDescriptor.ts +65 -52
  182. package/src/editor/core/toolLoader.ts +3 -3
  183. package/src/editor/core/toolOptions.ts +21 -2
  184. package/src/editor/core/tools.ts +10 -1
  185. package/src/editor/core/viewIsland.ts +1 -1
  186. package/src/editor/features/builtins.ts +76 -41
  187. package/src/editor/features/code/codeBlockTool.ts +16 -8
  188. package/src/editor/features/color/colorTools.ts +68 -0
  189. package/src/editor/features/details/details.ts +389 -0
  190. package/src/editor/features/details/detailsMarkdown.ts +158 -0
  191. package/src/editor/features/details/detailsTool.ts +69 -0
  192. package/src/editor/features/emoji/emojiTool.tsx +1 -1
  193. package/src/editor/features/heading/headingTool.ts +96 -0
  194. package/src/editor/features/history/historyTool.ts +42 -0
  195. package/src/editor/features/image/imageTool.ts +49 -0
  196. package/src/editor/features/layout/layoutTool.ts +40 -0
  197. package/src/editor/features/lists/listTools.ts +112 -0
  198. package/src/editor/features/marks/markTools.ts +78 -0
  199. package/src/editor/features/search/searchTool.ts +1 -1
  200. package/src/editor/features/table/tableTool.ts +3 -3
  201. package/src/editor/features/toc/RichTextToc.tsx +14 -3
  202. package/src/editor/features/toc/tocTool.ts +1 -1
  203. package/src/editor/features/wrapper/wrapperTool.ts +52 -0
  204. package/src/editor/index.ts +1 -0
  205. package/src/editor/schema-entry.ts +1 -1
  206. package/src/editor/styles/prose.ts +49 -0
  207. package/src/editor/ui/EditorToolbar.tsx +7 -50
  208. package/src/editor/ui/SelectionBubble.tsx +2 -5
  209. package/src/editor/ui/ToolButtonView.tsx +2 -1
  210. package/src/i18n/defaults.ts +3 -0
  211. package/src/i18n/messages.ts +14 -1
  212. package/src/icons.ts +7 -0
  213. package/src/locales/ru.ts +3 -0
@@ -1,4 +1,5 @@
1
1
  import type { Editor, Extensions } from "@tiptap/core";
2
+ import type { StarterKitOptions } from "@tiptap/starter-kit";
2
3
  import type { ComponentType, ReactNode } from "react";
3
4
  import type { UiMessages } from "../../i18n/messages.js";
4
5
  import type { IconProps } from "../../icons.js";
@@ -21,7 +22,7 @@ import type { Tool } from "./tools.js";
21
22
  * Внутреннее описание встроенного Инструмента (ADR-0058, п. 10).
22
23
  *
23
24
  * НАДФОРМА. Тип — надмножество будущего публичного описания своего
24
- * Инструмента: те же Часть для движка, Признаки Блока и UI-записи, а сверху
25
+ * Инструмента: те же Extensions, Признаки Блока и UI-записи, а сверху
25
26
  * привилегии кита, которых своему Инструменту не дают (теги Схемы кита,
26
27
  * модель содержимого, виджеты Панели, своё место в порядке поверхности).
27
28
  * Наружу описания встроенных не экспортируются: публичны только имена и
@@ -31,36 +32,36 @@ import type { Tool } from "./tools.js";
31
32
  * (`expandTools`, `features/builtins.ts`), и реестры по имени — движок, Меню
32
33
  * вставки, «¶», справочник сочетаний и следующие за ними — читают его
33
34
  * проекцию (`projectRecords`). Правило «выключенный Инструмент убирает и
34
- * кнопку, и Схему» этим держится само: записи и Часть для движка Инструмента,
35
+ * кнопку, и Схему» этим держится само: записи и Extensions Инструмента,
35
36
  * которого нет в списке, не попадают никуда.
36
37
  *
37
- * ПЕРЕЕЗД ПОСТЕПЕННЫЙ. Встроенный Инструмент без описания стоит в списке
38
- * заглушкой `legacy` и берётся из старых реестров (`INSERT_ITEMS`, `SHORTCUTS`,
39
- * `createEditorExtensions`…). Переезд Инструмента — его записи удаляются из
40
- * старых реестров и ложатся в описание в `BUILTIN_TOOLS`; место на поверхности
41
- * при этом не меняется — его держит порядок кита этой поверхности
42
- * (`INSERT_ORDER`, `BLOCK_TYPE_ORDER`), а не реестр записей.
38
+ * У КАЖДОГО ВСТРОЕННОГО — ОПИСАНИЕ: `BUILTIN_TOOLS` полон, и
39
+ * компилятор не даст добавить имя в `TOOLS` без описания. Место записи на
40
+ * поверхности держит порядок кита этой поверхности (`INSERT_ORDER`,
41
+ * `BLOCK_TYPE_ORDER`, `SHORTCUT_ORDER`, `ENGINE_ORDER`), а не описание. Виджеты
42
+ * Панели и Меню выделения, «…», подменю Ручки и поповеры картинки остаются
43
+ * компонентами кита и спрашивают набор по имени (`tools.has`).
43
44
  */
44
45
  export interface ToolDescriptor {
45
46
  /** Имя Инструмента — то же, что в `tools`; у своего — с Префиксом потребителя. */
46
47
  name: Tool | PrefixedName;
47
48
  /**
48
- * Заглушка не переехавшего встроенного: его записи и Часть для движка — в
49
- * старых реестрах. Остальные поля у заглушки пусты, кроме `blockActions`:
50
- * Действия над Блоком переезжают отдельно от остального и
51
- * читаются у всех описаний списка, переехавших и нет (`blockActionsOf`).
52
- */
53
- legacy?: true;
54
-
55
- /**
56
- * Часть для движка: расширения Tiptap Инструмента — Node, Mark, Extension,
49
+ * Extensions: расширения Tiptap Инструмента — Node, Mark, Extension,
57
50
  * плагины ProseMirror. Функция от контекста — привилегия кита: встроенным
58
51
  * нужны мосты `RichTextEditor` (поповер ссылки, Меню вставки, `Alt+F10`).
59
52
  * Второй аргумент — Опции этого Редактора (`options`), если они есть.
60
53
  */
61
54
  engine?: (context: ToolEngineContext, options?: object) => Extensions;
62
55
  /**
63
- * Загрузчик Части для движка (`toolLoader.ts`): движок создаётся после
56
+ * Доля StarterKit: ключи `StarterKit.configure`, которые включают Блоки и
57
+ * Метки Инструмента (`bold: {}`, `heading: { levels }`). Привилегия кита.
58
+ * Доли всех описаний сливаются в ОДИН `StarterKit` (`extensions.ts`): так
59
+ * порядок Меток в сохранённом HTML и список пакетов движка остаются теми же,
60
+ * что при StarterKit целиком. Ключ, которого нет ни в одной доле, выключен.
61
+ */
62
+ starterKit?: (context: ToolEngineContext) => Partial<StarterKitOptions>;
63
+ /**
64
+ * Загрузчик Extensions (`toolLoader.ts`): движок создаётся после
64
65
  * загрузчиков всех описаний списка, и `engine` читает отданное `loaded`.
65
66
  */
66
67
  load?: ToolLoader;
@@ -134,7 +135,7 @@ export interface ToolDescriptor {
134
135
  /** Пункты виджета «¶» Панели: у него свои слова и иконки (`blockTypes.ts`). */
135
136
  blockTypes?: readonly BlockTypeItem[];
136
137
  /**
137
- * Строки справочника сочетаний для клавиш, которые вешает Часть для движка
138
+ * Строки справочника сочетаний для клавиш, которые вешают Extensions
138
139
  * (`shortcuts.ts`): у записи сочетание — её поле, а у Метки из движка — нет.
139
140
  */
140
141
  shortcuts?: readonly ShortcutCommand[];
@@ -189,8 +190,16 @@ export interface ToolBridge {
189
190
  /** Символ-триггер Инструмента в списке описаний: публичная запись и имя владельца для сообщений. */
190
191
  export type TriggerRecord = CustomTrigger & { tool: string };
191
192
 
192
- /** Что Часть для движка встроенного получает от `RichTextEditor`. */
193
- export type ToolEngineContext = Omit<EditorExtensionOptions, "tools">;
193
+ /**
194
+ * Что Extensions встроенного Инструмента получают от `RichTextEditor`: мосты и —
195
+ * вместо всего набора — вопрос `has`. Связи между Инструментами (уровни
196
+ * заголовка при `heading1`, Обёртка в колонке Раскладки, очистка формата)
197
+ * спрашивают его, а не список описаний.
198
+ */
199
+ export type ToolEngineContext = Omit<EditorExtensionOptions, "tools"> & {
200
+ /** Включён ли Инструмент кита в этом Редакторе. */
201
+ has: (tool: Tool) => boolean;
202
+ };
194
203
 
195
204
  type EditorMessages = UiMessages["editor"];
196
205
 
@@ -276,24 +285,38 @@ export type ToolButton = ToolButtonFields &
276
285
  * Кнопка встроенного Инструмента: публичная форма и привилегии кита, которых
277
286
  * своей кнопке не дают.
278
287
  */
279
- export type KitToolButton = ToolButton & {
280
- /**
281
- * Группа справочника сочетаний. Нет — строка встаёт в группу Инструмента без
282
- * заголовка после групп кита, как у своего Инструмента.
283
- */
284
- shortcutGroup?: ShortcutGroup;
285
- /** Рамка поповера, если она не умолчание кита (у кнопки, по содержимому). */
286
- popoverFrame?: {
288
+ export type KitToolButton = ToolButton &
289
+ (
290
+ | {
291
+ /**
292
+ * Сочетание, которое вешает не кит, а Extensions (`undoRedo` у
293
+ * StarterKit, `Mod-B` у Метки): только подпись — подсказка,
294
+ * `aria-keyshortcuts` и строка справочника. Взаимоисключимо с
295
+ * `shortcut`: та и вешает клавишу, и подписывает её, и две привязки
296
+ * одной клавиши спорили бы.
297
+ */
298
+ keys: readonly string[];
299
+ shortcut?: undefined;
300
+ }
301
+ | { keys?: undefined }
302
+ ) & {
287
303
  /**
288
- * `selection` — рамка всегда у выделения, и щелчком по кнопке тоже: поповер
289
- * правит выделенный текст, и стоит у него при любом способе открыть
290
- * (поповер ссылки).
304
+ * Группа справочника сочетаний. Нет — строка встаёт в группу Инструмента без
305
+ * заголовка после групп кита, как у своего Инструмента.
291
306
  */
292
- anchor?: "selection";
293
- /** Классы размера и отступов рамки вместо умолчания `w-auto p-2`. */
294
- className?: string;
307
+ shortcutGroup?: ShortcutGroup;
308
+ /** Рамка поповера, если она не умолчание кита (у кнопки, по содержимому). */
309
+ popoverFrame?: {
310
+ /**
311
+ * `selection` — рамка всегда у выделения, и щелчком по кнопке тоже: поповер
312
+ * правит выделенный текст, и стоит у него при любом способе открыть
313
+ * (поповер ссылки).
314
+ */
315
+ anchor?: "selection";
316
+ /** Классы размера и отступов рамки вместо умолчания `w-auto p-2`. */
317
+ className?: string;
318
+ };
295
319
  };
296
- };
297
320
 
298
321
  /** Действие над Блоком: пункт меню Ручки Блока, в секции Инструмента перед «Удалить». */
299
322
  export interface BlockAction {
@@ -338,9 +361,9 @@ export interface TextCardRecord {
338
361
  * Развёрнутый набор Инструментов: список описаний и — как `Set` — имена
339
362
  * включённых Инструментов кита.
340
363
  *
341
- * Набор имён — тоже проекция списка: реестры, которые ещё фильтруют свои
342
- * записи по имени (`tools.has("table")`), читают его без правок. Свои
343
- * Инструменты есть только в списке описаний.
364
+ * Набор имён — тоже проекция списка: виджеты кита, которые спрашивают набор по
365
+ * имени (`tools.has("table")`), читают его без правок. Свои Инструменты есть
366
+ * только в списке описаний.
344
367
  */
345
368
  export interface ToolList extends ReadonlySet<Tool> {
346
369
  /**
@@ -350,8 +373,8 @@ export interface ToolList extends ReadonlySet<Tool> {
350
373
  */
351
374
  readonly error?: string;
352
375
  /**
353
- * Описания включённых Инструментов: встроенные в порядке кита (у не
354
- * переехавших — заглушки `legacy`), за ними свои в порядке `tools`.
376
+ * Описания включённых Инструментов: встроенные в порядке кита, за ними
377
+ * свои в порядке `tools`.
355
378
  */
356
379
  readonly descriptors: readonly ToolDescriptor[];
357
380
  }
@@ -380,11 +403,6 @@ export function toolOptions<Name extends ConfigurableToolName>(
380
403
  return descriptor?.options as RichTextTools[Name]["options"] | undefined;
381
404
  }
382
405
 
383
- /** Описания переехавших Инструментов набора — в порядке списка. */
384
- export function describedTools(tools: ToolList): readonly ToolDescriptor[] {
385
- return tools.descriptors.filter((descriptor) => !descriptor.legacy);
386
- }
387
-
388
406
  /**
389
407
  * Действия над Блоком набора — проекция списка описаний, в порядке кита. Фильтра
390
408
  * по Блоку здесь нет: имя и предикат видимости спрашивает меню Ручки на
@@ -429,7 +447,6 @@ export function blockActionVisible(
429
447
  * - `order` — место встроенных Инструментов на поверхности, привилегия кита.
430
448
  * Инструменты набора вне него идут следом, в порядке списка: так встаёт
431
449
  * запись, у которой своего места на поверхности нет.
432
- * - `legacy` — старый реестр: записи Инструмента, ещё не переехавшего на описание.
433
450
  * - `own` — поле описания с записями этой поверхности.
434
451
  *
435
452
  * Фильтра по включённым Инструментам здесь нет: его держит сам список.
@@ -437,14 +454,10 @@ export function blockActionVisible(
437
454
  export function projectRecords<R>(
438
455
  tools: ToolList,
439
456
  order: readonly Tool[],
440
- legacy: (name: Tool) => readonly R[] | undefined,
441
457
  own: (descriptor: ToolDescriptor) => readonly R[] | undefined
442
458
  ): R[] {
443
459
  const list = tools.descriptors;
444
460
  const placed = order.flatMap((name) => list.filter((descriptor) => descriptor.name === name));
445
461
  const rest = list.filter((descriptor) => !(order as readonly string[]).includes(descriptor.name));
446
- return [...placed, ...rest].flatMap(
447
- // Заглушка `legacy` бывает только у встроенного: имя — из `Tool`.
448
- (descriptor) => (descriptor.legacy ? legacy(descriptor.name as Tool) : own(descriptor)) ?? []
449
- );
462
+ return [...placed, ...rest].flatMap((descriptor) => own(descriptor) ?? []);
450
463
  }
@@ -3,17 +3,17 @@
3
3
  import { devError } from "../../lib/dev-warn.js";
4
4
 
5
5
  /*
6
- Загрузчики Частей для движка (ADR-0058, п. 1.3 и 11.3–11.6).
6
+ Загрузчики Extensions (ADR-0058, п. 1.3 и 11.3–11.6).
7
7
 
8
8
  Промис кэшируется на самой функции загрузчика, а не на Редакторе: второй
9
9
  Редактор на странице с тем же Инструментом ничего не ждёт и загрузчик не
10
10
  зовёт. Упавший остаётся в кэше упавшим — иначе каждая перерисовка звала бы
11
11
  его снова; заново его зовёт только «Повторить» (`retry`). Механизм общий:
12
- встроенный Инструмент с ленивой Частью для движка кладёт свой загрузчик в
12
+ встроенный Инструмент с ленивыми Extensions кладёт свой загрузчик в
13
13
  то же поле описания (`ToolDescriptor.load`).
14
14
  */
15
15
 
16
- /** Загрузчик Части для движка: функция с динамическим импортом. */
16
+ /** Загрузчик Extensions: функция с динамическим импортом. */
17
17
  export type ToolLoader = () => Promise<unknown>;
18
18
 
19
19
  /** Ход загрузки: промис не отклоняется никогда, итог — в `done` или `failed`. */
@@ -59,10 +59,25 @@ export interface TocToolOptions {
59
59
  listWidth: number | string;
60
60
  }
61
61
 
62
+ /**
63
+ * Опции Инструмента `details` (по расширению Details из Tiptap, ADR-0048).
64
+ */
65
+ export interface DetailsToolOptions {
66
+ /**
67
+ * Пишется ли открытость Блока в Документ: открытый Блок сохраняется с
68
+ * `open` и открытым же отдаётся в `RichTextView`. По умолчанию `false`, как
69
+ * у Tiptap: раскрытый при правке Блок не публикуется раскрытым без спроса,
70
+ * открытость живёт только в показе (в Редакторе и в Просмотре читатель
71
+ * открывает Блок сам). Со `true` Блок, вставленный из Меню вставки, сразу
72
+ * открыт и пишет `open`, а `open` из `value` и вставки читается.
73
+ */
74
+ persist: boolean;
75
+ }
76
+
62
77
  /**
63
78
  * Типы Инструментов Редактора по имени: что экран задаёт при настройке (`options`),
64
79
  * что читает (`state`) и что вызывает (`commands`). Расширяемый интерфейс, как
65
- * `Storage` и `Commands` у Tiptap: кит объявляет в нём `search` и `toc`, а
80
+ * `Storage` и `Commands` у Tiptap: кит объявляет в нём `search`, `toc` и `details`, а
66
81
  * потребитель дописывает свои Инструменты через declaration merging:
67
82
  *
68
83
  * ```ts
@@ -92,6 +107,10 @@ export interface RichTextTools {
92
107
  state: TocToolState;
93
108
  commands: TocToolCommands;
94
109
  };
110
+ // Только Опции: Состояния и Команд у сворачиваемого Блока нет.
111
+ details: {
112
+ options: DetailsToolOptions;
113
+ };
95
114
  }
96
115
 
97
116
  /**
@@ -182,7 +201,7 @@ export function mergeOptions(base: object, given: object): object {
182
201
  * опции (из JS без типов) заменяется умолчанием, в разработке — один
183
202
  * `console.error`; опции своего Инструмента проверяет его автор.
184
203
  *
185
- * Встроенный — только Инструмент с опциями: `search` и `toc`. Серверно-безопасна.
204
+ * Встроенный — только Инструмент с опциями: `search`, `toc` и `details`. Серверно-безопасна.
186
205
  */
187
206
  export function tool<Name extends ConfigurableToolName>(
188
207
  name: Name,
@@ -15,7 +15,7 @@
15
15
  * новым именем для него не ломающее. Зато имя без реализации стало бы
16
16
  * молчаливой ловушкой: `tools={["x"]}` компилировался бы и ничего не давал.
17
17
  * Так пришли `link`, `color`, `highlight`, `align`, `table`, `image`, `emoji`,
18
- * `layout`, `wrapper` и `source`; зарезервированных имён сейчас нет.
18
+ * `layout`, `wrapper`, `details` и `source`; зарезервированных имён сейчас нет.
19
19
  *
20
20
  * `heading1` — заголовок первого уровня, надстройка над `heading`:
21
21
  * с ним уровни Блока — 1–4, без него — 2–4. Отдельным инструментом он стоит
@@ -44,6 +44,14 @@
44
44
  * Выключенный инструмент разворачивает Обёртку из вставки и `value` в Блоки
45
45
  * подряд. В `basic` её нет по той же причине, что и Раскладки.
46
46
  *
47
+ * `details` — сворачиваемый Блок: заголовок (`summary`) и содержимое из Блоков
48
+ * (`details.ts`), API по расширению Details из Tiptap (ADR-0048: заказчика при
49
+ * выпуске не было). Кнопки Панели у него нет — Меню вставки и меню ручки.
50
+ * Выключенный инструмент разворачивает Блок из вставки и `value`: заголовок
51
+ * становится абзацем, содержимое идёт Блоками следом. Есть в `full`, в `basic`
52
+ * нет — по той же причине, что у Раскладки и Обёртки. Открытость в Документ
53
+ * пишет Опция `tool("details", { persist: true })`.
54
+ *
47
55
  * `source` — HTML-режим: кнопка «HTML» фиксированной Панели меняет
48
56
  * Документ на поле с его исходным HTML, а выход применяет правку через Схему,
49
57
  * как входящий `value`. Своей Схемы у него нет, как у `clearFormatting`:
@@ -88,6 +96,7 @@ export const TOOLS = [
88
96
  "image",
89
97
  "layout",
90
98
  "wrapper",
99
+ "details",
91
100
  "emoji",
92
101
  "source",
93
102
  "search",
@@ -7,7 +7,7 @@ import { customToolOf } from "./toolDefinition.js";
7
7
 
8
8
  Модуль серверный: `RichTextView` выбирает из `tools` лишь то, что нужно
9
9
  острову, и отдаёт клиентскому компоненту ссылки на Острова — описание целиком
10
- (Часть для движка, функции) через границу RSC не пройдёт. Выбор идёт по
10
+ (Extensions, функции) через границу RSC не пройдёт. Выбор идёт по
11
11
  Документу, как `hasCodeBlocks` у «Копировать»: Остров Блока, которого в
12
12
  Документе нет, в клиентский граф не попадает, а значит, и его код не грузится.
13
13
  */
@@ -2,13 +2,30 @@
2
2
 
3
3
  import Blockquote from "@tiptap/extension-blockquote";
4
4
  import HorizontalRule from "@tiptap/extension-horizontal-rule";
5
- import { DividerIcon, QuoteIcon, UnwrapIcon } from "../../icons.js";
5
+ import { DividerIcon, QuoteIcon } from "../../icons.js";
6
6
  import { turnIntoTypes } from "../core/blockTypes.js";
7
7
  import type { InsertItem } from "../core/insertItems.js";
8
- import type { ShortcutCommand } from "../core/shortcuts.js";
8
+ import { INSERT_MENU_TOOL } from "../core/insertMenuTool.js";
9
+ import { SHORTCUT_KEYS, type ShortcutCommand } from "../core/shortcuts.js";
10
+ import { ALIGN_TOOL, COLOR_TOOL, HIGHLIGHT_TOOL } from "./color/colorTools.js";
9
11
  import { CODE_BLOCK_TOOL } from "./code/codeBlockTool.js";
12
+ import { DETAILS_TOOL } from "./details/detailsTool.js";
10
13
  import { EMOJI_TOOL } from "./emoji/emojiTool.js";
14
+ import { HEADING1_TOOL, HEADING_TOOL } from "./heading/headingTool.js";
15
+ import { HISTORY_TOOL } from "./history/historyTool.js";
16
+ import { IMAGE_TOOL } from "./image/imageTool.js";
17
+ import { LAYOUT_TOOL } from "./layout/layoutTool.js";
11
18
  import { LINK_TOOL } from "./link/linkTool.js";
19
+ import { BULLET_LIST_TOOL, ORDERED_LIST_TOOL, TASK_LIST_TOOL } from "./lists/listTools.js";
20
+ import {
21
+ BOLD_TOOL,
22
+ CODE_TOOL,
23
+ ITALIC_TOOL,
24
+ STRIKE_TOOL,
25
+ SUBSCRIPT_TOOL,
26
+ SUPERSCRIPT_TOOL,
27
+ UNDERLINE_TOOL,
28
+ } from "./marks/markTools.js";
12
29
  import { devError } from "../../lib/dev-warn.js";
13
30
  import type { ToolEntry } from "../core/customTool.js";
14
31
  import { describeCustomTool, describeDefinition } from "../core/customToolEngine.js";
@@ -18,14 +35,14 @@ import { SEARCH_TOOL } from "./search/searchTool.js";
18
35
  import { TABLE_TOOL } from "./table/tableTool.js";
19
36
  import { TOC_TOOL } from "./toc/tocTool.js";
20
37
  import { resolveTools, TOOLS, type RichTextEditorPreset, type Tool } from "../core/tools.js";
21
- import { unwrapWrapperAt, WRAPPER_NODE } from "./wrapper/wrapper.js";
38
+ import { WRAPPER_TOOL } from "./wrapper/wrapperTool.js";
22
39
 
23
40
  /** Сочетание цитаты: одна запись на справочник, Меню вставки и «¶». */
24
41
  const BLOCKQUOTE_SHORTCUT: ShortcutCommand = {
25
42
  id: "blockquote",
26
43
  group: "blockType",
27
44
  tool: "blockquote",
28
- keys: ["mod", "shift", "B"],
45
+ keys: SHORTCUT_KEYS.blockquote,
29
46
  label: (m) => m.blockquote,
30
47
  };
31
48
 
@@ -43,47 +60,27 @@ const BLOCKQUOTE_ITEMS: readonly InsertItem[] = [
43
60
  ];
44
61
 
45
62
  /**
46
- * Описания встроенных Инструментов, переехавших со старых реестров
47
- * (ADR-0058, п. 10; тип — `toolDescriptor.ts`).
63
+ * Описания встроенных Инструментов (ADR-0058, п. 10; тип — `toolDescriptor.ts`).
48
64
  *
49
- * Сюда Инструмент переезжает целиком: его записи удаляются из старых
50
- * реестров, и с этой минуты он берётся только из описания. Кого здесь нет, тот
51
- * стоит в развёрнутом списке заглушкой `legacy` и по-прежнему живёт в старых
52
- * реестрах. `image`, `layout` и виджеты Панели переезжают постепенно, и выпуск
53
- * их не ждёт.
65
+ * Запись полная, по каждому имени `TOOLS`: встроенный Инструмент
66
+ * берётся только из описания, старых реестров по имени нет, и компилятор не
67
+ * пропустит новое имя без описания. Виджеты Панели и Меню выделения остаются
68
+ * компонентами кита и спрашивают набор по имени.
54
69
  */
55
- export const BUILTIN_TOOLS: Partial<Record<Tool, ToolDescriptor>> = {
70
+ export const BUILTIN_TOOLS: Record<Tool, ToolDescriptor> = {
56
71
  search: SEARCH_TOOL,
57
72
  // Оглавление несёт только Опции: полоса и список рисуются в `RichTextEditor`.
58
73
  toc: TOC_TOOL,
74
+ layout: LAYOUT_TOOL,
75
+ color: COLOR_TOOL,
76
+ highlight: HIGHLIGHT_TOOL,
77
+ align: ALIGN_TOOL,
78
+ wrapper: WRAPPER_TOOL,
79
+ details: DETAILS_TOOL,
59
80
  /*
60
- Обёртка переехала только Действием над Блоком, поэтому
61
- `legacy`: её узел, вставка и сочетания остаются в старых реестрах. «Тон ›» —
62
- меню-виджет, своего Инструмента у него нет, и он собирается в Ручке по-старому.
63
- */
64
- wrapper: {
65
- name: "wrapper",
66
- legacy: true,
67
- blockActions: [
68
- {
69
- id: "unwrap",
70
- label: (messages) => messages.unwrap,
71
- icon: UnwrapIcon,
72
- blocks: [WRAPPER_NODE],
73
- // Одна правка: Блоки встают на место Обёртки, отмена — одна.
74
- run: (editor, pos) =>
75
- void editor
76
- .chain()
77
- .focus()
78
- .command(({ tr }) => unwrapWrapperAt(tr, pos))
79
- .run(),
80
- },
81
- ],
82
- },
83
- /*
84
- Проверочный набор: Инструмент с пунктом вставки и Инструмент
85
- с «Превратить в». Поля `insertItems` и `engine` совпадают с будущим
86
- публичным описанием, чтобы его форма проверялась кодом кита.
81
+ Разделитель и цитата: Инструмент с пунктом вставки и Инструмент с
82
+ «Превратить в». Поля `insertItems` и `engine` те же, что у публичного
83
+ описания своего Инструмента: форму проверяет код кита.
87
84
  */
88
85
  horizontalRule: {
89
86
  name: "horizontalRule",
@@ -109,11 +106,49 @@ export const BUILTIN_TOOLS: Partial<Record<Tool, ToolDescriptor>> = {
109
106
  // Клавишу вешает расширение Tiptap; строка справочника — подпись к ней.
110
107
  shortcuts: [BLOCKQUOTE_SHORTCUT],
111
108
  },
109
+ history: HISTORY_TOOL,
110
+ bold: BOLD_TOOL,
111
+ italic: ITALIC_TOOL,
112
+ underline: UNDERLINE_TOOL,
113
+ strike: STRIKE_TOOL,
114
+ code: CODE_TOOL,
115
+ superscript: SUPERSCRIPT_TOOL,
116
+ subscript: SUBSCRIPT_TOOL,
117
+ heading: HEADING_TOOL,
118
+ heading1: HEADING1_TOOL,
112
119
  link: LINK_TOOL,
120
+ bulletList: BULLET_LIST_TOOL,
121
+ orderedList: ORDERED_LIST_TOOL,
122
+ taskList: TASK_LIST_TOOL,
123
+ image: IMAGE_TOOL,
113
124
  emoji: EMOJI_TOOL,
125
+ insertMenu: INSERT_MENU_TOOL,
126
+ /*
127
+ Своего расширения у «Очистить форматирование» нет: клавишу `⌘\` держит
128
+ `BlockCommands` ядра (он спрашивает `has("clearFormatting")`), а кнопка в
129
+ «…» — виджет кита. В описании — только строка справочника.
130
+ */
131
+ clearFormatting: {
132
+ name: "clearFormatting",
133
+ shortcuts: [
134
+ {
135
+ id: "clearFormatting",
136
+ group: "clear",
137
+ tool: "clearFormatting",
138
+ keys: SHORTCUT_KEYS.clearFormatting,
139
+ label: (m) => m.clearFormatting,
140
+ },
141
+ ],
142
+ },
143
+ /*
144
+ HTML-режим — виджет «HTML» Панели и режим `RichTextEditor`, показ по
145
+ `has("source")`. Ни Схемы, ни записей у Инструмента нет, но описание
146
+ есть у каждого встроенного — даже пустое.
147
+ */
148
+ source: { name: "source" },
114
149
  /*
115
150
  Ленивые встроенные (ADR-0058, п. 11.1): их нет в `basic`, а
116
- Часть для движка тяжелее 10 кБ gzip, поэтому она грузится загрузчиком
151
+ Extensions тяжелее 10 кБ gzip, поэтому они грузятся загрузчиком
117
152
  (`load`) — тем же, что у своего Инструмента.
118
153
  */
119
154
  codeBlock: CODE_BLOCK_TOOL,
@@ -184,7 +219,7 @@ export function expandTools(
184
219
  );
185
220
  const builtin = TOOLS.filter((name) => names.has(name)).map((name) =>
186
221
  resolveOptions(
187
- BUILTIN_TOOLS[name] ?? { name, legacy: true },
222
+ BUILTIN_TOOLS[name],
188
223
  configured.find((entry) => entry.name === name)
189
224
  )
190
225
  );
@@ -5,7 +5,7 @@ import { CodeBlockIcon } from "../../../icons.js";
5
5
  // Только тип: статический импорт модуля втянул бы чанк в «старт».
6
6
  import type * as CodeBlockChunk from "./codeBlock.js";
7
7
  import { normalizeCodeLanguage, type CodeLanguageId } from "./codeLanguages.js";
8
- import { shortcutKeys } from "../../core/shortcuts.js";
8
+ import { SHORTCUT_KEYS, shortcutKeys } from "../../core/shortcuts.js";
9
9
  import type { ToolDescriptor } from "../../core/toolDescriptor.js";
10
10
  import { loaded } from "../../core/toolLoader.js";
11
11
 
@@ -57,22 +57,30 @@ const CodeBlockShape = Node.create({
57
57
 
58
58
  /**
59
59
  * Блок кода на описании Инструмента (ADR-0058, п. 11.1): его нет в
60
- * `basic`, а Часть для движка тяжелее 10 кБ, поэтому она грузится тем же
60
+ * `basic`, а Extensions тяжелее 10 кБ, поэтому они грузятся тем же
61
61
  * загрузчиком, что у своего Инструмента. До загрузки Редактор с блоком кода —
62
62
  * Скелет (`RichTextEditor.tsx`).
63
63
  *
64
- * Переехали Часть для движка и пункты Меню вставки. Сочетание `Mod+Alt+C`
65
- * вешает расширение, и его строка справочника осталась в `SHORTCUTS`: на неё
66
- * ссылаются пункты меню (`shortcutKeys`), «Формат ›» и место строки цитаты
67
- * (`MOVED_BEFORE`). Кнопка «Блок кода» меню «…» и «¶» — виджеты Панели, они
68
- * переезжают позже (ADR-0058, «Последствия»).
64
+ * Сочетание `Mod+Alt+C` вешает расширение, поэтому строка справочника — только
65
+ * подпись (`shortcuts`); клавиши пунктов меню и «Формат ›» берутся из
66
+ * `SHORTCUT_KEYS`, место строки — из `SHORTCUT_ORDER`. Кнопка «Блок кода» меню
67
+ * «…» и «¶» — виджеты Панели и остаются у кита.
69
68
  */
70
69
  export const CODE_BLOCK_TOOL: ToolDescriptor = {
71
70
  name: "codeBlock",
72
71
  load: loadCodeBlock,
72
+ shortcuts: [
73
+ {
74
+ id: "codeBlock",
75
+ group: "blockType",
76
+ tool: "codeBlock",
77
+ keys: SHORTCUT_KEYS.codeBlock,
78
+ label: (m) => m.codeBlock,
79
+ },
80
+ ],
73
81
  /*
74
82
  `Alt+F10` блока кода (в кнопку языка) обязан сработать раньше общего
75
- `ToolbarFocus`: Части для движка описаний регистрируются после него
83
+ `ToolbarFocus`: Extensions описаний регистрируются после него
76
84
  (`extensions.ts`), а из равных по приоритету Tiptap первым опрашивает
77
85
  зарегистрированное последним.
78
86
  */
@@ -0,0 +1,68 @@
1
+ "use client";
2
+
3
+ import { SHORTCUT_KEYS } from "../../core/shortcuts.js";
4
+ import type { ToolDescriptor } from "../../core/toolDescriptor.js";
5
+ import {
6
+ HighlightExtension,
7
+ TEXT_COLOR_EXTENSIONS,
8
+ TextAlignExtension,
9
+ } from "./styleExtensions.js";
10
+
11
+ /*
12
+ Цвет, маркер и выравнивание пишут `data-color` и `data-align`, а не `style`
13
+ (`styleExtensions.ts`; единственное исключение — цвет автора, `customColor.ts`):
14
+ выключенный Инструмент не отдаёт ни атрибута, ни правила разбора, и такой
15
+ атрибут из вставки отбрасывается.
16
+
17
+ Виджеты — «Маркер» на Панели, «Цвет» и «Маркер» в Меню выделения
18
+ (`ColorMenus.tsx`, `SelectionBubble.tsx`) и ленивый поповер своего цвета —
19
+ остаются у кита, показ по `has(name)`. Сочетаний и пунктов вставки у цвета
20
+ и маркера нет, поэтому их описания — только Extensions.
21
+ */
22
+
23
+ /** Цвет текста: Метка `textStyle` с `data-color` и цвет автора. */
24
+ export const COLOR_TOOL: ToolDescriptor = {
25
+ name: "color",
26
+ engine: () => TEXT_COLOR_EXTENSIONS,
27
+ };
28
+
29
+ /** Маркер: Метка `highlight` с `data-color` (несколько цветов). */
30
+ export const HIGHLIGHT_TOOL: ToolDescriptor = {
31
+ name: "highlight",
32
+ engine: () => [HighlightExtension],
33
+ };
34
+
35
+ /**
36
+ * Выравнивание: `data-align` на абзаце и заголовке.
37
+ *
38
+ * Клавиши `Mod-Shift-L/E/R` вешает само расширение, а три строки справочника —
39
+ * подпись к ним. Меню «Выравнивание ▾» (`AlignMenu`, `FormatMenus.tsx`) — виджет
40
+ * кита, он читает те же сочетания из `SHORTCUT_KEYS`.
41
+ */
42
+ export const ALIGN_TOOL: ToolDescriptor = {
43
+ name: "align",
44
+ engine: () => [TextAlignExtension],
45
+ shortcuts: [
46
+ {
47
+ id: "alignLeft",
48
+ group: "align",
49
+ tool: "align",
50
+ keys: SHORTCUT_KEYS.alignLeft,
51
+ label: (m) => m.alignLeft,
52
+ },
53
+ {
54
+ id: "alignCenter",
55
+ group: "align",
56
+ tool: "align",
57
+ keys: SHORTCUT_KEYS.alignCenter,
58
+ label: (m) => m.alignCenter,
59
+ },
60
+ {
61
+ id: "alignRight",
62
+ group: "align",
63
+ tool: "align",
64
+ keys: SHORTCUT_KEYS.alignRight,
65
+ label: (m) => m.alignRight,
66
+ },
67
+ ],
68
+ };