@teriusu/rich-editor 0.10.8 → 0.11.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.
- package/dist/components/BubbleToolbar.svelte +40 -36
- package/dist/components/BubbleToolbar.svelte.d.ts +3 -0
- package/dist/components/BubbleToolbar.svelte.d.ts.map +1 -1
- package/dist/components/EmojiSuggestionMenu.svelte +291 -0
- package/dist/components/EmojiSuggestionMenu.svelte.d.ts +9 -0
- package/dist/components/EmojiSuggestionMenu.svelte.d.ts.map +1 -0
- package/dist/components/FixedToolbar.svelte +53 -47
- package/dist/components/FixedToolbar.svelte.d.ts +3 -0
- package/dist/components/FixedToolbar.svelte.d.ts.map +1 -1
- package/dist/components/FloatingHelper.svelte +219 -0
- package/dist/components/FloatingHelper.svelte.d.ts +14 -0
- package/dist/components/FloatingHelper.svelte.d.ts.map +1 -0
- package/dist/components/InputModal.svelte +7 -3
- package/dist/components/InputModal.svelte.d.ts +3 -0
- package/dist/components/InputModal.svelte.d.ts.map +1 -1
- package/dist/components/MathModal.svelte +33 -29
- package/dist/components/MathModal.svelte.d.ts +3 -0
- package/dist/components/MathModal.svelte.d.ts.map +1 -1
- package/dist/components/MediaPickerModal.svelte +7 -3
- package/dist/components/MediaPickerModal.svelte.d.ts +3 -0
- package/dist/components/MediaPickerModal.svelte.d.ts.map +1 -1
- package/dist/components/SlashCommandMenu.svelte +88 -72
- package/dist/components/SlashCommandMenu.svelte.d.ts +3 -0
- package/dist/components/SlashCommandMenu.svelte.d.ts.map +1 -1
- package/dist/components/TableBubbleMenu.svelte +41 -33
- package/dist/components/TableBubbleMenu.svelte.d.ts +3 -0
- package/dist/components/TableBubbleMenu.svelte.d.ts.map +1 -1
- package/dist/components/TipTapEditor.svelte +239 -127
- package/dist/components/TipTapEditor.svelte.d.ts +7 -0
- package/dist/components/TipTapEditor.svelte.d.ts.map +1 -1
- package/dist/extensions/CardBlock.d.ts.map +1 -1
- package/dist/extensions/CardBlock.js +6 -4
- package/dist/extensions/FileAttachment.d.ts.map +1 -1
- package/dist/extensions/FileAttachment.js +2 -1
- package/dist/extensions/FixedDetails.d.ts.map +1 -1
- package/dist/extensions/FixedDetails.js +5 -3
- package/dist/extensions/LegacyBlock.d.ts.map +1 -1
- package/dist/extensions/LegacyBlock.js +8 -6
- package/dist/extensions/Math.d.ts.map +1 -1
- package/dist/extensions/Math.js +5 -2
- package/dist/extensions/MbusVideo.d.ts.map +1 -1
- package/dist/extensions/MbusVideo.js +73 -9
- package/dist/extensions/MediaToolbar.d.ts +41 -0
- package/dist/extensions/MediaToolbar.d.ts.map +1 -0
- package/dist/extensions/MediaToolbar.js +196 -0
- package/dist/extensions/MediaToolbar.test.d.ts +2 -0
- package/dist/extensions/MediaToolbar.test.d.ts.map +1 -0
- package/dist/extensions/MediaToolbar.test.js +231 -0
- package/dist/extensions/OrderedListMarker.d.ts +2 -0
- package/dist/extensions/OrderedListMarker.d.ts.map +1 -0
- package/dist/extensions/OrderedListMarker.js +114 -0
- package/dist/extensions/OrderedListMarker.test.d.ts +2 -0
- package/dist/extensions/OrderedListMarker.test.d.ts.map +1 -0
- package/dist/extensions/OrderedListMarker.test.js +176 -0
- package/dist/extensions/PdfBlock.d.ts.map +1 -1
- package/dist/extensions/PdfBlock.js +25 -23
- package/dist/extensions/ResizableImage.d.ts.map +1 -1
- package/dist/extensions/ResizableImage.js +20 -1
- package/dist/extensions/TableKeymap.d.ts +16 -0
- package/dist/extensions/TableKeymap.d.ts.map +1 -0
- package/dist/extensions/TableKeymap.js +97 -0
- package/dist/extensions/TableKeymap.test.d.ts +2 -0
- package/dist/extensions/TableKeymap.test.d.ts.map +1 -0
- package/dist/extensions/TableKeymap.test.js +147 -0
- package/dist/extensions/TabsBlock.d.ts.map +1 -1
- package/dist/extensions/TabsBlock.js +12 -9
- package/dist/extensions/TiptapMidibus.d.ts.map +1 -1
- package/dist/extensions/TiptapMidibus.js +60 -6
- package/dist/extensions/UploadSkeleton.d.ts +61 -0
- package/dist/extensions/UploadSkeleton.d.ts.map +1 -0
- package/dist/extensions/UploadSkeleton.js +233 -0
- package/dist/extensions/UploadSkeleton.test.d.ts +2 -0
- package/dist/extensions/UploadSkeleton.test.d.ts.map +1 -0
- package/dist/extensions/UploadSkeleton.test.js +128 -0
- package/dist/extensions/VideoEmbed.d.ts +8 -0
- package/dist/extensions/VideoEmbed.d.ts.map +1 -1
- package/dist/extensions/VideoEmbed.js +226 -45
- package/dist/extensions/VideoEmbed.test.js +131 -2
- package/dist/i18n/en.d.ts +3 -0
- package/dist/i18n/en.d.ts.map +1 -0
- package/dist/i18n/en.js +191 -0
- package/dist/i18n/es.d.ts +3 -0
- package/dist/i18n/es.d.ts.map +1 -0
- package/dist/i18n/es.js +191 -0
- package/dist/i18n/index.d.ts +43 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/index.js +109 -0
- package/dist/i18n/index.test.d.ts +2 -0
- package/dist/i18n/index.test.d.ts.map +1 -0
- package/dist/i18n/index.test.js +173 -0
- package/dist/i18n/ja.d.ts +3 -0
- package/dist/i18n/ja.d.ts.map +1 -0
- package/dist/i18n/ja.js +191 -0
- package/dist/i18n/ko.d.ts +198 -0
- package/dist/i18n/ko.d.ts.map +1 -0
- package/dist/i18n/ko.js +215 -0
- package/dist/i18n/zh-hans.d.ts +3 -0
- package/dist/i18n/zh-hans.d.ts.map +1 -0
- package/dist/i18n/zh-hans.js +191 -0
- package/dist/i18n/zh-hant.d.ts +3 -0
- package/dist/i18n/zh-hant.d.ts.map +1 -0
- package/dist/i18n/zh-hant.js +192 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -1
- package/dist/static/highlight.d.ts +13 -0
- package/dist/static/highlight.d.ts.map +1 -0
- package/dist/static/highlight.js +99 -0
- package/dist/static/index.d.ts +33 -0
- package/dist/static/index.d.ts.map +1 -0
- package/dist/static/index.js +367 -0
- package/dist/static/index.test.d.ts +2 -0
- package/dist/static/index.test.d.ts.map +1 -0
- package/dist/static/index.test.js +194 -0
- package/dist/static/tokenizer.d.ts +52 -0
- package/dist/static/tokenizer.d.ts.map +1 -0
- package/dist/static/tokenizer.js +289 -0
- package/dist/styles/editor.css +1 -1
- package/dist/utils/emoji-data.d.ts +15 -0
- package/dist/utils/emoji-suggestion.d.ts +48 -0
- package/dist/utils/emoji-suggestion.d.ts.map +1 -0
- package/dist/utils/emoji-suggestion.js +51 -0
- package/dist/utils/emoji-suggestion.test.d.ts +2 -0
- package/dist/utils/emoji-suggestion.test.d.ts.map +1 -0
- package/dist/utils/emoji-suggestion.test.js +117 -0
- package/dist/utils/floating-helper.d.ts +60 -0
- package/dist/utils/floating-helper.d.ts.map +1 -0
- package/dist/utils/floating-helper.js +63 -0
- package/dist/utils/floating-helper.test.d.ts +2 -0
- package/dist/utils/floating-helper.test.d.ts.map +1 -0
- package/dist/utils/floating-helper.test.js +141 -0
- package/dist/utils/guard-vscode-paste.d.ts +19 -0
- package/dist/utils/guard-vscode-paste.d.ts.map +1 -0
- package/dist/utils/guard-vscode-paste.js +46 -0
- package/dist/utils/guard-vscode-paste.test.d.ts +2 -0
- package/dist/utils/guard-vscode-paste.test.d.ts.map +1 -0
- package/dist/utils/guard-vscode-paste.test.js +86 -0
- package/dist/utils/media-size.d.ts +29 -0
- package/dist/utils/media-size.d.ts.map +1 -0
- package/dist/utils/media-size.js +90 -0
- package/dist/utils/resize.d.ts +12 -0
- package/dist/utils/resize.d.ts.map +1 -1
- package/dist/utils/resize.js +5 -5
- package/dist/utils/sanitize.d.ts +2 -0
- package/dist/utils/sanitize.d.ts.map +1 -1
- package/dist/utils/sanitize.js +28 -3
- package/package.json +8 -1
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
import ToggleHeadingIcon from "./icons/ToggleHeadingIcon.svelte";
|
|
42
42
|
import InputModal from "./InputModal.svelte";
|
|
43
43
|
import type { ToolbarFeature, PromptHandler } from "../types";
|
|
44
|
+
import { defaultTranslator, type EditorTranslator } from "../i18n";
|
|
44
45
|
|
|
45
46
|
let {
|
|
46
47
|
editor,
|
|
@@ -52,6 +53,7 @@
|
|
|
52
53
|
onPromptMbus,
|
|
53
54
|
onPromptVideo,
|
|
54
55
|
toolbarEnd,
|
|
56
|
+
t = defaultTranslator,
|
|
55
57
|
}: {
|
|
56
58
|
editor: Editor;
|
|
57
59
|
features: Set<ToolbarFeature>;
|
|
@@ -76,6 +78,8 @@
|
|
|
76
78
|
* 여기에 넣으면 툴바의 flex 행에 그대로 얹혀 정렬이 저절로 맞는다.
|
|
77
79
|
*/
|
|
78
80
|
toolbarEnd?: Snippet;
|
|
81
|
+
/** 에디터 UI 번역 함수. 미주입 시 ko. */
|
|
82
|
+
t?: EditorTranslator;
|
|
79
83
|
} = $props();
|
|
80
84
|
|
|
81
85
|
/*
|
|
@@ -171,21 +175,21 @@
|
|
|
171
175
|
}
|
|
172
176
|
|
|
173
177
|
const currentBlockLabel = $derived.by(() => {
|
|
174
|
-
if (isActive("heading", { level: 1 })) return "
|
|
175
|
-
if (isActive("heading", { level: 2 })) return "
|
|
176
|
-
if (isActive("heading", { level: 3 })) return "
|
|
177
|
-
if (isActive("bulletList")) return "
|
|
178
|
-
if (isActive("orderedList")) return "
|
|
179
|
-
if (isActive("taskList")) return "
|
|
180
|
-
if (isActive("blockquote")) return "
|
|
178
|
+
if (isActive("heading", { level: 1 })) return t("heading1");
|
|
179
|
+
if (isActive("heading", { level: 2 })) return t("heading2");
|
|
180
|
+
if (isActive("heading", { level: 3 })) return t("heading3");
|
|
181
|
+
if (isActive("bulletList")) return t("bulletList");
|
|
182
|
+
if (isActive("orderedList")) return t("orderedList");
|
|
183
|
+
if (isActive("taskList")) return t("taskList");
|
|
184
|
+
if (isActive("blockquote")) return t("blockquote");
|
|
181
185
|
if (isActive("details")) {
|
|
182
186
|
// 토글 제목이면 단계까지 보여 준다 — 그냥 "토글" 이면 무엇을 고른 건지 안 보인다.
|
|
183
187
|
for (const lv of [1, 2, 3]) {
|
|
184
|
-
if (isActive("detailsSummary", { level: lv })) return
|
|
188
|
+
if (isActive("detailsSummary", { level: lv })) return t("toggleHeading", { level: lv });
|
|
185
189
|
}
|
|
186
|
-
return "
|
|
190
|
+
return t("toggle");
|
|
187
191
|
}
|
|
188
|
-
return "
|
|
192
|
+
return t("paragraph");
|
|
189
193
|
});
|
|
190
194
|
|
|
191
195
|
function runBlock(fn: () => void) {
|
|
@@ -238,7 +242,7 @@
|
|
|
238
242
|
type="button"
|
|
239
243
|
onclick={() => editor.chain().focus().undo().run()}
|
|
240
244
|
disabled={!canDo((c) => c.undo())}
|
|
241
|
-
aria-label=
|
|
245
|
+
aria-label={t('undo')}
|
|
242
246
|
class={cn(
|
|
243
247
|
"p-1.5 rounded-md transition-colors text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
244
248
|
!canDo((c) => c.undo()) && "opacity-30 pointer-events-none",
|
|
@@ -252,7 +256,7 @@
|
|
|
252
256
|
type="button"
|
|
253
257
|
onclick={() => editor.chain().focus().redo().run()}
|
|
254
258
|
disabled={!canDo((c) => c.redo())}
|
|
255
|
-
aria-label=
|
|
259
|
+
aria-label={t('redo')}
|
|
256
260
|
class={cn(
|
|
257
261
|
"p-1.5 rounded-md transition-colors text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
258
262
|
!canDo((c) => c.redo()) && "opacity-30 pointer-events-none",
|
|
@@ -271,7 +275,7 @@
|
|
|
271
275
|
<button
|
|
272
276
|
type="button"
|
|
273
277
|
onclick={() => (blockMenuOpen = !blockMenuOpen)}
|
|
274
|
-
aria-label=
|
|
278
|
+
aria-label={t('blockType')}
|
|
275
279
|
class="flex items-center gap-1 px-2 py-1.5 rounded-md transition-colors text-muted-foreground hover:bg-muted hover:text-foreground min-w-[96px]"
|
|
276
280
|
>
|
|
277
281
|
<span class="text-sm">{currentBlockLabel}</span>
|
|
@@ -301,7 +305,7 @@
|
|
|
301
305
|
onclick={() =>
|
|
302
306
|
runBlock(() => editor.chain().focus().setParagraph().run())}
|
|
303
307
|
>
|
|
304
|
-
<Pilcrow size={14} />
|
|
308
|
+
<Pilcrow size={14} /> {t('paragraph')}
|
|
305
309
|
</button>
|
|
306
310
|
{#if has('h1')}
|
|
307
311
|
<button
|
|
@@ -315,7 +319,7 @@
|
|
|
315
319
|
editor.chain().focus().toggleHeading({ level: 1 }).run(),
|
|
316
320
|
)}
|
|
317
321
|
>
|
|
318
|
-
<Heading1 size={14} />
|
|
322
|
+
<Heading1 size={14} /> {t('heading1')}
|
|
319
323
|
<span class="hce-menu-shortcut"># </span>
|
|
320
324
|
</button>
|
|
321
325
|
{/if}
|
|
@@ -331,7 +335,7 @@
|
|
|
331
335
|
editor.chain().focus().toggleHeading({ level: 2 }).run(),
|
|
332
336
|
)}
|
|
333
337
|
>
|
|
334
|
-
<Heading2 size={14} />
|
|
338
|
+
<Heading2 size={14} /> {t('heading2')}
|
|
335
339
|
<span class="hce-menu-shortcut">## </span>
|
|
336
340
|
</button>
|
|
337
341
|
{/if}
|
|
@@ -347,7 +351,7 @@
|
|
|
347
351
|
editor.chain().focus().toggleHeading({ level: 3 }).run(),
|
|
348
352
|
)}
|
|
349
353
|
>
|
|
350
|
-
<Heading3 size={14} />
|
|
354
|
+
<Heading3 size={14} /> {t('heading3')}
|
|
351
355
|
<span class="hce-menu-shortcut">### </span>
|
|
352
356
|
</button>
|
|
353
357
|
{/if}
|
|
@@ -364,7 +368,7 @@
|
|
|
364
368
|
onclick={() =>
|
|
365
369
|
runBlock(() => editor.chain().focus().toggleBulletList().run())}
|
|
366
370
|
>
|
|
367
|
-
<List size={14} />
|
|
371
|
+
<List size={14} /> {t('bulletList')}
|
|
368
372
|
<span class="hce-menu-shortcut">- </span>
|
|
369
373
|
</button>
|
|
370
374
|
{/if}
|
|
@@ -378,7 +382,7 @@
|
|
|
378
382
|
onclick={() =>
|
|
379
383
|
runBlock(() => editor.chain().focus().toggleOrderedList().run())}
|
|
380
384
|
>
|
|
381
|
-
<ListOrdered size={14} />
|
|
385
|
+
<ListOrdered size={14} /> {t('orderedList')}
|
|
382
386
|
<span class="hce-menu-shortcut">1. </span>
|
|
383
387
|
</button>
|
|
384
388
|
{/if}
|
|
@@ -392,7 +396,7 @@
|
|
|
392
396
|
onclick={() =>
|
|
393
397
|
runBlock(() => editor.chain().focus().toggleTaskList().run())}
|
|
394
398
|
>
|
|
395
|
-
<CheckSquare size={14} />
|
|
399
|
+
<CheckSquare size={14} /> {t('taskList')}
|
|
396
400
|
<span class="hce-menu-shortcut">[] </span>
|
|
397
401
|
</button>
|
|
398
402
|
{/if}
|
|
@@ -409,7 +413,7 @@
|
|
|
409
413
|
onclick={() =>
|
|
410
414
|
runBlock(() => editor.chain().focus().toggleBlockquote().run())}
|
|
411
415
|
>
|
|
412
|
-
<Quote size={14} />
|
|
416
|
+
<Quote size={14} /> {t('blockquote')}
|
|
413
417
|
<span class="hce-menu-shortcut">" </span>
|
|
414
418
|
</button>
|
|
415
419
|
{/if}
|
|
@@ -423,7 +427,7 @@
|
|
|
423
427
|
onclick={() =>
|
|
424
428
|
runBlock(() => editor.chain().focus().setDetails().run())}
|
|
425
429
|
>
|
|
426
|
-
<ChevronRight size={14} />
|
|
430
|
+
<ChevronRight size={14} /> {t('toggle')}
|
|
427
431
|
<span class="hce-menu-shortcut">> </span>
|
|
428
432
|
</button>
|
|
429
433
|
<!--
|
|
@@ -450,7 +454,7 @@
|
|
|
450
454
|
.run(),
|
|
451
455
|
)}
|
|
452
456
|
>
|
|
453
|
-
<ToggleHeadingIcon size={14} level={level as 1 | 2 | 3} />
|
|
457
|
+
<ToggleHeadingIcon size={14} level={level as 1 | 2 | 3} /> {t('toggleHeading', { level })}
|
|
454
458
|
<span class="hce-menu-shortcut">{'#'.repeat(level)} > </span>
|
|
455
459
|
</button>
|
|
456
460
|
{/each}
|
|
@@ -475,7 +479,7 @@
|
|
|
475
479
|
<button
|
|
476
480
|
type="button"
|
|
477
481
|
onclick={() => editor.chain().focus().toggleBold().run()}
|
|
478
|
-
aria-label=
|
|
482
|
+
aria-label={t('bold')}
|
|
479
483
|
class={cn(
|
|
480
484
|
"p-1.5 rounded-md transition-colors",
|
|
481
485
|
isActive("bold")
|
|
@@ -490,7 +494,7 @@
|
|
|
490
494
|
<button
|
|
491
495
|
type="button"
|
|
492
496
|
onclick={() => editor.chain().focus().toggleItalic().run()}
|
|
493
|
-
aria-label=
|
|
497
|
+
aria-label={t('italic')}
|
|
494
498
|
class={cn(
|
|
495
499
|
"p-1.5 rounded-md transition-colors",
|
|
496
500
|
isActive("italic")
|
|
@@ -505,7 +509,7 @@
|
|
|
505
509
|
<button
|
|
506
510
|
type="button"
|
|
507
511
|
onclick={() => editor.chain().focus().toggleUnderline().run()}
|
|
508
|
-
aria-label=
|
|
512
|
+
aria-label={t('underline')}
|
|
509
513
|
class={cn(
|
|
510
514
|
"p-1.5 rounded-md transition-colors",
|
|
511
515
|
isActive("underline")
|
|
@@ -520,7 +524,7 @@
|
|
|
520
524
|
<button
|
|
521
525
|
type="button"
|
|
522
526
|
onclick={() => editor.chain().focus().toggleStrike().run()}
|
|
523
|
-
aria-label=
|
|
527
|
+
aria-label={t('strike')}
|
|
524
528
|
class={cn(
|
|
525
529
|
"p-1.5 rounded-md transition-colors",
|
|
526
530
|
isActive("strike")
|
|
@@ -552,7 +556,7 @@
|
|
|
552
556
|
<button
|
|
553
557
|
type="button"
|
|
554
558
|
onclick={() => editor.chain().focus().setTextAlign('left').run()}
|
|
555
|
-
aria-label=
|
|
559
|
+
aria-label={t('alignLeft')}
|
|
556
560
|
class={cn(
|
|
557
561
|
"p-1.5 rounded-md transition-colors",
|
|
558
562
|
isActive({ textAlign: 'left' })
|
|
@@ -567,7 +571,7 @@
|
|
|
567
571
|
<button
|
|
568
572
|
type="button"
|
|
569
573
|
onclick={() => editor.chain().focus().setTextAlign('center').run()}
|
|
570
|
-
aria-label=
|
|
574
|
+
aria-label={t('alignCenter')}
|
|
571
575
|
class={cn(
|
|
572
576
|
"p-1.5 rounded-md transition-colors",
|
|
573
577
|
isActive({ textAlign: 'center' })
|
|
@@ -582,7 +586,7 @@
|
|
|
582
586
|
<button
|
|
583
587
|
type="button"
|
|
584
588
|
onclick={() => editor.chain().focus().setTextAlign('right').run()}
|
|
585
|
-
aria-label=
|
|
589
|
+
aria-label={t('alignRight')}
|
|
586
590
|
class={cn(
|
|
587
591
|
"p-1.5 rounded-md transition-colors",
|
|
588
592
|
isActive({ textAlign: 'right' })
|
|
@@ -603,11 +607,11 @@
|
|
|
603
607
|
<button
|
|
604
608
|
type="button"
|
|
605
609
|
onclick={() => (insertMenuOpen = !insertMenuOpen)}
|
|
606
|
-
aria-label=
|
|
610
|
+
aria-label={t('insert')}
|
|
607
611
|
class="flex items-center gap-1 px-2 py-1.5 rounded-md transition-colors text-muted-foreground hover:bg-muted hover:text-foreground"
|
|
608
612
|
>
|
|
609
613
|
<Plus size={iconSize} />
|
|
610
|
-
<span class="text-sm"
|
|
614
|
+
<span class="text-sm">{t('insert')}</span>
|
|
611
615
|
<ChevronDown size={12} />
|
|
612
616
|
</button>
|
|
613
617
|
{#if insertMenuOpen}
|
|
@@ -622,7 +626,7 @@
|
|
|
622
626
|
onclick={() =>
|
|
623
627
|
runInsert(() => editor.chain().focus().setCodeBlock().run())}
|
|
624
628
|
>
|
|
625
|
-
<Code2 size={14} />
|
|
629
|
+
<Code2 size={14} /> {t('codeBlock')}
|
|
626
630
|
<span class="hce-menu-shortcut">```</span>
|
|
627
631
|
</button>
|
|
628
632
|
{/if}
|
|
@@ -633,7 +637,7 @@
|
|
|
633
637
|
onclick={() =>
|
|
634
638
|
runInsert(() => editor.chain().focus().promptMathDisplay().run())}
|
|
635
639
|
>
|
|
636
|
-
<Sigma size={14} />
|
|
640
|
+
<Sigma size={14} /> {t('mathBlock')}
|
|
637
641
|
<span class="hce-menu-shortcut">$$</span>
|
|
638
642
|
</button>
|
|
639
643
|
<!--
|
|
@@ -648,7 +652,7 @@
|
|
|
648
652
|
onclick={() =>
|
|
649
653
|
runInsert(() => editor.chain().focus().promptMathInline().run())}
|
|
650
654
|
>
|
|
651
|
-
<Sigma size={14} />
|
|
655
|
+
<Sigma size={14} /> {t('mathInline')}
|
|
652
656
|
<span class="hce-menu-shortcut">$ $</span>
|
|
653
657
|
</button>
|
|
654
658
|
{/if}
|
|
@@ -667,7 +671,7 @@
|
|
|
667
671
|
class="w-full text-left px-2.5 py-1.5 text-xs transition-colors flex items-center gap-2 hover:bg-muted"
|
|
668
672
|
onclick={() => runInsert(onFileClick!)}
|
|
669
673
|
>
|
|
670
|
-
<Paperclip size={14} />
|
|
674
|
+
<Paperclip size={14} /> {t('file')}
|
|
671
675
|
</button>
|
|
672
676
|
{/if}
|
|
673
677
|
{#if has('table')}
|
|
@@ -679,7 +683,7 @@
|
|
|
679
683
|
insertTableSized(editor, { rows: 3, cols: 3, withHeaderRow: true }),
|
|
680
684
|
)}
|
|
681
685
|
>
|
|
682
|
-
<TableIcon size={14} />
|
|
686
|
+
<TableIcon size={14} /> {t('table3x3')}
|
|
683
687
|
</button>
|
|
684
688
|
{/if}
|
|
685
689
|
|
|
@@ -695,7 +699,7 @@
|
|
|
695
699
|
editor.chain().focus().setHorizontalRule().run(),
|
|
696
700
|
)}
|
|
697
701
|
>
|
|
698
|
-
<Minus size={14} />
|
|
702
|
+
<Minus size={14} /> {t('divider')}
|
|
699
703
|
<span class="hce-menu-shortcut">---</span>
|
|
700
704
|
</button>
|
|
701
705
|
{/if}
|
|
@@ -706,7 +710,7 @@
|
|
|
706
710
|
onclick={() =>
|
|
707
711
|
runInsert(() => editor.chain().focus().setColumns(2).run())}
|
|
708
712
|
>
|
|
709
|
-
<Columns2 size={14} />
|
|
713
|
+
<Columns2 size={14} /> {t('columns2')}
|
|
710
714
|
</button>
|
|
711
715
|
{/if}
|
|
712
716
|
{#if has('columns-3')}
|
|
@@ -716,7 +720,7 @@
|
|
|
716
720
|
onclick={() =>
|
|
717
721
|
runInsert(() => editor.chain().focus().setColumns(3).run())}
|
|
718
722
|
>
|
|
719
|
-
<Columns3 size={14} />
|
|
723
|
+
<Columns3 size={14} /> {t('columns3')}
|
|
720
724
|
</button>
|
|
721
725
|
{/if}
|
|
722
726
|
{#if has('tabs')}
|
|
@@ -726,7 +730,7 @@
|
|
|
726
730
|
onclick={() =>
|
|
727
731
|
runInsert(() => editor.chain().focus().setTabs(3).run())}
|
|
728
732
|
>
|
|
729
|
-
<PanelTop size={14} />
|
|
733
|
+
<PanelTop size={14} /> {t('tabs')}
|
|
730
734
|
</button>
|
|
731
735
|
{/if}
|
|
732
736
|
{#if has('card')}
|
|
@@ -735,7 +739,7 @@
|
|
|
735
739
|
class="w-full text-left px-2.5 py-1.5 text-xs transition-colors flex items-center gap-2 hover:bg-muted"
|
|
736
740
|
onclick={() => runInsert(() => editor.chain().focus().setCard().run())}
|
|
737
741
|
>
|
|
738
|
-
<SquareDashed size={14} />
|
|
742
|
+
<SquareDashed size={14} /> {t('card')}
|
|
739
743
|
</button>
|
|
740
744
|
{/if}
|
|
741
745
|
|
|
@@ -748,7 +752,7 @@
|
|
|
748
752
|
class="w-full text-left px-2.5 py-1.5 text-xs transition-colors flex items-center gap-2 hover:bg-muted"
|
|
749
753
|
onclick={addImage}
|
|
750
754
|
>
|
|
751
|
-
<ImageIcon size={14} />
|
|
755
|
+
<ImageIcon size={14} /> {t('image')}
|
|
752
756
|
</button>
|
|
753
757
|
{/if}
|
|
754
758
|
{#if has('video')}
|
|
@@ -761,7 +765,7 @@
|
|
|
761
765
|
class="w-full text-left px-2.5 py-1.5 text-xs transition-colors flex items-center gap-2 hover:bg-muted"
|
|
762
766
|
onclick={addVideo}
|
|
763
767
|
>
|
|
764
|
-
<Youtube size={14} />
|
|
768
|
+
<Youtube size={14} /> {t('video')}
|
|
765
769
|
</button>
|
|
766
770
|
{/if}
|
|
767
771
|
{#if has('mbus')}
|
|
@@ -770,7 +774,7 @@
|
|
|
770
774
|
class="w-full text-left px-2.5 py-1.5 text-xs transition-colors flex items-center gap-2 hover:bg-muted"
|
|
771
775
|
onclick={addMbus}
|
|
772
776
|
>
|
|
773
|
-
<Tv size={14} />
|
|
777
|
+
<Tv size={14} /> {t('mbusVideo')}
|
|
774
778
|
</button>
|
|
775
779
|
{/if}
|
|
776
780
|
</div>
|
|
@@ -783,24 +787,26 @@
|
|
|
783
787
|
<!-- (이미지 모달은 없다 — 에디터가 업로드/링크 탭 모달을 띄운다. `onImageClick` 참고.) -->
|
|
784
788
|
{#if modalState?.type === "video"}
|
|
785
789
|
<InputModal
|
|
786
|
-
title=
|
|
790
|
+
title={t('videoUrl')}
|
|
787
791
|
placeholder="https://www.youtube.com/watch?v=..."
|
|
788
792
|
onConfirm={(url) => {
|
|
789
793
|
editor.chain().focus().setVideoEmbed({ src: url }).run();
|
|
790
794
|
modalState = null;
|
|
791
795
|
}}
|
|
792
796
|
onCancel={() => (modalState = null)}
|
|
797
|
+
{t}
|
|
793
798
|
/>
|
|
794
799
|
{/if}
|
|
795
800
|
{#if modalState?.type === "mbus"}
|
|
796
801
|
<InputModal
|
|
797
|
-
title=
|
|
802
|
+
title={t('mbusVideoUrl')}
|
|
798
803
|
placeholder="https://play.mbus.tv/v1/hls/..."
|
|
799
804
|
onConfirm={(url) => {
|
|
800
805
|
editor.chain().focus().setMbusVideo({ src: url }).run();
|
|
801
806
|
modalState = null;
|
|
802
807
|
}}
|
|
803
808
|
onCancel={() => (modalState = null)}
|
|
809
|
+
{t}
|
|
804
810
|
/>
|
|
805
811
|
{/if}
|
|
806
812
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Editor } from "@tiptap/core";
|
|
2
2
|
import type { Snippet } from "svelte";
|
|
3
3
|
import type { ToolbarFeature, PromptHandler } from "../types";
|
|
4
|
+
import { type EditorTranslator } from "../i18n";
|
|
4
5
|
type $$ComponentProps = {
|
|
5
6
|
editor: Editor;
|
|
6
7
|
features: Set<ToolbarFeature>;
|
|
@@ -25,6 +26,8 @@ type $$ComponentProps = {
|
|
|
25
26
|
* 여기에 넣으면 툴바의 flex 행에 그대로 얹혀 정렬이 저절로 맞는다.
|
|
26
27
|
*/
|
|
27
28
|
toolbarEnd?: Snippet;
|
|
29
|
+
/** 에디터 UI 번역 함수. 미주입 시 ko. */
|
|
30
|
+
t?: EditorTranslator;
|
|
28
31
|
};
|
|
29
32
|
declare const FixedToolbar: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
30
33
|
type FixedToolbar = ReturnType<typeof FixedToolbar>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FixedToolbar.svelte.d.ts","sourceRoot":"","sources":["../../src/components/FixedToolbar.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAwCtC,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"FixedToolbar.svelte.d.ts","sourceRoot":"","sources":["../../src/components/FixedToolbar.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAwCtC,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC9D,OAAO,EAAqB,KAAK,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAElE,KAAK,gBAAgB,GAAI;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,GAAG,CAAC,cAAc,CAAC,CAAC;IAC9B,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB;;;;;OAKG;IACH,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB,YAAY,CAAC,EAAE,aAAa,CAAC;IAC7B,YAAY,CAAC,EAAE,aAAa,CAAC;IAC7B,0CAA0C;IAC1C,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,8BAA8B;IAC9B,CAAC,CAAC,EAAE,gBAAgB,CAAC;CACtB,CAAC;AAyiBJ,QAAA,MAAM,YAAY,sDAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Editor } from "@tiptap/core";
|
|
3
|
+
import { Sigma, Code2 } from "lucide-svelte";
|
|
4
|
+
import type { ToolbarFeature } from "../types";
|
|
5
|
+
import {
|
|
6
|
+
floatingHelperVisible,
|
|
7
|
+
helperInsertMath,
|
|
8
|
+
helperInsertCodeBlock,
|
|
9
|
+
FLOATING_HELPER_ON_CLASS,
|
|
10
|
+
} from "../utils/floating-helper";
|
|
11
|
+
import { defaultTranslator, type EditorTranslator } from "../i18n";
|
|
12
|
+
|
|
13
|
+
/*
|
|
14
|
+
* 빈 줄 플로팅 헬퍼의 **화면 절반** — 판정·커맨드는 `utils/floating-helper.ts`.
|
|
15
|
+
*
|
|
16
|
+
* 포커스된 최상위 빈 문단 옆에 "/로 명령어 입력" 안내와 수식·코드 즉시 삽입 버튼을
|
|
17
|
+
* 띄운다(main 정올 `Floating.svelte` 의 직접 구현 이식). 에디터 컨테이너
|
|
18
|
+
* (`.hce-editor-wrapper`, `position:relative`) 안에 `position:absolute` 로 그린다 —
|
|
19
|
+
* 포털이 없고 문서 흐름의 자리도 차지하지 않는다.
|
|
20
|
+
*
|
|
21
|
+
* ⚠️ 위치 계산 셋이 main 사고 이력의 재발 방지다:
|
|
22
|
+
* - `.hce-floating-helper` 는 CSS 에서 **처음부터** `position:absolute; width:max-content`
|
|
23
|
+
* 다. 블록 폭으로 측정된 뒤에 좁히면 넘침 보정이 화면 밖(left:-437px)으로 민 전력이
|
|
24
|
+
* 있다(main floating-ui 시절).
|
|
25
|
+
* - 등장 애니메이션은 **안쪽 요소에만** 건다. 바깥(위치 담당)에 transform 을 걸면
|
|
26
|
+
* 위치가 애니메이션 프레임에 덮인다(main `79f64c18`→`c9840fdb`).
|
|
27
|
+
* - 슬래시·이모지 메뉴가 열리면 `suppressed` 로 끈다 — 그 둘은 `position:fixed`
|
|
28
|
+
* 뷰포트 좌표라 좌표계가 달라 서로 피해 갈 수 없다.
|
|
29
|
+
*/
|
|
30
|
+
let {
|
|
31
|
+
editor,
|
|
32
|
+
features,
|
|
33
|
+
suppressed = false,
|
|
34
|
+
t = defaultTranslator,
|
|
35
|
+
}: {
|
|
36
|
+
editor: Editor;
|
|
37
|
+
features: Set<ToolbarFeature>;
|
|
38
|
+
suppressed?: boolean;
|
|
39
|
+
/** 에디터 UI 번역 함수. 미주입 시 ko. */
|
|
40
|
+
t?: EditorTranslator;
|
|
41
|
+
} = $props();
|
|
42
|
+
|
|
43
|
+
let visible = $state(false);
|
|
44
|
+
let pos = $state({ top: 0, left: 0 });
|
|
45
|
+
let helperEl = $state<HTMLDivElement>();
|
|
46
|
+
let focused = $state(false);
|
|
47
|
+
|
|
48
|
+
const showMath = $derived(features.has("math"));
|
|
49
|
+
const showCode = $derived(features.has("code-block"));
|
|
50
|
+
const hasButtons = $derived(showMath || showCode);
|
|
51
|
+
|
|
52
|
+
function updatePosition() {
|
|
53
|
+
const wrapper = editor.view.dom.closest(".hce-editor-wrapper") as HTMLElement | null;
|
|
54
|
+
if (!wrapper) return;
|
|
55
|
+
const { from } = editor.state.selection;
|
|
56
|
+
const caret = editor.view.coordsAtPos(from);
|
|
57
|
+
const box = wrapper.getBoundingClientRect();
|
|
58
|
+
let left = caret.right - box.left + 8;
|
|
59
|
+
let top = caret.top - box.top;
|
|
60
|
+
/*
|
|
61
|
+
* 요소가 이미 서 있으면 실측으로 보정한다 — 세로는 커서 줄에 가운데 맞춤,
|
|
62
|
+
* 가로는 래퍼 오른쪽을 넘으면 안으로 죈다(첫 프레임은 아래 `$effect` 가 재계산).
|
|
63
|
+
*/
|
|
64
|
+
if (helperEl) {
|
|
65
|
+
const h = helperEl.offsetHeight;
|
|
66
|
+
if (h) top = caret.top - box.top + (caret.bottom - caret.top - h) / 2;
|
|
67
|
+
const w = helperEl.offsetWidth;
|
|
68
|
+
const maxLeft = wrapper.clientWidth - w - 4;
|
|
69
|
+
if (w && left > maxLeft) left = Math.max(4, maxLeft);
|
|
70
|
+
}
|
|
71
|
+
pos = { top, left };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function evaluate() {
|
|
75
|
+
const show = floatingHelperVisible(editor, { focused, suppressed });
|
|
76
|
+
if (show) updatePosition();
|
|
77
|
+
visible = show;
|
|
78
|
+
/* placeholder 겹침 방지 — 근거는 `FLOATING_HELPER_ON_CLASS` 주석. */
|
|
79
|
+
editor.view.dom.classList.toggle(FLOATING_HELPER_ON_CLASS, show);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
$effect(() => {
|
|
83
|
+
focused = editor.view.hasFocus();
|
|
84
|
+
const onFocus = () => {
|
|
85
|
+
focused = true;
|
|
86
|
+
};
|
|
87
|
+
const onBlur = () => {
|
|
88
|
+
focused = false;
|
|
89
|
+
};
|
|
90
|
+
editor.on("transaction", evaluate);
|
|
91
|
+
editor.on("focus", onFocus);
|
|
92
|
+
editor.on("blur", onBlur);
|
|
93
|
+
return () => {
|
|
94
|
+
editor.off("transaction", evaluate);
|
|
95
|
+
editor.off("focus", onFocus);
|
|
96
|
+
editor.off("blur", onBlur);
|
|
97
|
+
editor.view.dom.classList.remove(FLOATING_HELPER_ON_CLASS);
|
|
98
|
+
};
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
/* 포커스·suppressed 가 바뀌면 재판정 (트랜잭션 없이 바뀌는 두 축). */
|
|
102
|
+
$effect(() => {
|
|
103
|
+
focused;
|
|
104
|
+
suppressed;
|
|
105
|
+
evaluate();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
/* 마운트 직후 실측(폭·높이)으로 한 번 더 자리를 잡는다. */
|
|
109
|
+
$effect(() => {
|
|
110
|
+
if (visible && helperEl) updatePosition();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
/* 래퍼 안쪽이 스크롤되는 호스트 대비 — 슬래시·이모지 메뉴와 같은 캡처 구독. */
|
|
114
|
+
$effect(() => {
|
|
115
|
+
if (!visible) return;
|
|
116
|
+
const onMove = () => updatePosition();
|
|
117
|
+
window.addEventListener("scroll", onMove, true);
|
|
118
|
+
window.addEventListener("resize", onMove);
|
|
119
|
+
return () => {
|
|
120
|
+
window.removeEventListener("scroll", onMove, true);
|
|
121
|
+
window.removeEventListener("resize", onMove);
|
|
122
|
+
};
|
|
123
|
+
});
|
|
124
|
+
</script>
|
|
125
|
+
|
|
126
|
+
{#if visible}
|
|
127
|
+
<!--
|
|
128
|
+
mousedown preventDefault: 버튼을 누르는 순간 에디터가 blur 되면 클릭이 닿기 전에
|
|
129
|
+
헬퍼가 사라진다(버튼과 함께). 포커스를 아예 뺏기지 않는 쪽이 main 의
|
|
130
|
+
`setTimeout(focus)` 복원보다 깔끔하다 — 툴바들이 쓰는 표준 수법.
|
|
131
|
+
-->
|
|
132
|
+
<div
|
|
133
|
+
bind:this={helperEl}
|
|
134
|
+
class="hce-floating-helper"
|
|
135
|
+
style="top: {pos.top}px; left: {pos.left}px"
|
|
136
|
+
role="toolbar"
|
|
137
|
+
tabindex="-1"
|
|
138
|
+
aria-label={t('emptyLineTools')}
|
|
139
|
+
onmousedown={(e) => e.preventDefault()}
|
|
140
|
+
>
|
|
141
|
+
<div class="hce-floating-helper-inner">
|
|
142
|
+
<span class="hce-floating-helper-hint">{t('floatingHint')}{hasButtons ? t('floatingHintOr') : ""}</span>
|
|
143
|
+
{#if showMath}
|
|
144
|
+
<button
|
|
145
|
+
type="button"
|
|
146
|
+
class="hce-floating-plus"
|
|
147
|
+
title={t('math')}
|
|
148
|
+
aria-label={t('insertMath')}
|
|
149
|
+
onclick={() => helperInsertMath(editor)}
|
|
150
|
+
>
|
|
151
|
+
<Sigma size={14} />
|
|
152
|
+
</button>
|
|
153
|
+
{/if}
|
|
154
|
+
{#if showCode}
|
|
155
|
+
<button
|
|
156
|
+
type="button"
|
|
157
|
+
class="hce-floating-plus"
|
|
158
|
+
title={t('codeBlock')}
|
|
159
|
+
aria-label={t('insertCodeBlock')}
|
|
160
|
+
onclick={() => helperInsertCodeBlock(editor)}
|
|
161
|
+
>
|
|
162
|
+
<Code2 size={14} />
|
|
163
|
+
</button>
|
|
164
|
+
{/if}
|
|
165
|
+
</div>
|
|
166
|
+
</div>
|
|
167
|
+
{/if}
|
|
168
|
+
|
|
169
|
+
<style>
|
|
170
|
+
/*
|
|
171
|
+
* ⚠️ `position:absolute` + `width:max-content` 는 **여기 고정**이다 — 위치·크기가
|
|
172
|
+
* 런타임 계산보다 먼저 서 있어야 실측 보정(`updatePosition`)이 옳은 폭을 잰다.
|
|
173
|
+
* z-index 100: 정올 sticky 브레드크럼(z:93)보다 위, 모달(z-50=50 계열 Tailwind 층과는
|
|
174
|
+
* 별개 스택) 아래를 노린 값.
|
|
175
|
+
*/
|
|
176
|
+
.hce-floating-helper {
|
|
177
|
+
position: absolute;
|
|
178
|
+
width: -moz-max-content;
|
|
179
|
+
width: max-content;
|
|
180
|
+
z-index: 100;
|
|
181
|
+
pointer-events: auto;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/* 등장 애니메이션은 안쪽에만 — 바깥 transform 은 위치 계산과 충돌한다. */
|
|
185
|
+
.hce-floating-helper-inner {
|
|
186
|
+
display: flex;
|
|
187
|
+
align-items: center;
|
|
188
|
+
gap: 6px;
|
|
189
|
+
animation: hce-floating-helper-in 0.18s ease-out;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
@keyframes hce-floating-helper-in {
|
|
193
|
+
from {
|
|
194
|
+
opacity: 0;
|
|
195
|
+
transform: translateX(-4px);
|
|
196
|
+
}
|
|
197
|
+
to {
|
|
198
|
+
opacity: 1;
|
|
199
|
+
transform: none;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
@media (prefers-reduced-motion: reduce) {
|
|
204
|
+
.hce-floating-helper-inner {
|
|
205
|
+
animation: none;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.hce-floating-helper-hint {
|
|
210
|
+
font-size: 0.875rem;
|
|
211
|
+
color: var(--muted-foreground);
|
|
212
|
+
opacity: 0.6;
|
|
213
|
+
pointer-events: none;
|
|
214
|
+
-webkit-user-select: none;
|
|
215
|
+
-moz-user-select: none;
|
|
216
|
+
user-select: none;
|
|
217
|
+
white-space: nowrap;
|
|
218
|
+
}
|
|
219
|
+
</style>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Editor } from "@tiptap/core";
|
|
2
|
+
import type { ToolbarFeature } from "../types";
|
|
3
|
+
import { type EditorTranslator } from "../i18n";
|
|
4
|
+
type $$ComponentProps = {
|
|
5
|
+
editor: Editor;
|
|
6
|
+
features: Set<ToolbarFeature>;
|
|
7
|
+
suppressed?: boolean;
|
|
8
|
+
/** 에디터 UI 번역 함수. 미주입 시 ko. */
|
|
9
|
+
t?: EditorTranslator;
|
|
10
|
+
};
|
|
11
|
+
declare const FloatingHelper: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
12
|
+
type FloatingHelper = ReturnType<typeof FloatingHelper>;
|
|
13
|
+
export default FloatingHelper;
|
|
14
|
+
//# sourceMappingURL=FloatingHelper.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FloatingHelper.svelte.d.ts","sourceRoot":"","sources":["../../src/components/FloatingHelper.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAE3C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAO/C,OAAO,EAAqB,KAAK,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAElE,KAAK,gBAAgB,GAAI;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,GAAG,CAAC,cAAc,CAAC,CAAC;IAC9B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,8BAA8B;IAC9B,CAAC,CAAC,EAAE,gBAAgB,CAAC;CACtB,CAAC;AA0IJ,QAAA,MAAM,cAAc,sDAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
|