@x-plat/design-system 0.10.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/dist/components/AutoResizeTextArea/index.cjs +14 -5
  2. package/dist/components/AutoResizeTextArea/index.css +15 -0
  3. package/dist/components/AutoResizeTextArea/index.d.cts +4 -0
  4. package/dist/components/AutoResizeTextArea/index.d.ts +4 -0
  5. package/dist/components/AutoResizeTextArea/index.js +14 -5
  6. package/dist/components/Badge/index.d.cts +1 -1
  7. package/dist/components/Badge/index.d.ts +1 -1
  8. package/dist/components/ChatInput/index.cjs +20 -7
  9. package/dist/components/ChatInput/index.css +15 -0
  10. package/dist/components/ChatInput/index.d.cts +4 -0
  11. package/dist/components/ChatInput/index.d.ts +4 -0
  12. package/dist/components/ChatInput/index.js +20 -7
  13. package/dist/components/CheckBox/index.cjs +8 -3
  14. package/dist/components/CheckBox/index.css +15 -0
  15. package/dist/components/CheckBox/index.d.cts +1 -1
  16. package/dist/components/CheckBox/index.d.ts +1 -1
  17. package/dist/components/CheckBox/index.js +8 -3
  18. package/dist/components/DatePicker/index.cjs +179 -64
  19. package/dist/components/DatePicker/index.css +19 -0
  20. package/dist/components/DatePicker/index.js +179 -64
  21. package/dist/components/Drawer/index.cjs +83 -4
  22. package/dist/components/Drawer/index.js +83 -4
  23. package/dist/components/Dropdown/index.cjs +57 -16
  24. package/dist/components/Dropdown/index.css +3 -0
  25. package/dist/components/Dropdown/index.js +57 -16
  26. package/dist/components/Editor/index.cjs +779 -50
  27. package/dist/components/Editor/index.css +100 -0
  28. package/dist/components/Editor/index.d.cts +1 -1
  29. package/dist/components/Editor/index.d.ts +1 -1
  30. package/dist/components/Editor/index.js +779 -50
  31. package/dist/components/FieldMessage/index.cjs +8 -3
  32. package/dist/components/FieldMessage/index.css +15 -0
  33. package/dist/components/FieldMessage/index.d.cts +11 -4
  34. package/dist/components/FieldMessage/index.d.ts +11 -4
  35. package/dist/components/FieldMessage/index.js +8 -3
  36. package/dist/components/FileUpload/index.cjs +8 -3
  37. package/dist/components/FileUpload/index.css +15 -0
  38. package/dist/components/FileUpload/index.js +8 -3
  39. package/dist/components/ImageSelector/index.cjs +8 -3
  40. package/dist/components/ImageSelector/index.css +15 -0
  41. package/dist/components/ImageSelector/index.js +8 -3
  42. package/dist/components/Input/index.cjs +15 -5
  43. package/dist/components/Input/index.css +15 -0
  44. package/dist/components/Input/index.d.cts +4 -0
  45. package/dist/components/Input/index.d.ts +4 -0
  46. package/dist/components/Input/index.js +15 -5
  47. package/dist/components/Modal/index.cjs +84 -5
  48. package/dist/components/Modal/index.js +84 -5
  49. package/dist/components/NotificationBadge/index.d.cts +1 -1
  50. package/dist/components/NotificationBadge/index.d.ts +1 -1
  51. package/dist/components/Pagination/index.d.cts +1 -1
  52. package/dist/components/Pagination/index.d.ts +1 -1
  53. package/dist/components/PopOver/index.cjs +55 -13
  54. package/dist/components/PopOver/index.css +3 -0
  55. package/dist/components/PopOver/index.js +55 -13
  56. package/dist/components/Progress/index.d.cts +1 -1
  57. package/dist/components/Progress/index.d.ts +1 -1
  58. package/dist/components/Radio/index.cjs +8 -3
  59. package/dist/components/Radio/index.css +15 -0
  60. package/dist/components/Radio/index.d.cts +1 -1
  61. package/dist/components/Radio/index.d.ts +1 -1
  62. package/dist/components/Radio/index.js +8 -3
  63. package/dist/components/Select/index.cjs +120 -39
  64. package/dist/components/Select/index.css +57 -0
  65. package/dist/components/Select/index.d.cts +11 -0
  66. package/dist/components/Select/index.d.ts +11 -0
  67. package/dist/components/Select/index.js +120 -39
  68. package/dist/components/Spinner/index.d.cts +1 -1
  69. package/dist/components/Spinner/index.d.ts +1 -1
  70. package/dist/components/Stat/index.cjs +84 -0
  71. package/dist/components/Stat/index.css +96 -0
  72. package/dist/components/Stat/index.d.cts +37 -0
  73. package/dist/components/Stat/index.d.ts +37 -0
  74. package/dist/components/Stat/index.js +57 -0
  75. package/dist/components/Steps/index.d.cts +1 -1
  76. package/dist/components/Steps/index.d.ts +1 -1
  77. package/dist/components/Switch/index.cjs +14 -5
  78. package/dist/components/Switch/index.css +15 -0
  79. package/dist/components/Switch/index.d.cts +6 -1
  80. package/dist/components/Switch/index.d.ts +6 -1
  81. package/dist/components/Switch/index.js +14 -5
  82. package/dist/components/Tab/index.cjs +49 -25
  83. package/dist/components/Tab/index.css +4 -0
  84. package/dist/components/Tab/index.js +49 -25
  85. package/dist/components/TextArea/index.cjs +14 -4
  86. package/dist/components/TextArea/index.css +15 -0
  87. package/dist/components/TextArea/index.d.cts +4 -0
  88. package/dist/components/TextArea/index.d.ts +4 -0
  89. package/dist/components/TextArea/index.js +14 -4
  90. package/dist/components/TimePicker/index.cjs +461 -0
  91. package/dist/components/TimePicker/index.css +175 -0
  92. package/dist/components/TimePicker/index.d.cts +42 -0
  93. package/dist/components/TimePicker/index.d.ts +42 -0
  94. package/dist/components/TimePicker/index.js +424 -0
  95. package/dist/components/index.cjs +1500 -398
  96. package/dist/components/index.css +385 -0
  97. package/dist/components/index.d.cts +3 -1
  98. package/dist/components/index.d.ts +3 -1
  99. package/dist/components/index.js +1498 -398
  100. package/dist/index.cjs +1524 -422
  101. package/dist/index.css +385 -0
  102. package/dist/index.d.cts +3 -1
  103. package/dist/index.d.ts +3 -1
  104. package/dist/index.js +1522 -422
  105. package/dist/status-njWqQWFV.d.cts +20 -0
  106. package/dist/status-njWqQWFV.d.ts +20 -0
  107. package/guidelines/AGENT_PROMPT.md +3 -3
  108. package/guidelines/Guidelines.md +3 -0
  109. package/guidelines/components/editor.md +141 -0
  110. package/guidelines/components/form.md +19 -0
  111. package/guidelines/components/stat.md +31 -0
  112. package/guidelines/components/time-picker.md +49 -0
  113. package/package.json +1 -1
  114. package/dist/status-g_KP3qT6.d.cts +0 -8
  115. package/dist/status-g_KP3qT6.d.ts +0 -8
@@ -53,6 +53,109 @@ function clsx() {
53
53
  }
54
54
  var clsx_default = clsx;
55
55
 
56
+ // src/components/Editor/highlight.ts
57
+ var CODE_LANGUAGES = [
58
+ { value: "plain", label: "\uC5C6\uC74C" },
59
+ { value: "javascript", label: "JavaScript" },
60
+ { value: "typescript", label: "TypeScript" },
61
+ { value: "json", label: "JSON" },
62
+ { value: "html", label: "HTML" },
63
+ { value: "css", label: "CSS" },
64
+ { value: "python", label: "Python" },
65
+ { value: "sql", label: "SQL" },
66
+ { value: "bash", label: "Bash" }
67
+ ];
68
+ var KEYWORDS = {
69
+ plain: [],
70
+ javascript: "const let var function return if else for while class new this import export from default async await try catch throw typeof instanceof null undefined true false break continue switch case do delete in of yield".split(" "),
71
+ typescript: "const let var function return if else for while class new this import export from default async await try catch throw typeof instanceof null undefined true false break continue switch case do delete in of yield interface type enum implements extends public private protected readonly as satisfies keyof infer never unknown any".split(" "),
72
+ json: ["true", "false", "null"],
73
+ html: [],
74
+ css: [],
75
+ python: "def class return if elif else for while import from as try except finally raise with lambda None True False and or not in is pass break continue global nonlocal yield async await".split(" "),
76
+ sql: "SELECT FROM WHERE INSERT INTO VALUES UPDATE SET DELETE CREATE TABLE ALTER DROP JOIN LEFT RIGHT INNER OUTER ON GROUP BY ORDER HAVING LIMIT OFFSET AS AND OR NOT NULL DISTINCT UNION INDEX PRIMARY KEY FOREIGN REFERENCES DEFAULT".split(" "),
77
+ bash: "if then else fi for while do done case esac function return export local echo cd ls rm cp mv mkdir sudo source set unset exit".split(" ")
78
+ };
79
+ var LINE_COMMENT = {
80
+ javascript: "//",
81
+ typescript: "//",
82
+ css: "",
83
+ python: "#",
84
+ sql: "--",
85
+ bash: "#"
86
+ };
87
+ var escape = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
88
+ var wrap = (cls, text) => `<span class="tok-${cls}">${escape(text)}</span>`;
89
+ var highlight = (code, lang) => {
90
+ if (lang === "plain" || !code) return escape(code);
91
+ const keywords = new Set(KEYWORDS[lang]);
92
+ const lineComment = LINE_COMMENT[lang];
93
+ const caseInsensitive = lang === "sql";
94
+ let out = "";
95
+ let i = 0;
96
+ while (i < code.length) {
97
+ const rest = code.slice(i);
98
+ if ((lang === "javascript" || lang === "typescript" || lang === "css") && rest.startsWith("/*")) {
99
+ const end = code.indexOf("*/", i + 2);
100
+ const stop = end === -1 ? code.length : end + 2;
101
+ out += wrap("comment", code.slice(i, stop));
102
+ i = stop;
103
+ continue;
104
+ }
105
+ if (lineComment && rest.startsWith(lineComment)) {
106
+ const nl = code.indexOf("\n", i);
107
+ const stop = nl === -1 ? code.length : nl;
108
+ out += wrap("comment", code.slice(i, stop));
109
+ i = stop;
110
+ continue;
111
+ }
112
+ const q = rest[0];
113
+ if (q === '"' || q === "'" || q === "`") {
114
+ let j = 1;
115
+ while (j < rest.length) {
116
+ if (rest[j] === "\\") {
117
+ j += 2;
118
+ continue;
119
+ }
120
+ if (rest[j] === q) {
121
+ j += 1;
122
+ break;
123
+ }
124
+ if (rest[j] === "\n" && q !== "`") break;
125
+ j += 1;
126
+ }
127
+ out += wrap("string", rest.slice(0, j));
128
+ i += j;
129
+ continue;
130
+ }
131
+ const num = /^\d+(\.\d+)?/.exec(rest);
132
+ if (num && !/[\w$]/.test(code[i - 1] ?? "")) {
133
+ out += wrap("number", num[0]);
134
+ i += num[0].length;
135
+ continue;
136
+ }
137
+ const word = /^[A-Za-z_$][\w$]*/.exec(rest);
138
+ if (word) {
139
+ const w = word[0];
140
+ const hit = caseInsensitive ? keywords.has(w.toUpperCase()) : keywords.has(w);
141
+ out += hit ? wrap("keyword", w) : escape(w);
142
+ i += w.length;
143
+ continue;
144
+ }
145
+ if (lang === "html" && rest.startsWith("<")) {
146
+ const tag = /^<\/?[A-Za-z][\w-]*/.exec(rest);
147
+ if (tag) {
148
+ out += wrap("keyword", tag[0]);
149
+ i += tag[0].length;
150
+ continue;
151
+ }
152
+ }
153
+ out += escape(code[i]);
154
+ i += 1;
155
+ }
156
+ return out;
157
+ };
158
+
56
159
  // src/components/Editor/Editor.tsx
57
160
  var import_jsx_runtime = require("react/jsx-runtime");
58
161
  var DEFAULT_TOOLBAR = [
@@ -61,14 +164,27 @@ var DEFAULT_TOOLBAR = [
61
164
  "underline",
62
165
  "strikethrough",
63
166
  "code",
64
- "heading",
167
+ "heading-1",
168
+ "heading-2",
169
+ "heading-3",
65
170
  "list",
66
171
  "ordered-list",
67
172
  "blockquote",
68
173
  "code-block",
69
174
  "link",
70
175
  "image",
71
- "divider"
176
+ "divider",
177
+ "checklist",
178
+ "table",
179
+ "align-left",
180
+ "align-center",
181
+ "align-right",
182
+ "highlight",
183
+ "superscript",
184
+ "subscript",
185
+ "clear-format",
186
+ "undo",
187
+ "redo"
72
188
  ];
73
189
  var ALLOWED_TAGS = /* @__PURE__ */ new Set([
74
190
  "p",
@@ -97,12 +213,51 @@ var ALLOWED_TAGS = /* @__PURE__ */ new Set([
97
213
  "img",
98
214
  "hr",
99
215
  "span",
100
- "div"
216
+ "div",
217
+ "sub",
218
+ "sup",
219
+ "mark",
220
+ "table",
221
+ "thead",
222
+ "tbody",
223
+ "tr",
224
+ "th",
225
+ "td"
101
226
  ]);
102
227
  var ALLOWED_ATTRS = {
103
228
  a: ["href", "target", "rel"],
104
- img: ["src", "alt"]
229
+ img: ["src", "alt", "width"],
230
+ // 정렬·체크리스트는 클래스로만 표현한다. style 을 열면 붙여넣기로 아무 CSS 나 들어온다
231
+ p: ["class"],
232
+ h1: ["class"],
233
+ h2: ["class"],
234
+ h3: ["class"],
235
+ h4: ["class"],
236
+ h5: ["class"],
237
+ h6: ["class"],
238
+ ul: ["class"],
239
+ ol: ["class"],
240
+ li: ["class", "data-checked"],
241
+ blockquote: ["class"],
242
+ div: ["class"],
243
+ span: ["class"],
244
+ pre: ["class"],
245
+ code: ["class"],
246
+ mark: ["class"],
247
+ table: ["class"],
248
+ td: ["class"],
249
+ th: ["class"],
250
+ tr: ["class"]
105
251
  };
252
+ var DROP_WITH_CONTENT = /* @__PURE__ */ new Set([
253
+ "script",
254
+ "style",
255
+ "noscript",
256
+ "iframe",
257
+ "object",
258
+ "embed",
259
+ "template"
260
+ ]);
106
261
  var sanitizeHtml = (input) => {
107
262
  const doc = new DOMParser().parseFromString(`<div>${input}</div>`, "text/html");
108
263
  const root = doc.body.firstElementChild;
@@ -111,6 +266,10 @@ var sanitizeHtml = (input) => {
111
266
  const children = Array.from(node.children);
112
267
  for (const child of children) {
113
268
  const tag = child.tagName.toLowerCase();
269
+ if (DROP_WITH_CONTENT.has(tag)) {
270
+ node.removeChild(child);
271
+ continue;
272
+ }
114
273
  if (!ALLOWED_TAGS.has(tag)) {
115
274
  while (child.firstChild) node.insertBefore(child.firstChild, child);
116
275
  node.removeChild(child);
@@ -141,7 +300,9 @@ var escapeHtml = (text) => {
141
300
  };
142
301
  var SLASH_ITEMS = [
143
302
  { key: "paragraph", label: "\uB2E8\uB77D", hint: "\uAE30\uBCF8 \uD14D\uC2A4\uD2B8" },
144
- { key: "heading", label: "\uC81C\uBAA9 1", hint: "\uD070 \uC81C\uBAA9" },
303
+ { key: "heading-1", label: "\uC81C\uBAA9 1", hint: "\uAC00\uC7A5 \uD070 \uC81C\uBAA9" },
304
+ { key: "heading-2", label: "\uC81C\uBAA9 2", hint: "" },
305
+ { key: "heading-3", label: "\uC81C\uBAA9 3", hint: "" },
145
306
  { key: "list", label: "\uAE00\uBA38\uB9AC \uAE30\uD638 \uBAA9\uB85D", hint: "\u2022 \uD56D\uBAA9" },
146
307
  { key: "ordered-list", label: "\uBC88\uD638 \uB9E4\uAE30\uAE30 \uBAA9\uB85D", hint: "1. \uD56D\uBAA9" },
147
308
  { key: "blockquote", label: "\uC778\uC6A9", hint: "" },
@@ -175,12 +336,18 @@ var Editor = (props) => {
175
336
  const [linkOpen, setLinkOpen] = import_react.default.useState(false);
176
337
  const [linkValue, setLinkValue] = import_react.default.useState("");
177
338
  const [linkPos, setLinkPos] = import_react.default.useState({ top: 0, left: 0 });
339
+ const lastEmittedRef = import_react.default.useRef(null);
178
340
  import_react.default.useEffect(() => {
179
- if (!editorRef.current) return;
180
- if (editorRef.current.innerHTML !== value) {
181
- editorRef.current.innerHTML = sanitizeHtml(value || "");
182
- updateEmpty();
183
- }
341
+ const el = editorRef.current;
342
+ if (!el) return;
343
+ if (value === lastEmittedRef.current) return;
344
+ if (el.innerHTML === value) return;
345
+ el.innerHTML = sanitizeHtml(value || "");
346
+ if (!el.firstChild) el.innerHTML = "<p><br></p>";
347
+ lastEmittedRef.current = null;
348
+ historyRef.current = [{ html: el.innerHTML, caret: 0 }];
349
+ historyIdxRef.current = 0;
350
+ updateEmpty();
184
351
  }, [value]);
185
352
  const updateEmpty = () => {
186
353
  const el = editorRef.current;
@@ -196,10 +363,19 @@ var Editor = (props) => {
196
363
  }
197
364
  };
198
365
  const restoreSelection = () => {
199
- if (!lastRangeRef.current) return;
366
+ const range = lastRangeRef.current;
367
+ if (!range) return;
368
+ if (!editorRef.current?.contains(range.startContainer)) {
369
+ lastRangeRef.current = null;
370
+ return;
371
+ }
200
372
  const sel = window.getSelection();
201
- sel?.removeAllRanges();
202
- sel?.addRange(lastRangeRef.current);
373
+ try {
374
+ sel?.removeAllRanges();
375
+ sel?.addRange(range);
376
+ } catch {
377
+ lastRangeRef.current = null;
378
+ }
203
379
  };
204
380
  const updateActiveFormats = () => {
205
381
  if (!editorRef.current) return;
@@ -215,6 +391,17 @@ var Editor = (props) => {
215
391
  if (document.queryCommandState("insertOrderedList")) next.add("ordered-list");
216
392
  const block = String(document.queryCommandValue("formatBlock") || "").toLowerCase();
217
393
  if (block) next.add(`block:${block}`);
394
+ if (document.queryCommandState("superscript")) next.add("superscript");
395
+ if (document.queryCommandState("subscript")) next.add("subscript");
396
+ } catch {
397
+ }
398
+ try {
399
+ const blockEl = currentBlock();
400
+ if (blockEl?.classList.contains("align-center")) next.add("align:center");
401
+ else if (blockEl?.classList.contains("align-right")) next.add("align:right");
402
+ else if (blockEl) next.add("align:left");
403
+ if (blockEl?.closest("mark")) next.add("highlight");
404
+ if (blockEl?.closest("ul.checklist")) next.add("checklist");
218
405
  } catch {
219
406
  }
220
407
  setActiveFormats(next);
@@ -228,7 +415,374 @@ var Editor = (props) => {
228
415
  const setBlock = (tag) => {
229
416
  exec("formatBlock", tag);
230
417
  };
418
+ const toggleBlock = (tag) => {
419
+ const current = String(document.queryCommandValue("formatBlock") || "").toLowerCase();
420
+ setBlock(current === tag ? "p" : tag);
421
+ };
422
+ const historyRef = import_react.default.useRef([]);
423
+ const historyIdxRef = import_react.default.useRef(-1);
424
+ const lastPushAtRef = import_react.default.useRef(0);
425
+ const isRestoringRef = import_react.default.useRef(false);
426
+ const caretOffset = (rootEl) => {
427
+ const el = rootEl ?? editorRef.current;
428
+ const sel = window.getSelection();
429
+ if (!el || !sel || sel.rangeCount === 0 || !el.contains(sel.anchorNode)) return 0;
430
+ const range = sel.getRangeAt(0).cloneRange();
431
+ range.selectNodeContents(el);
432
+ range.setEnd(sel.getRangeAt(0).endContainer, sel.getRangeAt(0).endOffset);
433
+ return range.toString().length;
434
+ };
435
+ const setCaretOffset = (offset, rootEl) => {
436
+ const el = rootEl ?? editorRef.current;
437
+ if (!el) return;
438
+ const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT);
439
+ let remaining = offset;
440
+ let node = null;
441
+ while (node = walker.nextNode()) {
442
+ const len = node.textContent?.length ?? 0;
443
+ if (remaining <= len) {
444
+ const range = document.createRange();
445
+ range.setStart(node, remaining);
446
+ range.collapse(true);
447
+ const sel = window.getSelection();
448
+ sel?.removeAllRanges();
449
+ sel?.addRange(range);
450
+ return;
451
+ }
452
+ remaining -= len;
453
+ }
454
+ placeCaretAtEnd(el);
455
+ };
456
+ const pushHistory = (force = false) => {
457
+ const el = editorRef.current;
458
+ if (!el || isRestoringRef.current) return;
459
+ const html = el.innerHTML;
460
+ const hist = historyRef.current;
461
+ const cur = hist[historyIdxRef.current];
462
+ if (cur && cur.html === html) return;
463
+ const now = Date.now();
464
+ const coalesce = !force && cur && now - lastPushAtRef.current < 500;
465
+ lastPushAtRef.current = now;
466
+ if (coalesce) {
467
+ hist[historyIdxRef.current] = { html, caret: caretOffset() };
468
+ return;
469
+ }
470
+ hist.length = historyIdxRef.current + 1;
471
+ hist.push({ html, caret: caretOffset() });
472
+ if (hist.length > 200) hist.shift();
473
+ historyIdxRef.current = hist.length - 1;
474
+ };
475
+ const applySnapshot = (snap) => {
476
+ const el = editorRef.current;
477
+ if (!el) return;
478
+ isRestoringRef.current = true;
479
+ el.innerHTML = snap.html;
480
+ setCaretOffset(snap.caret);
481
+ isRestoringRef.current = false;
482
+ lastEmittedRef.current = el.innerHTML;
483
+ onChange?.(el.innerHTML);
484
+ updateEmpty();
485
+ updateActiveFormats();
486
+ };
487
+ const undo = () => {
488
+ pushHistory(true);
489
+ if (historyIdxRef.current <= 0) return;
490
+ historyIdxRef.current -= 1;
491
+ applySnapshot(historyRef.current[historyIdxRef.current]);
492
+ };
493
+ const redo = () => {
494
+ if (historyIdxRef.current >= historyRef.current.length - 1) return;
495
+ historyIdxRef.current += 1;
496
+ applySnapshot(historyRef.current[historyIdxRef.current]);
497
+ };
498
+ const langOf = (pre) => {
499
+ const cls = Array.from(pre.classList).find((c) => c.startsWith("lang-"));
500
+ return cls?.slice(5) ?? "plain";
501
+ };
502
+ const [codeLang, setCodeLang] = import_react.default.useState("plain");
503
+ const highlightRafRef = import_react.default.useRef(0);
504
+ const rehighlight = (pre) => {
505
+ if (composingRef.current) return;
506
+ const code = pre.querySelector("code");
507
+ if (!code) return;
508
+ const lang = langOf(pre);
509
+ const text = code.textContent || "";
510
+ const next = highlight(text, lang);
511
+ if (code.innerHTML === next) return;
512
+ const caret = caretOffset(code);
513
+ isRestoringRef.current = true;
514
+ code.innerHTML = next;
515
+ setCaretOffset(caret, code);
516
+ isRestoringRef.current = false;
517
+ };
518
+ const scheduleHighlight = () => {
519
+ const pre = closestPre();
520
+ if (!pre) return;
521
+ cancelAnimationFrame(highlightRafRef.current);
522
+ highlightRafRef.current = requestAnimationFrame(() => rehighlight(pre));
523
+ };
524
+ const changeCodeLang = (lang) => {
525
+ const pre = closestPre();
526
+ if (!pre) return;
527
+ pushHistory(true);
528
+ Array.from(pre.classList).filter((c) => c.startsWith("lang-")).forEach((c) => pre.classList.remove(c));
529
+ if (lang !== "plain") pre.classList.add(`lang-${lang}`);
530
+ setCodeLang(lang);
531
+ rehighlight(pre);
532
+ handleInput();
533
+ };
534
+ const closestPre = () => {
535
+ const sel = window.getSelection();
536
+ const node = sel?.anchorNode;
537
+ if (!node || !editorRef.current?.contains(node)) return null;
538
+ const el = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;
539
+ return el?.closest("pre") ?? null;
540
+ };
541
+ const insertCodeBlock = () => {
542
+ pushHistory(true);
543
+ const el = editorRef.current;
544
+ const sel = window.getSelection();
545
+ if (!el || !sel || sel.rangeCount === 0) return;
546
+ const existing = closestPre();
547
+ if (existing) {
548
+ const p = document.createElement("p");
549
+ p.textContent = existing.textContent || "";
550
+ if (!p.textContent) p.innerHTML = "<br>";
551
+ existing.replaceWith(p);
552
+ placeCaretAtEnd(p);
553
+ handleInput();
554
+ return;
555
+ }
556
+ const range = sel.getRangeAt(0);
557
+ const frag = range.cloneContents();
558
+ const holder = document.createElement("div");
559
+ holder.appendChild(frag);
560
+ const BLOCK = /^(p|div|h[1-6]|li|blockquote|pre|tr)$/i;
561
+ const linesOf = (node) => {
562
+ const out = [];
563
+ let cur = "";
564
+ for (const child of Array.from(node.childNodes)) {
565
+ if (child.nodeType === Node.TEXT_NODE) {
566
+ cur += child.textContent || "";
567
+ } else if (child.nodeName === "BR") {
568
+ out.push(cur);
569
+ cur = "";
570
+ } else if (BLOCK.test(child.nodeName)) {
571
+ if (cur) {
572
+ out.push(cur);
573
+ cur = "";
574
+ }
575
+ const sub = linesOf(child);
576
+ out.push(...sub.length > 0 ? sub : [""]);
577
+ } else {
578
+ cur += linesOf(child).join("\n");
579
+ }
580
+ }
581
+ if (cur) out.push(cur);
582
+ return out;
583
+ };
584
+ const text = linesOf(holder).join("\n").replace(/\n+$/, "");
585
+ const pre = document.createElement("pre");
586
+ const code = document.createElement("code");
587
+ code.textContent = text || "";
588
+ pre.appendChild(code);
589
+ range.deleteContents();
590
+ range.insertNode(pre);
591
+ ensureParagraphAfter(pre);
592
+ placeCaretAtEnd(code);
593
+ handleInput();
594
+ };
595
+ const insertTextAtCaret = (text) => {
596
+ const sel = window.getSelection();
597
+ if (!sel || sel.rangeCount === 0) return;
598
+ const range = sel.getRangeAt(0);
599
+ range.deleteContents();
600
+ const node = document.createTextNode(text);
601
+ range.insertNode(node);
602
+ range.setStartAfter(node);
603
+ range.collapse(true);
604
+ sel.removeAllRanges();
605
+ sel.addRange(range);
606
+ handleInput();
607
+ };
608
+ const ensureParagraphAfter = (node) => {
609
+ const next = node.nextElementSibling;
610
+ if (next && next.tagName === "P") return;
611
+ const p = document.createElement("p");
612
+ p.innerHTML = "<br>";
613
+ node.after(p);
614
+ };
615
+ const placeCaretAtEnd = (node) => {
616
+ const sel = window.getSelection();
617
+ if (!sel) return;
618
+ const r2 = document.createRange();
619
+ r2.selectNodeContents(node);
620
+ r2.collapse(false);
621
+ sel.removeAllRanges();
622
+ sel.addRange(r2);
623
+ editorRef.current?.focus();
624
+ };
625
+ const clearFormat = () => {
626
+ exec("removeFormat");
627
+ exec("unlink");
628
+ setBlock("p");
629
+ };
630
+ const indentList = (outdent) => {
631
+ exec(outdent ? "outdent" : "indent");
632
+ };
633
+ const inListItem = () => {
634
+ const sel = window.getSelection();
635
+ const node = sel?.anchorNode;
636
+ if (!node || !editorRef.current?.contains(node)) return false;
637
+ const el = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;
638
+ return !!el?.closest("li");
639
+ };
640
+ const currentBlock = () => {
641
+ const sel = window.getSelection();
642
+ const node = sel?.anchorNode;
643
+ const root = editorRef.current;
644
+ if (!node || !root?.contains(node)) return null;
645
+ let el = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;
646
+ while (el && el !== root && !/^(P|H[1-6]|LI|BLOCKQUOTE|PRE|DIV|TD|TH)$/.test(el.tagName)) {
647
+ el = el.parentElement;
648
+ }
649
+ return el && el !== root ? el : null;
650
+ };
651
+ const setAlign = (align) => {
652
+ const block = currentBlock();
653
+ if (!block) return;
654
+ pushHistory(true);
655
+ block.classList.remove("align-left", "align-center", "align-right");
656
+ if (align !== "left") block.classList.add(`align-${align}`);
657
+ handleInput();
658
+ };
659
+ const toggleChecklist = () => {
660
+ pushHistory(true);
661
+ const block = currentBlock();
662
+ const list = block?.closest("ul");
663
+ if (list?.classList.contains("checklist")) {
664
+ list.classList.remove("checklist");
665
+ list.querySelectorAll("li").forEach((li) => li.removeAttribute("data-checked"));
666
+ handleInput();
667
+ return;
668
+ }
669
+ if (list) {
670
+ list.classList.add("checklist");
671
+ list.querySelectorAll("li").forEach((li) => li.setAttribute("data-checked", "false"));
672
+ handleInput();
673
+ return;
674
+ }
675
+ exec("insertUnorderedList");
676
+ const made = currentBlock()?.closest("ul");
677
+ if (made) {
678
+ made.classList.add("checklist");
679
+ made.querySelectorAll("li").forEach((li) => li.setAttribute("data-checked", "false"));
680
+ }
681
+ handleInput();
682
+ };
683
+ const handleEditorClick = (e) => {
684
+ if (readOnly) return;
685
+ const target = e.target;
686
+ const li = target.closest?.("li");
687
+ if (!li || !li.closest("ul.checklist")) return;
688
+ const rect = li.getBoundingClientRect();
689
+ if (e.clientX - rect.left > 20) return;
690
+ e.preventDefault();
691
+ pushHistory(true);
692
+ li.setAttribute("data-checked", li.getAttribute("data-checked") === "true" ? "false" : "true");
693
+ handleInput();
694
+ };
695
+ const toggleHighlight = () => {
696
+ const sel = window.getSelection();
697
+ if (!sel || sel.rangeCount === 0 || sel.isCollapsed) return;
698
+ pushHistory(true);
699
+ const range = sel.getRangeAt(0);
700
+ const node = sel.anchorNode;
701
+ const el = node?.nodeType === Node.ELEMENT_NODE ? node : node?.parentElement;
702
+ const existing = el?.closest("mark");
703
+ if (existing) {
704
+ const parent = existing.parentNode;
705
+ while (existing.firstChild) parent?.insertBefore(existing.firstChild, existing);
706
+ parent?.removeChild(existing);
707
+ handleInput();
708
+ return;
709
+ }
710
+ const mark = document.createElement("mark");
711
+ try {
712
+ range.surroundContents(mark);
713
+ } catch {
714
+ const frag = range.extractContents();
715
+ mark.appendChild(frag);
716
+ range.insertNode(mark);
717
+ }
718
+ handleInput();
719
+ };
720
+ const insertTable = (cols = 3, rows = 2) => {
721
+ const sel = window.getSelection();
722
+ if (!sel || sel.rangeCount === 0) return;
723
+ pushHistory(true);
724
+ const table = document.createElement("table");
725
+ const thead = document.createElement("thead");
726
+ const headRow = document.createElement("tr");
727
+ for (let c = 0; c < cols; c++) {
728
+ const th = document.createElement("th");
729
+ th.innerHTML = "<br>";
730
+ headRow.appendChild(th);
731
+ }
732
+ thead.appendChild(headRow);
733
+ table.appendChild(thead);
734
+ const tbody = document.createElement("tbody");
735
+ for (let r2 = 0; r2 < rows; r2++) {
736
+ const tr = document.createElement("tr");
737
+ for (let c = 0; c < cols; c++) {
738
+ const td = document.createElement("td");
739
+ td.innerHTML = "<br>";
740
+ tr.appendChild(td);
741
+ }
742
+ tbody.appendChild(tr);
743
+ }
744
+ table.appendChild(tbody);
745
+ const range = sel.getRangeAt(0);
746
+ range.deleteContents();
747
+ range.insertNode(table);
748
+ ensureParagraphAfter(table);
749
+ const first = table.querySelector("th");
750
+ if (first) placeCaretAtEnd(first);
751
+ handleInput();
752
+ };
753
+ const moveTableCell = (backwards) => {
754
+ const block = currentBlock();
755
+ const cell = block?.closest("td, th");
756
+ if (!cell) return false;
757
+ const table = cell.closest("table");
758
+ if (!table) return false;
759
+ const cells = Array.from(table.querySelectorAll("td, th"));
760
+ const i = cells.indexOf(cell);
761
+ const next = cells[i + (backwards ? -1 : 1)];
762
+ if (next) {
763
+ placeCaretAtEnd(next);
764
+ return true;
765
+ }
766
+ if (!backwards) {
767
+ pushHistory(true);
768
+ const lastRow = table.querySelector("tbody")?.lastElementChild;
769
+ if (lastRow) {
770
+ const tr = document.createElement("tr");
771
+ for (let c = 0; c < lastRow.children.length; c++) {
772
+ const td = document.createElement("td");
773
+ td.innerHTML = "<br>";
774
+ tr.appendChild(td);
775
+ }
776
+ lastRow.after(tr);
777
+ placeCaretAtEnd(tr.firstElementChild);
778
+ handleInput();
779
+ return true;
780
+ }
781
+ }
782
+ return false;
783
+ };
231
784
  const insertDivider = () => {
785
+ pushHistory(true);
232
786
  exec("insertHorizontalRule");
233
787
  const el = editorRef.current;
234
788
  if (el && el.lastElementChild?.tagName === "HR") {
@@ -274,7 +828,9 @@ var Editor = (props) => {
274
828
  const range = sel.getRangeAt(0);
275
829
  slashStartRangeRef.current = range.cloneRange();
276
830
  const rect = range.getBoundingClientRect();
277
- const editorRect = editorRef.current.getBoundingClientRect();
831
+ const editorEl = editorRef.current;
832
+ if (!editorEl) return;
833
+ const editorRect = editorEl.getBoundingClientRect();
278
834
  setSlashPos({
279
835
  top: rect.bottom - editorRect.top + 4,
280
836
  left: rect.left - editorRect.left
@@ -319,8 +875,14 @@ var Editor = (props) => {
319
875
  case "paragraph":
320
876
  setBlock("p");
321
877
  break;
322
- case "heading":
323
- setBlock("h2");
878
+ case "heading-1":
879
+ toggleBlock("h1");
880
+ break;
881
+ case "heading-2":
882
+ toggleBlock("h2");
883
+ break;
884
+ case "heading-3":
885
+ toggleBlock("h3");
324
886
  break;
325
887
  case "list":
326
888
  exec("insertUnorderedList");
@@ -332,7 +894,7 @@ var Editor = (props) => {
332
894
  setBlock("blockquote");
333
895
  break;
334
896
  case "code-block":
335
- setBlock("pre");
897
+ insertCodeBlock();
336
898
  break;
337
899
  case "divider":
338
900
  insertDivider();
@@ -350,7 +912,9 @@ var Editor = (props) => {
350
912
  if (!sel || sel.rangeCount === 0) return;
351
913
  const range = sel.getRangeAt(0);
352
914
  const rect = range.getBoundingClientRect();
353
- const editorRect = editorRef.current.getBoundingClientRect();
915
+ const editorEl2 = editorRef.current;
916
+ if (!editorEl2) return;
917
+ const editorRect = editorEl2.getBoundingClientRect();
354
918
  setLinkPos({
355
919
  top: rect.bottom - editorRect.top + 4,
356
920
  left: rect.left - editorRect.left
@@ -383,13 +947,13 @@ var Editor = (props) => {
383
947
  if (node.nodeType !== Node.TEXT_NODE) return false;
384
948
  const text = (node.textContent || "").slice(0, range.startOffset);
385
949
  const patterns = [
386
- [/^###\s$/, () => setBlock("h3")],
387
- [/^##\s$/, () => setBlock("h2")],
388
- [/^#\s$/, () => setBlock("h1")],
389
- [/^-\s$/, () => exec("insertUnorderedList")],
390
- [/^\*\s$/, () => exec("insertUnorderedList")],
391
- [/^1\.\s$/, () => exec("insertOrderedList")],
392
- [/^>\s$/, () => setBlock("blockquote")]
950
+ [/^###$/, () => setBlock("h3")],
951
+ [/^##$/, () => setBlock("h2")],
952
+ [/^#$/, () => setBlock("h1")],
953
+ [/^-$/, () => exec("insertUnorderedList")],
954
+ [/^\*$/, () => exec("insertUnorderedList")],
955
+ [/^1\.$/, () => exec("insertOrderedList")],
956
+ [/^>$/, () => setBlock("blockquote")]
393
957
  ];
394
958
  for (const [re, fn] of patterns) {
395
959
  if (re.test(text)) {
@@ -409,7 +973,11 @@ var Editor = (props) => {
409
973
  if (composingRef.current) return;
410
974
  const el = editorRef.current;
411
975
  if (!el) return;
412
- onChange?.(el.innerHTML);
976
+ const html = el.innerHTML;
977
+ lastEmittedRef.current = html;
978
+ pushHistory();
979
+ scheduleHighlight();
980
+ onChange?.(html);
413
981
  updateEmpty();
414
982
  updateActiveFormats();
415
983
  if (slashOpen) {
@@ -437,6 +1005,40 @@ var Editor = (props) => {
437
1005
  };
438
1006
  const handleKeyDown = (e) => {
439
1007
  if (readOnly) return;
1008
+ if (composingRef.current || e.nativeEvent.isComposing || e.keyCode === 229) return;
1009
+ const pre = closestPre();
1010
+ if (pre && !slashOpen) {
1011
+ if (e.key === "Enter" && !e.shiftKey) {
1012
+ const text = pre.textContent || "";
1013
+ if (text.endsWith("\n\n")) {
1014
+ e.preventDefault();
1015
+ const code = pre.querySelector("code") ?? pre;
1016
+ code.textContent = text.replace(/\n\n$/, "");
1017
+ ensureParagraphAfter(pre);
1018
+ const next = pre.nextElementSibling;
1019
+ if (next) placeCaretAtEnd(next);
1020
+ handleInput();
1021
+ return;
1022
+ }
1023
+ e.preventDefault();
1024
+ insertTextAtCaret("\n");
1025
+ return;
1026
+ }
1027
+ if (e.key === "Tab") {
1028
+ e.preventDefault();
1029
+ insertTextAtCaret(" ");
1030
+ return;
1031
+ }
1032
+ }
1033
+ if (e.key === "Tab" && moveTableCell(e.shiftKey)) {
1034
+ e.preventDefault();
1035
+ return;
1036
+ }
1037
+ if (e.key === "Tab" && inListItem()) {
1038
+ e.preventDefault();
1039
+ indentList(e.shiftKey);
1040
+ return;
1041
+ }
440
1042
  if (slashOpen) {
441
1043
  if (e.key === "ArrowDown") {
442
1044
  e.preventDefault();
@@ -460,6 +1062,21 @@ var Editor = (props) => {
460
1062
  return;
461
1063
  }
462
1064
  }
1065
+ if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "z") {
1066
+ e.preventDefault();
1067
+ if (e.shiftKey) redo();
1068
+ else undo();
1069
+ return;
1070
+ }
1071
+ if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.key.toLowerCase() === "v") {
1072
+ plainPasteRef.current = true;
1073
+ return;
1074
+ }
1075
+ if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "y") {
1076
+ e.preventDefault();
1077
+ redo();
1078
+ return;
1079
+ }
463
1080
  if ((e.metaKey || e.ctrlKey) && !e.shiftKey && !e.altKey) {
464
1081
  const k = e.key.toLowerCase();
465
1082
  if (k === "b") {
@@ -483,6 +1100,7 @@ var Editor = (props) => {
483
1100
  return;
484
1101
  }
485
1102
  }
1103
+ if (pre) return;
486
1104
  if (enableSlashCommand && e.key === "/") {
487
1105
  setTimeout(openSlashMenu, 0);
488
1106
  }
@@ -492,11 +1110,21 @@ var Editor = (props) => {
492
1110
  }
493
1111
  }
494
1112
  };
1113
+ const plainPasteRef = import_react.default.useRef(false);
495
1114
  const handlePaste = (e) => {
496
1115
  if (readOnly) return;
497
1116
  e.preventDefault();
498
- const html = e.clipboardData.getData("text/html");
499
1117
  const text = e.clipboardData.getData("text/plain");
1118
+ if (plainPasteRef.current) {
1119
+ plainPasteRef.current = false;
1120
+ insertTextAtCaret(text);
1121
+ return;
1122
+ }
1123
+ if (closestPre()) {
1124
+ insertTextAtCaret(text);
1125
+ return;
1126
+ }
1127
+ const html = e.clipboardData.getData("text/html");
500
1128
  const clean = html ? sanitizeHtml(html) : escapeHtml(text);
501
1129
  document.execCommand("insertHTML", false, clean);
502
1130
  handleInput();
@@ -518,8 +1146,11 @@ var Editor = (props) => {
518
1146
  };
519
1147
  const handleSelectionUpdate = () => {
520
1148
  updateActiveFormats();
1149
+ const pre = closestPre();
1150
+ setCodeLang(pre ? langOf(pre) : "plain");
521
1151
  };
522
1152
  const onToolbarAction = (item) => {
1153
+ if (closestPre() && !["code-block", "undo", "redo"].includes(item)) return;
523
1154
  switch (item) {
524
1155
  case "bold":
525
1156
  exec("bold");
@@ -548,8 +1179,14 @@ var Editor = (props) => {
548
1179
  handleInput();
549
1180
  break;
550
1181
  }
551
- case "heading":
552
- setBlock("h2");
1182
+ case "heading-1":
1183
+ toggleBlock("h1");
1184
+ break;
1185
+ case "heading-2":
1186
+ toggleBlock("h2");
1187
+ break;
1188
+ case "heading-3":
1189
+ toggleBlock("h3");
553
1190
  break;
554
1191
  case "list":
555
1192
  exec("insertUnorderedList");
@@ -561,7 +1198,7 @@ var Editor = (props) => {
561
1198
  setBlock("blockquote");
562
1199
  break;
563
1200
  case "code-block":
564
- setBlock("pre");
1201
+ insertCodeBlock();
565
1202
  break;
566
1203
  case "link":
567
1204
  openLinkEditor();
@@ -572,6 +1209,39 @@ var Editor = (props) => {
572
1209
  case "divider":
573
1210
  insertDivider();
574
1211
  break;
1212
+ case "checklist":
1213
+ toggleChecklist();
1214
+ break;
1215
+ case "align-left":
1216
+ setAlign("left");
1217
+ break;
1218
+ case "align-center":
1219
+ setAlign("center");
1220
+ break;
1221
+ case "align-right":
1222
+ setAlign("right");
1223
+ break;
1224
+ case "highlight":
1225
+ toggleHighlight();
1226
+ break;
1227
+ case "superscript":
1228
+ exec("superscript");
1229
+ break;
1230
+ case "subscript":
1231
+ exec("subscript");
1232
+ break;
1233
+ case "table":
1234
+ insertTable();
1235
+ break;
1236
+ case "clear-format":
1237
+ clearFormat();
1238
+ break;
1239
+ case "undo":
1240
+ undo();
1241
+ break;
1242
+ case "redo":
1243
+ redo();
1244
+ break;
575
1245
  }
576
1246
  };
577
1247
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: clsx_default("lib-xplat-editor", isFocused && "focused", readOnly && "readonly"), children: [
@@ -580,7 +1250,10 @@ var Editor = (props) => {
580
1250
  {
581
1251
  items: toolbar,
582
1252
  active: activeFormats,
583
- onAction: onToolbarAction
1253
+ onAction: onToolbarAction,
1254
+ codeLang,
1255
+ inCodeBlock: activeFormats.has("block:pre"),
1256
+ onCodeLangChange: changeCodeLang
584
1257
  }
585
1258
  ),
586
1259
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "lib-xplat-editor__content", style: { minHeight }, children: [
@@ -591,9 +1264,14 @@ var Editor = (props) => {
591
1264
  ref: editorRef,
592
1265
  contentEditable: !readOnly,
593
1266
  suppressContentEditableWarning: true,
1267
+ role: "textbox",
1268
+ "aria-multiline": "true",
1269
+ "aria-readonly": readOnly || void 0,
1270
+ "aria-label": placeholder,
594
1271
  className: "lib-xplat-editor__editable",
595
1272
  onInput: handleInput,
596
1273
  onKeyDown: handleKeyDown,
1274
+ onClick: handleEditorClick,
597
1275
  onKeyUp: handleSelectionUpdate,
598
1276
  onMouseUp: handleSelectionUpdate,
599
1277
  onPaste: handlePaste,
@@ -635,14 +1313,27 @@ var TOOLBAR_LABEL = {
635
1313
  underline: "U",
636
1314
  strikethrough: "S",
637
1315
  code: "<>",
638
- heading: "H",
1316
+ "heading-1": "H1",
1317
+ "heading-2": "H2",
1318
+ "heading-3": "H3",
639
1319
  list: "\u2022",
640
1320
  "ordered-list": "1.",
641
1321
  blockquote: "\u275D",
642
1322
  "code-block": "[ ]",
643
1323
  link: "\u{1F517}",
644
1324
  image: "\u{1F5BC}",
645
- divider: "\u2014"
1325
+ divider: "\u2014",
1326
+ checklist: "\u2611",
1327
+ "align-left": "\u21E4",
1328
+ "align-center": "\u2194",
1329
+ "align-right": "\u21E5",
1330
+ highlight: "\u{1F58D}",
1331
+ superscript: "x\xB2",
1332
+ subscript: "x\u2082",
1333
+ table: "\u25A6",
1334
+ "clear-format": "\u232B",
1335
+ undo: "\u21B6",
1336
+ redo: "\u21B7"
646
1337
  };
647
1338
  var TOOLBAR_TITLE = {
648
1339
  bold: "\uAD75\uAC8C (\u2318B)",
@@ -650,36 +1341,74 @@ var TOOLBAR_TITLE = {
650
1341
  underline: "\uBC11\uC904 (\u2318U)",
651
1342
  strikethrough: "\uCDE8\uC18C\uC120",
652
1343
  code: "\uC778\uB77C\uC778 \uCF54\uB4DC",
653
- heading: "\uC81C\uBAA9",
1344
+ "heading-1": "\uC81C\uBAA9 1",
1345
+ "heading-2": "\uC81C\uBAA9 2",
1346
+ "heading-3": "\uC81C\uBAA9 3",
654
1347
  list: "\uAE00\uBA38\uB9AC \uAE30\uD638 \uBAA9\uB85D",
655
1348
  "ordered-list": "\uBC88\uD638 \uB9E4\uAE30\uAE30 \uBAA9\uB85D",
656
1349
  blockquote: "\uC778\uC6A9",
657
1350
  "code-block": "\uCF54\uB4DC \uBE14\uB85D",
658
1351
  link: "\uB9C1\uD06C (\u2318K)",
659
1352
  image: "\uC774\uBBF8\uC9C0",
660
- divider: "\uAD6C\uBD84\uC120"
1353
+ divider: "\uAD6C\uBD84\uC120",
1354
+ checklist: "\uCCB4\uD06C\uB9AC\uC2A4\uD2B8",
1355
+ "align-left": "\uC67C\uCABD \uC815\uB82C",
1356
+ "align-center": "\uAC00\uC6B4\uB370 \uC815\uB82C",
1357
+ "align-right": "\uC624\uB978\uCABD \uC815\uB82C",
1358
+ highlight: "\uD615\uAD11\uD39C",
1359
+ superscript: "\uC704 \uCCA8\uC790",
1360
+ subscript: "\uC544\uB798 \uCCA8\uC790",
1361
+ table: "\uD45C \uC0BD\uC785",
1362
+ "clear-format": "\uC11C\uC2DD \uC9C0\uC6B0\uAE30",
1363
+ undo: "\uB418\uB3CC\uB9AC\uAE30 (\u2318Z)",
1364
+ redo: "\uB2E4\uC2DC\uD558\uAE30 (\u2318\u21E7Z)"
661
1365
  };
662
1366
  var isActive = (item, active) => {
663
- if (item === "heading") return active.has("block:h1") || active.has("block:h2") || active.has("block:h3");
1367
+ if (item === "heading-1") return active.has("block:h1");
1368
+ if (item === "heading-2") return active.has("block:h2");
1369
+ if (item === "heading-3") return active.has("block:h3");
664
1370
  if (item === "blockquote") return active.has("block:blockquote");
665
1371
  if (item === "code-block") return active.has("block:pre");
1372
+ if (item === "align-left") return active.has("align:left");
1373
+ if (item === "align-center") return active.has("align:center");
1374
+ if (item === "align-right") return active.has("align:right");
666
1375
  return active.has(item);
667
1376
  };
668
- var EditorToolbar = ({ items, active, onAction }) => {
669
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "lib-xplat-editor__toolbar", children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
670
- "button",
671
- {
672
- type: "button",
673
- className: clsx_default("lib-xplat-editor__toolbar-btn", isActive(item, active) && "active"),
674
- title: TOOLBAR_TITLE[item],
675
- onMouseDown: (e) => {
676
- e.preventDefault();
677
- onAction(item);
1377
+ var EditorToolbar = ({
1378
+ items,
1379
+ active,
1380
+ onAction,
1381
+ codeLang,
1382
+ inCodeBlock,
1383
+ onCodeLangChange
1384
+ }) => {
1385
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "lib-xplat-editor__toolbar", children: [
1386
+ items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1387
+ "button",
1388
+ {
1389
+ type: "button",
1390
+ className: clsx_default("lib-xplat-editor__toolbar-btn", isActive(item, active) && "active"),
1391
+ title: TOOLBAR_TITLE[item],
1392
+ onMouseDown: (e) => {
1393
+ e.preventDefault();
1394
+ onAction(item);
1395
+ },
1396
+ children: TOOLBAR_LABEL[item]
678
1397
  },
679
- children: TOOLBAR_LABEL[item]
680
- },
681
- item
682
- )) });
1398
+ item
1399
+ )),
1400
+ inCodeBlock && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1401
+ "select",
1402
+ {
1403
+ className: "lib-xplat-editor__lang",
1404
+ value: codeLang,
1405
+ "aria-label": "\uCF54\uB4DC \uC5B8\uC5B4",
1406
+ onMouseDown: (e) => e.stopPropagation(),
1407
+ onChange: (e) => onCodeLangChange(e.target.value),
1408
+ children: CODE_LANGUAGES.map((l) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: l.value, children: l.label }, l.value))
1409
+ }
1410
+ )
1411
+ ] });
683
1412
  };
684
1413
  var SlashMenu = ({ position, items, activeIndex, onSelect, onClose }) => {
685
1414
  import_react.default.useEffect(() => {