@moda-editor/core 0.1.1 → 0.1.3

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 (57) hide show
  1. package/dist/conformance.d.ts.map +1 -1
  2. package/dist/conformance.js +140 -0
  3. package/dist/conformance.js.map +1 -1
  4. package/dist/editor/extension.d.ts +4 -2
  5. package/dist/editor/extension.d.ts.map +1 -1
  6. package/dist/features/ace.d.ts.map +1 -1
  7. package/dist/features/ace.js +23 -12
  8. package/dist/features/ace.js.map +1 -1
  9. package/dist/features/code-block.d.ts +29 -0
  10. package/dist/features/code-block.d.ts.map +1 -1
  11. package/dist/features/code-block.js +551 -36
  12. package/dist/features/code-block.js.map +1 -1
  13. package/dist/features/image.d.ts +14 -1
  14. package/dist/features/image.d.ts.map +1 -1
  15. package/dist/features/image.js +279 -11
  16. package/dist/features/image.js.map +1 -1
  17. package/dist/features/marks.d.ts.map +1 -1
  18. package/dist/features/marks.js +28 -1
  19. package/dist/features/marks.js.map +1 -1
  20. package/dist/features/table.d.ts +16 -3
  21. package/dist/features/table.d.ts.map +1 -1
  22. package/dist/features/table.js +295 -8
  23. package/dist/features/table.js.map +1 -1
  24. package/dist/index.d.ts +3 -2
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +3 -2
  27. package/dist/index.js.map +1 -1
  28. package/dist/parse/sanitize.d.ts.map +1 -1
  29. package/dist/parse/sanitize.js +10 -1
  30. package/dist/parse/sanitize.js.map +1 -1
  31. package/dist/presets/highlight.d.ts +17 -0
  32. package/dist/presets/highlight.d.ts.map +1 -1
  33. package/dist/presets/highlight.js +51 -0
  34. package/dist/presets/highlight.js.map +1 -1
  35. package/dist/state/transaction.d.ts +0 -7
  36. package/dist/state/transaction.d.ts.map +1 -1
  37. package/dist/state/transaction.js +30 -1
  38. package/dist/state/transaction.js.map +1 -1
  39. package/dist/styles.css +441 -28
  40. package/dist/view/dom.d.ts.map +1 -1
  41. package/dist/view/dom.js +62 -22
  42. package/dist/view/dom.js.map +1 -1
  43. package/dist/view/input.d.ts.map +1 -1
  44. package/dist/view/input.js +158 -20
  45. package/dist/view/input.js.map +1 -1
  46. package/dist/view/mount.d.ts.map +1 -1
  47. package/dist/view/mount.js +16 -4
  48. package/dist/view/mount.js.map +1 -1
  49. package/dist/view/render.d.ts +7 -1
  50. package/dist/view/render.d.ts.map +1 -1
  51. package/dist/view/render.js +5 -1
  52. package/dist/view/render.js.map +1 -1
  53. package/dist/view/serialize.d.ts +8 -0
  54. package/dist/view/serialize.d.ts.map +1 -1
  55. package/dist/view/serialize.js +19 -0
  56. package/dist/view/serialize.js.map +1 -1
  57. package/package.json +1 -1
package/dist/styles.css CHANGED
@@ -12,6 +12,8 @@
12
12
  --me-gutter-bg: rgba(15, 23, 42, 0.035);
13
13
  --me-gutter-color: #94a3b8;
14
14
  --me-line-active-num: #3b82f6;
15
+ /* atom 노드 셀렉션 링 + 이미지 핸들 — 거터와 같은 이유로 루트에 둔다 */
16
+ --me-node-sel-color: #3b82f6;
15
17
  display: flex;
16
18
  flex-direction: column;
17
19
  gap: 8px;
@@ -53,7 +55,6 @@
53
55
  * 크롬을 담당한다 ─────────────────────────────────────────── */
54
56
  .me-frame {
55
57
  --me-frame-shadow: 0 1px 2px rgba(15, 23, 42, 0.06);
56
- --me-focus-ring: rgba(59, 130, 246, 0.18);
57
58
  --me-border-color: #e2e8f0;
58
59
  --me-text-color: #1e293b;
59
60
  --me-font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas,
@@ -67,9 +68,10 @@
67
68
  overflow: hidden;
68
69
  }
69
70
 
71
+ /* 포커스 표시는 테두리 색만 — 글로우 링은 입력 중 카드 전체를
72
+ * 파란 띠로 둘러 산만했다 */
70
73
  .me-frame:focus-within {
71
74
  border-color: var(--me-editor-focus, #3b82f6);
72
- box-shadow: 0 0 0 3px var(--me-focus-ring);
73
75
  }
74
76
 
75
77
  /* ── 에디터 — Ace 계열 코드 에디터 서페이스: 모노스페이스 본문 +
@@ -82,7 +84,6 @@
82
84
  --me-code-bg: #f1f5f9;
83
85
  --me-code-border: #e2e8f0;
84
86
  --me-gutter-border: var(--me-border-color);
85
- --me-line-active-bg: rgba(59, 130, 246, 0.07);
86
87
  --me-sel-bg: rgba(59, 130, 246, 0.22);
87
88
  --me-print-margin: rgba(148, 163, 184, 0.4);
88
89
 
@@ -182,11 +183,11 @@
182
183
  }
183
184
 
184
185
  /* 활성 라인 — aceFeature의 paintOverlay가 캐럿의 최상위 블록에
185
- * me-line-active를 단다 (Ace highlightActiveLine 관례).
186
- * 코드블록은 .me-cl.is-active가 이미 줄 단위로 칠하니 배경은 뺀다 */
187
- .me-editor > *.me-line-active:not(pre) {
188
- background-color: var(--me-line-active-bg);
189
- }
186
+ * me-line-active를 단다. 배경 띠는 칠하지 않는다(문서 편집에서
187
+ * 파란 띠가 산만하다는 피드백) — 활성 표시는 거터 번호 강조
188
+ * (.me-gnum.is-active)로만 한다. 코드블록은 코드 에디터 관례대로
189
+ * .me-cl.is-active가 줄 단위 배경을 칠한다. 클래스는 계속 달리므로
190
+ * 원하면 제품 CSS에서 배경을 되살릴 수 있다 */
190
191
 
191
192
  /* 선택 영역 — Ace 선택색 관례 (코드블록 안도 같은 변수) */
192
193
  .me-editor ::selection {
@@ -243,10 +244,12 @@
243
244
  font-size: 0.88em;
244
245
  }
245
246
 
246
- /* 리스트 — li 안 문단은 자체 여백이 없다 (li가 줄 간격을 담당) */
247
+ /* 리스트 — li 안 문단은 자체 여백이 없다 (li가 줄 간격을 담당).
248
+ * 패딩이 마커 열이다: outside 마커(•, 1.)가 텍스트 왼쪽에 붙어
249
+ * 그려지므로, 마커가 거터 구분선에 닿지 않을 만큼의 들여쓰기가 필요 */
247
250
  .me-editor ul,
248
251
  .me-editor ol {
249
- padding-left: 1.4em;
252
+ padding-left: 2.1em;
250
253
  }
251
254
  .me-editor li > p {
252
255
  margin: 0;
@@ -256,14 +259,15 @@
256
259
  margin: 2px 0;
257
260
  }
258
261
 
259
- /* 할일 목록 — 마커는 ::before, 클릭 영역은 li의 왼쪽 패딩(~28px) */
262
+ /* 할일 목록 — 마커는 ::before, 클릭 영역은 li의 왼쪽 패딩(~24px).
263
+ * ul 패딩과 li 패딩을 더해 텍스트 시작점이 다른 리스트와 맞는다 */
260
264
  .me-editor ul.me-tasks {
261
- padding-left: 4px;
265
+ padding-left: 8px;
262
266
  }
263
267
  .me-editor li.me-task {
264
268
  list-style: none;
265
269
  position: relative;
266
- padding-left: 28px;
270
+ padding-left: 24px;
267
271
  }
268
272
  .me-editor li.me-task::before {
269
273
  --me-checkbox-border: #94a3b8;
@@ -322,6 +326,280 @@
322
326
  max-width: 100%;
323
327
  border-radius: 6px;
324
328
  margin: 0 0 8px;
329
+ cursor: default;
330
+ }
331
+
332
+ /* atom 노드 셀렉션 — 클릭으로 선택된 img/hr 등에 파란 링을 둔다
333
+ * (input.ts의 paintOverlays가 is-selected를 마킹한다) */
334
+ .me-editor [data-me-path].is-selected {
335
+ outline: 2px solid var(--me-node-sel-color);
336
+ outline-offset: 2px;
337
+ }
338
+ .me-editor hr.is-selected {
339
+ /* hr은 테두리가 굵어 outline이 묻힌다 — 위아래 여백으로 링을 띄운다 */
340
+ outline-offset: 4px;
341
+ }
342
+
343
+ /* ── 이미지 선택 오버레이 — 선택된 img 위에 얹히는 편집/리사이즈 UI.
344
+ * .me-editor의 형제(.me-root의 absolute 자식)로 둬 DOM↔Point 매핑을
345
+ * 깨지 않는다. 래퍼는 pointer-events:none — 이미지 자체 클릭은 그대로
346
+ * 통과하고 핸들/바만 이벤트를 받는다 */
347
+ .me-imgui {
348
+ position: absolute;
349
+ z-index: 40;
350
+ pointer-events: none;
351
+ }
352
+ .me-imgui[hidden] {
353
+ display: none;
354
+ }
355
+ /* 우하단 크기 핸들 — nwse-resize (GDocs/Word 관례) */
356
+ .me-imgui .me-img-handle {
357
+ position: absolute;
358
+ right: -6px;
359
+ bottom: -6px;
360
+ width: 12px;
361
+ height: 12px;
362
+ border-radius: 4px;
363
+ background: var(--me-toolbar-bg, #fff);
364
+ border: 2px solid var(--me-node-sel-color);
365
+ box-shadow: 0 1px 3px rgba(15, 23, 42, 0.3);
366
+ cursor: nwse-resize;
367
+ pointer-events: auto;
368
+ touch-action: none;
369
+ }
370
+ /* 액션 바 — 이미지 위쪽. 루트 상단에 붙으면 is-below로 아래로 뒤집는다 */
371
+ .me-imgui .me-img-bar {
372
+ position: absolute;
373
+ left: 0;
374
+ top: -6px;
375
+ transform: translateY(-100%);
376
+ display: flex;
377
+ flex-wrap: wrap;
378
+ gap: 3px;
379
+ padding: 4px;
380
+ max-width: min(320px, 90vw);
381
+ background: var(--me-toolbar-bg, #fff);
382
+ border: 1px solid var(--me-border-color);
383
+ border-radius: 6px;
384
+ box-shadow: 0 4px 14px rgba(15, 23, 42, 0.18);
385
+ pointer-events: auto;
386
+ }
387
+ .me-imgui.is-below .me-img-bar {
388
+ top: auto;
389
+ bottom: -6px;
390
+ transform: translateY(100%);
391
+ }
392
+ .me-imgui .me-imgbtn {
393
+ padding: 3px 8px;
394
+ font: inherit;
395
+ font-size: 12px;
396
+ color: inherit;
397
+ background: var(--me-btn-bg, transparent);
398
+ border: 1px solid var(--me-border-color);
399
+ border-radius: 4px;
400
+ cursor: pointer;
401
+ }
402
+ .me-imgui .me-imgbtn:hover {
403
+ background: var(--me-tool-hover-bg, #e2e8f0);
404
+ }
405
+ .me-imgui .me-imgbtn[data-img-act="del"]:hover {
406
+ color: #dc2626;
407
+ border-color: #dc2626;
408
+ }
409
+ /* 편집 폼 — 액션 바 안의 두 번째 행 (wrap으로 전체 폭을 차지) */
410
+ .me-imgui .me-imgedit {
411
+ flex-basis: 100%;
412
+ display: flex;
413
+ gap: 4px;
414
+ }
415
+ .me-imgui .me-imgedit[hidden] {
416
+ display: none;
417
+ }
418
+ .me-imgui .me-imged-src,
419
+ .me-imgui .me-imged-alt {
420
+ width: min(150px, 30vw);
421
+ padding: 4px 6px;
422
+ font: inherit;
423
+ font-size: 12px;
424
+ color: inherit;
425
+ background: transparent;
426
+ border: 1px solid var(--me-border-color);
427
+ border-radius: 4px;
428
+ outline: none;
429
+ }
430
+ .me-imgui .me-imged-src:focus,
431
+ .me-imgui .me-imged-alt:focus {
432
+ border-color: var(--me-accent, #2563eb);
433
+ }
434
+
435
+ /* ── 표 편집 오버레이 — 캐럿이 셀 안에 있을 때 표 위에 얹히는 UI.
436
+ * .me-imgui와 같은 패턴: 래퍼 pointer-events:none, 바/핸들만 받는다 */
437
+ .me-tableui {
438
+ position: absolute;
439
+ z-index: 40;
440
+ pointer-events: none;
441
+ }
442
+ .me-tableui[hidden] {
443
+ display: none;
444
+ }
445
+ /* 액션 바 — 표 위쪽 중앙. 루트 상단에 붙으면 is-below로 안쪽에 띄운다 */
446
+ .me-tableui .me-tbl-bar {
447
+ position: absolute;
448
+ left: 50%;
449
+ top: -6px;
450
+ transform: translate(-50%, -100%);
451
+ display: flex;
452
+ gap: 3px;
453
+ padding: 4px;
454
+ background: var(--me-toolbar-bg, #fff);
455
+ border: 1px solid var(--me-border-color);
456
+ border-radius: 6px;
457
+ box-shadow: 0 4px 14px rgba(15, 23, 42, 0.18);
458
+ white-space: nowrap;
459
+ pointer-events: auto;
460
+ }
461
+ .me-tableui.is-below .me-tbl-bar {
462
+ top: 4px;
463
+ transform: translate(-50%, 0);
464
+ }
465
+ .me-tableui .me-tblbtn {
466
+ padding: 3px 8px;
467
+ font: inherit;
468
+ font-size: 12px;
469
+ color: inherit;
470
+ background: var(--me-btn-bg, transparent);
471
+ border: 1px solid var(--me-border-color);
472
+ border-radius: 4px;
473
+ cursor: pointer;
474
+ }
475
+ .me-tableui .me-tblbtn:hover {
476
+ background: var(--me-tool-hover-bg, #e2e8f0);
477
+ }
478
+ .me-tableui .me-tblbtn[data-tbl-act="tabledel"]:hover,
479
+ .me-tableui .me-tblbtn[data-tbl-act="rowdel"]:hover,
480
+ .me-tableui .me-tblbtn[data-tbl-act="coldel"]:hover {
481
+ color: #dc2626;
482
+ border-color: #dc2626;
483
+ }
484
+ /* 열 경계 핸들 — 각 열 우측 경계의 8px 스트립. 평소엔 투명하고
485
+ * hover/드래그 시 리사이즈 커서 + 강조선 (GDocs 열 리사이즈 관례) */
486
+ .me-tableui .me-tbl-cols {
487
+ position: absolute;
488
+ inset: 0;
489
+ }
490
+ .me-tableui .me-col-handle {
491
+ position: absolute;
492
+ top: 0;
493
+ width: 8px;
494
+ cursor: col-resize;
495
+ pointer-events: auto;
496
+ touch-action: none;
497
+ }
498
+ .me-tableui .me-col-handle::after {
499
+ content: "";
500
+ position: absolute;
501
+ top: 0;
502
+ bottom: 0;
503
+ left: 3px;
504
+ width: 2px;
505
+ background: transparent;
506
+ transition: background 0.1s;
507
+ }
508
+ .me-tableui .me-col-handle:hover::after,
509
+ .me-tableui .me-col-handle:active::after {
510
+ background: var(--me-node-sel-color, #2563eb);
511
+ }
512
+
513
+ /* 코드블록 크롬 바 — 캐럿이 들어간 pre의 우측 상단(언어 라벨 자리).
514
+ * Notion 코드블록 언어 피커 관례. 래퍼는 좌표만 잡고 자식이 이벤트를 받는다 */
515
+ .me-codeui {
516
+ position: absolute;
517
+ z-index: 40;
518
+ transform: translateX(-100%);
519
+ display: flex;
520
+ align-items: center;
521
+ gap: 3px;
522
+ padding: 2px 4px;
523
+ background: var(--me-toolbar-bg, #fff);
524
+ border: 1px solid var(--me-border-color);
525
+ border-radius: 6px;
526
+ box-shadow: 0 2px 8px rgba(15, 23, 42, 0.12);
527
+ white-space: nowrap;
528
+ }
529
+ .me-codeui[hidden] {
530
+ display: none;
531
+ }
532
+ .me-codeui .me-codelang,
533
+ .me-codehead .me-codelang {
534
+ font: inherit;
535
+ font-size: 11px;
536
+ max-width: 7.5em;
537
+ padding: 1px 2px;
538
+ color: var(--me-text-muted, #64748b);
539
+ background: transparent;
540
+ border: none;
541
+ border-radius: 3px;
542
+ cursor: pointer;
543
+ }
544
+ .me-codeui .me-codelang:hover,
545
+ .me-codehead .me-codelang:hover {
546
+ background: var(--me-tool-hover-bg, #e2e8f0);
547
+ }
548
+ .me-codeui .me-codebtn,
549
+ .me-codehead .me-codebtn {
550
+ padding: 1px 7px;
551
+ font: inherit;
552
+ font-size: 11px;
553
+ color: var(--me-text-muted, #64748b);
554
+ background: transparent;
555
+ border: none;
556
+ border-radius: 3px;
557
+ cursor: pointer;
558
+ }
559
+ .me-codeui .me-codebtn:hover,
560
+ .me-codehead .me-codebtn:hover {
561
+ color: inherit;
562
+ background: var(--me-tool-hover-bg, #e2e8f0);
563
+ }
564
+ /* 캐럿이 들어간 코드블록은 첫 줄 우측에 바 자리를 넓힌다 —
565
+ * 바가 코드 텍스트를 가리지 않도록 (기본 언어 라벨 패딩 5em을 덮는다) */
566
+ .me-editor pre.me-line-active .me-cl:first-child {
567
+ padding-right: 13.5em;
568
+ }
569
+
570
+ /* 코드블록 노드 뷰 — 언어/정리/복사 크롬 헤더(.me-codehead) + pre
571
+ * 아울렛을 한 장의 카드로 묶는다 (Notion/VS Code 코드 카드 관례).
572
+ * 헤더는 .me-node-view의 ce=false 영역이라 캐럿 매핑에 무영향 */
573
+ .me-codeview {
574
+ background: var(--me-code-bg);
575
+ border: 1px solid var(--me-code-border);
576
+ border-radius: 6px;
577
+ }
578
+ .me-codehead {
579
+ display: flex;
580
+ align-items: center;
581
+ justify-content: flex-end;
582
+ gap: 3px;
583
+ padding: 2px 6px;
584
+ border-bottom: 1px solid var(--me-code-border);
585
+ border-radius: 6px 6px 0 0;
586
+ user-select: none;
587
+ }
588
+ /* Notion 관례 — 언어 표시는 좌측, 액션 버튼은 우측 */
589
+ .me-codehead .me-codelang {
590
+ margin-right: auto;
591
+ }
592
+ /* 카드 안 pre — 카드가 배경/테두리를 담당하므로 pre 자체 크롬은 뗀다 */
593
+ .me-codeview > .me-node-content {
594
+ margin: 0;
595
+ border: none;
596
+ border-radius: 0;
597
+ background: transparent;
598
+ }
599
+ /* 노드 뷰 코드블록은 헤더가 언어·액션을 담당 — 오버레이 라벨/바용
600
+ * 첫 줄 우측 패딩(5em/13.5em 규칙보다 뒤에 선언해 덮는다)을 되돌린다 */
601
+ .me-editor .me-codeview .me-cl:first-child {
602
+ padding-right: 0;
325
603
  }
326
604
 
327
605
  /* 형광펜 — 브라우저 기본 mark 색을 attr 색이 덮는다 */
@@ -509,21 +787,25 @@
509
787
  font-size: 12px;
510
788
  }
511
789
 
512
- /* 팝오버 — 링크/이미지 입력 패널 (버블 메뉴와 같은 크롬) */
790
+ /* 팝오버 — 링크/이미지 입력 패널 + 표 크기 피커 (버블 메뉴와 같은
791
+ * 크롬). 트리거 버튼 아래에 앵커로 뜨고, 좁은 뷰포트에서는 접혀서
792
+ * 넘치지 않는다 (flex-wrap + max-width) */
513
793
  .me-pop {
514
794
  position: fixed;
515
795
  z-index: 60;
516
796
  display: flex;
517
797
  align-items: center;
798
+ flex-wrap: wrap;
518
799
  gap: 4px;
519
800
  padding: 6px;
801
+ max-width: calc(100vw - 16px);
520
802
  background: var(--me-toolbar-bg, #fff);
521
803
  border: 1px solid var(--me-border-color);
522
804
  border-radius: 8px;
523
805
  box-shadow: 0 8px 24px rgba(15, 23, 42, 0.18);
524
806
  }
525
807
  .me-pop-input {
526
- width: 220px;
808
+ width: min(220px, 46vw);
527
809
  padding: 5px 8px;
528
810
  font-size: 12px;
529
811
  color: inherit;
@@ -557,6 +839,38 @@
557
839
  align-items: center;
558
840
  }
559
841
 
842
+ /* 표 크기 피커 — GDocs 표 삽입 그리드 관례. 열 수는 data.ts의
843
+ * TGRID_COLS(10)와 짝 — 바꾸면 둘 다 갱신한다 */
844
+ .me-tgrid {
845
+ display: grid;
846
+ grid-template-columns: repeat(10, 18px);
847
+ gap: 2px;
848
+ }
849
+ .me-tcell {
850
+ width: 18px;
851
+ height: 18px;
852
+ padding: 0;
853
+ border: 1px solid var(--me-border-color);
854
+ border-radius: 3px;
855
+ background: transparent;
856
+ cursor: pointer;
857
+ }
858
+ .me-tcell.is-on {
859
+ background: var(--me-tool-active-bg, #dbeafe);
860
+ border-color: var(--me-accent, #2563eb);
861
+ }
862
+ .me-tcell:focus-visible {
863
+ outline: 2px solid var(--me-editor-focus, #3b82f6);
864
+ outline-offset: -2px;
865
+ }
866
+ .me-tgrid-label {
867
+ padding: 0 4px;
868
+ font-size: 11px;
869
+ color: var(--me-text-color);
870
+ opacity: 0.7;
871
+ white-space: nowrap;
872
+ }
873
+
560
874
  /* 링크 카드 — 캐럿이 링크 위에 있을 때 뜨는 액션 칩 (GDocs 관례):
561
875
  * URL 표시 + 복사/수정/제거. 수정은 링크 팝오버로 이어진다 */
562
876
  .me-linkcard {
@@ -640,9 +954,19 @@
640
954
 
641
955
  display: flex;
642
956
  align-items: center;
643
- flex-wrap: wrap;
957
+ /* 한 줄 원칙 — 넘치는 항목은 두 줄로 내리지 않고 ⋯ 메뉴로 보낸다
958
+ * (data-rsp 티어, 아래 미디어 쿼리).
959
+ * overflow:hidden을 쓰지 않는다 — absolute인 ⋯/A▾의 .me-menu가
960
+ * 툴바 아래로 열릴 때 같이 잘린다. ⋯는 absolute로 우측에 고정하고
961
+ * padding-right로 그 자리를 비워 넘치는 항목이 그 밑으로 스치게 한다 */
962
+ flex-wrap: nowrap;
963
+ /* relative+z-index: 툴바 전체를 에디터(.me-editor relative) 위에
964
+ * 올려 ⋯/A▾ 드롭다운이 본문에 가려지지 않게 한다. 팝오버/버블
965
+ * (fixed, z 50~60)보다는 아래라 겹쳐도 그쪽이 위에 뜬다 */
966
+ position: relative;
967
+ z-index: 20;
644
968
  gap: 2px;
645
- padding: 6px 8px;
969
+ padding: 6px 44px 6px 8px;
646
970
  background: var(--me-toolbar-bg);
647
971
  border-bottom: 1px solid var(--me-border-color);
648
972
  /* IDE 크롬 — 툴 라벨도 코드 에디터 폰트로 */
@@ -669,9 +993,25 @@
669
993
  font-size: 13px;
670
994
  font-weight: 600;
671
995
  cursor: pointer;
996
+ display: inline-flex;
997
+ align-items: center;
998
+ justify-content: center;
672
999
  transition: background-color 0.12s ease, color 0.12s ease;
673
1000
  }
674
1001
 
1002
+ /* 툴바 아이콘 — 24px viewBox stroke 아이콘을 currentColor로.
1003
+ * fragment는 경로만 들고 있으므로 스트로크는 여기서 공급한다 */
1004
+ .me-ic {
1005
+ display: block;
1006
+ width: 16px;
1007
+ height: 16px;
1008
+ fill: none;
1009
+ stroke: currentColor;
1010
+ stroke-width: 2;
1011
+ stroke-linecap: round;
1012
+ stroke-linejoin: round;
1013
+ }
1014
+
675
1015
  .me-tool:hover:not(:disabled) {
676
1016
  background: var(--me-tool-hover-bg);
677
1017
  }
@@ -679,6 +1019,7 @@
679
1019
  /* 키보드 포커스 표시 — VSCode 크롬 관례 (마우스 클릭엔 링 없음) */
680
1020
  .me-tool:focus-visible,
681
1021
  .me-overflow > summary:focus-visible,
1022
+ .me-styles > summary:focus-visible,
682
1023
  .me-size:focus-visible,
683
1024
  .me-blockfmt:focus-visible {
684
1025
  outline: 2px solid var(--me-editor-focus, #3b82f6);
@@ -708,20 +1049,38 @@
708
1049
  min-width: 74px;
709
1050
  }
710
1051
 
711
- /* 오버플로 패널 — 덜 쓰는 커맨드는 ⋯ 아래에 모은다 (GDocs ⋮
712
- * overflow 관례). details/summary라 프레임워크 상태 없이 토글되고,
713
- * 패널 크롬은 버블 메뉴와 같다 */
714
- .me-overflow {
1052
+ /* 오버플로/스타일 드롭다운 — 덜 쓰는 커맨드는 ⋯ 아래, 인라인 글자
1053
+ * 서식은 A▾ 아래에 모은다 (GDocs ⋮ overflow·Format 메뉴 관례).
1054
+ * details/summary라 프레임워크 상태 없이 토글되고, 패널 크롬은
1055
+ * 버블 메뉴와 같다 */
1056
+ .me-overflow,
1057
+ .me-styles {
715
1058
  position: relative;
716
1059
  display: inline-flex;
717
1060
  }
718
- .me-overflow > summary {
1061
+ /* ⋯는 항상 우측 끝에 살아있어야 한다 — absolute로 핀 고정해 항목이
1062
+ * 넘쳐도 오버플로 진입점이 사라지지 않는다. 넘친 항목이 밑으로
1063
+ * 스치므로 불투명 배경으로 덮는다. absolute라 이 details 자체가
1064
+ * .me-menu의 containing block이다 */
1065
+ .me-toolbar > .me-overflow {
1066
+ position: absolute;
1067
+ /* translateY 중앙정렬 대신 패딩과 같은 top — transform은 스태킹
1068
+ * 컨텍스트를 만들어 자식 .me-menu의 z-index를 가둔다 */
1069
+ right: 8px;
1070
+ top: 6px;
1071
+ flex-shrink: 0;
1072
+ background: var(--me-toolbar-bg);
1073
+ border-radius: 6px;
1074
+ }
1075
+ .me-overflow > summary,
1076
+ .me-styles > summary {
719
1077
  list-style: none;
720
1078
  cursor: pointer;
721
1079
  user-select: none;
722
1080
  display: inline-flex;
723
1081
  align-items: center;
724
1082
  justify-content: center;
1083
+ gap: 2px;
725
1084
  min-width: 30px;
726
1085
  height: 30px;
727
1086
  padding: 0 7px;
@@ -730,20 +1089,34 @@
730
1089
  font-weight: 600;
731
1090
  color: var(--me-text-color);
732
1091
  }
733
- .me-overflow > summary::-webkit-details-marker {
1092
+ .me-overflow > summary::-webkit-details-marker,
1093
+ .me-styles > summary::-webkit-details-marker {
734
1094
  display: none;
735
1095
  }
736
- .me-overflow > summary:hover {
1096
+ .me-overflow > summary:hover,
1097
+ .me-styles > summary:hover {
737
1098
  background: var(--me-tool-hover-bg);
738
1099
  }
739
- .me-overflow[open] > summary {
1100
+ .me-overflow[open] > summary,
1101
+ .me-styles[open] > summary {
740
1102
  background: var(--me-tool-active-bg);
741
1103
  color: var(--me-tool-active-color);
742
1104
  }
1105
+ /* A▾ 드롭다운 표시 — 스타일 패널 버튼 뒤의 캐럿 */
1106
+ .me-styles > summary::after {
1107
+ content: "";
1108
+ width: 0;
1109
+ height: 0;
1110
+ border-left: 3px solid transparent;
1111
+ border-right: 3px solid transparent;
1112
+ border-top: 4px solid currentColor;
1113
+ opacity: 0.6;
1114
+ }
743
1115
  .me-menu {
744
1116
  position: absolute;
745
1117
  top: calc(100% + 6px);
746
1118
  right: 0;
1119
+ left: auto;
747
1120
  z-index: 60;
748
1121
  display: flex;
749
1122
  align-items: center;
@@ -755,8 +1128,41 @@
755
1128
  box-shadow: 0 8px 24px rgba(15, 23, 42, 0.18);
756
1129
  white-space: nowrap;
757
1130
  }
1131
+ /* A▾ 패널은 툴바 중간에서 열리므로 버튼 왼쪽 아래에 붙인다 */
1132
+ .me-styles .me-menu {
1133
+ right: auto;
1134
+ left: 0;
1135
+ }
758
1136
 
759
- /* 툴바 인라인 스타일 컨트롤 — 컬러 피커/크기 셀렉트 */
1137
+ /* ── 반응형 툴바 — 한 줄 원칙 + ⋯ 오버플로 축소 ────────────────
1138
+ * data-rsp 티어: 1 = ≤900px(삽입/정렬 그룹), 2 = ≤620px(목록·보조
1139
+ * 서식 + A▾ 패널). 본줄의 티어 요소는 숨기고, ⋯ 메뉴 안의 같은
1140
+ * 항목(data-rsp가 달린 복사본)만 연다 — 항목은 양쪽에 렌더되고
1141
+ * 표시 여부만 CSS가 가른다. 브레이크포인트는 TOOL_ITEMS의 rsp
1142
+ * 값과 짝이다 (뷰포트 기준 — 데모 .page가 툴바 폭을 지배한다) */
1143
+ .me-menu [data-rsp] {
1144
+ display: none;
1145
+ }
1146
+ @media (max-width: 900px) {
1147
+ .me-toolbar > [data-rsp="1"],
1148
+ .me-toolbar > .me-styles[data-rsp="1"] {
1149
+ display: none;
1150
+ }
1151
+ .me-menu [data-rsp="1"] {
1152
+ display: inline-flex;
1153
+ }
1154
+ }
1155
+ @media (max-width: 620px) {
1156
+ .me-toolbar > [data-rsp="2"],
1157
+ .me-toolbar > .me-styles[data-rsp="2"] {
1158
+ display: none;
1159
+ }
1160
+ .me-menu [data-rsp="2"] {
1161
+ display: inline-flex;
1162
+ }
1163
+ }
1164
+
1165
+ /* 툴바 인라인 스타일 컨트롤 — A▾ 패널 안의 컬러 피커/크기 셀렉트 */
760
1166
  .me-style {
761
1167
  display: inline-flex;
762
1168
  align-items: center;
@@ -843,10 +1249,10 @@
843
1249
  --me-gutter-bg: rgba(0, 0, 0, 0.22);
844
1250
  --me-gutter-color: #8f908a;
845
1251
  --me-line-active-num: #c2c2bf;
1252
+ --me-node-sel-color: #66d9ef;
846
1253
  }
847
1254
 
848
1255
  .me-frame[data-me-theme="dark"] {
849
- --me-focus-ring: rgba(102, 217, 239, 0.25);
850
1256
  background: #1e1f1c;
851
1257
  }
852
1258
 
@@ -856,7 +1262,6 @@
856
1262
  --me-code-bg: #1e1f1c;
857
1263
  --me-code-border: #3e3d32;
858
1264
  --me-gutter-border: #3e3d32;
859
- --me-line-active-bg: rgba(255, 255, 255, 0.045);
860
1265
  --me-sel-bg: rgba(73, 72, 62, 0.95);
861
1266
  --me-print-margin: rgba(73, 72, 62, 0.9);
862
1267
  }
@@ -919,6 +1324,14 @@
919
1324
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.5);
920
1325
  }
921
1326
 
1327
+ [data-me-theme="dark"] .me-imgui {
1328
+ --me-toolbar-bg: #1e1f1c;
1329
+ --me-btn-bg: #3e3d32;
1330
+ --me-tool-hover-bg: #575643;
1331
+ --me-accent: #66d9ef;
1332
+ box-shadow: none;
1333
+ }
1334
+
922
1335
  [data-me-theme="dark"] .me-pop,
923
1336
  [data-me-theme="dark"] .me-linkcard {
924
1337
  --me-toolbar-bg: #1e1f1c;
@@ -1 +1 @@
1
- {"version":3,"file":"dom.d.ts","sourceRoot":"","sources":["../../src/view/dom.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAEnD;;;;;;;;GAQG;AAEH,eAAO,MAAM,SAAS,iBAAiB,CAAC;AAqDxC,wCAAwC;AACxC,wBAAgB,UAAU,CACxB,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,IAAI,EACb,SAAS,EAAE,MAAM,GAChB,KAAK,GAAG,IAAI,CA0Cd;AAED,+CAA+C;AAC/C,wBAAgB,UAAU,CACxB,IAAI,EAAE,WAAW,EACjB,KAAK,EAAE,KAAK,GACX;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAmFvC;AAED,6CAA6C;AAC7C,wBAAgB,YAAY,CAC1B,IAAI,EAAE,WAAW,GAChB;IAAE,MAAM,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,KAAK,CAAA;CAAE,GAAG,IAAI,CAmCxC;AAED,8BAA8B;AAC9B,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,WAAW,EACjB,MAAM,EAAE,KAAK,EACb,KAAK,EAAE,KAAK,GACX,IAAI,CAMN"}
1
+ {"version":3,"file":"dom.d.ts","sourceRoot":"","sources":["../../src/view/dom.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAEnD;;;;;;;;GAQG;AAEH,eAAO,MAAM,SAAS,iBAAiB,CAAC;AAqDxC,wCAAwC;AACxC,wBAAgB,UAAU,CACxB,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,IAAI,EACb,SAAS,EAAE,MAAM,GAChB,KAAK,GAAG,IAAI,CA0Cd;AAED,+CAA+C;AAC/C,wBAAgB,UAAU,CACxB,IAAI,EAAE,WAAW,EACjB,KAAK,EAAE,KAAK,GACX;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAsHvC;AAED,6CAA6C;AAC7C,wBAAgB,YAAY,CAC1B,IAAI,EAAE,WAAW,GAChB;IAAE,MAAM,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,KAAK,CAAA;CAAE,GAAG,IAAI,CAmCxC;AAED,8BAA8B;AAC9B,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,WAAW,EACjB,MAAM,EAAE,KAAK,EACb,KAAK,EAAE,KAAK,GACX,IAAI,CAMN"}