@mieweb/ui 0.11.0-dev.34 → 0.11.0-dev.36

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 (156) hide show
  1. package/MIGRATION.md +45 -11
  2. package/dist/{EncounterMdyEditor-73PTTIRY.cjs → EncounterMdyEditor-46WMYRFI.cjs} +6 -6
  3. package/dist/{EncounterMdyEditor-73PTTIRY.cjs.map → EncounterMdyEditor-46WMYRFI.cjs.map} +1 -1
  4. package/dist/{EncounterMdyEditor-3SQY4MK2.js → EncounterMdyEditor-L7RBLE7B.js} +3 -3
  5. package/dist/{EncounterMdyEditor-3SQY4MK2.js.map → EncounterMdyEditor-L7RBLE7B.js.map} +1 -1
  6. package/dist/{chunk-4SUEBKTY.js → chunk-2OTPQPJS.js} +52 -44
  7. package/dist/chunk-2OTPQPJS.js.map +1 -0
  8. package/dist/{chunk-MZR634HI.cjs → chunk-3M2BBOXF.cjs} +9 -9
  9. package/dist/{chunk-MZR634HI.cjs.map → chunk-3M2BBOXF.cjs.map} +1 -1
  10. package/dist/{chunk-6UYMO3PF.cjs → chunk-47AOS5EC.cjs} +10 -2
  11. package/dist/chunk-47AOS5EC.cjs.map +1 -0
  12. package/dist/{chunk-DXNVQQYN.cjs → chunk-6L7CTEX2.cjs} +51 -44
  13. package/dist/chunk-6L7CTEX2.cjs.map +1 -0
  14. package/dist/{chunk-RTADGBBN.js → chunk-AIO2GCTL.js} +3 -3
  15. package/dist/{chunk-RTADGBBN.js.map → chunk-AIO2GCTL.js.map} +1 -1
  16. package/dist/{chunk-EX7Q5545.js → chunk-AKC677ZF.js} +5 -5
  17. package/dist/{chunk-EX7Q5545.js.map → chunk-AKC677ZF.js.map} +1 -1
  18. package/dist/{chunk-XEHD2HA5.cjs → chunk-FRPELUI5.cjs} +3 -3
  19. package/dist/{chunk-XEHD2HA5.cjs.map → chunk-FRPELUI5.cjs.map} +1 -1
  20. package/dist/{chunk-DGHS5IVD.js → chunk-GJSLKD4Q.js} +3 -3
  21. package/dist/{chunk-DGHS5IVD.js.map → chunk-GJSLKD4Q.js.map} +1 -1
  22. package/dist/{chunk-VMEVLBI3.js → chunk-GKMMDKYY.js} +3 -3
  23. package/dist/{chunk-VMEVLBI3.js.map → chunk-GKMMDKYY.js.map} +1 -1
  24. package/dist/{chunk-46RYJYN2.cjs → chunk-GU6TPVXF.cjs} +6 -6
  25. package/dist/{chunk-46RYJYN2.cjs.map → chunk-GU6TPVXF.cjs.map} +1 -1
  26. package/dist/chunk-IALTKN3Y.js +1017 -0
  27. package/dist/chunk-IALTKN3Y.js.map +1 -0
  28. package/dist/{chunk-33Y3KQWL.js → chunk-IGRHPZOJ.js} +3 -3
  29. package/dist/{chunk-33Y3KQWL.js.map → chunk-IGRHPZOJ.js.map} +1 -1
  30. package/dist/{chunk-PLBWZW4F.js → chunk-KAQNYX7B.js} +3 -3
  31. package/dist/{chunk-PLBWZW4F.js.map → chunk-KAQNYX7B.js.map} +1 -1
  32. package/dist/{chunk-SPGP252Q.js → chunk-KB2PLV5Q.js} +4 -4
  33. package/dist/{chunk-SPGP252Q.js.map → chunk-KB2PLV5Q.js.map} +1 -1
  34. package/dist/{chunk-NCPDUJKJ.cjs → chunk-KQXD6BFO.cjs} +4 -4
  35. package/dist/{chunk-NCPDUJKJ.cjs.map → chunk-KQXD6BFO.cjs.map} +1 -1
  36. package/dist/{chunk-4VSK3EE5.cjs → chunk-L77XJPKC.cjs} +3 -3
  37. package/dist/{chunk-4VSK3EE5.cjs.map → chunk-L77XJPKC.cjs.map} +1 -1
  38. package/dist/{chunk-PGJLW2NO.cjs → chunk-LLIG4X6M.cjs} +7 -7
  39. package/dist/{chunk-PGJLW2NO.cjs.map → chunk-LLIG4X6M.cjs.map} +1 -1
  40. package/dist/{chunk-NW6D6AFL.js → chunk-LWLHCUDP.js} +10 -10
  41. package/dist/{chunk-NW6D6AFL.js.map → chunk-LWLHCUDP.js.map} +1 -1
  42. package/dist/{chunk-YR45POH2.js → chunk-MAEANAAJ.js} +3 -3
  43. package/dist/{chunk-YR45POH2.js.map → chunk-MAEANAAJ.js.map} +1 -1
  44. package/dist/{chunk-X5HLVACK.js → chunk-MLCIINXT.js} +4 -4
  45. package/dist/{chunk-X5HLVACK.js.map → chunk-MLCIINXT.js.map} +1 -1
  46. package/dist/{chunk-JEXEFWEM.js → chunk-MNXIN4MS.js} +10 -2
  47. package/dist/chunk-MNXIN4MS.js.map +1 -0
  48. package/dist/{chunk-PHUTJKHL.cjs → chunk-MXIIPFTA.cjs} +3 -3
  49. package/dist/{chunk-PHUTJKHL.cjs.map → chunk-MXIIPFTA.cjs.map} +1 -1
  50. package/dist/{chunk-QO4A6U73.cjs → chunk-NCG7NJAW.cjs} +93 -30
  51. package/dist/chunk-NCG7NJAW.cjs.map +1 -0
  52. package/dist/{chunk-25MPUVTM.js → chunk-NIE3WBJH.js} +94 -31
  53. package/dist/chunk-NIE3WBJH.js.map +1 -0
  54. package/dist/{chunk-4GSFVD5V.js → chunk-NUHHWELE.js} +3 -3
  55. package/dist/{chunk-4GSFVD5V.js.map → chunk-NUHHWELE.js.map} +1 -1
  56. package/dist/{chunk-4M2KE4O5.cjs → chunk-OGFSFUIX.cjs} +7 -7
  57. package/dist/{chunk-4M2KE4O5.cjs.map → chunk-OGFSFUIX.cjs.map} +1 -1
  58. package/dist/{chunk-YSIFPHLX.js → chunk-OJQMC426.js} +2 -2
  59. package/dist/{chunk-YSIFPHLX.js.map → chunk-OJQMC426.js.map} +1 -1
  60. package/dist/{chunk-SVC7NPQ6.cjs → chunk-PDLVSJMZ.cjs} +6 -6
  61. package/dist/{chunk-SVC7NPQ6.cjs.map → chunk-PDLVSJMZ.cjs.map} +1 -1
  62. package/dist/{chunk-KD7YD6US.cjs → chunk-QOQQVNP3.cjs} +2 -2
  63. package/dist/{chunk-KD7YD6US.cjs.map → chunk-QOQQVNP3.cjs.map} +1 -1
  64. package/dist/{chunk-6O6EYKOG.cjs → chunk-R4QWCP2T.cjs} +65 -65
  65. package/dist/{chunk-6O6EYKOG.cjs.map → chunk-R4QWCP2T.cjs.map} +1 -1
  66. package/dist/chunk-RCTIPBLU.cjs +1039 -0
  67. package/dist/chunk-RCTIPBLU.cjs.map +1 -0
  68. package/dist/{chunk-FSWHZY3T.cjs → chunk-RD2I67C4.cjs} +3 -3
  69. package/dist/{chunk-FSWHZY3T.cjs.map → chunk-RD2I67C4.cjs.map} +1 -1
  70. package/dist/{chunk-YGSPIDE4.js → chunk-RM4CBS7Y.js} +3 -3
  71. package/dist/{chunk-YGSPIDE4.js.map → chunk-RM4CBS7Y.js.map} +1 -1
  72. package/dist/{chunk-3O3Q3DX7.cjs → chunk-S3LTUSO5.cjs} +21 -21
  73. package/dist/{chunk-3O3Q3DX7.cjs.map → chunk-S3LTUSO5.cjs.map} +1 -1
  74. package/dist/{chunk-2OXNJJHJ.cjs → chunk-TERHGQ73.cjs} +3 -3
  75. package/dist/{chunk-2OXNJJHJ.cjs.map → chunk-TERHGQ73.cjs.map} +1 -1
  76. package/dist/{chunk-DO7EED3B.cjs → chunk-WZ7WTIVU.cjs} +3 -3
  77. package/dist/{chunk-DO7EED3B.cjs.map → chunk-WZ7WTIVU.cjs.map} +1 -1
  78. package/dist/{chunk-566FIEPE.js → chunk-YFZNSP7F.js} +3 -3
  79. package/dist/{chunk-566FIEPE.js.map → chunk-YFZNSP7F.js.map} +1 -1
  80. package/dist/{chunk-I6Y2KLTZ.js → chunk-ZP4H5HA4.js} +3 -3
  81. package/dist/{chunk-I6Y2KLTZ.js.map → chunk-ZP4H5HA4.js.map} +1 -1
  82. package/dist/components/ActionPlan/index.cjs +1 -1
  83. package/dist/components/ActionPlan/index.js +1 -1
  84. package/dist/components/AlertDialog/index.cjs +7 -7
  85. package/dist/components/AlertDialog/index.js +3 -3
  86. package/dist/components/CustomizableDashboard/index.cjs +1 -1
  87. package/dist/components/CustomizableDashboard/index.js +1 -1
  88. package/dist/components/DateInput/index.cjs +3 -3
  89. package/dist/components/DateInput/index.js +2 -2
  90. package/dist/components/EmailEditor/index.cjs +16 -13
  91. package/dist/components/EmailEditor/index.js +7 -4
  92. package/dist/components/KeyboardShortcutsOverlay/index.cjs +5 -5
  93. package/dist/components/KeyboardShortcutsOverlay/index.js +3 -3
  94. package/dist/components/Markdown/index.cjs +10 -10
  95. package/dist/components/Markdown/index.js +2 -2
  96. package/dist/components/MediaEditor/index.cjs +9 -9
  97. package/dist/components/MediaEditor/index.js +4 -4
  98. package/dist/components/MediaFeed/index.cjs +4 -4
  99. package/dist/components/MediaFeed/index.js +3 -3
  100. package/dist/components/Modal/index.cjs +10 -10
  101. package/dist/components/Modal/index.js +2 -2
  102. package/dist/components/RadialExplorer/index.cjs +3 -3
  103. package/dist/components/RadialExplorer/index.js +2 -2
  104. package/dist/components/RecordHeader/index.cjs +4 -4
  105. package/dist/components/RecordHeader/index.js +2 -2
  106. package/dist/components/RecordLayout/index.cjs +1 -1
  107. package/dist/components/RecordLayout/index.js +1 -1
  108. package/dist/components/Sheet/index.cjs +1 -1
  109. package/dist/components/Sheet/index.js +1 -1
  110. package/dist/components/SuperChat/index.cjs +6 -6
  111. package/dist/components/SuperChat/index.js +5 -5
  112. package/dist/components/SuperChat/plugins/index.cjs +1 -1
  113. package/dist/components/SuperChat/plugins/index.js +1 -1
  114. package/dist/components/TagEditor/index.cjs +4 -4
  115. package/dist/components/TagEditor/index.js +2 -2
  116. package/dist/components/Textarea/index.cjs +7 -4
  117. package/dist/components/Textarea/index.js +4 -1
  118. package/dist/esheet.cjs +122 -123
  119. package/dist/esheet.cjs.map +1 -1
  120. package/dist/esheet.js +25 -26
  121. package/dist/esheet.js.map +1 -1
  122. package/dist/hooks/index.cjs +15 -15
  123. package/dist/hooks/index.js +3 -3
  124. package/dist/index.cjs +456 -425
  125. package/dist/index.cjs.map +1 -1
  126. package/dist/index.d.cts +19 -10
  127. package/dist/index.d.ts +19 -10
  128. package/dist/index.js +80 -42
  129. package/dist/index.js.map +1 -1
  130. package/dist/kerebron.cjs +32 -62
  131. package/dist/kerebron.cjs.map +1 -1
  132. package/dist/kerebron.css +145 -8
  133. package/dist/kerebron.d.cts +299 -1
  134. package/dist/kerebron.d.ts +299 -1
  135. package/dist/kerebron.js +22 -63
  136. package/dist/kerebron.js.map +1 -1
  137. package/dist/{model-EFXX5JZN.cjs → model-5PB6KNYH.cjs} +20 -20
  138. package/dist/{model-EFXX5JZN.cjs.map → model-5PB6KNYH.cjs.map} +1 -1
  139. package/dist/{model-TAARSYDY.js → model-OGWTZEXR.js} +6 -6
  140. package/dist/{model-TAARSYDY.js.map → model-OGWTZEXR.js.map} +1 -1
  141. package/dist/styles.css +1 -1
  142. package/dist/tailwind-preset.cjs +4 -4
  143. package/dist/tailwind-preset.js +1 -1
  144. package/dist/templates.cjs +31 -31
  145. package/dist/templates.js +2 -2
  146. package/dist/utils/index.cjs +5 -5
  147. package/dist/utils/index.d.cts +12 -12
  148. package/dist/utils/index.d.ts +12 -12
  149. package/dist/utils/index.js +1 -1
  150. package/package.json +3 -13
  151. package/dist/chunk-25MPUVTM.js.map +0 -1
  152. package/dist/chunk-4SUEBKTY.js.map +0 -1
  153. package/dist/chunk-6UYMO3PF.cjs.map +0 -1
  154. package/dist/chunk-DXNVQQYN.cjs.map +0 -1
  155. package/dist/chunk-JEXEFWEM.js.map +0 -1
  156. package/dist/chunk-QO4A6U73.cjs.map +0 -1
@@ -0,0 +1,1017 @@
1
+ import { RichEditor } from './chunk-NIE3WBJH.js';
2
+ import { processDictation, MacroLibrary } from './chunk-EWPNM6OO.js';
3
+ import { useMessages } from './chunk-5YOT4DVH.js';
4
+ import { Dropdown, DropdownItem, DropdownSeparator, DropdownSubmenu, DropdownLabel } from './chunk-OYUIEJPN.js';
5
+ import { useMediaQuery } from './chunk-HTZ27DGZ.js';
6
+ import { usePrefersReducedMotion } from './chunk-XDU65GLW.js';
7
+ import { useDirection } from './chunk-YVKBXNPK.js';
8
+ import { Button } from './chunk-WX2XB6FP.js';
9
+ import { AnimatedPresence, Animated } from './chunk-F4N67NAF.js';
10
+ import { cn } from './chunk-TQONQSJO.js';
11
+ import * as React2 from 'react';
12
+ import { useRef, useEffect } from 'react';
13
+ import { Extension, CoreEditor } from '@kerebron/editor';
14
+ import { CodeEditorKit } from '@kerebron/editor-kits/CodeEditorKit';
15
+ import { createAssetLoad } from '@kerebron/wasm/web';
16
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
17
+ import { TextSelection, Plugin } from 'prosemirror-state';
18
+ import { MenuItem } from '@kerebron/extension-menu';
19
+ import { Plus } from 'lucide-react';
20
+
21
+ var CodeEditor = ({
22
+ value = "",
23
+ onChange,
24
+ lang = "typescript"
25
+ }) => {
26
+ const editorRef = useRef(null);
27
+ const editorInstance = useRef(null);
28
+ const onChangeRef = useRef(onChange);
29
+ onChangeRef.current = onChange;
30
+ useEffect(() => {
31
+ const root = editorRef.current;
32
+ if (!root) return;
33
+ const applySelectLabel = () => {
34
+ root.querySelectorAll("select.codejar-select").forEach((element) => {
35
+ if (!element.getAttribute("aria-label")) {
36
+ element.setAttribute("aria-label", "Code editor language");
37
+ }
38
+ });
39
+ };
40
+ applySelectLabel();
41
+ const observer = new MutationObserver(() => applySelectLabel());
42
+ observer.observe(root, { childList: true, subtree: true });
43
+ return () => observer.disconnect();
44
+ }, []);
45
+ useEffect(() => {
46
+ if (!editorRef.current) return;
47
+ const editor = CoreEditor.create({
48
+ element: editorRef.current,
49
+ uri: "file:///untitled.ts",
50
+ assetLoad: createAssetLoad("/kerebron-wasm"),
51
+ editorKits: [new CodeEditorKit(lang)]
52
+ });
53
+ editorInstance.current = editor;
54
+ const onTransaction = async () => {
55
+ if (!editorInstance.current) return;
56
+ try {
57
+ const buffer = await editorInstance.current.saveDocument("text/plain");
58
+ onChangeRef.current?.(new globalThis.TextDecoder().decode(buffer));
59
+ } catch (err) {
60
+ console.error("Failed to read code:", err);
61
+ }
62
+ };
63
+ editor.addEventListener("transaction", onTransaction);
64
+ if (value) {
65
+ editor.loadDocumentText("text/plain", value).catch((err) => console.error("Failed to load code:", err));
66
+ }
67
+ return () => {
68
+ editor.removeEventListener("transaction", onTransaction);
69
+ editor.destroy();
70
+ };
71
+ }, []);
72
+ return /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx("div", { ref: editorRef, className: "kb-component" }) }) });
73
+ };
74
+ CodeEditor.displayName = "CodeEditor";
75
+
76
+ // src/components/TextAreaRich/speech.ts
77
+ function getSpeechRecognition() {
78
+ if (typeof window === "undefined") return void 0;
79
+ const w = window;
80
+ return w.SpeechRecognition || w.webkitSpeechRecognition;
81
+ }
82
+ var TextAreaRichExtension = class extends Extension {
83
+ constructor(onKeyDown) {
84
+ super({});
85
+ this.onKeyDown = onKeyDown;
86
+ }
87
+ onKeyDown;
88
+ name = "textAreaRichShortcuts";
89
+ getProseMirrorPlugins() {
90
+ return [new Plugin({ props: { handleKeyDown: this.onKeyDown } })];
91
+ }
92
+ };
93
+ var viewOf = (editor) => editor.view;
94
+ var UNITLESS_STYLE_PROPS = /* @__PURE__ */ new Set([
95
+ "animationIterationCount",
96
+ "aspectRatio",
97
+ "borderImageOutset",
98
+ "borderImageSlice",
99
+ "borderImageWidth",
100
+ "boxFlex",
101
+ "boxFlexGroup",
102
+ "boxOrdinalGroup",
103
+ "columnCount",
104
+ "columns",
105
+ "flex",
106
+ "flexGrow",
107
+ "flexPositive",
108
+ "flexShrink",
109
+ "flexNegative",
110
+ "flexOrder",
111
+ "fontWeight",
112
+ "gridArea",
113
+ "gridRow",
114
+ "gridRowEnd",
115
+ "gridRowSpan",
116
+ "gridRowStart",
117
+ "gridColumn",
118
+ "gridColumnEnd",
119
+ "gridColumnSpan",
120
+ "gridColumnStart",
121
+ "lineClamp",
122
+ "lineHeight",
123
+ "opacity",
124
+ "order",
125
+ "orphans",
126
+ "tabSize",
127
+ "widows",
128
+ "zIndex",
129
+ "zoom",
130
+ // SVG-related
131
+ "fillOpacity",
132
+ "floodOpacity",
133
+ "stopOpacity",
134
+ "strokeDasharray",
135
+ "strokeDashoffset",
136
+ "strokeMiterlimit",
137
+ "strokeOpacity",
138
+ "strokeWidth"
139
+ ]);
140
+ function insertPlainText(view, text, from = view.state.selection.from, to = view.state.selection.to) {
141
+ const { schema, selection, storedMarks } = view.state;
142
+ const marks = (from === selection.from && to === selection.to ? storedMarks : null) ?? view.state.doc.resolve(from).marks();
143
+ const br = schema.nodes.br ?? schema.nodes.hard_break;
144
+ const lines = text.replace(/\r\n?/g, "\n").split("\n");
145
+ const nodes = [];
146
+ lines.forEach((line, index) => {
147
+ if (index > 0) {
148
+ if (br) nodes.push(br.create(null, null, marks));
149
+ else if (line) nodes.push(schema.text(" ", marks));
150
+ }
151
+ if (line) nodes.push(schema.text(line, marks));
152
+ });
153
+ let tr = view.state.tr.replaceWith(from, to, nodes);
154
+ const end = Math.min(
155
+ from + nodes.reduce((size, node) => size + node.nodeSize, 0),
156
+ tr.doc.content.size
157
+ );
158
+ tr = tr.setSelection(TextSelection.near(tr.doc.resolve(end)));
159
+ view.dispatch(tr.scrollIntoView());
160
+ }
161
+ function docIsEmpty(view) {
162
+ const { doc } = view.state;
163
+ return doc.childCount === 1 && !!doc.firstChild && doc.firstChild.isTextblock && doc.firstChild.content.size === 0;
164
+ }
165
+ var BLOCK_FORMAT_ITEMS = [
166
+ { key: "text", label: "Text", command: "setParagraph" },
167
+ { key: "heading1", label: "Heading 1", command: "setHeading1" },
168
+ { key: "heading2", label: "Heading 2", command: "setHeading2" },
169
+ { key: "heading3", label: "Heading 3", command: "setHeading3" },
170
+ { key: "bulletList", label: "Bullet list", command: "toggleBulletList" },
171
+ { key: "numberedList", label: "Numbered list", command: "toggleOrderedList" },
172
+ { key: "quote", label: "Quote", command: "toggleBlockquote" }
173
+ ];
174
+ var TextAreaRichKerebron = React2.forwardRef(
175
+ ({
176
+ value = "",
177
+ onChange,
178
+ placeholder,
179
+ className,
180
+ enableDictation = true,
181
+ onDictationError,
182
+ disabled = false,
183
+ macros,
184
+ defaultMacrosOpen = false,
185
+ macrosOnFocus = false,
186
+ compactMacros = false,
187
+ onMacroInsert,
188
+ macroLabels,
189
+ onCreateMacro,
190
+ toolbar,
191
+ defaultToolbar = "hidden",
192
+ onToolbarChange,
193
+ blockMenuLabels,
194
+ id,
195
+ readOnly = false,
196
+ editorClassName,
197
+ editorStyle,
198
+ "aria-label": ariaLabel,
199
+ "aria-labelledby": ariaLabelledBy,
200
+ "aria-describedby": ariaDescribedBy,
201
+ "aria-invalid": ariaInvalid,
202
+ "aria-required": ariaRequired
203
+ }, ref) => {
204
+ const inert = disabled || readOnly;
205
+ const wrapperRef = React2.useRef(null);
206
+ const gutterWrapperRef = React2.useRef(null);
207
+ const editorHandleRef = React2.useRef(null);
208
+ const editorRef = React2.useRef(null);
209
+ const [ready, setReady] = React2.useState(false);
210
+ React2.useImperativeHandle(
211
+ ref,
212
+ () => ready && editorRef.current ? viewOf(editorRef.current).dom : wrapperRef.current,
213
+ [ready]
214
+ );
215
+ const [macrosOpen, setMacrosOpen] = React2.useState(
216
+ defaultMacrosOpen && !macrosOnFocus
217
+ );
218
+ const [uncontrolledToolbar, setUncontrolledToolbar] = React2.useState(defaultToolbar);
219
+ const toolbarVisible = (toolbar ?? uncontrolledToolbar) === "visible";
220
+ const [isListening, setIsListening] = React2.useState(false);
221
+ const [speechSupported, setSpeechSupported] = React2.useState(false);
222
+ const [isEmpty, setIsEmpty] = React2.useState(true);
223
+ const [editorTop, setEditorTop] = React2.useState(0);
224
+ const [blockMenuOpen, setBlockMenuOpen] = React2.useState(false);
225
+ const [gutterTop, setGutterTop] = React2.useState(4);
226
+ const recognitionRef = React2.useRef(null);
227
+ const isListeningRef = React2.useRef(false);
228
+ const shouldRestartRef = React2.useRef(false);
229
+ const dictationSessionRef = React2.useRef(0);
230
+ const hoverBlockRef = React2.useRef(null);
231
+ const blockTargetPosRef = React2.useRef(null);
232
+ const firstBlockItemRef = React2.useRef(null);
233
+ const macroPanelId = React2.useId();
234
+ const macroSidePanel = useMediaQuery("(min-width: 768px)");
235
+ const prefersReducedMotion = usePrefersReducedMotion();
236
+ const direction = useDirection(wrapperRef);
237
+ const hasMacros = Boolean(macros);
238
+ const hasMacroEntries = Boolean(
239
+ macros?.some((group) => group.macros.length > 0)
240
+ );
241
+ const gutterActive = !inert;
242
+ const macrosRef = React2.useRef(macros);
243
+ macrosRef.current = macros;
244
+ const hasMacrosRef = React2.useRef(hasMacros);
245
+ hasMacrosRef.current = hasMacros;
246
+ const onMacroInsertRef = React2.useRef(onMacroInsert);
247
+ onMacroInsertRef.current = onMacroInsert;
248
+ const onChangeRef = React2.useRef(onChange);
249
+ onChangeRef.current = onChange;
250
+ const inertRef = React2.useRef(inert);
251
+ inertRef.current = inert;
252
+ const macrosOpenRef = React2.useRef(macrosOpen);
253
+ macrosOpenRef.current = macrosOpen;
254
+ const toolbarVisibleRef = React2.useRef(toolbarVisible);
255
+ toolbarVisibleRef.current = toolbarVisible;
256
+ const dictationAvailableRef = React2.useRef(false);
257
+ dictationAvailableRef.current = enableDictation && speechSupported;
258
+ const onDictationErrorRef = React2.useRef(onDictationError);
259
+ onDictationErrorRef.current = onDictationError;
260
+ const m = useMessages();
261
+ const reportError = (message) => {
262
+ if (onDictationErrorRef.current) onDictationErrorRef.current(message);
263
+ else if (typeof console !== "undefined") console.warn(message);
264
+ };
265
+ const setToolbarVisibility = (next) => {
266
+ if (toolbar === void 0) setUncontrolledToolbar(next);
267
+ onToolbarChange?.(next);
268
+ };
269
+ const focusEditor = () => editorHandleRef.current?.focus();
270
+ const currentView = () => editorRef.current ? viewOf(editorRef.current) : null;
271
+ const changeWaitersRef = React2.useRef([]);
272
+ const publishedDocRef = React2.useRef(null);
273
+ const handleChange = (next) => {
274
+ publishedDocRef.current = currentView()?.state.doc ?? null;
275
+ onChangeRef.current?.(next);
276
+ const waiters = changeWaitersRef.current;
277
+ changeWaitersRef.current = [];
278
+ for (const resolve of waiters) resolve();
279
+ };
280
+ const afterNextChange = () => new Promise((resolve) => {
281
+ const timer = setTimeout(resolve, 250);
282
+ changeWaitersRef.current.push(() => {
283
+ clearTimeout(timer);
284
+ resolve();
285
+ });
286
+ });
287
+ const insertMacro = (macro) => {
288
+ if (inertRef.current) return;
289
+ if (!onMacroInsertRef.current) {
290
+ const view2 = currentView();
291
+ if (!view2) return;
292
+ view2.focus();
293
+ insertPlainText(view2, macro.content);
294
+ return;
295
+ }
296
+ const view = currentView();
297
+ const pending = view && view.state.doc !== publishedDocRef.current;
298
+ const invoke = () => {
299
+ if (inertRef.current) return;
300
+ const liveView = currentView();
301
+ if (!liveView) return;
302
+ liveView.focus();
303
+ if (onMacroInsertRef.current?.(macro) === true) return;
304
+ insertPlainText(liveView, macro.content);
305
+ };
306
+ if (pending) void afterNextChange().then(invoke);
307
+ else invoke();
308
+ };
309
+ const expandTypedShortcut = (view) => {
310
+ const groups = macrosRef.current;
311
+ if (!groups) return false;
312
+ const { selection } = view.state;
313
+ if (!selection.empty) return false;
314
+ const { $from } = selection;
315
+ const textBefore = $from.parent.textBetween(
316
+ 0,
317
+ $from.parentOffset,
318
+ void 0,
319
+ "\n"
320
+ );
321
+ const match = /(\S+)$/.exec(textBefore);
322
+ if (!match) return false;
323
+ const lowered = match[1].toLowerCase();
324
+ const macro = groups.flatMap((group) => group.macros).find((candidate) => candidate.shortcut?.toLowerCase() === lowered);
325
+ if (!macro) return false;
326
+ const from = $from.pos - match[1].length;
327
+ if (!onMacroInsertRef.current) {
328
+ insertPlainText(view, macro.content, from, $from.pos);
329
+ return true;
330
+ }
331
+ const editor = editorRef.current;
332
+ view.dispatch(view.state.tr.delete(from, $from.pos));
333
+ if (!editor) return true;
334
+ let insertAt = from;
335
+ let targetValid = true;
336
+ const trackInsertionPoint = (event) => {
337
+ const tr = event.detail?.transaction;
338
+ if (!tr) return;
339
+ const mapped = tr.mapping.mapResult(insertAt);
340
+ if (mapped.deleted) targetValid = false;
341
+ insertAt = mapped.pos;
342
+ };
343
+ editor.addEventListener("transaction", trackInsertionPoint);
344
+ void afterNextChange().then(() => {
345
+ editor.removeEventListener("transaction", trackInsertionPoint);
346
+ if (inertRef.current || editorRef.current !== editor) return;
347
+ if (onMacroInsertRef.current?.(macro) === true) return;
348
+ if (!targetValid) return;
349
+ const liveView = viewOf(editor);
350
+ liveView.focus();
351
+ const at = Math.min(insertAt, liveView.state.doc.content.size);
352
+ insertPlainText(liveView, macro.content, at, at);
353
+ });
354
+ return true;
355
+ };
356
+ const resetGutter = () => {
357
+ hoverBlockRef.current = null;
358
+ setGutterTop(editorTop + 4);
359
+ };
360
+ const handleGutterMouseMove = (event) => {
361
+ if (blockMenuOpen) return;
362
+ const view = currentView();
363
+ const wrapper = gutterWrapperRef.current;
364
+ if (!view || !wrapper) return;
365
+ let target = null;
366
+ let top = null;
367
+ for (const child of Array.from(view.dom.children)) {
368
+ const rect = child.getBoundingClientRect();
369
+ if (rect.width === 0 && rect.height === 0) continue;
370
+ if (rect.top <= event.clientY) {
371
+ target = child;
372
+ top = rect.top;
373
+ }
374
+ }
375
+ if (!target || top === null) {
376
+ resetGutter();
377
+ return;
378
+ }
379
+ hoverBlockRef.current = target;
380
+ const wrapperTop = wrapper.getBoundingClientRect().top;
381
+ const next = Math.max(Math.round(top - wrapperTop), editorTop + 4);
382
+ setGutterTop((current) => current === next ? current : next);
383
+ };
384
+ const handleBlockMenuOpenChange = (open) => {
385
+ const view = currentView();
386
+ if (open) {
387
+ const block = hoverBlockRef.current;
388
+ blockTargetPosRef.current = view && block && view.dom.contains(block) ? view.posAtDOM(block, 0) : null;
389
+ } else if (view && document.activeElement?.closest('[role="menu"]')) {
390
+ view.focus();
391
+ }
392
+ setBlockMenuOpen(open);
393
+ };
394
+ const openBlockMenuAtCaret = () => {
395
+ const view = currentView();
396
+ const wrapper = gutterWrapperRef.current;
397
+ if (view && wrapper) {
398
+ const { from } = view.state.selection;
399
+ blockTargetPosRef.current = from;
400
+ try {
401
+ const coords = view.coordsAtPos(from);
402
+ const wrapperTop = wrapper.getBoundingClientRect().top;
403
+ setGutterTop(
404
+ Math.max(Math.round(coords.top - wrapperTop), editorTop + 4)
405
+ );
406
+ } catch {
407
+ }
408
+ }
409
+ setBlockMenuOpen(true);
410
+ };
411
+ const applyBlockTarget = (view) => {
412
+ const pos = blockTargetPosRef.current;
413
+ if (pos === null) return;
414
+ const clamped = Math.min(pos, view.state.doc.content.size);
415
+ view.dispatch(
416
+ view.state.tr.setSelection(
417
+ TextSelection.near(view.state.doc.resolve(clamped))
418
+ )
419
+ );
420
+ };
421
+ const applyBlockFormat = (command) => {
422
+ const editor = editorRef.current;
423
+ if (inert || !editor) return;
424
+ setBlockMenuOpen(false);
425
+ const view = viewOf(editor);
426
+ view.focus();
427
+ applyBlockTarget(view);
428
+ editor.run[command]?.();
429
+ };
430
+ React2.useEffect(() => {
431
+ if (!blockMenuOpen) return;
432
+ const frame = requestAnimationFrame(
433
+ () => firstBlockItemRef.current?.focus()
434
+ );
435
+ return () => cancelAnimationFrame(frame);
436
+ }, [blockMenuOpen]);
437
+ const toggleDictation = React2.useRef(async () => {
438
+ const SpeechRecognition = getSpeechRecognition();
439
+ if (!SpeechRecognition) return;
440
+ if (isListeningRef.current || recognitionRef.current) {
441
+ stopDictation();
442
+ return;
443
+ }
444
+ const session = ++dictationSessionRef.current;
445
+ try {
446
+ const stream = await navigator.mediaDevices.getUserMedia({
447
+ audio: true
448
+ });
449
+ stream.getTracks().forEach((track) => track.stop());
450
+ } catch {
451
+ reportError(
452
+ "Microphone access is required for dictation. Please allow microphone access in your browser settings and try again."
453
+ );
454
+ return;
455
+ }
456
+ if (session !== dictationSessionRef.current) return;
457
+ if (!toolbarVisibleRef.current || inertRef.current) return;
458
+ const recognition = new SpeechRecognition();
459
+ recognition.continuous = true;
460
+ recognition.interimResults = true;
461
+ recognition.lang = "en-US";
462
+ recognition.maxAlternatives = 1;
463
+ recognition.onstart = () => {
464
+ if (session !== dictationSessionRef.current) {
465
+ try {
466
+ recognition.stop();
467
+ } catch {
468
+ }
469
+ return;
470
+ }
471
+ setIsListening(true);
472
+ isListeningRef.current = true;
473
+ shouldRestartRef.current = true;
474
+ };
475
+ recognition.onresult = (event) => {
476
+ if (session !== dictationSessionRef.current || recognitionRef.current !== recognition) {
477
+ return;
478
+ }
479
+ if (inertRef.current) return;
480
+ let finalTranscript = "";
481
+ for (let i = event.resultIndex; i < event.results.length; i++) {
482
+ const result = event.results[i];
483
+ if (result.isFinal) finalTranscript += result[0].transcript;
484
+ }
485
+ const view = currentView();
486
+ if (finalTranscript && view) {
487
+ view.focus();
488
+ insertPlainText(view, processDictation(finalTranscript) + " ");
489
+ }
490
+ };
491
+ recognition.onerror = (event) => {
492
+ if (session !== dictationSessionRef.current || recognitionRef.current !== recognition) {
493
+ return;
494
+ }
495
+ if (event.error === "no-speech" || event.error === "aborted") return;
496
+ if (event.error === "not-allowed") {
497
+ reportError(
498
+ "Microphone access was denied. Please allow microphone access in your browser settings and try again."
499
+ );
500
+ shouldRestartRef.current = false;
501
+ isListeningRef.current = false;
502
+ setIsListening(false);
503
+ return;
504
+ }
505
+ if (event.error === "network") return;
506
+ shouldRestartRef.current = false;
507
+ isListeningRef.current = false;
508
+ setIsListening(false);
509
+ };
510
+ recognition.onend = () => {
511
+ if (session === dictationSessionRef.current && shouldRestartRef.current && isListeningRef.current && !inertRef.current) {
512
+ setTimeout(() => {
513
+ if (session === dictationSessionRef.current && shouldRestartRef.current && isListeningRef.current && !inertRef.current) {
514
+ try {
515
+ recognition.start();
516
+ } catch {
517
+ shouldRestartRef.current = false;
518
+ isListeningRef.current = false;
519
+ if (recognitionRef.current === recognition)
520
+ recognitionRef.current = null;
521
+ setIsListening(false);
522
+ }
523
+ }
524
+ }, 100);
525
+ } else {
526
+ if (recognitionRef.current === recognition)
527
+ recognitionRef.current = null;
528
+ if (session === dictationSessionRef.current) setIsListening(false);
529
+ }
530
+ };
531
+ recognitionRef.current = recognition;
532
+ try {
533
+ recognition.start();
534
+ } catch {
535
+ recognitionRef.current = null;
536
+ reportError("Failed to start speech recognition. Please try again.");
537
+ }
538
+ }).current;
539
+ const stopDictation = React2.useCallback(() => {
540
+ dictationSessionRef.current += 1;
541
+ shouldRestartRef.current = false;
542
+ isListeningRef.current = false;
543
+ const recognition = recognitionRef.current;
544
+ recognitionRef.current = null;
545
+ try {
546
+ recognition?.stop();
547
+ } catch {
548
+ }
549
+ setIsListening(false);
550
+ }, []);
551
+ React2.useEffect(() => {
552
+ setSpeechSupported(Boolean(getSpeechRecognition()));
553
+ }, []);
554
+ React2.useEffect(() => {
555
+ if (!toolbarVisible || inert || !enableDictation) stopDictation();
556
+ }, [toolbarVisible, inert, enableDictation, stopDictation]);
557
+ React2.useEffect(() => stopDictation, [stopDictation]);
558
+ const openBlockMenuAtCaretRef = React2.useRef(openBlockMenuAtCaret);
559
+ openBlockMenuAtCaretRef.current = openBlockMenuAtCaret;
560
+ const expandTypedShortcutRef = React2.useRef(expandTypedShortcut);
561
+ expandTypedShortcutRef.current = expandTypedShortcut;
562
+ const [menuAddon] = React2.useState(() => {
563
+ const macrosItem = new MenuItem({
564
+ icon: {
565
+ width: 24,
566
+ height: 24,
567
+ // Material Symbols "text snippet" (Apache-2.0)
568
+ path: "M20.41 8.41l-4.83-4.83c-.37-.37-.88-.58-1.41-.58H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V9.83c0-.53-.21-1.04-.59-1.42zM7 7h7v2H7V7zm10 10H7v-2h10v2zm0-4H7v-2h10v2z"
569
+ },
570
+ title: macroLabels?.showMacros ?? "Show macros",
571
+ class: "tar-menu-macros",
572
+ select: () => hasMacrosRef.current,
573
+ enable: () => !inertRef.current,
574
+ active: () => macrosOpenRef.current,
575
+ run: () => {
576
+ setMacrosOpen((open) => !open);
577
+ return true;
578
+ }
579
+ });
580
+ const dictateItem = new MenuItem({
581
+ icon: {
582
+ width: 24,
583
+ height: 24,
584
+ // Material Symbols "mic" (Apache-2.0)
585
+ path: "M12 14c1.66 0 2.99-1.34 2.99-3L15 5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3zm5.3-3c0 3-2.54 5.1-5.3 5.1S6.7 14 6.7 11H5c0 3.41 2.72 6.23 6 6.72V21h2v-3.28c3.28-.48 6-3.3 6-6.72h-1.7z"
586
+ },
587
+ title: m("Start dictation"),
588
+ class: "tar-menu-dictate",
589
+ select: () => dictationAvailableRef.current,
590
+ enable: () => !inertRef.current,
591
+ active: () => isListeningRef.current,
592
+ run: () => {
593
+ void toggleDictation();
594
+ return true;
595
+ }
596
+ });
597
+ const modifyMenu = (menus) => [
598
+ ...menus,
599
+ // The patched MenuItem types `render().dom` as nullable, which no
600
+ // longer satisfies MenuElement; our items always render a button.
601
+ [macrosItem, dictateItem]
602
+ ];
603
+ const kit = {
604
+ getExtensions: () => [
605
+ new TextAreaRichExtension((view, event) => {
606
+ if (inertRef.current) return false;
607
+ if (event.key === "/" && (event.ctrlKey || event.metaKey) && !event.altKey) {
608
+ openBlockMenuAtCaretRef.current();
609
+ return true;
610
+ }
611
+ if (event.key !== " " || event.isComposing || event.ctrlKey || event.metaKey || event.altKey) {
612
+ return false;
613
+ }
614
+ return expandTypedShortcutRef.current(view);
615
+ })
616
+ ]
617
+ };
618
+ return { modifyMenu, kits: [kit] };
619
+ });
620
+ React2.useEffect(() => {
621
+ const root = wrapperRef.current;
622
+ if (!root || !ready) return;
623
+ const sync = () => {
624
+ for (const toolbar2 of root.querySelectorAll(
625
+ ".kb-custom-menu, .kb-custom-menu__overflow-menu"
626
+ )) {
627
+ toolbar2.inert = inert;
628
+ }
629
+ for (const macrosButton of root.querySelectorAll(
630
+ ".tar-menu-macros"
631
+ )) {
632
+ macrosButton.style.display = hasMacros ? "" : "none";
633
+ macrosButton.classList.toggle("kb-menu__button--disabled", inert);
634
+ macrosButton.setAttribute("aria-disabled", String(inert));
635
+ macrosButton.classList.toggle("kb-menu__button--active", macrosOpen);
636
+ macrosButton.setAttribute("aria-pressed", String(macrosOpen));
637
+ const title = macrosOpen ? macroLabels?.hideMacros ?? "Hide macros" : macroLabels?.showMacros ?? "Show macros";
638
+ macrosButton.setAttribute("title", title);
639
+ macrosButton.setAttribute("aria-label", title);
640
+ }
641
+ for (const dictateButton of root.querySelectorAll(
642
+ ".tar-menu-dictate"
643
+ )) {
644
+ dictateButton.style.display = enableDictation && speechSupported ? "" : "none";
645
+ dictateButton.classList.toggle("kb-menu__button--disabled", inert);
646
+ dictateButton.setAttribute("aria-disabled", String(inert));
647
+ dictateButton.classList.toggle(
648
+ "kb-menu__button--active",
649
+ isListening
650
+ );
651
+ dictateButton.setAttribute("aria-pressed", String(isListening));
652
+ const title = isListening ? m("Stop dictation") : m("Start dictation");
653
+ dictateButton.setAttribute("title", title);
654
+ dictateButton.setAttribute("aria-label", title);
655
+ }
656
+ };
657
+ sync();
658
+ const observer = new MutationObserver((mutations) => {
659
+ if (mutations.some((mutation) => mutation.addedNodes.length > 0)) {
660
+ sync();
661
+ }
662
+ });
663
+ observer.observe(root, { childList: true, subtree: true });
664
+ return () => observer.disconnect();
665
+ }, [
666
+ ready,
667
+ inert,
668
+ hasMacros,
669
+ macrosOpen,
670
+ macroLabels,
671
+ enableDictation,
672
+ speechSupported,
673
+ isListening,
674
+ toolbarVisible,
675
+ m
676
+ ]);
677
+ const handleReady = (editor) => {
678
+ editorRef.current = editor;
679
+ publishedDocRef.current = viewOf(editor).state.doc;
680
+ setIsEmpty(docIsEmpty(viewOf(editor)));
681
+ editor.addEventListener("transaction", () => {
682
+ if (editorRef.current !== editor) return;
683
+ setIsEmpty(docIsEmpty(viewOf(editor)));
684
+ });
685
+ setReady(true);
686
+ };
687
+ React2.useEffect(() => {
688
+ const view = currentView();
689
+ if (!view || !ready) return;
690
+ view.setProps({
691
+ attributes: {
692
+ role: "textbox",
693
+ "aria-multiline": "true",
694
+ ...id ? { id } : {},
695
+ ...ariaLabel ?? placeholder ? { "aria-label": ariaLabel ?? placeholder } : {},
696
+ ...ariaLabelledBy ? { "aria-labelledby": ariaLabelledBy } : {},
697
+ ...ariaDescribedBy ? { "aria-describedby": ariaDescribedBy } : {},
698
+ ...ariaInvalid ? { "aria-invalid": String(ariaInvalid) } : {},
699
+ // Booleanish: the string "false" must not read as required.
700
+ ...ariaRequired !== void 0 ? { "aria-required": String(ariaRequired) } : {},
701
+ ...readOnly ? { "aria-readonly": "true" } : {},
702
+ ...disabled ? { "aria-disabled": "true" } : {},
703
+ // Read-only keeps keyboard focus and text selection available;
704
+ // ProseMirror drops natural tabbability once `editable` is false.
705
+ tabindex: disabled ? "-1" : "0",
706
+ // setProps replaces the create-time attributes, so the base
707
+ // kb-editor class must be restated alongside any custom class.
708
+ class: editorClassName ? `kb-editor ${editorClassName}` : "kb-editor"
709
+ }
710
+ });
711
+ }, [
712
+ ready,
713
+ id,
714
+ ariaLabel,
715
+ placeholder,
716
+ ariaLabelledBy,
717
+ ariaDescribedBy,
718
+ ariaInvalid,
719
+ ariaRequired,
720
+ readOnly,
721
+ disabled,
722
+ editorClassName
723
+ ]);
724
+ const appliedEditorStyle = React2.useRef([]);
725
+ React2.useEffect(() => {
726
+ const view = currentView();
727
+ const wrapper = wrapperRef.current;
728
+ if (!view || !wrapper || !ready) return;
729
+ const editableStyle = view.dom.style;
730
+ for (const [style, property] of appliedEditorStyle.current) {
731
+ style.removeProperty(property);
732
+ }
733
+ appliedEditorStyle.current = [];
734
+ if (!editorStyle) return;
735
+ for (const [key, raw] of Object.entries(editorStyle)) {
736
+ if (raw === null || raw === void 0) continue;
737
+ const isCustomProperty = key.startsWith("--");
738
+ const property = isCustomProperty ? key : key.replace(/[A-Z]/g, (char) => `-${char.toLowerCase()}`);
739
+ const value2 = typeof raw === "number" && !isCustomProperty && !UNITLESS_STYLE_PROPS.has(key) ? `${raw}px` : String(raw);
740
+ const style = isCustomProperty ? wrapper.style : editableStyle;
741
+ style.setProperty(property, value2);
742
+ appliedEditorStyle.current.push([style, property]);
743
+ }
744
+ }, [ready, editorStyle]);
745
+ React2.useEffect(() => {
746
+ const view = currentView();
747
+ const wrapper = gutterWrapperRef.current;
748
+ if (!view || !wrapper || !ready) return;
749
+ const measure = () => {
750
+ const top = Math.max(
751
+ Math.round(
752
+ view.dom.getBoundingClientRect().top - wrapper.getBoundingClientRect().top
753
+ ),
754
+ 0
755
+ );
756
+ setEditorTop((current) => current === top ? current : top);
757
+ const hovered = hoverBlockRef.current;
758
+ if (hovered?.isConnected) {
759
+ const next = Math.max(
760
+ Math.round(
761
+ hovered.getBoundingClientRect().top - wrapper.getBoundingClientRect().top
762
+ ),
763
+ top + 4
764
+ );
765
+ setGutterTop((current) => current === next ? current : next);
766
+ } else {
767
+ hoverBlockRef.current = null;
768
+ setGutterTop(top + 4);
769
+ }
770
+ };
771
+ measure();
772
+ const observer = new ResizeObserver(measure);
773
+ observer.observe(wrapper);
774
+ return () => observer.disconnect();
775
+ }, [ready, toolbarVisible]);
776
+ return /* @__PURE__ */ jsx(
777
+ "div",
778
+ {
779
+ ref: wrapperRef,
780
+ "data-slot": "textarea-rich",
781
+ "data-toolbar": toolbarVisible ? "visible" : "hidden",
782
+ "data-inert": inert ? "" : void 0,
783
+ "data-macros-open": hasMacros && macrosOpen ? "" : void 0,
784
+ "data-compact-macros": compactMacros ? "" : void 0,
785
+ "data-gutter": gutterActive ? "" : void 0,
786
+ "data-block-menu": blockMenuOpen ? "" : void 0,
787
+ className: cn(
788
+ "border-input rounded-lg border",
789
+ "transition-colors duration-200",
790
+ "focus-within:ring-ring focus-within:border-transparent focus-within:ring-2",
791
+ "flex flex-col",
792
+ disabled && "bg-muted/20",
793
+ className
794
+ ),
795
+ onFocus: hasMacros && macrosOnFocus ? () => setMacrosOpen(true) : void 0,
796
+ onBlur: hasMacros && macrosOnFocus ? (event) => {
797
+ const related = event.relatedTarget;
798
+ if (related && event.currentTarget.contains(related)) return;
799
+ let menu = related?.closest('[role="menu"]');
800
+ while (menu && menu.id) {
801
+ const trigger = document.querySelector(
802
+ `[aria-controls="${globalThis.CSS.escape(menu.id)}"]`
803
+ );
804
+ if (!trigger) break;
805
+ if (event.currentTarget.contains(trigger)) return;
806
+ menu = trigger.closest('[role="menu"]');
807
+ }
808
+ setMacrosOpen(false);
809
+ } : void 0,
810
+ children: /* @__PURE__ */ jsxs("div", { className: "relative flex min-h-0 flex-1 flex-col", children: [
811
+ /* @__PURE__ */ jsxs(
812
+ "div",
813
+ {
814
+ ref: gutterWrapperRef,
815
+ "data-slot": "textarea-rich-body",
816
+ className: cn("relative flex-1", "group/tar-body"),
817
+ onMouseMove: gutterActive ? handleGutterMouseMove : void 0,
818
+ children: [
819
+ gutterActive && // eslint-disable-next-line jsx-a11y/no-static-element-interactions -- mousedown here only guards editor focus (preventDefault); the real controls inside are native buttons.
820
+ /* @__PURE__ */ jsx(
821
+ "div",
822
+ {
823
+ "data-slot": "textarea-rich-block-gutter",
824
+ className: "absolute start-1 z-10",
825
+ style: { top: gutterTop },
826
+ onMouseDown: (event) => event.preventDefault(),
827
+ children: /* @__PURE__ */ jsxs(
828
+ Dropdown,
829
+ {
830
+ open: blockMenuOpen,
831
+ onOpenChange: handleBlockMenuOpenChange,
832
+ placement: "bottom-start",
833
+ trigger: /* @__PURE__ */ jsx(
834
+ Button,
835
+ {
836
+ type: "button",
837
+ variant: "ghost",
838
+ size: "sm",
839
+ className: cn(
840
+ "h-6 w-6 p-0",
841
+ "opacity-0 transition-opacity",
842
+ "group-hover/tar-body:opacity-100",
843
+ "focus-visible:opacity-100",
844
+ "[@media(hover:none)]:opacity-100",
845
+ blockMenuOpen && "opacity-100"
846
+ ),
847
+ "aria-label": blockMenuLabels?.blockMenu ?? "Block menu",
848
+ title: blockMenuLabels?.blockMenu ?? "Block menu",
849
+ children: /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4" })
850
+ }
851
+ ),
852
+ children: [
853
+ BLOCK_FORMAT_ITEMS.map((item, index) => /* @__PURE__ */ jsx(
854
+ DropdownItem,
855
+ {
856
+ ref: index === 0 ? firstBlockItemRef : void 0,
857
+ onClick: () => applyBlockFormat(item.command),
858
+ children: blockMenuLabels?.[item.key] ?? item.label
859
+ },
860
+ item.key
861
+ )),
862
+ /* @__PURE__ */ jsx(DropdownSeparator, {}),
863
+ hasMacros && macros && /* @__PURE__ */ jsxs(Fragment, { children: [
864
+ /* @__PURE__ */ jsx(
865
+ DropdownSubmenu,
866
+ {
867
+ label: blockMenuLabels?.insertMacro ?? "Insert macro",
868
+ disabled: !hasMacroEntries,
869
+ children: blockMenuOpen && macros.flatMap((group) => [
870
+ ...macros.length > 1 ? [
871
+ /* @__PURE__ */ jsx(DropdownLabel, { children: group.label }, `h:${group.id}`)
872
+ ] : [],
873
+ ...group.macros.map((macro) => /* @__PURE__ */ jsx(
874
+ DropdownItem,
875
+ {
876
+ className: "[&>[data-slot=dropdown-item-label]]:min-w-0 [&>[data-slot=dropdown-item-label]]:flex-1",
877
+ onClick: () => {
878
+ setBlockMenuOpen(false);
879
+ const view = currentView();
880
+ if (view) {
881
+ view.focus();
882
+ applyBlockTarget(view);
883
+ }
884
+ insertMacro(macro);
885
+ },
886
+ children: /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 items-center gap-2", children: [
887
+ macro.shortcut && /* @__PURE__ */ jsx(
888
+ "span",
889
+ {
890
+ dir: "ltr",
891
+ className: "border-border bg-background text-muted-foreground rounded border px-1 font-mono text-[11px]",
892
+ children: macro.shortcut
893
+ }
894
+ ),
895
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: macro.label })
896
+ ] })
897
+ },
898
+ `m:${JSON.stringify([group.id, macro.id])}`
899
+ ))
900
+ ])
901
+ }
902
+ ),
903
+ /* @__PURE__ */ jsx(
904
+ DropdownItem,
905
+ {
906
+ onClick: () => {
907
+ setBlockMenuOpen(false);
908
+ setMacrosOpen(true);
909
+ focusEditor();
910
+ },
911
+ children: blockMenuLabels?.showMacroLibrary ?? "Show macro library"
912
+ }
913
+ ),
914
+ /* @__PURE__ */ jsx(DropdownSeparator, {})
915
+ ] }),
916
+ /* @__PURE__ */ jsx(
917
+ DropdownItem,
918
+ {
919
+ onClick: () => {
920
+ setBlockMenuOpen(false);
921
+ setToolbarVisibility(
922
+ toolbarVisible ? "hidden" : "visible"
923
+ );
924
+ focusEditor();
925
+ },
926
+ children: toolbarVisible ? blockMenuLabels?.hideToolbar ?? "Hide toolbar" : blockMenuLabels?.showToolbar ?? "Show toolbar"
927
+ }
928
+ )
929
+ ]
930
+ }
931
+ )
932
+ }
933
+ ),
934
+ isEmpty && placeholder && /* @__PURE__ */ jsx(
935
+ "div",
936
+ {
937
+ "aria-hidden": "true",
938
+ className: cn(
939
+ "text-muted-foreground pointer-events-none absolute start-0 end-0 z-[1]",
940
+ "truncate px-3 py-[7px]",
941
+ "transition-[padding] duration-200 motion-reduce:transition-none",
942
+ // Mirrors the editable's sliding gutter indent (permanent
943
+ // on touch devices, where the + trigger is always visible).
944
+ gutterActive && "group-focus-within/tar-body:ps-10 group-hover/tar-body:ps-10 [@media(hover:none)]:ps-10",
945
+ gutterActive && blockMenuOpen && "ps-10",
946
+ hasMacros && macrosOpen && "md:me-72",
947
+ editorClassName
948
+ ),
949
+ style: {
950
+ top: editorTop,
951
+ // Match the editable's metrics by construction: the same
952
+ // variables (inherited from the wrapper) with the same
953
+ // kerebron.css fallbacks that size the .kb-editor rule.
954
+ fontSize: "var(--textarea-rich-font-size, 1rem)",
955
+ lineHeight: "var(--textarea-rich-line-height, 1.5rem)"
956
+ },
957
+ children: placeholder
958
+ }
959
+ ),
960
+ /* @__PURE__ */ jsx(
961
+ RichEditor,
962
+ {
963
+ ref: editorHandleRef,
964
+ value,
965
+ onChange: handleChange,
966
+ valueType: "text/html",
967
+ strictValue: true,
968
+ modifyMenu: menuAddon.modifyMenu,
969
+ editorKits: menuAddon.kits,
970
+ onReady: handleReady,
971
+ disabled,
972
+ readOnly
973
+ }
974
+ )
975
+ ]
976
+ }
977
+ ),
978
+ hasMacros && /* @__PURE__ */ jsx(AnimatedPresence, { children: macrosOpen && /* @__PURE__ */ jsx(
979
+ Animated,
980
+ {
981
+ preset: macroSidePanel ? "menu" : "collapse",
982
+ enabled: macroSidePanel || !prefersReducedMotion,
983
+ mode: "presence",
984
+ custom: direction === "rtl" ? "left" : "right",
985
+ fallbackClassName: "animate-fade-in",
986
+ className: "border-border flex max-h-72 flex-col overflow-hidden border-t md:absolute md:end-0 md:bottom-0 md:max-h-none md:w-72 md:overflow-visible md:border-s md:border-t-0",
987
+ style: { top: editorTop },
988
+ children: /* @__PURE__ */ jsx(
989
+ MacroLibrary,
990
+ {
991
+ id: macroPanelId,
992
+ groups: macros,
993
+ onSelect: insertMacro,
994
+ targetLabel: ariaLabel ?? placeholder,
995
+ onClose: macrosOnFocus ? void 0 : () => {
996
+ setMacrosOpen(false);
997
+ focusEditor();
998
+ },
999
+ onCreateMacro,
1000
+ labels: macroLabels,
1001
+ compact: compactMacros,
1002
+ className: "min-h-0 flex-1"
1003
+ }
1004
+ )
1005
+ },
1006
+ "macro-panel"
1007
+ ) })
1008
+ ] })
1009
+ }
1010
+ );
1011
+ }
1012
+ );
1013
+ TextAreaRichKerebron.displayName = "TextAreaRichKerebron";
1014
+
1015
+ export { CodeEditor, TextAreaRichKerebron };
1016
+ //# sourceMappingURL=chunk-IALTKN3Y.js.map
1017
+ //# sourceMappingURL=chunk-IALTKN3Y.js.map