@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
@@ -8,7 +8,7 @@ source: src/editor/index.ts
8
8
 
9
9
  ## Кратко для AI
10
10
 
11
- Карта того, чем продукт расширяет `RichTextEditor` из `@olddevs/ui/editor`, и граница стабильности этого API. Единица расширения — **свой Инструмент** (`CustomTool`): описание одной возможности Редактора, которое передаётся в `tools` вперемешку с именами кита. Он несёт Блоки и Метки с правилами Схемы (это данные), Часть для движка (расширения Tiptap, сразу или Загрузчиком) и UI-записи: кнопки, Пункты вставки, Действия над Блоком, Карточки у текста. Есть и свой Символ-триггер (`trigger`): `@` для упоминаний открывает тот же список, что `/`. Код экрана управляет полем через Хэндл (`ref`) и хук `useRichTextEditorState`, узнаёт о Готовности и Фокусе поля колбэками `onReady`, `onFocus` и `onBlur`, кладёт своё в Строку статуса (`status`) и сообщает об ошибке командой `showNotice`. У Инструмента три типизированные части, как `options`, `storage` и `commands` у расширения Tiptap: **Опции** (`tool(name, options)` у встроенных `search` и `toc`, `defineTool` у своего), **Состояние** (хук `useRichTextToolState`) и **Команды** (`handle.tool(name)`); их типы собраны в расширяемой карте `RichTextTools`, куда свой Инструмент дописывается через `declare module "@olddevs/ui/editor"`. Тот же массив `tools` понимают серверная очистка (`sanitizeRichText`, `richTextSchema`) и `RichTextView` с Островами. Всё перечисленное подчиняется semver кита с первого выпуска, HTML-соглашение заморожено, мажор Tiptap — мажор кита (раздел «Стабильность»). Подробности каждого пункта живут на странице [RichTextEditor](../components/inputs/rich-text-editor.md); здесь — карта, граница контракта и рецепты.
11
+ Карта того, чем продукт расширяет `RichTextEditor` из `@olddevs/ui/editor`, и граница стабильности этого API. Единица расширения — **свой Инструмент** (`CustomTool`): описание одной возможности Редактора, которое передаётся в `tools` вперемешку с именами кита. Он несёт Блоки и Метки с правилами Схемы (это данные), Extensions (расширения Tiptap, сразу или Загрузчиком) и UI-записи: кнопки, Пункты вставки, Действия над Блоком, Карточки у текста. Есть и свой Символ-триггер (`trigger`): `@` для упоминаний открывает тот же список, что `/`. Код экрана управляет полем через Хэндл (`ref`) и хук `useRichTextEditorState`, узнаёт о Готовности и Фокусе поля колбэками `onReady`, `onFocus` и `onBlur`, кладёт своё в Строку статуса (`status`) и сообщает об ошибке командой `showNotice`. У Инструмента три типизированные части, как `options`, `storage` и `commands` у расширения Tiptap: **Опции** (`tool(name, options)` у встроенных `search` и `toc`, `defineTool` у своего), **Состояние** (хук `useRichTextToolState`) и **Команды** (`handle.tool(name)`); их типы собраны в расширяемой карте `RichTextTools`, куда свой Инструмент дописывается через `declare module "@olddevs/ui/editor"`. Тот же массив `tools` понимают серверная очистка (`sanitizeRichText`, `richTextSchema`) и `RichTextView` с Островами. Всё перечисленное подчиняется semver кита с первого выпуска, HTML-соглашение заморожено, мажор Tiptap — мажор кита (раздел «Стабильность»). Подробности каждого пункта живут на странице [RichTextEditor](../components/inputs/rich-text-editor.md); здесь — карта, граница контракта и рецепты.
12
12
 
13
13
  ```tsx
14
14
  import {
@@ -39,39 +39,39 @@ import { sanitizeRichText } from "@olddevs/ui/editor/schema";
39
39
 
40
40
  - Нужное уже есть среди Инструментов кита ([«Инструменты и Пресеты»](../components/inputs/rich-text-editor.md#инструменты-и-пресеты)): передайте имя в `tools`, своё не пишите.
41
41
  - Чтобы спрятать встроенную кнопку или пункт, сузьте `tools`. Подменить встроенный Инструмент по имени нельзя: выключите его и поставьте свой под своим именем.
42
- - Опции есть только у `search` и `toc`; `tool("bold", …)` не компилируется. Опции остальных встроенных добавляются по одному, когда о них попросят. Поменять поведение встроенного Инструмента подменой нельзя (`extend()` как у Tiptap нет): описания встроенных наружу не выходят.
42
+ - Опции есть только у `search`, `toc` и `details`; `tool("bold", …)` не компилируется. Опции остальных встроенных добавляются по одному, когда о них попросят. Поменять поведение встроенного Инструмента подменой нельзя (`extend()` как у Tiptap нет): описания встроенных наружу не выходят.
43
43
  - Сменить Опции у смонтированного поля нельзя: они читаются один раз, при создании движка, вместе с `tools`. Перемонтируйте поле ключом.
44
44
  - Произвольный React в строке Панели и в меню нельзя: кит рисует записи сам, React допускается только в содержимом поповера (кнопки, Пункта вставки, Действия над Блоком) и в Карточке у текста.
45
- - Во вставку и drop пропом вмешаться нельзя: это делает Часть для движка своего Инструмента ([«Порядок вставки и drop»](../components/inputs/rich-text-editor.md#порядок-вставки-и-drop)).
45
+ - Во вставку и drop пропом вмешаться нельзя: это делают Extensions своего Инструмента ([«Порядок вставки и drop»](../components/inputs/rich-text-editor.md#порядок-вставки-и-drop)).
46
46
  - Чего нет, пока потребители об этом не попросили (ADR-0058, раздел 13): `insertImage` и HTML выделения в Хэндле, `onSelectionChange`, `onPaste`, `onDrop`, `onEmpty`, `onDestroy`, события истории, сигнал об удалённых картинках, запертые Блоки шаблона, вид записи «меню», Карточки и Строка статуса в `RichTextView`. До тех пор у экрана есть `ref.current.editor` и его события.
47
47
  - Свои символы-триггеры вроде `@` в этот перечень больше не входят: они поддержаны полем `trigger` Инструмента (ADR-0064, раздел «Символ-триггер» ниже).
48
48
 
49
49
  ## Карта API
50
50
 
51
- | Что | Где в API | Подробно |
52
- | ------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
53
- | Свой Инструмент | `CustomTool` в `tools`; Пресеты — `RICH_TEXT_PRESETS` | [«Свой Инструмент»](../components/inputs/rich-text-editor.md#свой-инструмент) |
54
- | Правила Схемы и HTML-соглашение | `blocks` (`CustomBlock`), `marks` (`CustomMark`), `CustomAttribute`, `PrefixedName` | [«Свой Инструмент»](../components/inputs/rich-text-editor.md#свой-инструмент) |
55
- | Кнопка | `buttons` (`CustomToolButton`, `CustomToolPopover`, `CustomToolLabel`) | [«Кнопка своего Инструмента»](../components/inputs/rich-text-editor.md#кнопка-своего-инструмента) |
56
- | Пункт вставки, Действие над Блоком | `insertItems` (`CustomInsertItem`), `blockActions` (`CustomBlockAction`) | [«Пункты вставки и Действия над Блоком»](../components/inputs/rich-text-editor.md#пункты-вставки-и-действия-над-блоком) |
57
- | Символ-триггер | `trigger` (`CustomTrigger`, `TriggerItem`, `TriggerContext`) | [«Свой символ-триггер»](../components/inputs/rich-text-editor.md#свой-символ-триггер) |
58
- | Карточка у текста | `textCards` (`CustomTextCard`), помощник `markRange` | [«Карточка у текста»](../components/inputs/rich-text-editor.md#карточка-у-текста) |
59
- | Хэндл и своя панель | `RichTextEditorHandle`, `useRichTextEditorState(ref, selector)` | [«Хэндл (`ref`) и своя панель вне поля»](../components/inputs/rich-text-editor.md#хэндл-ref-и-своя-панель-вне-поля) |
60
- | Опции встроенного Инструмента | `tool(name, options)` → `ConfiguredTool` для `search` и `toc`; `SearchToolOptions`, `TocToolOptions`, `TocLevel` | [«Опции встроенного Инструмента: `tool()`»](../components/inputs/rich-text-editor.md#опции-встроенного-инструмента-tool) |
61
- | Свой Инструмент с Опциями, Состоянием, Командами | `defineTool({ name, defaults, create, state, commands })` → `ToolDefinition`, `tool(definition, options)` | [«Опции, Состояние и Команды»](editor-extension.md#опции-состояние-и-команды) |
62
- | Типы Опций, Состояния и Команд | расширяемый интерфейс `RichTextTools` | [«Карта `RichTextTools` и declaration merging»](editor-extension.md#карта-richtexttools-и-declaration-merging) |
63
- | Состояние Инструмента | `useRichTextToolState(ref, name, selector?)`, `SearchToolState`, `TocToolState`, `TocToolHeading` | [«Состояние Инструмента: `useRichTextToolState`»](../components/inputs/rich-text-editor.md#состояние-инструмента-userichtexttoolstate) |
64
- | Команды Инструмента | `handle.tool(name)`, `SearchToolCommands`, `TocToolCommands` | [«Команды Инструмента: `handle.tool()`»](../components/inputs/rich-text-editor.md#команды-инструмента-handletool) |
65
- | События | `onReady`, `onFocus`, `onBlur`; вставка и drop — Частью для движка | [«События»](../components/inputs/rich-text-editor.md#события) |
66
- | Строка статуса и Сообщение поля | проп `status`; команда движка `showNotice`, тип `FieldNotice` | [«Строка статуса и Сообщение поля»](../components/inputs/rich-text-editor.md#строка-статуса-и-сообщение-поля) |
67
- | Загрузчик, Готовность и Скелет | `engine: () => import(…)` | [«Загрузчик Части для движка»](../components/inputs/rich-text-editor.md#загрузчик-части-для-движка), [«Скелет»](../components/inputs/rich-text-editor.md#скелет) |
68
- | Текст атома: `getText`, `maxLength`, пустота | `renderText` Части для движка | [«Текст атома»](../components/inputs/rich-text-editor.md#текст-атома) |
69
- | Сервер, Markdown, Просмотр | `sanitizeRichText(html, DOMPurify, tools)`, `richTextSchema(tools)`, `RichTextView` | [«Вне Редактора»](../components/inputs/rich-text-editor.md#вне-редактора-сервер-markdown-и-просмотр), [RichTextView, «Свои Инструменты: третий аргумент»](../components/content/rich-text-view.md#свои-инструменты-третий-аргумент) |
70
- | Остров своего Блока | `CustomBlock.island`, `CustomIslandProps`, проп `tools` у `RichTextView` | [RichTextView, «Остров своего Блока»](../components/content/rich-text-view.md#остров-своего-блока) |
51
+ | Что | Где в API | Подробно |
52
+ | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
53
+ | Свой Инструмент | `CustomTool` в `tools`; Пресеты — `RICH_TEXT_PRESETS` | [«Свой Инструмент»](../components/inputs/rich-text-editor.md#свой-инструмент) |
54
+ | Правила Схемы и HTML-соглашение | `blocks` (`CustomBlock`), `marks` (`CustomMark`), `CustomAttribute`, `PrefixedName` | [«Свой Инструмент»](../components/inputs/rich-text-editor.md#свой-инструмент) |
55
+ | Кнопка | `buttons` (`CustomToolButton`, `CustomToolPopover`, `CustomToolLabel`) | [«Кнопка своего Инструмента»](../components/inputs/rich-text-editor.md#кнопка-своего-инструмента) |
56
+ | Пункт вставки, Действие над Блоком | `insertItems` (`CustomInsertItem`), `blockActions` (`CustomBlockAction`) | [«Пункты вставки и Действия над Блоком»](../components/inputs/rich-text-editor.md#пункты-вставки-и-действия-над-блоком) |
57
+ | Символ-триггер | `trigger` (`CustomTrigger`, `TriggerItem`, `TriggerContext`) | [«Свой символ-триггер»](../components/inputs/rich-text-editor.md#свой-символ-триггер) |
58
+ | Карточка у текста | `textCards` (`CustomTextCard`), помощник `markRange` | [«Карточка у текста»](../components/inputs/rich-text-editor.md#карточка-у-текста) |
59
+ | Хэндл и своя панель | `RichTextEditorHandle`, `useRichTextEditorState(ref, selector)` | [«Хэндл (`ref`) и своя панель вне поля»](../components/inputs/rich-text-editor.md#хэндл-ref-и-своя-панель-вне-поля) |
60
+ | Опции встроенного Инструмента | `tool(name, options)` → `ConfiguredTool` для `search`, `toc` и `details`; `SearchToolOptions`, `TocToolOptions`, `TocLevel`, `DetailsToolOptions` | [«Опции встроенного Инструмента: `tool()`»](../components/inputs/rich-text-editor.md#опции-встроенного-инструмента-tool) |
61
+ | Свой Инструмент с Опциями, Состоянием, Командами | `defineTool({ name, defaults, create, state, commands })` → `ToolDefinition`, `tool(definition, options)` | [«Опции, Состояние и Команды»](editor-extension.md#опции-состояние-и-команды) |
62
+ | Типы Опций, Состояния и Команд | расширяемый интерфейс `RichTextTools` | [«Карта `RichTextTools` и declaration merging»](editor-extension.md#карта-richtexttools-и-declaration-merging) |
63
+ | Состояние Инструмента | `useRichTextToolState(ref, name, selector?)`, `SearchToolState`, `TocToolState`, `TocToolHeading` | [«Состояние Инструмента: `useRichTextToolState`»](../components/inputs/rich-text-editor.md#состояние-инструмента-userichtexttoolstate) |
64
+ | Команды Инструмента | `handle.tool(name)`, `SearchToolCommands`, `TocToolCommands` | [«Команды Инструмента: `handle.tool()`»](../components/inputs/rich-text-editor.md#команды-инструмента-handletool) |
65
+ | События | `onReady`, `onFocus`, `onBlur`; вставка и drop — через Extensions | [«События»](../components/inputs/rich-text-editor.md#события) |
66
+ | Строка статуса и Сообщение поля | проп `status`; команда движка `showNotice`, тип `FieldNotice` | [«Строка статуса и Сообщение поля»](../components/inputs/rich-text-editor.md#строка-статуса-и-сообщение-поля) |
67
+ | Загрузчик, Готовность и Скелет | `engine: () => import(…)` | [«Загрузчик Extensions»](../components/inputs/rich-text-editor.md#загрузчик-extensions), [«Скелет»](../components/inputs/rich-text-editor.md#скелет) |
68
+ | Текст атома: `getText`, `maxLength`, пустота | `renderText` из Extensions | [«Текст атома»](../components/inputs/rich-text-editor.md#текст-атома) |
69
+ | Сервер, Markdown, Просмотр | `sanitizeRichText(html, DOMPurify, tools)`, `richTextSchema(tools)`, `RichTextView` | [«Вне Редактора»](../components/inputs/rich-text-editor.md#вне-редактора-сервер-markdown-и-просмотр), [RichTextView, «Свои Инструменты: третий аргумент»](../components/content/rich-text-view.md#свои-инструменты-третий-аргумент) |
70
+ | Остров своего Блока | `CustomBlock.island`, `CustomIslandProps`, проп `tools` у `RichTextView` | [RichTextView, «Остров своего Блока»](../components/content/rich-text-view.md#остров-своего-блока) |
71
71
 
72
72
  ## Свой Инструмент и HTML-соглашение
73
73
 
74
- Описание одно, внутри две части. **Правила Схемы** — обычные данные без Tiptap: их читают Редактор, серверная очистка и Просмотр. **Часть для движка** — расширения Tiptap целиком (Node, Mark, Extension, плагины ProseMirror, React-вид узла): только поведение, а разбор и вывод HTML ставит кит. Обязательны имя, Часть для движка и правило Схемы для каждого её Node и Mark; остальное — по умолчанию: обычный Блок без Признаков, выгрузка в Markdown его содержимым, ритм абзаца в Просмотре, без UI-записей. Инструмент может не нести Блока вовсе — например, только фильтр вставки.
74
+ Описание одно, внутри две части. **Правила Схемы** — обычные данные без Tiptap: их читают Редактор, серверная очистка и Просмотр. **Extensions** — расширения Tiptap целиком (Node, Mark, Extension, плагины ProseMirror, React-вид узла): только поведение, а разбор и вывод HTML ставит кит. Обязательны имя, Extensions и правило Схемы для каждого их Node и Mark; остальное — по умолчанию: обычный Блок без Признаков, выгрузка в Markdown его содержимым, ритм абзаца в Просмотре, без UI-записей. Инструмент может не нести Блока вовсе — например, только фильтр вставки.
75
75
 
76
76
  Всё своё называется с Префиксом потребителя через подчёркивание: Инструмент `acme_mergeTag`, атрибут `acme_size`. В именах кита подчёркивания нет, поэтому своё и встроенное не пересекаются ни в массиве `tools`, ни в сохранённом Документе. HTML своего пишет и читает кит:
77
77
 
@@ -82,7 +82,7 @@ import { sanitizeRichText } from "@olddevs/ui/editor/schema";
82
82
  | Метка | `<span data-mark="acme_comment">…</span>` |
83
83
  | Атрибут | `data-acme_size="m"` — значение по правилу: список, шаблон или URL |
84
84
 
85
- Массив `tools` проверяется один раз, при создании движка (с Загрузчиками — когда они отработали). Имя без префикса, совпадение имён, Node или Mark в Части для движка без правила Схемы (и наоборот), запись без обязательного поля дают ошибку поля «Редактор не запустился», а не тихая потеря Блоков при первом сохранении. Неизвестная **строка** в `tools` по-прежнему отбрасывается молча.
85
+ Массив `tools` проверяется один раз, при создании движка (с Загрузчиками — когда они отработали). Имя без префикса, совпадение имён, Node или Mark в Extensions без правила Схемы (и наоборот), запись без обязательного поля дают ошибку поля «Редактор не запустился», а не тихая потеря Блоков при первом сохранении. Неизвестная **строка** в `tools` по-прежнему отбрасывается молча.
86
86
 
87
87
  ## UI-записи
88
88
 
@@ -98,7 +98,7 @@ import { sanitizeRichText } from "@olddevs/ui/editor/schema";
98
98
  - **Подпись** — строка или функция, получающая `{ locale }` (локаль `UiProvider`); каталог сообщений `UiProvider` для сторонних Инструментов не расширяется, переопределение — опциями фабрики Инструмента (рецепт ниже).
99
99
  - **Иконка** обязательна у кнопки, пункта и Действия: компонент с контрактом `IconProps` из `@olddevs/ui/icons` или свой.
100
100
  - **Поповер** — у кнопки, Пункта вставки и Действия над Блоком ровно одно из `run` и `popover`. Содержимое — функция, получающая `{ editor, close }` (у Действия — ещё `pos` Блока), рамку держит кит: слой оверлея, Стекло всплывающих поверхностей Редактора, стек Escape, имя (подпись записи), возврат фокуса и выделения в Документ. Карточка у текста лежит на том же Стекле, поэтому сплошной фон содержимому не нужен: он закроет подложку. При включённой в системе настройке прозрачности или контраста подложка и так станет сплошной. Поповер встаёт у кнопки (щелчок), у каретки (сочетание кнопки, пункт из «/», «+», «Добавить ›», «Формат ›» и его сочетание) или у Блока (Действие из меню Ручки и его сочетание).
101
- - **Сочетание** (`shortcut`) — единственный источник: кит вешает клавишу и пишет её в подсказку, `aria-keyshortcuts` и Справочник сочетаний. Клавиши Части для движка в справочник не попадают.
101
+ - **Сочетание** (`shortcut`) — единственный источник: кит вешает клавишу и пишет её в подсказку, `aria-keyshortcuts` и Справочник сочетаний. Клавиши, которые вешают Extensions, в справочник не попадают.
102
102
  - **Выключенный Инструмент** уносит свои записи отовсюду.
103
103
 
104
104
  ## Символ-триггер
@@ -124,11 +124,11 @@ import { sanitizeRichText } from "@olddevs/ui/editor/schema";
124
124
  | Состояние | Снимок, который экран читает | Кит у встроенных; поле `state` у `defineTool` | `useRichTextToolState(ref, name, selector?)` |
125
125
  | Команды | То, что экран вызывает | Кит у встроенных; поле `commands` у `defineTool` | `ref.current?.tool(name)?.команда()` |
126
126
 
127
- Часть для движка по-прежнему следует мажору Tiptap (ADR-0058, п. 12.4). Три новые части от Tiptap не зависят: их типы и поведение подчиняются semver кита.
127
+ Extensions по-прежнему следуют мажору Tiptap (ADR-0058, п. 12.4). Три новые части от Tiptap не зависят: их типы и поведение подчиняются semver кита.
128
128
 
129
129
  ### `tool()`: встроенный Инструмент с Опциями
130
130
 
131
- `tool(name, options)` возвращает Настроенный Инструмент — непрозрачную запись для `tools` (её поля читает только кит). Опции есть у `search` (стартовые флажки) и `toc` (`offsetVar`, `levels`, `listWidth`); их значения и пример — на странице [RichTextEditor](../components/inputs/rich-text-editor.md#опции-встроенного-инструмента-tool). Запись включает Инструмент и без имени в `tools`, а имя из Пресета ей уступает. Две записи `tool()` с одним именем — ошибка набора.
131
+ `tool(name, options)` возвращает Настроенный Инструмент — непрозрачную запись для `tools` (её поля читает только кит). Опции есть у `search` (стартовые флажки), `toc` (`offsetVar`, `levels`, `listWidth`) и `details` (`persist`: пишется ли открытость сворачиваемого Блока в Документ); их значения и пример — на странице [RichTextEditor](../components/inputs/rich-text-editor.md#опции-встроенного-инструмента-tool). Запись включает Инструмент и без имени в `tools`, а имя из Пресета ей уступает. Две записи `tool()` с одним именем — ошибка набора.
132
132
 
133
133
  `tool(definition, options)` настраивает тем же способом определение `defineTool`; Опции берутся из его `defaults`.
134
134
 
@@ -137,17 +137,17 @@ import { sanitizeRichText } from "@olddevs/ui/editor/schema";
137
137
  `defineTool({ name, defaults, create, state?, commands? })` возвращает определение — элемент `tools`, который работает и без `tool()` (с умолчаниями из `defaults`), и с ним.
138
138
 
139
139
  - **`defaults`** — полный набор Опций. Тип Опций выводится из него, второй раз его объявлять не нужно. Литерал расширяется до своего типа (`tone: "info"` станет `string`): нужен union-тип — укажите тип значения, `tone: "info" as "info" | "warning"`.
140
- - **`create(options)`** отдаёт тело описания — ту же форму, что `CustomTool`, без имени (`CustomToolBody`): Часть для движка, правила Схемы, UI-записи. Зовётся один раз на запись `tools` (определение само или объект, который вернул `tool(definition, options)`): кит запоминает результат по паре «определение и объект Опций записи». Всё, что замкнуто в теле `create` (счётчик, кэш), поэтому общее у всех Редакторов с этой записью. Запись `tool(…)`, созданная в теле компонента, на каждом рендере новая, и Остров в `RichTextView` на каждом рендере перемонтируется: выносите набор `tools` из рендера, в константу модуля или в `useMemo`. Загрузчик Части для движка держите константой модуля: кэш загрузчика живёт на самой функции, и новая функция на каждый вызов `create` загружала бы Часть заново для каждой записи.
140
+ - **`create(options)`** отдаёт тело описания — ту же форму, что `CustomTool`, без имени (`CustomToolBody`): Extensions, правила Схемы, UI-записи. Зовётся один раз на запись `tools` (определение само или объект, который вернул `tool(definition, options)`): кит запоминает результат по паре «определение и объект Опций записи». Всё, что замкнуто в теле `create` (счётчик, кэш), поэтому общее у всех Редакторов с этой записью. Запись `tool(…)`, созданная в теле компонента, на каждом рендере новая, и Остров в `RichTextView` на каждом рендере перемонтируется: выносите набор `tools` из рендера, в константу модуля или в `useMemo`. Загрузчик Extensions держите константой модуля: кэш загрузчика живёт на самой функции, и новая функция на каждый вызов `create` загружала бы Extensions заново для каждой записи.
141
141
  - **`state({ editor, options })`** — Состояние для `useRichTextToolState`. Её зовут при каждой транзакции и при каждом чтении хука, поэтому она должна быть быстрой и без побочных эффектов, а результат — данные без DOM-узлов. Примитивы, массивы и простые объекты сравниваются по значению, поэтому экран перерисовывается, только когда снимок изменился. `Date`, `Map`, `Set` и экземпляры классов сравниваются по ссылке: `state`, который создаёт их заново на каждый вызов, перерисует экран на каждой транзакции. Отдавайте простые данные (`date.toISOString()`) или ту же ссылку, пока данные не изменились. Нет `state` — у Инструмента нет Состояния, и хук отдаёт `null`.
142
142
  - **`commands({ editor, options })`** — Команды для `handle.tool(name)`. Их зовут при каждом обращении к Хэндлу: только обёртки над движком, без подписок. Команда возвращает `boolean`, как у встроенных. Нет `commands` — `handle.tool(name)` отдаёт `null`.
143
143
  - **Слияние Опций.** Вложенные простые объекты сливаются с умолчаниями, массивы и функции заменяются целиком — как `configure` у Tiptap v3. Значение `undefined` не в счёт: остаётся то, что было. Объект `defaults` не меняется, объект Опций общий для `create`, `state` и `commands`: читайте его, но не меняйте.
144
144
  - **Опции читаются один раз**, при создании движка, вместе с набором. Чтобы сменить их, перемонтируйте Редактор ключом (`key`). Значения Опций своего Инструмента проверяет его автор: кит этого не делает.
145
- - **Все правила своего Инструмента действуют** (ADR-0058): Префикс потребителя в имени, уникальность имени в `tools` (определение дважды, с `tool()` и без, — тоже ошибка набора), проверки набора, Загрузчик Части для движка. `defineTool` — сахар над той же формой `CustomTool`: отдельного пути сборки у него нет. Обычный объект `CustomTool` в `tools` работает как раньше, без Опций, Состояния и Команд.
145
+ - **Все правила своего Инструмента действуют** (ADR-0058): Префикс потребителя в имени, уникальность имени в `tools` (определение дважды, с `tool()` и без, — тоже ошибка набора), проверки набора, Загрузчик Extensions. `defineTool` — сахар над той же формой `CustomTool`: отдельного пути сборки у него нет. Обычный объект `CustomTool` в `tools` работает как раньше, без Опций, Состояния и Команд.
146
146
  - **Сервер.** `defineTool` и `tool()` живут в модуле без Tiptap и React в рантайме (Tiptap там — только типы), поэтому набор `tools` собирается в серверном модуле, рядом с остальной конфигурацией экрана. Определение содержит функции, как и `CustomTool`, поэтому клиентскому компоненту оно отдаётся вместе с модулем (импортом), а не пропом через границу RSC. Настроенный встроенный Инструмент состоит из данных: при данных в опциях его можно передать и пропом.
147
147
 
148
148
  ### Карта `RichTextTools` и declaration merging
149
149
 
150
- Типы всех трёх частей собраны в интерфейсе `RichTextTools`, как `Storage` и `Commands` у Tiptap: ключ — имя Инструмента, значение — `{ options; state; commands }`. Кит объявляет в нём `search` и `toc`, свой Инструмент потребитель дописывает сам:
150
+ Типы всех трёх частей собраны в интерфейсе `RichTextTools`, как `Storage` и `Commands` у Tiptap: ключ — имя Инструмента, значение — `{ options; state; commands }`. Кит объявляет в нём `search`, `toc` и `details` (у последнего только `options`), свой Инструмент потребитель дописывает сам:
151
151
 
152
152
  ```ts
153
153
  declare module "@olddevs/ui/editor" {
@@ -164,7 +164,7 @@ declare module "@olddevs/ui/editor" {
164
164
  - Из карты выводятся тип снимка в `useRichTextToolState`, тип Команд в `handle.tool()` и Опции `tool()` встроенного. Опции своего Инструмента `tool()` берёт из `defaults` его определения, поэтому поле `options` в карте описывает Опции для читателей карты (`RichTextTools["acme_note"]["options"]`), а `tool()` его не использует.
165
165
  - **Типизация добровольна.** Инструмент, не дописанный в карту, работает: его Состояние и Команды имеют тип `unknown`.
166
166
  - Файл с `declare module` должен быть модулем (в нём есть `import` или `export`); иначе объявление не дополняет карту, а заводит свой модуль. Положите его рядом с определением Инструмента.
167
- - Карту не стоит путать с типизацией движка. Кто типизирует Часть для движка через `declare module "@tiptap/core"` (`Commands`, `Storage`), продолжает так делать: это слой движка и его мажор, а `RichTextTools` — слой кита.
167
+ - Карту не стоит путать с типизацией движка. Кто типизирует Extensions через `declare module "@tiptap/core"` (`Commands`, `Storage`), продолжает так делать: это слой движка и его мажор, а `RichTextTools` — слой кита.
168
168
 
169
169
  ### Сквозной пример
170
170
 
@@ -300,12 +300,12 @@ export function ArticleBody({ html }: { html: string }) {
300
300
 
301
301
  ## Загрузчики, Готовность и Скелет
302
302
 
303
- Часть для движка можно отдать Загрузчиком — `engine: () => import("./engine.js").then((m) => m.engine)`: модуль с описанием тогда импортирует и сервер, а Tiptap уходит в отдельный чанк. Загрузка стартует с первого рендера в браузере, промис кэшируется на функции Загрузчика (поэтому Загрузчик — одна функция на модуль). **Готовность** — движок создан и Загрузчики всех включённых Инструментов, своих и тяжёлых встроенных (`codeBlock`, `table`), отработали. До неё поле — **Скелет**: рамка, место Панели и Документ, очищенный Схемой, без ввода и без прыжка высоты; на сервере — рамка без Документа. Сбой Загрузчика — ошибка поля с «Повторить», Документ остаётся виден. Подробно — [«Загрузчик Части для движка»](../components/inputs/rich-text-editor.md#загрузчик-части-для-движка) и [«Скелет»](../components/inputs/rich-text-editor.md#скелет).
303
+ Extensions можно отдать Загрузчиком — `engine: () => import("./engine.js").then((m) => m.engine)`: модуль с описанием тогда импортирует и сервер, а Tiptap уходит в отдельный чанк. Загрузка стартует с первого рендера в браузере, промис кэшируется на функции Загрузчика (поэтому Загрузчик — одна функция на модуль). **Готовность** — движок создан и Загрузчики всех включённых Инструментов, своих и тяжёлых встроенных (`codeBlock`, `table`), отработали. До неё поле — **Скелет**: рамка, место Панели и Документ, очищенный Схемой, без ввода и без прыжка высоты; на сервере — рамка без Документа. Сбой Загрузчика — ошибка поля с «Повторить», Документ остаётся виден. Подробно — [«Загрузчик Extensions»](../components/inputs/rich-text-editor.md#загрузчик-extensions) и [«Скелет»](../components/inputs/rich-text-editor.md#скелет).
304
304
 
305
305
  ## Сервер, Markdown, Просмотр и Остров
306
306
 
307
307
  - **Сервер** чистит Документ тем же массивом `tools`: `sanitizeRichText(html, DOMPurify, tools)`, правила данными — `richTextSchema(tools)`. Оба есть и в лёгком `@olddevs/ui/editor/schema` без Tiptap и React — вместе с `markRange`: описание своего Инструмента с Карточкой у текста собирается и в модуле сервера. Имена Инструментов кита в `tools` белый список не сужают: очистка пропускает всё, что пропускает кит. Без третьего аргумента очистка прежняя, и свои Блоки срезаются.
308
- - **Markdown** — правила в Части для движка (`renderMarkdown`, `markdownTokenizer`, `parseMarkdown`). Без них Блок выгружается содержимым, Атом — Текстом атома, и текст не теряется.
308
+ - **Markdown** — правила в Extensions (`renderMarkdown`, `markdownTokenizer`, `parseMarkdown`). Без них Блок выгружается содержимым, Атом — Текстом атома, и текст не теряется.
309
309
  - **`RichTextView`** показывает свой Блок очищенным HTML с ритмом абзаца; вид — ваш CSS по `data-block`. **Остров** — клиентский компонент в поле `island` Блока: оживает после монтирования и только если такой Блок есть в Документе; `RichTextView` для этого получает тот же `tools`.
310
310
 
311
311
  ## Стабильность
@@ -317,34 +317,34 @@ export function ArticleBody({ html }: { html: string }) {
317
317
  - описание своего Инструмента (`CustomTool` и его поля, в том числе `trigger`), UI-записи и их Отведённые места, правила Схемы (`CustomBlock`, `CustomMark`, `CustomAttribute`), Признаки Блока;
318
318
  - HTML-соглашение (`data-block`, `data-mark`, `data-<префикс>_<имя>`);
319
319
  - методы Хэндла, `useRichTextEditorState`, колбэки `onReady`, `onFocus`, `onBlur`, проп `status`, команда `showNotice` и тип `FieldNotice`;
320
- - Опции, Состояние и Команды (ADR-0069): карта `RichTextTools`, `tool()`, `defineTool()` и его типы (`ToolDefinition`, `ConfiguredTool`), `useRichTextToolState`, `handle.tool()`, а также поля Опций, Состояния и Команд `search` и `toc`;
320
+ - Опции, Состояние и Команды (ADR-0069): карта `RichTextTools`, `tool()`, `defineTool()` и его типы (`ToolDefinition`, `ConfiguredTool`), `useRichTextToolState`, `handle.tool()`, а также поля Опций, Состояния и Команд `search` и `toc` и Опции `details`;
321
321
  - третий аргумент `sanitizeRichText` и `richTextSchema`, проп `tools` и Остров у `RichTextView`, `markRange`;
322
322
  - имена встроенных Инструментов (`ToolName`) и сам факт Пресетов-массивов `RICH_TEXT_PRESETS` — но не их состав (ниже).
323
323
 
324
- **Не входят:** DOM и классы кита вокруг Отведённого места; id встроенных кнопок и пунктов; внутреннее описание встроенных Инструментов (надмножество публичной формы) — наружу оно не экспортируется; точное место записи внутри поверхности; вид Скелета; форма Настроенного Инструмента изнутри (запись непрозрачна, её поля читает кит); момент прокрутки после Команд Поиска (она происходит чуть позже вызова); имена файлов и чанков в `dist`, как любой путь внутри пакета ([«Публичный контракт»](../conventions.md#публичный-контракт)). API сырого `editor` и Части для движка — это API Tiptap, он следует мажору движка.
324
+ **Не входят:** DOM и классы кита вокруг Отведённого места; id встроенных кнопок и пунктов; внутреннее описание встроенных Инструментов (надмножество публичной формы) — наружу оно не экспортируется; точное место записи внутри поверхности; вид Скелета; форма Настроенного Инструмента изнутри (запись непрозрачна, её поля читает кит); момент прокрутки после Команд Поиска (она происходит чуть позже вызова); имена файлов и чанков в `dist`, как любой путь внутри пакета ([«Публичный контракт»](../conventions.md#публичный-контракт)). API сырого `editor` и Extensions — это API Tiptap, он следует мажору движка.
325
325
 
326
326
  **Сохранённый Документ не ломают ни minor, ни patch.** HTML-соглашение заморожено: `data-block` и `data-mark` расширяются только добавлением. Если форму придётся сменить, это мажор, и до следующего мажора кит читает обе формы — и разбором движка, и санитайзером.
327
327
 
328
328
  **Ломающим считается нарушение контракта, а не сдвиг пикселей:**
329
329
 
330
- | Изменение | Версия |
331
- | ----------------------------------------------------------------------------------------- | -------------------------------------------- |
332
- | Новое обязательное поле описания | major |
333
- | Новый Признак Блока или значение по умолчанию, меняющее поведение уже описанных Блоков | major |
334
- | Отказ от «одно Отведённое место на поверхность» или «свои записи в порядке `tools`» | major |
335
- | Перенос вида записи на другую поверхность | major |
336
- | Смена формы HTML-соглашения | major, и до следующего мажора читаются обе |
337
- | Новый мажор Tiptap — даже без правок в коде кита: ломаются Части для движка у потребителя | major |
338
- | Удаление Инструмента из Пресета, переименование Инструмента | major |
339
- | Удаление публичного имени | major, после `@deprecated` в minor (ниже) |
340
- | Новое необязательное поле, новый вид записи, метод Хэндла или колбэк | minor |
341
- | Новое поле Опций или Состояния, новая Команда, опция у другого встроенного Инструмента | minor |
342
- | Удаление поля Опций, Состояния или Команды, смена его смысла | major |
343
- | Новый встроенный Инструмент в Пресете | minor, строка changeset выделена жирным |
344
- | Сдвиг Отведённого места внутри поверхности | minor со строкой changeset |
345
- | Подъём нижней границы Tiptap внутри мажора | minor |
346
- | Ужесточение очистки ради безопасности | patch или minor с заметкой в migration-notes |
347
- | Вид Скелета, DOM и классы вокруг Отведённого места | вне контракта |
330
+ | Изменение | Версия |
331
+ | -------------------------------------------------------------------------------------- | -------------------------------------------- |
332
+ | Новое обязательное поле описания | major |
333
+ | Новый Признак Блока или значение по умолчанию, меняющее поведение уже описанных Блоков | major |
334
+ | Отказ от «одно Отведённое место на поверхность» или «свои записи в порядке `tools`» | major |
335
+ | Перенос вида записи на другую поверхность | major |
336
+ | Смена формы HTML-соглашения | major, и до следующего мажора читаются обе |
337
+ | Новый мажор Tiptap — даже без правок в коде кита: ломаются Extensions у потребителя | major |
338
+ | Удаление Инструмента из Пресета, переименование Инструмента | major |
339
+ | Удаление публичного имени | major, после `@deprecated` в minor (ниже) |
340
+ | Новое необязательное поле, новый вид записи, метод Хэндла или колбэк | minor |
341
+ | Новое поле Опций или Состояния, новая Команда, опция у другого встроенного Инструмента | minor |
342
+ | Удаление поля Опций, Состояния или Команды, смена его смысла | major |
343
+ | Новый встроенный Инструмент в Пресете | minor, строка changeset выделена жирным |
344
+ | Сдвиг Отведённого места внутри поверхности | minor со строкой changeset |
345
+ | Подъём нижней границы Tiptap внутри мажора | minor |
346
+ | Ужесточение очистки ради безопасности | patch или minor с заметкой в migration-notes |
347
+ | Вид Скелета, DOM и классы вокруг Отведённого места | вне контракта |
348
348
 
349
349
  - **Мажор Tiptap — мажор кита.** Кит поддерживает один мажор Tiptap за раз; движка нет в манифесте кита, и диапазон версий (`^3.31.4`) держит потребитель по странице [RichTextEditor](../components/inputs/rich-text-editor.md#установка).
350
350
  - **Пресет** — «то, что кит считает полным набором», а не замороженный список. Кому нужен стабильный состав, передаёт точный список имён.
@@ -500,7 +500,7 @@ export const EDITOR_TOOLS = [
500
500
 
501
501
  ### `modulepreload` для ленивых чанков
502
502
 
503
- Предзагрузки кит не публикует. Чанки Частей для движка (своих Загрузчиков и встроенных `codeBlock` и `table` в `full`) начинают грузиться с первого рендера Редактора, и это время закрывает Скелет. Чтобы чанк пришёл вместе со страницей, поставьте на него `<link rel="modulepreload">` (в React 19 — `preloadModule(url)` из `react-dom`); URL с хешем берите из манифеста своего сборщика: имена чанков кита в контракт не входят. Пример — [RichTextEditor, «Загрузчик Части для движка»](../components/inputs/rich-text-editor.md#загрузчик-части-для-движка); цена сценариев «старт» и «полный» — [«Размер импорта»](../getting-started.md#размер-импорта).
503
+ Предзагрузки кит не публикует. Чанки Extensions (своих Загрузчиков и встроенных `codeBlock` и `table` в `full`) начинают грузиться с первого рендера Редактора, и это время закрывает Скелет. Чтобы чанк пришёл вместе со страницей, поставьте на него `<link rel="modulepreload">` (в React 19 — `preloadModule(url)` из `react-dom`); URL с хешем берите из манифеста своего сборщика: имена чанков кита в контракт не входят. Пример — [RichTextEditor, «Загрузчик Extensions»](../components/inputs/rich-text-editor.md#загрузчик-extensions); цена сценариев «старт» и «полный» — [«Размер импорта»](../getting-started.md#размер-импорта).
504
504
 
505
505
  ## Ссылки
506
506
 
@@ -1,5 +1,5 @@
1
1
  ---
2
- sourceHash: 24a78e5196fa
2
+ sourceHash: 9e3157586a55
3
3
  ---
4
4
 
5
5
  # Message catalog `UiMessages`
@@ -276,6 +276,9 @@ The Editor's strings ([rich-text-editor.md](../components/inputs/rich-text-edito
276
276
  | `wrapperToneName.neutral` | string | Important | Важно |
277
277
  | `wrapperToneName.warning` | string | Warning | Внимание |
278
278
  | `wrapperToneName.danger` | string | Caution | Осторожно |
279
+ | `details` | string | Collapsible block | Сворачиваемый блок |
280
+ | `unwrapDetails` | string | Remove collapsing | Снять сворачивание |
281
+ | `detailsToggle` | function | — | — |
279
282
  | `emoji` | string | Emoji | Эмодзи |
280
283
  | `emojiGrid` | string | Choose an emoji | Выберите эмодзи |
281
284
  | `emojiCategory.favorites` | string | Favorites | Избранное |
@@ -278,6 +278,9 @@
278
278
  | `wrapperToneName.neutral` | строка | Important | Важно |
279
279
  | `wrapperToneName.warning` | строка | Warning | Внимание |
280
280
  | `wrapperToneName.danger` | строка | Caution | Осторожно |
281
+ | `details` | строка | Collapsible block | Сворачиваемый блок |
282
+ | `unwrapDetails` | строка | Remove collapsing | Снять сворачивание |
283
+ | `detailsToggle` | функция | — | — |
281
284
  | `emoji` | строка | Emoji | Эмодзи |
282
285
  | `emojiGrid` | строка | Choose an emoji | Выберите эмодзи |
283
286
  | `emojiCategory.favorites` | строка | Favorites | Избранное |
@@ -2,7 +2,7 @@
2
2
  category: api
3
3
  status: stable
4
4
  source: src/index.ts
5
- sourceHash: 902814b6f802
5
+ sourceHash: 19d3eaf4181f
6
6
  ---
7
7
 
8
8
  # Compatibility and migration notes
@@ -22,7 +22,7 @@ became relevant.
22
22
  - When changing a default, accessibility, keyboard behavior or the shape of a
23
23
  controlled value: TypeScript may not detect such a migration.
24
24
  - When replacing one component with another, for example, when the old API goes away in favor of
25
- an existing family.
25
+ an existing component family.
26
26
 
27
27
  ## When not to use
28
28
 
@@ -110,7 +110,7 @@ ref.current?.tool("search")?.open({ replace: true });
110
110
 
111
111
  Unchanged: the same panel, the same focus, the same shortcuts. The differences are small: `tool("search")` is `null`
112
112
  without the `search` Tool in `tools` and before the engine exists, so a call through `?.` does nothing, just as
113
- `openSearch` did; `open` returns a `boolean` (`false` in HTML mode) instead of `void`.
113
+ `openSearch` did; `open` returns a `boolean` (`false` in Source mode) instead of `void`.
114
114
 
115
115
  ### Action before updating
116
116
 
@@ -282,12 +282,12 @@ the same page.
282
282
  `onChange`, `onUploadImage` and `focus()` do not change, and the previous code builds.
283
283
  The note is needed because five changes are visible to current consumers
284
284
  of `@olddevs/ui/editor`, and one requires installing packages with pnpm. The new API
285
- (a custom Tool, the Handle, events, the Status line) requires no action — it is on the
285
+ (a custom Tool, the Handle, events, the Status bar) requires no action — it is on the
286
286
  [extension API page](editor-extension.md).
287
287
 
288
288
  ### 1. Two new peer packages
289
289
 
290
- The blockquote and the divider take their Engine part from `@tiptap/extension-blockquote` and
290
+ The blockquote and the divider take their Extensions from `@tiptap/extension-blockquote` and
291
291
  `@tiptap/extension-horizontal-rule` — optional peer dependencies of the
292
292
  `@olddevs/ui/editor` entry point with the same `^3.31.4` range (since 3.46.0 the engine
293
293
  packages are no longer in the kit manifest; see the entry above).
@@ -329,7 +329,7 @@ did not fit goes away whole, together with everything after it.
329
329
 
330
330
  **Action before updating.** Check the server-side length validation against the kit's rule:
331
331
  the Document's text without markup and Block separators, an Atom by the length of its text.
332
- The kit has no server-side counter: the weight of a custom Atom depends on its Engine part.
332
+ The kit has no server-side counter: the weight of a custom Atom depends on its Extensions.
333
333
 
334
334
  ### 3. Skeleton before Readiness
335
335
 
@@ -381,7 +381,7 @@ arrive with the page, add `modulepreload` for them
381
381
  **What changed.** "Uploading…", "uploaded", an upload refusal, "not an image" and
382
382
  "files are not accepted" go through the same channel as the new engine command
383
383
  `showNotice`: one place under the field, a new message replaces the previous one, and an error
384
- (`role="alert"`) stays until the next Author edit. The texts and roles are the same, but the
384
+ (`role="alert"`) stays until the next User edit. The texts and roles are the same, but the
385
385
  live region `role="status"` is now present in **every** Editor, not only with the
386
386
  `image` Tool.
387
387
 
@@ -578,7 +578,7 @@ is parameterized by your state (`SavedView<T>`) and is not tied to
578
578
  `TableState`; storage, sharing and the URL stay entirely with you. For details, see the
579
579
  [component page](../components/table/saved-views.md).
580
580
 
581
- **The `FormField` hint and error reach the react-aria families.** For
581
+ **The `FormField` hint and error reach the react-aria component families.** For
582
582
  `Select`, `Combobox`, `DatePicker` and `TagInput`, the description from `FormField` is now tied to
583
583
  the field through `aria-describedby`. **This is an accessibility fix, and it changes
584
584
  what is announced:** where a hint was visible on screen but did not reach a screen reader, a
@@ -646,7 +646,7 @@ above came from: `npm run check:dist`, `npm run check:consumer`,
646
646
  with changesets.
647
647
 
648
648
  **Deliberately postponed:** Figma Code Connect (priority P2 in the specification, only after
649
- the names and axes stabilize); binding the description for the react-aria families (`Select`,
649
+ the names and axes stabilize); binding the description for the react-aria component families (`Select`,
650
650
  `Combobox`, `DatePicker`, `TagInput`) — they have their own description slots; merging
651
651
  `UploadState` and `TimelineStatus` — it breaks a public type and waits for a major;
652
652
  widening the peer to React 18 — it doubles the consumer's CI build.
@@ -735,7 +735,7 @@ the description is **supplemented, not replaced**: the attribute holds a list.
735
735
 
736
736
  `TextControl` and everything built on it (`MaskedInput`,
737
737
  `PhoneInput`, `PasswordInput`, `SearchControl`), plus `NumberInput`, pick up the description. The react-aria
738
- families (`Select`, `Combobox`, `DatePicker`, `TagInput`) carry their own description slots
738
+ component families (`Select`, `Combobox`, `DatePicker`, `TagInput`) carry their own description slots
739
739
  and do not pick up the `FormField` hint yet.
740
740
 
741
741
  **3. A character that does not fit the mask is announced.** A character that did not fit the mask token
@@ -817,7 +817,7 @@ something read it; `DataTable` became that reader.
817
817
  `tableStateToParams`, `tableStateFromParams`, `DEFAULT_TABLE_STATE`.
818
818
 
819
819
  **5. Selection is within a page.** "Select all" marks the `items` that arrived;
820
- the kit does not support selecting the whole result set, and the labels about it do not lie.
820
+ the kit does not support selecting all the filtered rows, and the labels about it do not lie.
821
821
  If you need selection beyond the page, that is your code, not the kit's.
822
822
 
823
823
  **6. State is carried into the URL without selection and expansion.** The list of keys
@@ -287,7 +287,7 @@ Shift+F10 — меню ячейки. Новое: кнопки внутри яч
287
287
 
288
288
  ### 1. Два новых peer-пакета
289
289
 
290
- Цитата и разделитель берут Часть для движка из `@tiptap/extension-blockquote` и
290
+ Цитата и разделитель берут Extensions из `@tiptap/extension-blockquote` и
291
291
  `@tiptap/extension-horizontal-rule` — опциональных peer-зависимостей входа
292
292
  `@olddevs/ui/editor` с тем же диапазоном `^3.31.4` (с 3.46.0 пакеты движка из
293
293
  манифеста кита убраны, см. запись выше).
@@ -329,7 +329,7 @@ pnpm add @tiptap/extension-blockquote @tiptap/extension-horizontal-rule
329
329
 
330
330
  **Действие до обновления.** Серверную проверку длины сверьте с правилом кита:
331
331
  текст Документа без разметки и разделителей Блоков, Атом — длиной своего текста.
332
- Серверного счётчика у кита нет: вес своего Атома зависит от его Части для движка.
332
+ Серверного счётчика у кита нет: вес своего Атома зависит от его Extensions.
333
333
 
334
334
  ### 3. Скелет до Готовности
335
335
 
@@ -156,10 +156,10 @@ nothing outside React** and subscribes to nothing. The kit provides the shape, a
156
156
  ### Invariants the hook exists to keep
157
157
 
158
158
  - changing **filters, sorting or the page size resets the page** to the first one. Otherwise the user looks at
159
- page four of a result set of ten rows, and reads the empty space as "nothing found";
159
+ page four of ten filtered rows, and reads the empty space as "nothing found";
160
160
  - **selection is not reset.** The reverse looks tidier, but it breaks a bulk action exactly when that action is
161
161
  needed most;
162
- - density and column visibility do **not** touch the page. A filter narrows the result set, not the look;
162
+ - density and column visibility do **not** touch the page. A filter narrows the rows, not the look;
163
163
  - an explicit `page` in the same call cancels the reset. This is how the state is restored from the address,
164
164
  in full and in one go.
165
165
 
@@ -190,7 +190,7 @@ router.replace(`?${tableStateToParams(state)}`);
190
190
  state. There are two reasons. The list of keys has no length limit. And a link with a selection, opened the next
191
191
  day, marks records that no longer exist. Selection is session state, not URL state.
192
192
 
193
- The restored state is passed to the hook as a whole, in one `set` call. The invariant "narrowing the result set
193
+ The restored state is passed to the hook as a whole, in one `set` call. The invariant "narrowing the filtered rows
194
194
  resets the page" is cancelled by an explicit page in the same call. Otherwise a link would always open the first
195
195
  page.
196
196
 
@@ -207,7 +207,7 @@ For how to build a range in the UI, see [FilterBar](../components/table/filter-b
207
207
 
208
208
  ## Constants, classes and public types: where they are documented
209
209
 
210
- These are public exports, but not standalone components. The page of the family they belong to describes them,
210
+ These are public exports, but not standalone components. The page of the component family they belong to describes them,
211
211
  so that AI does not treat a class as a general-purpose API:
212
212
 
213
213
  | Exports | Documentation |
@@ -221,8 +221,8 @@ so that AI does not treat a class as a general-purpose API:
221
221
 
222
222
  Each `*Props` type and each component-specific type exported with a component (for example `TableProps`,
223
223
  `CommandItem`, `SelectItem`, `KeyboardSelection` or `DrawerSide`) stays part of that component's public API. It
224
- is described on the page of its family. The [component catalog](../components/index.md) links the public exports
225
- to the family pages. `src/index.ts` is the final source for all exports.
224
+ is described on the page of its component family. The [component catalog](../components/index.md) links the public exports
225
+ to the component family pages. `src/index.ts` is the final source for all exports.
226
226
 
227
227
  ## Related sources and checks
228
228
 
@@ -12,7 +12,7 @@ sourceHash: 982b0da00a2f
12
12
 
13
13
  ## Summary for AI
14
14
 
15
- `SplitButton` is a primary action with a menu trigger for its variants next to it, as **two separate buttons**. The accessible name of the menu trigger is built from `label`, so `label` is a string, not a node. Menu items go in `children`.
15
+ `SplitButton` is a primary action with a menu button for its variants next to it, as **two separate buttons**. The accessible name of the menu button is built from `label`, so `label` is a string, not a node. Menu items go in `children`.
16
16
 
17
17
  ## When to use
18
18
 
@@ -38,9 +38,9 @@ sourceHash: 982b0da00a2f
38
38
 
39
39
  ## Public API
40
40
 
41
- - `label` is a **string**: the label of the primary action and the source of the name of the menu trigger.
41
+ - `label` is a **string**: the label of the primary action and the source of the name of the menu button.
42
42
  - `icon` is the icon on the left of the primary action. If a button can enter `loading`, give it an icon from the start: otherwise the loading indicator adds a slot, and the label shifts.
43
- - `onClick` is called only by the primary action; the menu trigger does not call it.
43
+ - `onClick` is called only by the primary action; the menu button does not call it.
44
44
  - `variant`, `tone`, `size` are the same axes as on `Button` and mean the same thing.
45
45
  - `disabled` and `loading` disable **both** buttons and close a menu that is already open — and **do not bring it back** when `disabled` or `loading` is lifted: otherwise the menu would open by itself once a submission ended.
46
46
  - `placement` is the side the panel opens to, `bottom end` by default. The side values of `Menu` are narrowed here: a panel that slides sideways lands on top of the primary action.
@@ -57,20 +57,20 @@ two. These are two separate controls.
57
57
 
58
58
  ## Why `label` is a string
59
59
 
60
- The name of the menu trigger is built from the label. A node would give a name
60
+ The name of the menu button is built from the label. A node would give a name
61
61
  that does not exist — or would require a second prop with the same label as a
62
62
  string. One label would live in two places and drift apart at the first edit.
63
63
 
64
64
  ## Keyboard
65
65
 
66
- - **Two tab stops, not one.** Tab goes to the primary action, the next Tab to the menu trigger. The whole rationale of the component rests on this: a single button whose right part opens a menu would leave the second outcome unreachable from the keyboard.
67
- - The menu opens from the menu trigger with Enter, Space and Down Arrow — `MenuTrigger` does this.
68
- - Escape closes the menu and **returns focus to the menu trigger** instead of sending it to `<body>`.
66
+ - **Two tab stops, not one.** Tab goes to the primary action, the next Tab to the menu button. The whole rationale of the component rests on this: a single button whose right part opens a menu would leave the second outcome unreachable from the keyboard.
67
+ - The menu opens from the menu button with Enter, Space and Down Arrow — `MenuTrigger` does this.
68
+ - Escape closes the menu and **returns focus to the menu button** instead of sending it to `<body>`.
69
69
  - Disabling the component while the menu is open closes the panel: otherwise it would look disabled while the items kept working.
70
70
 
71
71
  ## Accessibility and semantics
72
72
 
73
- - The menu trigger's name comes from the message catalog: `splitButton.more({ action })` → "Другие действия: Экспорт" with `@olddevs/ui/locales/ru`, "More actions: Export" without a provider. Without the action's label, several menu triggers on a page are indistinguishable when read aloud by a screen reader.
74
- - The menu trigger carries `aria-haspopup` and `aria-expanded` — `MenuTrigger` sets them.
73
+ - The menu button's name comes from the message catalog: `splitButton.more({ action })` → "Другие действия: Экспорт" with `@olddevs/ui/locales/ru`, "More actions: Export" without a provider. Without the action's label, several menu buttons on a page are indistinguishable when read aloud by a screen reader.
74
+ - The menu button carries `aria-haspopup` and `aria-expanded` — `MenuTrigger` sets them.
75
75
  - The group does not get `role="group"`: an unnamed group is an extra screen reader announcement, and a name would duplicate the button's label.
76
76
  - See `src/components/actions/SplitButton.tsx`, `src/components/actions/SplitButton.stories.tsx`, `tests/split-button.test.tsx`, [Menu](../overlays/menu.md).
@@ -117,7 +117,7 @@ the row: a manual `stopPropagation` is not needed. A detail row and the expander
117
117
  button do not call the action. A row with an action gets a pointer cursor.
118
118
 
119
119
  **Expansion is the only axis that could not be added as a layer on top.**
120
- `DataTable` adds column visibility, order, density and the table bar (`toolbar`)
120
+ `DataTable` adds column visibility, order, density and the table toolbar (`toolbar`)
121
121
  from the outside: they change what goes into `Table` through props. A detail row
122
122
  lives **inside** `<tbody>`, between the rows of the react-aria collection, and
123
123
  cannot be inserted from the outside. The analysis is in
@@ -77,9 +77,9 @@ import { AspectRatio, Kbd, ScrollArea, Separator } from "@olddevs/ui";
77
77
 
78
78
  ### `Kbd`
79
79
 
80
- | Prop | Default | Meaning |
81
- | ------ | ------- | ------------------------------------------------------------------- |
82
- | `size` | `sm` | `sm` — inside a menu, `md` — in a standalone shortcuts cheat sheet. |
80
+ | Prop | Default | Meaning |
81
+ | ------ | ------- | ----------------------------------------------------------------------- |
82
+ | `size` | `sm` | `sm` — inside a menu, `md` — in a standalone keyboard shortcuts dialog. |
83
83
 
84
84
  `Kbd` renders the Apple modifier symbols (`⌘ ⌥ ⇧ ⌃`) in a string label in a
85
85
  proportional font and everything else in a monospace one. The kit's monospace
@@ -67,7 +67,7 @@ export function Article({ html }: { html: string }) {
67
67
  ### Which headings are included
68
68
 
69
69
  - `h1`–`h4` at the top level of the Document and inside a Wrapper.
70
- - Headings in the columns of a Layout are not included: two headings at the same height in neighboring columns confuse the highlight of the current section. Headings inside custom Blocks and their Islands are not included either: their content is part of the Block, not of the Document's structure.
70
+ - Headings in the columns of a Column layout are not included: two headings at the same height in neighboring columns confuse the highlight of the current section. Headings inside custom Blocks and their Islands are not included either: their content is part of the Block, not of the Document's structure.
71
71
  - The `levels` prop narrows the set: `levels={[2, 3]}` keeps only `h2` and `h3`. Other levels are in neither the strip nor the list; an empty array leaves no headings at all.
72
72
  - With fewer than two headings (after `levels`) the component renders nothing, and the screen's column stays empty.
73
73