@moda-editor/core 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +16 -0
  3. package/dist/commands.d.ts +78 -0
  4. package/dist/commands.d.ts.map +1 -0
  5. package/dist/commands.js +299 -0
  6. package/dist/commands.js.map +1 -0
  7. package/dist/conformance.d.ts +59 -0
  8. package/dist/conformance.d.ts.map +1 -0
  9. package/dist/conformance.js +1609 -0
  10. package/dist/conformance.js.map +1 -0
  11. package/dist/core.d.ts +37 -0
  12. package/dist/core.d.ts.map +1 -0
  13. package/dist/core.js +117 -0
  14. package/dist/core.js.map +1 -0
  15. package/dist/editor/editor.d.ts +104 -0
  16. package/dist/editor/editor.d.ts.map +1 -0
  17. package/dist/editor/editor.js +204 -0
  18. package/dist/editor/editor.js.map +1 -0
  19. package/dist/editor/extension.d.ts +170 -0
  20. package/dist/editor/extension.d.ts.map +1 -0
  21. package/dist/editor/extension.js +2 -0
  22. package/dist/editor/extension.js.map +1 -0
  23. package/dist/features/ace.d.ts +9 -0
  24. package/dist/features/ace.d.ts.map +1 -0
  25. package/dist/features/ace.js +144 -0
  26. package/dist/features/ace.js.map +1 -0
  27. package/dist/features/alignment.d.ts +9 -0
  28. package/dist/features/alignment.d.ts.map +1 -0
  29. package/dist/features/alignment.js +28 -0
  30. package/dist/features/alignment.js.map +1 -0
  31. package/dist/features/blockquote.d.ts +9 -0
  32. package/dist/features/blockquote.d.ts.map +1 -0
  33. package/dist/features/blockquote.js +65 -0
  34. package/dist/features/blockquote.js.map +1 -0
  35. package/dist/features/code-block.d.ts +77 -0
  36. package/dist/features/code-block.d.ts.map +1 -0
  37. package/dist/features/code-block.js +986 -0
  38. package/dist/features/code-block.js.map +1 -0
  39. package/dist/features/doc.d.ts +15 -0
  40. package/dist/features/doc.d.ts.map +1 -0
  41. package/dist/features/doc.js +79 -0
  42. package/dist/features/doc.js.map +1 -0
  43. package/dist/features/feature.d.ts +26 -0
  44. package/dist/features/feature.d.ts.map +1 -0
  45. package/dist/features/feature.js +8 -0
  46. package/dist/features/feature.js.map +1 -0
  47. package/dist/features/hard-break.d.ts +9 -0
  48. package/dist/features/hard-break.d.ts.map +1 -0
  49. package/dist/features/hard-break.js +48 -0
  50. package/dist/features/hard-break.js.map +1 -0
  51. package/dist/features/heading.d.ts +3 -0
  52. package/dist/features/heading.d.ts.map +1 -0
  53. package/dist/features/heading.js +31 -0
  54. package/dist/features/heading.js.map +1 -0
  55. package/dist/features/horizontal-rule.d.ts +9 -0
  56. package/dist/features/horizontal-rule.d.ts.map +1 -0
  57. package/dist/features/horizontal-rule.js +64 -0
  58. package/dist/features/horizontal-rule.js.map +1 -0
  59. package/dist/features/image.d.ts +12 -0
  60. package/dist/features/image.d.ts.map +1 -0
  61. package/dist/features/image.js +116 -0
  62. package/dist/features/image.js.map +1 -0
  63. package/dist/features/index.d.ts +31 -0
  64. package/dist/features/index.d.ts.map +1 -0
  65. package/dist/features/index.js +65 -0
  66. package/dist/features/index.js.map +1 -0
  67. package/dist/features/inline-style.d.ts +12 -0
  68. package/dist/features/inline-style.d.ts.map +1 -0
  69. package/dist/features/inline-style.js +126 -0
  70. package/dist/features/inline-style.js.map +1 -0
  71. package/dist/features/link.d.ts +29 -0
  72. package/dist/features/link.d.ts.map +1 -0
  73. package/dist/features/link.js +198 -0
  74. package/dist/features/link.js.map +1 -0
  75. package/dist/features/lists.d.ts +14 -0
  76. package/dist/features/lists.d.ts.map +1 -0
  77. package/dist/features/lists.js +178 -0
  78. package/dist/features/lists.js.map +1 -0
  79. package/dist/features/marks.d.ts +6 -0
  80. package/dist/features/marks.d.ts.map +1 -0
  81. package/dist/features/marks.js +68 -0
  82. package/dist/features/marks.js.map +1 -0
  83. package/dist/features/script.d.ts +2 -0
  84. package/dist/features/script.d.ts.map +1 -0
  85. package/dist/features/script.js +55 -0
  86. package/dist/features/script.js.map +1 -0
  87. package/dist/features/table.d.ts +20 -0
  88. package/dist/features/table.d.ts.map +1 -0
  89. package/dist/features/table.js +195 -0
  90. package/dist/features/table.js.map +1 -0
  91. package/dist/features/task-list.d.ts +10 -0
  92. package/dist/features/task-list.d.ts.map +1 -0
  93. package/dist/features/task-list.js +68 -0
  94. package/dist/features/task-list.js.map +1 -0
  95. package/dist/features/testkit.d.ts +32 -0
  96. package/dist/features/testkit.d.ts.map +1 -0
  97. package/dist/features/testkit.js +92 -0
  98. package/dist/features/testkit.js.map +1 -0
  99. package/dist/history.d.ts +40 -0
  100. package/dist/history.d.ts.map +1 -0
  101. package/dist/history.js +192 -0
  102. package/dist/history.js.map +1 -0
  103. package/dist/index.d.ts +39 -0
  104. package/dist/index.d.ts.map +1 -0
  105. package/dist/index.js +29 -0
  106. package/dist/index.js.map +1 -0
  107. package/dist/model/node.d.ts +47 -0
  108. package/dist/model/node.d.ts.map +1 -0
  109. package/dist/model/node.js +30 -0
  110. package/dist/model/node.js.map +1 -0
  111. package/dist/model/schema.d.ts +105 -0
  112. package/dist/model/schema.d.ts.map +1 -0
  113. package/dist/model/schema.js +46 -0
  114. package/dist/model/schema.js.map +1 -0
  115. package/dist/model/traverse.d.ts +36 -0
  116. package/dist/model/traverse.d.ts.map +1 -0
  117. package/dist/model/traverse.js +123 -0
  118. package/dist/model/traverse.js.map +1 -0
  119. package/dist/mount.d.ts +24 -0
  120. package/dist/mount.d.ts.map +1 -0
  121. package/dist/mount.js +45 -0
  122. package/dist/mount.js.map +1 -0
  123. package/dist/parse/html.d.ts +8 -0
  124. package/dist/parse/html.d.ts.map +1 -0
  125. package/dist/parse/html.js +247 -0
  126. package/dist/parse/html.js.map +1 -0
  127. package/dist/parse/sanitize.d.ts +20 -0
  128. package/dist/parse/sanitize.d.ts.map +1 -0
  129. package/dist/parse/sanitize.js +116 -0
  130. package/dist/parse/sanitize.js.map +1 -0
  131. package/dist/presets/highlight.d.ts +23 -0
  132. package/dist/presets/highlight.d.ts.map +1 -0
  133. package/dist/presets/highlight.js +170 -0
  134. package/dist/presets/highlight.js.map +1 -0
  135. package/dist/presets/starter.d.ts +14 -0
  136. package/dist/presets/starter.d.ts.map +1 -0
  137. package/dist/presets/starter.js +18 -0
  138. package/dist/presets/starter.js.map +1 -0
  139. package/dist/state/apply.d.ts +64 -0
  140. package/dist/state/apply.d.ts.map +1 -0
  141. package/dist/state/apply.js +528 -0
  142. package/dist/state/apply.js.map +1 -0
  143. package/dist/state/navigate.d.ts +5 -0
  144. package/dist/state/navigate.d.ts.map +1 -0
  145. package/dist/state/navigate.js +81 -0
  146. package/dist/state/navigate.js.map +1 -0
  147. package/dist/state/selection.d.ts +44 -0
  148. package/dist/state/selection.d.ts.map +1 -0
  149. package/dist/state/selection.js +63 -0
  150. package/dist/state/selection.js.map +1 -0
  151. package/dist/state/state.d.ts +39 -0
  152. package/dist/state/state.d.ts.map +1 -0
  153. package/dist/state/state.js +123 -0
  154. package/dist/state/state.js.map +1 -0
  155. package/dist/state/transaction.d.ts +234 -0
  156. package/dist/state/transaction.d.ts.map +1 -0
  157. package/dist/state/transaction.js +988 -0
  158. package/dist/state/transaction.js.map +1 -0
  159. package/dist/styles.css +948 -0
  160. package/dist/types.d.ts +44 -0
  161. package/dist/types.d.ts.map +1 -0
  162. package/dist/types.js +8 -0
  163. package/dist/types.js.map +1 -0
  164. package/dist/view/dom.d.ts +26 -0
  165. package/dist/view/dom.d.ts.map +1 -0
  166. package/dist/view/dom.js +249 -0
  167. package/dist/view/dom.js.map +1 -0
  168. package/dist/view/input.d.ts +45 -0
  169. package/dist/view/input.d.ts.map +1 -0
  170. package/dist/view/input.js +901 -0
  171. package/dist/view/input.js.map +1 -0
  172. package/dist/view/mount.d.ts +26 -0
  173. package/dist/view/mount.d.ts.map +1 -0
  174. package/dist/view/mount.js +119 -0
  175. package/dist/view/mount.js.map +1 -0
  176. package/dist/view/render.d.ts +64 -0
  177. package/dist/view/render.d.ts.map +1 -0
  178. package/dist/view/render.js +203 -0
  179. package/dist/view/render.js.map +1 -0
  180. package/dist/view/serialize.d.ts +4 -0
  181. package/dist/view/serialize.d.ts.map +1 -0
  182. package/dist/view/serialize.js +37 -0
  183. package/dist/view/serialize.js.map +1 -0
  184. package/package.json +44 -0
@@ -0,0 +1,948 @@
1
+ /* @moda-editor/core 공통 스타일 — 어댑터는 @import로 재사용한다.
2
+ * 색상은 --me-* CSS 변수로 노출해 테마를 연다. */
3
+
4
+ .me-root {
5
+ --me-border-color: #e2e8f0;
6
+ --me-text-color: #1e293b;
7
+ --me-font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas,
8
+ "Liberation Mono", monospace;
9
+ /* 거터·활성줄 변수는 루트에 둔다 — .me-gutter 오버레이가 .me-editor의
10
+ * 형제라 .me-editor 스코프 변수를 못 본다. 자식인 .me-editor도
11
+ * 같은 변수를 상속해 그라디언트에 쓴다 */
12
+ --me-gutter-bg: rgba(15, 23, 42, 0.035);
13
+ --me-gutter-color: #94a3b8;
14
+ --me-line-active-num: #3b82f6;
15
+ display: flex;
16
+ flex-direction: column;
17
+ gap: 8px;
18
+ position: relative;
19
+ color: var(--me-text-color);
20
+ font: inherit;
21
+ }
22
+
23
+ .me-search {
24
+ padding: 6px 10px;
25
+ border: 1px solid var(--me-border-color);
26
+ border-radius: 6px;
27
+ }
28
+
29
+ .me-list {
30
+ margin: 0;
31
+ padding: 0;
32
+ list-style: none;
33
+ border: 1px solid var(--me-border-color);
34
+ border-radius: 6px;
35
+ }
36
+
37
+ .me-item {
38
+ padding: 6px 10px;
39
+ border-bottom: 1px solid var(--me-border-color);
40
+ }
41
+
42
+ .me-item:last-child {
43
+ border-bottom: none;
44
+ }
45
+
46
+ .me-count {
47
+ font-size: 12px;
48
+ opacity: 0.7;
49
+ }
50
+
51
+ /* ── 에디터 크롬 — .me-frame이 툴바+에디터+상태바를 하나의 카드로
52
+ * 묶는다. 단독 .me-editor도 테두리를 갖지만 frame 안에서는 frame이
53
+ * 크롬을 담당한다 ─────────────────────────────────────────── */
54
+ .me-frame {
55
+ --me-frame-shadow: 0 1px 2px rgba(15, 23, 42, 0.06);
56
+ --me-focus-ring: rgba(59, 130, 246, 0.18);
57
+ --me-border-color: #e2e8f0;
58
+ --me-text-color: #1e293b;
59
+ --me-font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas,
60
+ "Liberation Mono", monospace;
61
+
62
+ color: var(--me-text-color);
63
+ background: #ffffff;
64
+ border: 1px solid var(--me-border-color);
65
+ border-radius: 10px;
66
+ box-shadow: var(--me-frame-shadow);
67
+ overflow: hidden;
68
+ }
69
+
70
+ .me-frame:focus-within {
71
+ border-color: var(--me-editor-focus, #3b82f6);
72
+ box-shadow: 0 0 0 3px var(--me-focus-ring);
73
+ }
74
+
75
+ /* ── 에디터 — Ace 계열 코드 에디터 서페이스: 모노스페이스 본문 +
76
+ * 라인번호 거터 + 활성 줄 + 80열 print margin. 거터 번호는
77
+ * aceFeature의 .me-gutter 오버레이가 그린다 — 편집 영역 안의
78
+ * ::before/::marker는 Chrome Home/End 줄 경계를 깨뜨린다 ── */
79
+ .me-editor {
80
+ --me-editor-bg: #ffffff;
81
+ --me-editor-focus: #3b82f6;
82
+ --me-code-bg: #f1f5f9;
83
+ --me-code-border: #e2e8f0;
84
+ --me-gutter-border: var(--me-border-color);
85
+ --me-line-active-bg: rgba(59, 130, 246, 0.07);
86
+ --me-sel-bg: rgba(59, 130, 246, 0.22);
87
+ --me-print-margin: rgba(148, 163, 184, 0.4);
88
+
89
+ min-height: 160px;
90
+ position: relative;
91
+ padding: 10px 16px 10px 3.9em;
92
+ border: 1px solid var(--me-border-color);
93
+ border-radius: 10px;
94
+ color: var(--me-text-color, #1e293b);
95
+ caret-color: var(--me-editor-focus);
96
+ font-family: var(--me-font-mono);
97
+ font-size: 13.5px;
98
+ outline: none;
99
+ white-space: pre-wrap;
100
+ line-height: 1.6;
101
+ background:
102
+ /* 거터 구분선 */
103
+ linear-gradient(
104
+ to right,
105
+ transparent 0 calc(3.9em - 1px),
106
+ var(--me-gutter-border) calc(3.9em - 1px) 3.9em,
107
+ transparent 3.9em
108
+ ),
109
+ /* print margin — 80열 (Ace showPrintMargin 관례) */
110
+ linear-gradient(
111
+ to right,
112
+ transparent 0 calc(3.9em + 80ch),
113
+ var(--me-print-margin) calc(3.9em + 80ch) calc(3.9em + 80ch + 1px),
114
+ transparent calc(3.9em + 80ch + 1px)
115
+ ),
116
+ /* 거터 컬럼 배경 */
117
+ linear-gradient(
118
+ to right,
119
+ var(--me-gutter-bg) 0 3.9em,
120
+ transparent 3.9em
121
+ ),
122
+ var(--me-editor-bg);
123
+ }
124
+
125
+ /* 라인번호 거터 — .me-gutter 오버레이(aceFeature)가 각 블록 위치에
126
+ * 맞춰 번호를 그린다. 번호가 contenteditable 밖의 실제 DOM이라
127
+ * Home/End 캐럿 경계 계산과 DOM↔doc 오프셋 매핑 모두에 무영향.
128
+ * 코드블록은 자체 .me-cl 줄 번호를 쓰므로 pre에는 블록 번호가 없다 */
129
+ /* 오버레이는 왼쪽 거터 스트립에 한정한다 — 텍스트 영역과 겹치는
130
+ * 절대위치 요소는 Chrome의 줄 경계 계산을 깨뜨려 Home/End가 멈춘다.
131
+ * overflow:hidden(스크롤 컨테이너화)·user-select:none(편집 경계
132
+ * 취급)도 같은 이유로 금지 */
133
+ .me-gutter {
134
+ position: absolute;
135
+ left: 0;
136
+ top: 0;
137
+ bottom: 0;
138
+ width: 3.9em;
139
+ pointer-events: none;
140
+ }
141
+ /* user-select:none은 Chrome이 선택 불가 요소를 편집 경계로 취급해
142
+ * 캐럿 줄 경계를 깨뜨린다 — 쓰지 않는다. 오버레이 자체가
143
+ * pointer-events:none+editable 밖이라 선택될 경로도 없다 */
144
+ .me-gnum {
145
+ position: absolute;
146
+ width: 3.9em;
147
+ padding-right: 0.9em;
148
+ box-sizing: border-box;
149
+ text-align: right;
150
+ color: var(--me-gutter-color);
151
+ font-family: var(--me-font-mono);
152
+ font-size: 12px;
153
+ }
154
+ .me-gnum.me-gcode {
155
+ width: 3.4em;
156
+ padding-right: 0.7em;
157
+ opacity: 0.65;
158
+ }
159
+ /* 언어 라벨은 pre 첫 줄의 우측 패딩 자리에 놓인다 — 텍스트와
160
+ * 겹치지 않는 게 Home/End 안전 조건이라 첫 줄에 여백을 만든다 */
161
+ .me-editor pre[data-language] .me-cl:first-child {
162
+ padding-right: 5em;
163
+ }
164
+ .me-gnum.is-active {
165
+ color: var(--me-line-active-num);
166
+ font-weight: 600;
167
+ opacity: 1;
168
+ }
169
+ /* 코드 언어 라벨 — pre[data-language] ::before 대체. .me-gutter
170
+ * 스트립 밖(텍스트 영역)에 뜨므로 Home/End를 깨지 않게 pre 첫
171
+ * 줄에 우측 패딩을 확보해 텍스트와 겹치지 않게 둔다 */
172
+ .me-glang {
173
+ position: absolute;
174
+ transform: translateX(-100%);
175
+ padding: 1px 6px;
176
+ font-size: 12px;
177
+ line-height: 1.6;
178
+ color: var(--me-text-muted, #64748b);
179
+ background: var(--me-btn-bg, #f1f5f9);
180
+ border: 1px solid var(--me-border-color);
181
+ border-radius: 4px;
182
+ }
183
+
184
+ /* 활성 라인 — 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
+ }
190
+
191
+ /* 선택 영역 — Ace 선택색 관례 (코드블록 안도 같은 변수) */
192
+ .me-editor ::selection {
193
+ background: var(--me-sel-bg);
194
+ }
195
+
196
+ .me-editor:focus {
197
+ border-color: var(--me-editor-focus);
198
+ }
199
+
200
+ /* frame 안에서는 frame이 크롬을 담당 — 에디터 자체 테두리는 뗀다 */
201
+ .me-frame .me-editor {
202
+ border: none;
203
+ border-radius: 0;
204
+ }
205
+
206
+ .me-frame .me-editor:focus {
207
+ border: none;
208
+ }
209
+
210
+ .me-editor p,
211
+ .me-editor h1,
212
+ .me-editor h2,
213
+ .me-editor h3,
214
+ .me-editor h4,
215
+ .me-editor h5,
216
+ .me-editor h6,
217
+ .me-editor blockquote,
218
+ .me-editor pre,
219
+ .me-editor ul,
220
+ .me-editor ol,
221
+ .me-editor table {
222
+ margin: 0 0 8px;
223
+ }
224
+
225
+ .me-editor > :last-child,
226
+ .me-editor > .me-node-view:last-child {
227
+ margin-bottom: 0;
228
+ }
229
+
230
+ .me-editor h1 { font-size: 1.55em; }
231
+ .me-editor h2 { font-size: 1.35em; }
232
+ .me-editor h3 { font-size: 1.2em; }
233
+ .me-editor h4,
234
+ .me-editor h5,
235
+ .me-editor h6 { font-size: 1.05em; }
236
+
237
+ .me-editor code {
238
+ padding: 0.08em 0.35em;
239
+ background: var(--me-code-bg);
240
+ border: 1px solid var(--me-code-border);
241
+ border-radius: 4px;
242
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
243
+ font-size: 0.88em;
244
+ }
245
+
246
+ /* 리스트 — li 안 문단은 자체 여백이 없다 (li가 줄 간격을 담당) */
247
+ .me-editor ul,
248
+ .me-editor ol {
249
+ padding-left: 1.4em;
250
+ }
251
+ .me-editor li > p {
252
+ margin: 0;
253
+ }
254
+ .me-editor li > ul,
255
+ .me-editor li > ol {
256
+ margin: 2px 0;
257
+ }
258
+
259
+ /* 할일 목록 — 마커는 ::before, 클릭 영역은 li의 왼쪽 패딩(~28px) */
260
+ .me-editor ul.me-tasks {
261
+ padding-left: 4px;
262
+ }
263
+ .me-editor li.me-task {
264
+ list-style: none;
265
+ position: relative;
266
+ padding-left: 28px;
267
+ }
268
+ .me-editor li.me-task::before {
269
+ --me-checkbox-border: #94a3b8;
270
+ --me-checkbox-checked: #2563eb;
271
+ content: "";
272
+ position: absolute;
273
+ left: 4px;
274
+ top: 0.28em;
275
+ width: 15px;
276
+ height: 15px;
277
+ border: 1.5px solid var(--me-checkbox-border);
278
+ border-radius: 4px;
279
+ background: var(--me-editor-bg, #fff);
280
+ cursor: pointer;
281
+ }
282
+ .me-editor li.me-task[data-checked="true"]::before {
283
+ background: var(--me-checkbox-checked);
284
+ border-color: var(--me-checkbox-checked);
285
+ }
286
+ .me-editor li.me-task[data-checked="true"]::after {
287
+ content: "";
288
+ position: absolute;
289
+ left: 8.5px;
290
+ top: 0.42em;
291
+ width: 4px;
292
+ height: 8px;
293
+ border: solid #fff;
294
+ border-width: 0 2px 2px 0;
295
+ transform: rotate(45deg);
296
+ pointer-events: none;
297
+ }
298
+ .me-editor li.me-task[data-checked="true"] > p {
299
+ color: var(--me-quote-color, #64748b);
300
+ text-decoration: line-through;
301
+ }
302
+
303
+ /* 표 */
304
+ .me-editor table.me-table {
305
+ width: 100%;
306
+ border-collapse: collapse;
307
+ table-layout: fixed;
308
+ }
309
+ .me-editor .me-table td {
310
+ border: 1px solid var(--me-border-color);
311
+ padding: 6px 10px;
312
+ vertical-align: top;
313
+ min-width: 2em;
314
+ }
315
+ .me-editor .me-table td > p {
316
+ margin: 0;
317
+ }
318
+
319
+ /* 이미지 */
320
+ .me-editor img {
321
+ display: block;
322
+ max-width: 100%;
323
+ border-radius: 6px;
324
+ margin: 0 0 8px;
325
+ }
326
+
327
+ /* 형광펜 — 브라우저 기본 mark 색을 attr 색이 덮는다 */
328
+ .me-editor mark {
329
+ padding: 0.05em 0.15em;
330
+ border-radius: 2px;
331
+ }
332
+
333
+ /* 인용구 */
334
+ .me-editor blockquote {
335
+ --me-quote-color: #64748b;
336
+ padding: 2px 0 2px 12px;
337
+ border-left: 3px solid var(--me-border-color);
338
+ color: var(--me-quote-color);
339
+ }
340
+ .me-editor blockquote > :last-child {
341
+ margin-bottom: 0;
342
+ }
343
+
344
+ /* 코드 블록 — 개행이 보이도록 pre-wrap, 타이핑은 순수 텍스트 */
345
+ .me-editor pre {
346
+ padding: 10px 12px;
347
+ background: var(--me-code-bg);
348
+ border: 1px solid var(--me-code-border);
349
+ border-radius: 6px;
350
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
351
+ font-size: 0.88em;
352
+ white-space: pre-wrap;
353
+ /* 코드블록 Tab은 \t 문자를 넣는다 — 탭 폭은 4 (CodeMirror 관례) */
354
+ tab-size: 4;
355
+
356
+ /* 구문 하이라이트 토큰 팔레트 — GitHub Light 계열.
357
+ * 테마는 --me-tok-* 변수로 바꾼다 */
358
+ --me-tok-keyword: #cf222e;
359
+ --me-tok-string: #0a3069;
360
+ --me-tok-comment: #6e7781;
361
+ --me-tok-number: #0550ae;
362
+ --me-tok-literal: #0550ae;
363
+ --me-tok-function: #8250df;
364
+ --me-tok-tag: #116329;
365
+ --me-tok-attr: #953800;
366
+ }
367
+ .me-editor pre code {
368
+ padding: 0;
369
+ background: none;
370
+ border: none;
371
+ }
372
+
373
+ /* 구문 하이라이트 토큰 — 뷰 레벨 데코레이션 (doc에 마크로 안 새김) */
374
+ .me-tok-keyword {
375
+ color: var(--me-tok-keyword);
376
+ }
377
+ .me-tok-string {
378
+ color: var(--me-tok-string);
379
+ }
380
+ .me-tok-comment {
381
+ color: var(--me-tok-comment);
382
+ font-style: italic;
383
+ }
384
+ .me-tok-number,
385
+ .me-tok-literal {
386
+ color: var(--me-tok-number);
387
+ }
388
+ .me-tok-function {
389
+ color: var(--me-tok-function);
390
+ }
391
+ .me-tok-tag {
392
+ color: var(--me-tok-tag);
393
+ }
394
+ .me-tok-attr {
395
+ color: var(--me-tok-attr);
396
+ }
397
+
398
+ /* 코드 줄 번호 거터 — render.ts가 토큰을 줄 단위 .me-cl로 묶고,
399
+ * 번호는 .me-gutter 오버레이가 줄 위치에 맞춰 그린다 (Ace 거터 관례).
400
+ * 거터 배경은 background gradient — DOM↔doc 오프셋 매핑에 무영향.
401
+ * .me-cl이 있는 pre에서만 발동한다 */
402
+ .me-editor pre:has(.me-cl) {
403
+ position: relative;
404
+ padding-left: 3.4em;
405
+ background:
406
+ /* 거터 구분선 */
407
+ linear-gradient(
408
+ to right,
409
+ transparent 0 calc(3.4em - 1px),
410
+ var(--me-code-border) calc(3.4em - 1px) 3.4em,
411
+ transparent 3.4em
412
+ ),
413
+ /* print margin — 80열 (Ace showPrintMargin 관례) */
414
+ linear-gradient(
415
+ to right,
416
+ transparent 0 calc(3.4em + 80ch),
417
+ var(--me-print-margin) calc(3.4em + 80ch) calc(3.4em + 80ch + 1px),
418
+ transparent calc(3.4em + 80ch + 1px)
419
+ ),
420
+ /* 거터 컬럼 배경 */
421
+ linear-gradient(
422
+ to right,
423
+ var(--me-gutter-bg) 0 3.4em,
424
+ transparent 3.4em
425
+ ),
426
+ var(--me-code-bg);
427
+ }
428
+ /* 활성 줄 — 바인딩의 restoreSelection이 DOM에 is-active를 단다
429
+ * (Ace highlightActiveLine 관례) — 텍스트 띠 + 거터 번호 강조 */
430
+ .me-cl.is-active {
431
+ background: var(--me-code-active-bg, rgba(59, 130, 246, 0.08));
432
+ }
433
+ /* 코드 선택 영역 — 에디터 전역 ::selection(var(--me-sel-bg))을 공유한다 */
434
+
435
+ /* 보이지 않는 문자 (Ace showInvisibles 관례) — render 계층이
436
+ * 순수 공백 조각을 .me-ws/.me-ws-tab 스팬으로 분리해 두고, 토글이
437
+ * 켜진 [data-me-invisibles] 아래에서만 점/화살표 배경을 그린다.
438
+ * 글리프가 아니라 배경이라 텍스트 폭·캐럿 좌표가 안 변한다 */
439
+ .me-frame[data-me-invisibles] .me-ws {
440
+ background-image: radial-gradient(
441
+ circle at 50% 60%,
442
+ var(--me-invisible, rgba(148, 163, 184, 0.55)) 0.055em,
443
+ transparent 0.1em
444
+ );
445
+ background-size: 1ch 100%;
446
+ background-repeat: repeat-x;
447
+ }
448
+ .me-frame[data-me-invisibles] .me-ws-tab {
449
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M0 4h9M6.5 1l3 3-3 3' fill='none' stroke='%2394a3b8' stroke-width='1'/%3E%3C/svg%3E");
450
+ background-repeat: no-repeat;
451
+ background-position: left center;
452
+ background-size: auto 60%;
453
+ }
454
+
455
+ /* 코드 줄바꿈 토글 (Ace wrap 관례) */
456
+ .me-frame[data-me-wrap] .me-editor pre {
457
+ white-space: pre-wrap;
458
+ word-break: break-word;
459
+ }
460
+
461
+ /* 괄호 쌍 하이라이트 (Ace bracket matching) — input 바인딩이
462
+ * CSS Custom Highlight API로 "me-bracket" Range를 등록한다 */
463
+ ::highlight(me-bracket) {
464
+ background-color: var(--me-bracket-bg, rgba(59, 130, 246, 0.28));
465
+ }
466
+
467
+ /* 구분선 — atom 노드라 data-me-path를 직접 갖는다 */
468
+ .me-editor hr {
469
+ border: none;
470
+ border-top: 1px solid var(--me-border-color);
471
+ margin: 12px 0;
472
+ }
473
+
474
+ /* 링크 */
475
+ .me-editor a {
476
+ --me-link-color: #2563eb;
477
+ color: var(--me-link-color);
478
+ text-decoration: underline;
479
+ text-underline-offset: 2px;
480
+ }
481
+
482
+ /* 플레이스홀더 — 문서가 빈 문단 하나뿐일 때만 표시 (PM 관례) */
483
+ .me-editor > p:only-child:has(> br:only-child)::before,
484
+ .me-editor > p:only-child:has(> span > br:only-child)::before {
485
+ --me-placeholder-color: #94a3b8;
486
+ content: var(--me-placeholder-text, "내용을 입력하세요…");
487
+ float: left;
488
+ height: 0;
489
+ color: var(--me-placeholder-color);
490
+ pointer-events: none;
491
+ }
492
+
493
+ /* 선택 버블 메뉴 — 텍스트 선택 시 떠오르는 인라인 서식 바 */
494
+ .me-bubble {
495
+ position: fixed;
496
+ z-index: 50;
497
+ display: flex;
498
+ align-items: center;
499
+ gap: 2px;
500
+ padding: 4px;
501
+ background: var(--me-toolbar-bg, #fff);
502
+ border: 1px solid var(--me-border-color);
503
+ border-radius: 8px;
504
+ box-shadow: 0 4px 16px rgba(15, 23, 42, 0.14);
505
+ }
506
+ .me-bubble .me-tool {
507
+ min-width: 26px;
508
+ height: 26px;
509
+ font-size: 12px;
510
+ }
511
+
512
+ /* 팝오버 — 링크/이미지 입력 패널 (버블 메뉴와 같은 크롬) */
513
+ .me-pop {
514
+ position: fixed;
515
+ z-index: 60;
516
+ display: flex;
517
+ align-items: center;
518
+ gap: 4px;
519
+ padding: 6px;
520
+ background: var(--me-toolbar-bg, #fff);
521
+ border: 1px solid var(--me-border-color);
522
+ border-radius: 8px;
523
+ box-shadow: 0 8px 24px rgba(15, 23, 42, 0.18);
524
+ }
525
+ .me-pop-input {
526
+ width: 220px;
527
+ padding: 5px 8px;
528
+ font-size: 12px;
529
+ color: inherit;
530
+ background: transparent;
531
+ border: 1px solid var(--me-border-color);
532
+ border-radius: 5px;
533
+ outline: none;
534
+ }
535
+ .me-pop-input:focus {
536
+ border-color: var(--me-accent, #2563eb);
537
+ }
538
+ .me-pop-btn {
539
+ padding: 5px 10px;
540
+ font-size: 12px;
541
+ color: inherit;
542
+ background: var(--me-btn-bg, #f1f5f9);
543
+ border: 1px solid var(--me-border-color);
544
+ border-radius: 5px;
545
+ cursor: pointer;
546
+ }
547
+ .me-pop-btn:hover {
548
+ background: var(--me-btn-hover-bg, #e2e8f0);
549
+ }
550
+ .me-pop-btn[data-pop-act="ok"] {
551
+ color: #fff;
552
+ background: var(--me-accent, #2563eb);
553
+ border-color: var(--me-accent, #2563eb);
554
+ }
555
+ .me-pop-file {
556
+ display: inline-flex;
557
+ align-items: center;
558
+ }
559
+
560
+ /* 링크 카드 — 캐럿이 링크 위에 있을 때 뜨는 액션 칩 (GDocs 관례):
561
+ * URL 표시 + 복사/수정/제거. 수정은 링크 팝오버로 이어진다 */
562
+ .me-linkcard {
563
+ position: fixed;
564
+ z-index: 60;
565
+ display: flex;
566
+ align-items: center;
567
+ gap: 4px;
568
+ padding: 5px 6px;
569
+ max-width: 320px;
570
+ background: var(--me-toolbar-bg, #fff);
571
+ border: 1px solid var(--me-border-color);
572
+ border-radius: 8px;
573
+ box-shadow: 0 8px 24px rgba(15, 23, 42, 0.18);
574
+ }
575
+ .me-linkcard-url {
576
+ flex: 1;
577
+ min-width: 0;
578
+ padding: 0 2px;
579
+ font-size: 12px;
580
+ color: var(--me-link-color, #2563eb);
581
+ text-decoration: none;
582
+ white-space: nowrap;
583
+ overflow: hidden;
584
+ text-overflow: ellipsis;
585
+ }
586
+ .me-linkcard-url:hover {
587
+ text-decoration: underline;
588
+ }
589
+ .me-linkcard .me-pop-btn {
590
+ padding: 3px 8px;
591
+ font-size: 11px;
592
+ }
593
+
594
+ /* 코드블록 언어 라벨 — attrs.language → data-language.
595
+ * 라벨 자체는 .me-gutter의 .me-glang 오버레이가 그린다 */
596
+
597
+ /* 커스텀 노드 뷰 래퍼 — 블록 간격을 블록과 맞춘다 */
598
+ .me-node-view {
599
+ margin: 0 0 8px;
600
+ }
601
+
602
+ /* 편집 가능한 노드 뷰 콘텐츠 아울렛 — 안쪽 자식이 AST 자식과 1:1 */
603
+ .me-node-content {
604
+ flex: 1;
605
+ min-width: 0;
606
+ outline: none;
607
+ }
608
+
609
+ /* 데모용 callout 노드 컴포넌트 — 아이콘 크롬 + 편집 콘텐츠.
610
+ * 왼쪽 액센트 바는 GitHub alert/Notion callout 관례 */
611
+ .me-callout {
612
+ --me-callout-bg: #eff6ff;
613
+ --me-callout-border: #bfdbfe;
614
+ --me-callout-accent: #3b82f6;
615
+
616
+ display: flex;
617
+ gap: 8px;
618
+ padding: 8px 12px;
619
+ background: var(--me-callout-bg);
620
+ border: 1px solid var(--me-callout-border);
621
+ border-left: 3px solid var(--me-callout-accent);
622
+ border-radius: 6px;
623
+ }
624
+ .me-callout-icon {
625
+ user-select: none;
626
+ color: var(--me-callout-accent);
627
+ }
628
+ /* 아울렛 안 블록은 에디터 블록과 같은 간격을 쓰되 마지막은 붙인다 */
629
+ .me-callout .me-node-content > :last-child {
630
+ margin-bottom: 0;
631
+ }
632
+
633
+ /* ── 툴바 — 데모/스타터용 에디터 크롬. 어댑터 계약이 아니라
634
+ * 선택적 UI라 앱이 자체 툴바를 써도 된다 ─────────────────────── */
635
+ .me-toolbar {
636
+ --me-toolbar-bg: #f8fafc;
637
+ --me-tool-hover-bg: #e2e8f0;
638
+ --me-tool-active-bg: #dbeafe;
639
+ --me-tool-active-color: #1d4ed8;
640
+
641
+ display: flex;
642
+ align-items: center;
643
+ flex-wrap: wrap;
644
+ gap: 2px;
645
+ padding: 6px 8px;
646
+ background: var(--me-toolbar-bg);
647
+ border-bottom: 1px solid var(--me-border-color);
648
+ /* IDE 크롬 — 툴 라벨도 코드 에디터 폰트로 */
649
+ font-family: var(--me-font-mono);
650
+ font-size: 12.5px;
651
+ }
652
+
653
+ /* frame 밖에서 단독으로 쓸 때도 카드 형태를 유지한다 */
654
+ .me-toolbar:not(.me-frame .me-toolbar) {
655
+ border: 1px solid var(--me-border-color);
656
+ border-radius: 8px;
657
+ margin-bottom: 8px;
658
+ }
659
+
660
+ .me-tool {
661
+ min-width: 30px;
662
+ height: 30px;
663
+ padding: 0 7px;
664
+ border: none;
665
+ border-radius: 6px;
666
+ background: transparent;
667
+ color: var(--me-text-color);
668
+ font: inherit;
669
+ font-size: 13px;
670
+ font-weight: 600;
671
+ cursor: pointer;
672
+ transition: background-color 0.12s ease, color 0.12s ease;
673
+ }
674
+
675
+ .me-tool:hover:not(:disabled) {
676
+ background: var(--me-tool-hover-bg);
677
+ }
678
+
679
+ /* 키보드 포커스 표시 — VSCode 크롬 관례 (마우스 클릭엔 링 없음) */
680
+ .me-tool:focus-visible,
681
+ .me-overflow > summary:focus-visible,
682
+ .me-size:focus-visible,
683
+ .me-blockfmt:focus-visible {
684
+ outline: 2px solid var(--me-editor-focus, #3b82f6);
685
+ outline-offset: -2px;
686
+ }
687
+
688
+ .me-tool.is-active {
689
+ background: var(--me-tool-active-bg);
690
+ color: var(--me-tool-active-color);
691
+ }
692
+
693
+ .me-tool:disabled {
694
+ opacity: 0.35;
695
+ cursor: default;
696
+ }
697
+
698
+ .me-sep {
699
+ width: 1px;
700
+ height: 18px;
701
+ margin: 0 5px;
702
+ background: var(--me-border-color);
703
+ }
704
+
705
+ /* 블록 서식 드롭다운 — GDocs paragraph-style 관례. ¶/H1~H3 버튼
706
+ * 열 대신 현재 서식을 보여주는 select 하나 (.me-size 크롬 공유) */
707
+ .me-blockfmt {
708
+ min-width: 74px;
709
+ }
710
+
711
+ /* 오버플로 패널 — 덜 쓰는 커맨드는 ⋯ 아래에 모은다 (GDocs ⋮
712
+ * overflow 관례). details/summary라 프레임워크 상태 없이 토글되고,
713
+ * 패널 크롬은 버블 메뉴와 같다 */
714
+ .me-overflow {
715
+ position: relative;
716
+ display: inline-flex;
717
+ }
718
+ .me-overflow > summary {
719
+ list-style: none;
720
+ cursor: pointer;
721
+ user-select: none;
722
+ display: inline-flex;
723
+ align-items: center;
724
+ justify-content: center;
725
+ min-width: 30px;
726
+ height: 30px;
727
+ padding: 0 7px;
728
+ border-radius: 6px;
729
+ font-size: 14px;
730
+ font-weight: 600;
731
+ color: var(--me-text-color);
732
+ }
733
+ .me-overflow > summary::-webkit-details-marker {
734
+ display: none;
735
+ }
736
+ .me-overflow > summary:hover {
737
+ background: var(--me-tool-hover-bg);
738
+ }
739
+ .me-overflow[open] > summary {
740
+ background: var(--me-tool-active-bg);
741
+ color: var(--me-tool-active-color);
742
+ }
743
+ .me-menu {
744
+ position: absolute;
745
+ top: calc(100% + 6px);
746
+ right: 0;
747
+ z-index: 60;
748
+ display: flex;
749
+ align-items: center;
750
+ gap: 2px;
751
+ padding: 5px;
752
+ background: var(--me-toolbar-bg, #fff);
753
+ border: 1px solid var(--me-border-color);
754
+ border-radius: 8px;
755
+ box-shadow: 0 8px 24px rgba(15, 23, 42, 0.18);
756
+ white-space: nowrap;
757
+ }
758
+
759
+ /* 툴바 인라인 스타일 컨트롤 — 컬러 피커/크기 셀렉트 */
760
+ .me-style {
761
+ display: inline-flex;
762
+ align-items: center;
763
+ gap: 3px;
764
+ height: 30px;
765
+ padding: 0 4px;
766
+ font-size: 13px;
767
+ font-weight: 600;
768
+ color: var(--me-text-color);
769
+ cursor: pointer;
770
+ }
771
+ .me-style input[type="color"] {
772
+ width: 18px;
773
+ height: 18px;
774
+ padding: 0;
775
+ border: 1px solid var(--me-border-color);
776
+ border-radius: 4px;
777
+ background: transparent;
778
+ cursor: pointer;
779
+ }
780
+ .me-style mark {
781
+ padding: 0 3px;
782
+ border-radius: 3px;
783
+ }
784
+ .me-size {
785
+ height: 30px;
786
+ padding: 0 6px;
787
+ border: 1px solid var(--me-border-color);
788
+ border-radius: 6px;
789
+ background: var(--me-toolbar-bg);
790
+ color: var(--me-text-color);
791
+ font: inherit;
792
+ font-size: 13px;
793
+ cursor: pointer;
794
+ }
795
+
796
+ /* 상태 표시줄 — 왼쪽은 블록/마크, 오른쪽은 Ln·Col/블록 수
797
+ * (VSCode 상태바 관례 — 위치 정보는 우측 정렬) */
798
+ .me-statusbar {
799
+ --me-status-color: #64748b;
800
+ display: flex;
801
+ align-items: center;
802
+ justify-content: space-between;
803
+ gap: 12px;
804
+ padding: 5px 12px;
805
+ border-top: 1px solid var(--me-border-color);
806
+ background: var(--me-toolbar-bg, #f8fafc);
807
+ font-size: 11px;
808
+ font-family: ui-monospace, monospace;
809
+ color: var(--me-status-color);
810
+ white-space: nowrap;
811
+ }
812
+ .me-status-left,
813
+ .me-status-right {
814
+ min-width: 0;
815
+ overflow: hidden;
816
+ text-overflow: ellipsis;
817
+ }
818
+ .me-status-right {
819
+ flex-shrink: 0;
820
+ }
821
+
822
+ /* frame 밖 단독 사용 시 */
823
+ .me-statusbar:not(.me-frame .me-statusbar) {
824
+ border: none;
825
+ background: transparent;
826
+ padding: 4px 0 0;
827
+ opacity: 0.7;
828
+ }
829
+
830
+ /* ── 다크 테마 — Ace Monokai 계열. .me-frame 또는 그 조상에
831
+ * data-me-theme="dark" ────────────────────────────────────── */
832
+ [data-me-theme="dark"] {
833
+ --me-border-color: #3e3d32;
834
+ --me-text-color: #f8f8f2;
835
+ }
836
+
837
+ /* EditorView/WidgetView의 .me-root 래퍼가 팔레트 기본값을 갖고 있어
838
+ * frame의 다크 변수를 중간에서 덮는다 — 테마 아래의 .me-root는
839
+ * 테마 변수를 그대로 상속하도록 명시 오버라이드 */
840
+ [data-me-theme="dark"] .me-root {
841
+ --me-border-color: #3e3d32;
842
+ --me-text-color: #f8f8f2;
843
+ --me-gutter-bg: rgba(0, 0, 0, 0.22);
844
+ --me-gutter-color: #8f908a;
845
+ --me-line-active-num: #c2c2bf;
846
+ }
847
+
848
+ .me-frame[data-me-theme="dark"] {
849
+ --me-focus-ring: rgba(102, 217, 239, 0.25);
850
+ background: #1e1f1c;
851
+ }
852
+
853
+ [data-me-theme="dark"] .me-editor {
854
+ --me-editor-bg: #272822;
855
+ --me-editor-focus: #66d9ef;
856
+ --me-code-bg: #1e1f1c;
857
+ --me-code-border: #3e3d32;
858
+ --me-gutter-border: #3e3d32;
859
+ --me-line-active-bg: rgba(255, 255, 255, 0.045);
860
+ --me-sel-bg: rgba(73, 72, 62, 0.95);
861
+ --me-print-margin: rgba(73, 72, 62, 0.9);
862
+ }
863
+
864
+ /* 구문 하이라이트 다크 팔레트 — Monokai */
865
+ [data-me-theme="dark"] .me-editor pre {
866
+ --me-tok-keyword: #f92672;
867
+ --me-tok-string: #e6db74;
868
+ --me-tok-comment: #75715e;
869
+ --me-tok-number: #ae81ff;
870
+ --me-tok-literal: #ae81ff;
871
+ --me-tok-function: #a6e22e;
872
+ --me-tok-tag: #f92672;
873
+ --me-tok-attr: #a6e22e;
874
+ --me-code-active-bg: rgba(255, 255, 255, 0.05);
875
+ }
876
+
877
+ [data-me-theme="dark"] .me-toolbar {
878
+ --me-toolbar-bg: #1e1f1c;
879
+ --me-tool-hover-bg: #3e3d32;
880
+ --me-tool-active-bg: rgba(102, 217, 239, 0.14);
881
+ --me-tool-active-color: #66d9ef;
882
+ }
883
+
884
+ /* 폼 컨트롤은 UA 라이트 기본값을 상속해 다크에서 튄다 — 명시 오버라이드 */
885
+ [data-me-theme="dark"] .me-toolbar .me-size,
886
+ [data-me-theme="dark"] .me-toolbar .me-blockfmt,
887
+ [data-me-theme="dark"] .me-toolbar .me-style input[type="color"] {
888
+ background: #1e1f1c;
889
+ border-color: #3e3d32;
890
+ color: #f8f8f2;
891
+ }
892
+
893
+ [data-me-theme="dark"] .me-statusbar {
894
+ --me-toolbar-bg: #1e1f1c;
895
+ --me-status-color: #8f908a;
896
+ }
897
+
898
+ [data-me-theme="dark"] .me-callout {
899
+ --me-callout-bg: #2d2e25;
900
+ --me-callout-border: #575643;
901
+ --me-callout-accent: #66d9ef;
902
+ }
903
+
904
+ [data-me-theme="dark"] .me-editor blockquote {
905
+ --me-quote-color: #a09f93;
906
+ }
907
+
908
+ [data-me-theme="dark"] .me-editor a {
909
+ --me-link-color: #66d9ef;
910
+ }
911
+
912
+ [data-me-theme="dark"] .me-editor li.me-task::before {
913
+ --me-checkbox-border: #575643;
914
+ --me-checkbox-checked: #a6e22e;
915
+ }
916
+
917
+ [data-me-theme="dark"] .me-bubble {
918
+ --me-toolbar-bg: #1e1f1c;
919
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.5);
920
+ }
921
+
922
+ [data-me-theme="dark"] .me-pop,
923
+ [data-me-theme="dark"] .me-linkcard {
924
+ --me-toolbar-bg: #1e1f1c;
925
+ --me-btn-bg: #3e3d32;
926
+ --me-btn-hover-bg: #575643;
927
+ --me-accent: #66d9ef;
928
+ --me-link-color: #66d9ef;
929
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
930
+ }
931
+
932
+ [data-me-theme="dark"] .me-glang {
933
+ --me-text-muted: #8f908a;
934
+ --me-btn-bg: #3e3d32;
935
+ }
936
+
937
+ [data-me-theme="dark"] .me-editor ::placeholder,
938
+ [data-me-theme="dark"] .me-editor > p:only-child:has(> br:only-child)::before,
939
+ [data-me-theme="dark"]
940
+ .me-editor
941
+ > p:only-child:has(> span > br:only-child)::before {
942
+ --me-placeholder-color: #6a6b60;
943
+ }
944
+
945
+ /* 테마 토글 버튼 — 다크 상태에서는 활성색으로 표시 */
946
+ [data-me-theme="dark"] .me-tool[data-me-cmd="theme"] {
947
+ color: var(--me-tool-active-color);
948
+ }