@teriusu/rich-editor 0.9.8 → 0.9.9

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.
@@ -1 +1 @@
1
- {"version":3,"file":"PdfBlock.d.ts","sourceRoot":"","sources":["../../src/extensions/PdfBlock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,IAAI,UAAU,EAAmB,MAAM,cAAc,CAAC;AAKnE,eAAO,MAAM,QAAQ,sBAujBnB,CAAC"}
1
+ {"version":3,"file":"PdfBlock.d.ts","sourceRoot":"","sources":["../../src/extensions/PdfBlock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,IAAI,UAAU,EAAmB,MAAM,cAAc,CAAC;AAKnE,eAAO,MAAM,QAAQ,sBAgkBnB,CAAC"}
@@ -91,6 +91,7 @@ export const PdfBlock = TiptapNode.create({
91
91
  // 클로저의 `node` 를 쓰면 그 사이의 다른 속성 변경을 리사이즈가 되돌린다.
92
92
  let currentNode = node;
93
93
  let detachResize = null;
94
+ const icon = (paths) => `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">${paths}</svg>`;
94
95
  const dom = document.createElement("div");
95
96
  dom.classList.add("my-2");
96
97
  dom.contentEditable = "false";
@@ -102,11 +103,9 @@ export const PdfBlock = TiptapNode.create({
102
103
  dom.style.maxWidth = "100%";
103
104
  if (node.attrs.width)
104
105
  dom.style.width = node.attrs.width;
105
- const wrapper = document.createElement("div");
106
- wrapper.className =
107
- "border rounded-lg overflow-hidden bg-muted/30 transition-shadow";
108
- wrapper.style.borderColor = "var(--border)";
109
- dom.appendChild(wrapper);
106
+ const frame = document.createElement("div");
107
+ frame.className = "pdf-frame";
108
+ dom.appendChild(frame);
110
109
  // 리사이즈 핸들 (편집 가능 모드에서만)
111
110
  if (editor.isEditable) {
112
111
  detachResize = attachResize({
@@ -118,30 +117,30 @@ export const PdfBlock = TiptapNode.create({
118
117
  label: "PDF 너비 조절",
119
118
  });
120
119
  }
121
- // Header
122
- const header = document.createElement("div");
123
- // ⚠️ `justify-between` 이 아니라 spacer 로 민다. 이름칸이 넓어져도 오른쪽 버튼이
124
- // 밀려나지 않는다.
125
- header.className =
126
- "flex items-center gap-2 px-3 py-1 border-b border-border select-none";
127
- header.style.background = "var(--muted)";
128
- wrapper.appendChild(header);
120
+ /*
121
+ * 조작부는 판 **안에** 떠 있다. 통째로 `data-pdf-control` 이라 `stopEvent` 가
122
+ * ProseMirror 를 막아 준다 — 버튼·입력칸이 선택/타이핑에 가로채이지 않는다.
123
+ * 빈 영역은 `pointer-events: none` 이라 그 위로 드래그·클릭이 그대로 통과한다.
124
+ */
125
+ const overlay = document.createElement("div");
126
+ overlay.className = "pdf-overlay";
127
+ overlay.setAttribute("data-pdf-control", "");
128
+ // 판에는 캔버스·상태문구 **뒤에** 붙인다(아래) — 여기서는 만들어 채우기만 한다.
129
+ const endCluster = document.createElement("div");
130
+ endCluster.className = "pdf-cluster pdf-cluster-end";
131
+ overlay.appendChild(endCluster);
129
132
  const cleanName = (raw) => (raw || "").replace(/[?#].*$/, "").trim() || "PDF";
130
- /**
131
- * 헤더 표시 이름.
133
+ /*
134
+ * ⚠️ **표시 이름은 화면에서 뺐다**(사용자 결정) — 편집용 입력칸도, 읽기용 글자도
135
+ * 없다. 판 위에 이름표가 늘 붙어 있는 게 소음이었다.
136
+ *
137
+ * 그래도 `label` 속성 자체는 **스키마에 남긴다.** 이미 이름을 지정해 둔 문서가
138
+ * 있고, 속성을 지우면 그 문서를 한 번 열었다 저장하는 것만으로 값이 조용히
139
+ * 사라진다. 지금은 아무도 읽지 않지만 왕복은 그대로 된다.
132
140
  *
133
- * ⚠️ **표시 전용이다.** 다운로드 파일명은 계속 `name` 을 쓴다 — 표시 이름을
134
- * `1강 자료` 로 바꿨다고 확장자 없는 파일이 내려가면 안 된다.
141
+ * 다운로드 파일명은 원래부터 `label` 이 아니라 `name` 이다 — 표시 이름을
142
+ * `1강 자료` 로 바꿨다고 확장자 없는 파일이 내려가면 안 되기 때문.
135
143
  */
136
- let resolvedName = null;
137
- const fallbackName = () => cleanName(resolvedName ?? currentNode.attrs.name);
138
- const displayName = () => {
139
- const label = currentNode.attrs.label;
140
- const trimmed = typeof label === "string" ? label.trim() : "";
141
- return trimmed || fallbackName();
142
- };
143
- let nameSpan = null;
144
- let nameInput = null;
145
144
  /** 노드 속성을 되쓴다. 리사이즈(`attachResize`)와 **같은 방식**. */
146
145
  const setAttrs = (patch) => {
147
146
  const pos = getPos();
@@ -152,101 +151,36 @@ export const PdfBlock = TiptapNode.create({
152
151
  ...patch,
153
152
  }));
154
153
  };
155
- /** 입력칸이 글자 길이만큼만 차지하게 한다(빈 상자가 넓게 보이지 않도록). */
156
- const fitInput = () => {
157
- if (!nameInput)
158
- return;
159
- nameInput.size = Math.min(40, Math.max(6, nameInput.value.length + 1));
160
- };
161
- /** 표시 이름을 화면에 반영한다. 입력 중일 때는 건드리지 않는다. */
162
- const syncName = () => {
163
- const next = displayName();
164
- if (nameSpan)
165
- nameSpan.textContent = next;
166
- if (nameInput &&
167
- document.activeElement !== nameInput &&
168
- nameInput.value !== next) {
169
- nameInput.value = next;
170
- }
171
- fitInput();
172
- };
173
- if (editor.isEditable) {
174
- // 그 자리에서 이름을 고쳐 쓴다. 확정은 blur / Enter, 되돌리기는 Escape.
175
- nameInput = document.createElement("input");
176
- nameInput.type = "text";
177
- nameInput.className =
178
- // ⚠️ `select-text` — 헤더가 `select-none` 이라 이 칸까지 선택이 막힌다. 이름을
179
- // 드래그해 고쳐 쓰려면 여기만 되돌려야 한다.
180
- /*
181
- * ⚠️ 평소에도 **입력칸으로 보여야 한다**(사용자 지적). 예전엔 테두리·면이 투명이라
182
- * hover 하기 전에는 그냥 글자였고, 고칠 수 있다는 걸 알 방법이 없었다.
183
- * 헤더 면(`--muted`) 위에 `--background` 면 + 1px 테두리를 두면 한눈에 칸으로 읽힌다.
184
- */
185
- "pdf-name-input select-text text-xs text-muted-foreground min-w-0 bg-background rounded px-1.5 py-0.5 border border-border hover:border-ring focus:border-ring focus:text-foreground outline-none transition-colors";
186
- nameInput.style.maxWidth = "220px";
187
- nameInput.contentEditable = "false";
188
- nameInput.draggable = false;
189
- // ProseMirror 가 이 칸의 키/포인터를 가져가지 않도록 하는 표식(`stopEvent`).
190
- nameInput.setAttribute("data-pdf-control", "");
191
- nameInput.title = "표시 이름 (비우면 파일명)";
192
- nameInput.setAttribute("aria-label", "PDF 표시 이름");
193
- nameInput.value = displayName();
194
- fitInput();
195
- /** 빈칸이거나 파일명과 같으면 `label: null` — 다시 파일명을 따라간다. */
196
- const commitLabel = () => {
197
- if (!nameInput)
198
- return;
199
- const typed = nameInput.value.trim();
200
- const next = typed && typed !== fallbackName() ? typed : null;
201
- const current = currentNode.attrs.label ?? null;
202
- if (next !== current)
203
- setAttrs({ label: next });
204
- nameInput.value = next || fallbackName();
205
- fitInput();
206
- };
207
- nameInput.addEventListener("input", fitInput);
208
- nameInput.addEventListener("keydown", (e) => {
209
- // tiptap 이 단축키·타이핑을 가로채면 이 칸에 글자가 안 들어간다.
210
- e.stopPropagation();
211
- if (e.key === "Enter") {
212
- e.preventDefault();
213
- commitLabel();
214
- nameInput?.blur();
215
- }
216
- else if (e.key === "Escape") {
217
- e.preventDefault();
218
- // 되돌린 뒤 blur — 값이 현재와 같아져 blur 의 커밋은 무시된다.
219
- if (nameInput)
220
- nameInput.value = displayName();
221
- nameInput?.blur();
222
- }
223
- });
224
- nameInput.addEventListener("mousedown", (e) => e.stopPropagation());
225
- nameInput.addEventListener("blur", commitLabel);
226
- header.appendChild(nameInput);
227
- }
228
- else {
229
- nameSpan = document.createElement("span");
230
- nameSpan.className =
231
- "text-xs text-muted-foreground truncate select-none min-w-0";
232
- nameSpan.style.maxWidth = "220px";
233
- nameSpan.style.userSelect = "none";
234
- nameSpan.textContent = displayName();
235
- header.appendChild(nameSpan);
236
- }
237
- const spacer = document.createElement("div");
238
- spacer.className = "flex-1";
239
- header.appendChild(spacer);
240
154
  // 너비 프리셋 (편집 가능 모드에서만)
241
- const PRESET_BASE = "px-1.5 py-0.5 rounded text-xs leading-none tabular-nums transition-colors select-none";
242
- const PRESET_IDLE = `${PRESET_BASE} text-muted-foreground hover:bg-background pdf-width-preset`;
243
- const PRESET_ACTIVE = `${PRESET_BASE} bg-primary text-primary-foreground pdf-width-preset is-active`;
155
+ const PRESET_IDLE = "pdf-preset";
156
+ const PRESET_ACTIVE = "pdf-preset is-active";
244
157
  const presetButtons = [];
245
158
  if (editor.isEditable) {
246
- const presetGroup = document.createElement("div");
247
- presetGroup.className = "flex items-center gap-0.5";
248
- // ⚠️ 25% 는 뺐다 — 그 폭이면 PDF 글자를 읽을 수 없어 고를 이유가 없다(사용자 결정).
249
- for (const value of ["50%", "75%", "100%"]) {
159
+ /*
160
+ * ⚠️ 프리셋은 **문서에 저장되는 폭**이지 확대가 아니다. 옆의 다운로드·새 탭과
161
+ * 성격이 달라(저장됨 vs 그때뿐) 구분선으로 갈라 둔다 — 안 그러면 "100% 누르면
162
+ * 확대되겠지" 로 읽힌다.
163
+ */
164
+ /*
165
+ * 사다리를 고른 근거 — **폭 프리셋이지만 정작 걸리는 건 높이**다.
166
+ * 페이지 높이 = 폭 × 비율이고, A4 는 세로형 1.41 / 가로형 0.71 이다.
167
+ * 정올 실측(본문 칼럼 1378px, 뷰포트 1221px, 상단 고정물 113px → 쓸 높이 1108px):
168
+ *
169
+ * 폭 세로형 높이 가로형 높이
170
+ * 100% 1950 (화면 밖) 974 (들어감)
171
+ * 75% 1462 (화면 밖) 730 (들어감)
172
+ * 50% 975 (딱 맞음) 487
173
+ * 25% 487 243
174
+ *
175
+ * 즉 **세로형의 50% 가 가로형의 100% 와 같은 화면 높이**다 — 두 방향을 한 사다리로
176
+ * 덮으려면 "절반" 단이 반드시 있어야 한다.
177
+ *
178
+ * 25% 는 한때 "글자를 못 읽어 고를 이유가 없다"고 뺐다가 되살렸다. 그 판단은
179
+ * **읽는 단만 상정한 것**이었다. 25% 는 읽는 단이 아니라 **썸네일 단**이다 —
180
+ * 세로형에서 487px 이면 문단 사이에 끼워도 글 흐름을 안 끊고, 자세히 볼 땐
181
+ * `새 탭에서 열기` 가 받는다.
182
+ */
183
+ for (const value of ["25%", "50%", "75%", "100%"]) {
250
184
  const btn = document.createElement("button");
251
185
  btn.type = "button";
252
186
  btn.className = PRESET_IDLE;
@@ -267,10 +201,12 @@ export const PdfBlock = TiptapNode.create({
267
201
  e.stopPropagation();
268
202
  setAttrs({ width: value });
269
203
  });
270
- presetGroup.appendChild(btn);
204
+ endCluster.appendChild(btn);
271
205
  presetButtons.push({ value, el: btn });
272
206
  }
273
- header.appendChild(presetGroup);
207
+ const divider = document.createElement("div");
208
+ divider.className = "pdf-divider";
209
+ endCluster.appendChild(divider);
274
210
  }
275
211
  /** 현재 `width` 와 같은 프리셋을 눌린 상태로 만든다. */
276
212
  const syncPresets = (width) => {
@@ -280,17 +216,12 @@ export const PdfBlock = TiptapNode.create({
280
216
  }
281
217
  };
282
218
  syncPresets(node.attrs.width ?? null);
283
- const btnGroup = document.createElement("div");
284
- btnGroup.className = "flex items-center gap-1";
285
- header.appendChild(btnGroup);
286
219
  const downloadLink = document.createElement("a");
287
220
  downloadLink.rel = "noopener noreferrer";
288
221
  downloadLink.setAttribute("download", cleanName(node.attrs.name));
289
- downloadLink.className =
290
- "p-1 rounded hover:bg-muted transition-colors text-muted-foreground cursor-pointer";
222
+ downloadLink.className = "pdf-ctl";
291
223
  downloadLink.title = "\uB2E4\uC6B4\uB85C\uB4DC";
292
- downloadLink.innerHTML =
293
- '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>';
224
+ downloadLink.innerHTML = icon('<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/>');
294
225
  downloadLink.addEventListener("click", async (e) => {
295
226
  if (!downloadLink.href)
296
227
  return;
@@ -314,68 +245,178 @@ export const PdfBlock = TiptapNode.create({
314
245
  window.open(downloadLink.href, "_blank", "noopener,noreferrer");
315
246
  }
316
247
  });
317
- btnGroup.appendChild(downloadLink);
248
+ endCluster.appendChild(downloadLink);
249
+ /*
250
+ * \uC804\uCCB4\uD654\uBA74 \uB300\uC2E0 **\uC0C8 \uD0ED**\uC774\uB2E4(\uC0AC\uC6A9\uC790 \uACB0\uC815). \uBE0C\uB77C\uC6B0\uC800 \uAE30\uBCF8 PDF \uBDF0\uC5B4\uAC00 \uCC3D \uC804\uCCB4\uB97C \uC4F0\uACE0
251
+ * \uAC80\uC0C9\u00B7\uC778\uC1C4\u00B7\uD655\uB300\uAE4C\uC9C0 \uC5B9\uD600 \uC624\uBBC0\uB85C, \uC6B0\uB9AC\uAC00 \uB9CC\uB4E0 \uC804\uCCB4\uD654\uBA74\uBCF4\uB2E4 \uC77D\uAE30\uC5D0 \uB0AB\uB2E4.
252
+ */
318
253
  const openLink = document.createElement("a");
319
254
  openLink.target = "_blank";
320
255
  openLink.rel = "noopener noreferrer";
321
- openLink.className =
322
- "p-1 rounded hover:bg-muted transition-colors text-muted-foreground";
256
+ openLink.className = "pdf-ctl";
323
257
  openLink.title = "\uC0C8 \uD0ED\uC5D0\uC11C \uC5F4\uAE30";
324
- openLink.innerHTML =
325
- '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg>';
326
- btnGroup.appendChild(openLink);
258
+ openLink.innerHTML = icon('<path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>');
259
+ endCluster.appendChild(openLink);
327
260
  if (editor.isEditable) {
328
261
  const deleteBtn = document.createElement("button");
329
262
  deleteBtn.type = "button";
330
- deleteBtn.className =
331
- "p-1 rounded hover:bg-destructive/10 transition-colors text-muted-foreground hover:text-destructive";
263
+ deleteBtn.className = "pdf-ctl pdf-ctl-danger";
332
264
  deleteBtn.title = "\uC0AD\uC81C";
333
- deleteBtn.innerHTML =
334
- '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/></svg>';
265
+ deleteBtn.innerHTML = icon('<path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/>');
335
266
  deleteBtn.addEventListener("click", () => {
336
267
  const pos = getPos();
337
268
  if (pos != null) {
338
269
  editor.commands.deleteRange({
339
270
  from: pos,
340
- to: pos + node.nodeSize,
271
+ to: pos + currentNode.nodeSize,
341
272
  });
342
273
  }
343
274
  });
344
- btnGroup.appendChild(deleteBtn);
275
+ endCluster.appendChild(deleteBtn);
345
276
  }
346
- // Content area
347
- const contentArea = document.createElement("div");
348
- contentArea.className = "flex items-center justify-center";
349
- wrapper.appendChild(contentArea);
350
277
  const canvas = document.createElement("canvas");
351
- canvas.className = "shadow-sm";
352
- contentArea.appendChild(canvas);
353
- const loadingDiv = document.createElement("div");
354
- loadingDiv.className = "flex items-center justify-center p-8";
355
- loadingDiv.innerHTML =
356
- '<p class="text-sm text-muted-foreground animate-pulse">PDF \uB85C\uB529 \uC911...</p>';
357
- contentArea.appendChild(loadingDiv);
278
+ frame.appendChild(canvas);
279
+ const statusDiv = document.createElement("div");
280
+ statusDiv.className = "pdf-status";
281
+ statusDiv.textContent = "PDF \uB85C\uB529 \uC911...";
282
+ frame.appendChild(statusDiv);
358
283
  canvas.style.display = "none";
359
- let navDiv = null;
284
+ // \uC870\uC791\uBD80\uB294 \uB9C8\uC9C0\uB9C9 \u2014 `position:absolute` \uB77C \uC21C\uC11C\uC640 \uBB34\uAD00\uD558\uAC8C \uC704\uC5D0 \uADF8\uB824\uC9C0\uC9C0\uB9CC,
285
+ // \uB0B4\uC6A9 \uB2E4\uC74C\uC5D0 \uC624\uB294 \uAC8C \uBCF4\uC870\uAE30\uC220\uC5D0\uAC8C\uB3C4 \uB9DE\uB294 \uC21C\uC11C\uB2E4.
286
+ frame.appendChild(overlay);
287
+ /* \u2500\u2500 \uD398\uC774\uC9C0 \uC774\uB3D9 \u2014 \uC88C\uC6B0 \uD654\uC0B4\uD45C + \uC544\uB798 \uAC00\uC6B4\uB370 \uCABD\uC218 \u2500\u2500 */
288
+ const prevBtn = document.createElement("button");
289
+ prevBtn.type = "button";
290
+ prevBtn.className = "pdf-ctl pdf-nav pdf-nav-prev pdf-cluster";
291
+ prevBtn.title = "\uC774\uC804 \uD398\uC774\uC9C0";
292
+ prevBtn.setAttribute("aria-label", "\uC774\uC804 \uD398\uC774\uC9C0");
293
+ prevBtn.innerHTML = icon('<path d="m15 18-6-6 6-6"/>');
294
+ const nextBtn = document.createElement("button");
295
+ nextBtn.type = "button";
296
+ nextBtn.className = "pdf-ctl pdf-nav pdf-nav-next pdf-cluster";
297
+ nextBtn.title = "\uB2E4\uC74C \uD398\uC774\uC9C0";
298
+ nextBtn.setAttribute("aria-label", "\uB2E4\uC74C \uD398\uC774\uC9C0");
299
+ nextBtn.innerHTML = icon('<path d="m9 18 6-6-6-6"/>');
300
+ /*
301
+ * \uD310\uC774 \uD654\uBA74\uBCF4\uB2E4 \uAE38 \uB54C \uC77D\uAE30 \uC870\uC791\uBD80\uAC00 \uB530\uB77C\uC624\uAC8C \uD558\uB294 \uB808\uC77C. `position: sticky` \uB77C
302
+ * \uD310\uC744 \uC9C0\uB098\uB294 \uB3D9\uC548 \uD56D\uC0C1 \uBCF4\uC774\uB294 \uC601\uC5ED\uC5D0 \uBA38\uBB34\uB978\uB2E4. \uD310\uC774 \uD654\uBA74\uBCF4\uB2E4 \uC9E7\uC73C\uBA74
303
+ * `max-height: 100%` \uAC00 \uB808\uC77C\uC744 \uD310 \uD06C\uAE30\uB85C \uC904\uC5EC \uC608\uC804\uACFC \uAC19\uC774 \uD310 \uAC00\uC6B4\uB370\u00b7\uBC11\uC5D0 \uB193\uC778\uB2E4.
304
+ */
305
+ const navRail = document.createElement("div");
306
+ navRail.className = "pdf-nav-rail";
307
+ const pageCluster = document.createElement("div");
308
+ pageCluster.className = "pdf-cluster pdf-cluster-page";
309
+ const pageBtn = document.createElement("button");
310
+ pageBtn.type = "button";
311
+ pageBtn.className = "pdf-page";
312
+ pageBtn.title = "\uCABD \uBC88\uD638\uB85C \uC774\uB3D9";
313
+ pageCluster.appendChild(pageBtn);
314
+ /*
315
+ * \uCABD\uC218\uB97C \uB20C\uB7EC **\uBC88\uD638\uB85C \uAC74\uB108\uB6F4\uB2E4.** \uC774\uAC8C \uC5C6\uC73C\uBA74 30\uCABD\uC9DC\uB9AC \uC790\uB8CC\uC5D0\uC11C \uB4A4\uB85C \uAC00\uB824\uBA74
316
+ * \uD654\uC0B4\uD45C\uB97C 30\uBC88 \uB20C\uB7EC\uC57C \uD55C\uB2E4 \u2014 \uD398\uC774\uC9C0 \uB118\uAE40 \uBAA8\uB378\uC758 \uAC00\uC7A5 \uC544\uD508 \uC9C0\uC810\uC774\uB2E4.
317
+ */
318
+ const pageInput = document.createElement("input");
319
+ pageInput.type = "text";
320
+ pageInput.inputMode = "numeric";
321
+ pageInput.className = "pdf-page-input";
322
+ pageInput.setAttribute("aria-label", "\uCABD \uBC88\uD638");
323
+ pageInput.style.display = "none";
324
+ pageCluster.appendChild(pageInput);
360
325
  let currentPage = 1;
361
326
  let totalPages = 0;
362
327
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
363
328
  let pdfDoc = null;
364
329
  let rendering = false;
330
+ let renderQueued = false;
331
+ /** 쪽수 표시와 화살표 비활성 상태를 현재 페이지에 맞춘다. */
332
+ const syncPageUi = () => {
333
+ pageBtn.textContent = `${currentPage} / ${totalPages}`;
334
+ prevBtn.disabled = currentPage <= 1;
335
+ nextBtn.disabled = currentPage >= totalPages;
336
+ };
337
+ const goToPage = (next) => {
338
+ const clamped = Math.min(totalPages, Math.max(1, next));
339
+ if (clamped === currentPage)
340
+ return;
341
+ currentPage = clamped;
342
+ syncPageUi();
343
+ renderPage();
344
+ };
345
+ prevBtn.addEventListener("click", () => goToPage(currentPage - 1));
346
+ nextBtn.addEventListener("click", () => goToPage(currentPage + 1));
347
+ const closePageInput = () => {
348
+ pageInput.style.display = "none";
349
+ pageBtn.style.display = "";
350
+ };
351
+ pageBtn.addEventListener("click", () => {
352
+ pageInput.value = String(currentPage);
353
+ pageBtn.style.display = "none";
354
+ pageInput.style.display = "";
355
+ pageInput.focus();
356
+ pageInput.select();
357
+ });
358
+ pageInput.addEventListener("keydown", (e) => {
359
+ // tiptap 이 단축키를 가로채면 숫자가 안 들어간다(이름칸과 같은 이유).
360
+ e.stopPropagation();
361
+ if (e.key === "Enter") {
362
+ e.preventDefault();
363
+ const parsed = Number.parseInt(pageInput.value, 10);
364
+ if (Number.isFinite(parsed))
365
+ goToPage(parsed);
366
+ closePageInput();
367
+ }
368
+ else if (e.key === "Escape") {
369
+ e.preventDefault();
370
+ closePageInput();
371
+ }
372
+ });
373
+ pageInput.addEventListener("blur", closePageInput);
374
+ /*
375
+ * 화살표 키는 **조작부 안에 포커스가 있을 때만** 받는다. 블록 전체에서 가로채면
376
+ * 편집 중 이 블록을 지나 위아래로 빠져나가는 길이 막힌다(atom 노드라 화살표가
377
+ * ProseMirror 의 탈출 경로다). 넘김 버튼을 한 번 누르면 그때부터 키가 듣는다.
378
+ */
379
+ overlay.addEventListener("keydown", (e) => {
380
+ if (e.target instanceof HTMLInputElement)
381
+ return;
382
+ if (e.key !== "ArrowLeft" && e.key !== "ArrowRight")
383
+ return;
384
+ e.preventDefault();
385
+ e.stopPropagation();
386
+ goToPage(currentPage + (e.key === "ArrowLeft" ? -1 : 1));
387
+ });
388
+ /*
389
+ * ⚠️ **그리는 중에 들어온 요청을 버리면 안 된다.** 예전엔 `if (rendering) return` 으로
390
+ * 그냥 흘려보냈는데, 폭 프리셋이나 창 크기 변경이 **직전 렌더가 끝나기 전에** 오면
391
+ * 그 요청이 통째로 사라져 캔버스가 옛 크기에 머문다(다음 렌더까지 아무도 다시
392
+ * 부르지 않는다). 겹치면 한 번 예약해 뒀다가 끝나고 이어서 그린다.
393
+ * pdf.js 는 같은 캔버스에 렌더가 겹치면 던지므로 **직렬화 자체는 유지**한다.
394
+ */
365
395
  async function renderPage() {
366
- if (!pdfDoc || rendering || destroyed)
396
+ if (!pdfDoc || destroyed)
397
+ return;
398
+ if (rendering) {
399
+ renderQueued = true;
367
400
  return;
401
+ }
368
402
  rendering = true;
369
403
  try {
370
404
  const pageObj = await pdfDoc.getPage(currentPage);
371
405
  if (destroyed)
372
406
  return;
373
407
  const unscaledViewport = pageObj.getViewport({ scale: 1 });
374
- const availableWidth = contentArea.clientWidth;
375
- if (availableWidth <= 0) {
376
- rendering = false;
408
+ const availableWidth = frame.clientWidth;
409
+ if (availableWidth <= 0)
377
410
  return;
378
- }
411
+ /*
412
+ * 폭에만 맞춘다 — **높이는 제한하지 않는다.**
413
+ *
414
+ * ⚠️ 한때 `min(폭맞춤, 0.78vh)` 로 한 장이 화면에 들어가게 했다가 되돌렸다.
415
+ * A4 는 세로가 길어서 웬만한 창에서는 **높이 쪽이 항상 먼저 걸리고**, 그러면
416
+ * 폭 프리셋 50%/75%/100% 가 **전부 같은 크기로 그려진다**(실측: 50% 도 100% 도
417
+ * 캔버스 673px). 저자가 고른 폭이 무의미해지는 건 얻는 것보다 잃는 게 크다.
418
+ * 판이 화면보다 길 때 조작부가 따라오게 하는 건 CSS(`.pdf-nav-rail`)가 맡는다.
419
+ */
379
420
  const scale = availableWidth / unscaledViewport.width;
380
421
  const viewport = pageObj.getViewport({ scale });
381
422
  const dpr = window.devicePixelRatio || 1;
@@ -384,10 +425,8 @@ export const PdfBlock = TiptapNode.create({
384
425
  canvas.style.width = `${viewport.width}px`;
385
426
  canvas.style.height = `${viewport.height}px`;
386
427
  const ctx = canvas.getContext("2d");
387
- if (!ctx) {
388
- rendering = false;
428
+ if (!ctx)
389
429
  return;
390
- }
391
430
  ctx.resetTransform();
392
431
  ctx.clearRect(0, 0, canvas.width, canvas.height);
393
432
  ctx.scale(dpr, dpr);
@@ -396,10 +435,9 @@ export const PdfBlock = TiptapNode.create({
396
435
  finally {
397
436
  rendering = false;
398
437
  }
399
- if (navDiv) {
400
- const pageSpan = navDiv.querySelector(".page-counter");
401
- if (pageSpan)
402
- pageSpan.textContent = `${currentPage} / ${totalPages}`;
438
+ if (renderQueued && !destroyed) {
439
+ renderQueued = false;
440
+ await renderPage();
403
441
  }
404
442
  }
405
443
  async function loadPdf(src) {
@@ -412,60 +450,29 @@ export const PdfBlock = TiptapNode.create({
412
450
  if (destroyed)
413
451
  return;
414
452
  totalPages = pdfDoc.numPages;
415
- loadingDiv.style.display = "none";
453
+ statusDiv.style.display = "none";
416
454
  canvas.style.display = "";
455
+ // \uD55C \uC7A5\uC9DC\uB9AC\uC5D4 \uB118\uAE40 \uC870\uC791\uC744 \uC544\uC608 \uBD99\uC774\uC9C0 \uC54A\uB294\uB2E4 \u2014 \uB204\uB97C \uC218 \uC5C6\uB294 \uBC84\uD2BC\uC740 \uC18C\uC74C\uC774\uB2E4.
417
456
  if (totalPages > 1) {
418
- navDiv = document.createElement("div");
419
- navDiv.className =
420
- "flex items-center justify-center gap-4 px-3 py-1 border-t border-border";
421
- navDiv.style.background = "var(--muted)";
422
- const prevBtn = document.createElement("button");
423
- prevBtn.type = "button";
424
- prevBtn.className =
425
- "p-1.5 rounded hover:bg-muted transition-colors disabled:opacity-30 text-foreground";
426
- prevBtn.innerHTML =
427
- '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m15 18-6-6 6-6"/></svg>';
428
- prevBtn.addEventListener("click", () => {
429
- if (currentPage > 1) {
430
- currentPage--;
431
- prevBtn.disabled = currentPage <= 1;
432
- nextBtn.disabled = currentPage >= totalPages;
433
- renderPage();
434
- }
435
- });
436
- navDiv.appendChild(prevBtn);
437
- const pageSpan = document.createElement("span");
438
- pageSpan.className = "text-sm tabular-nums page-counter select-none";
439
- pageSpan.style.userSelect = "none";
440
- pageSpan.textContent = `1 / ${totalPages}`;
441
- navDiv.appendChild(pageSpan);
442
- const nextBtn = document.createElement("button");
443
- nextBtn.type = "button";
444
- nextBtn.className =
445
- "p-1.5 rounded hover:bg-muted transition-colors disabled:opacity-30 text-foreground";
446
- nextBtn.innerHTML =
447
- '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>';
448
- nextBtn.addEventListener("click", () => {
449
- if (currentPage < totalPages) {
450
- currentPage++;
451
- prevBtn.disabled = currentPage <= 1;
452
- nextBtn.disabled = currentPage >= totalPages;
453
- renderPage();
454
- }
455
- });
456
- navDiv.appendChild(nextBtn);
457
- wrapper.appendChild(navDiv);
457
+ navRail.appendChild(prevBtn);
458
+ navRail.appendChild(nextBtn);
459
+ navRail.appendChild(pageCluster);
460
+ overlay.appendChild(navRail);
461
+ syncPageUi();
458
462
  }
459
463
  renderPage();
464
+ /*
465
+ * \u26A0\uFE0F \uAD00\uCC30 \uB300\uC0C1\uC740 `dom` \uC774\uC9C0 \uD310\uC774 \uC544\uB2C8\uB2E4. \uD310\uC744 \uBCF4\uBA74 \uCE94\uBC84\uC2A4 \uB192\uC774\uAC00 \uBC14\uB014 \uB54C\uB9C8\uB2E4
466
+ * \uB2E4\uC2DC \uBD88\uB824 \uB418\uBA39\uC784\uC774 \uC0DD\uAE34\uB2E4 \u2014 \uD3ED\uC740 `dom` \uC5D0\uC11C\uB9CC \uBC14\uB010\uB2E4.
467
+ */
460
468
  resizeObserver = new ResizeObserver(() => {
461
469
  clearTimeout(resizeTimeout);
462
470
  resizeTimeout = setTimeout(renderPage, 100);
463
471
  });
464
- resizeObserver.observe(contentArea);
472
+ resizeObserver.observe(dom);
465
473
  }
466
474
  catch {
467
- loadingDiv.innerHTML =
468
- '<p class="text-sm text-muted-foreground">PDF\uB97C \uBD88\uB7EC\uC62C \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.</p>';
475
+ statusDiv.textContent = "PDF\uB97C \uBD88\uB7EC\uC62C \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.";
469
476
  }
470
477
  }
471
478
  // URL 해결 후 PDF 로드
@@ -497,10 +504,8 @@ export const PdfBlock = TiptapNode.create({
497
504
  resolver(node.attrs.fileId)
498
505
  .then((result) => {
499
506
  if (result.name) {
500
- // 다운로드 파일명은 항상 실제 파일명. 표시는 `label` 이 있으면 그쪽이 이긴다.
501
- resolvedName = result.name;
507
+ // \uB2E4\uC6B4\uB85C\uB4DC \uD30C\uC77C\uBA85\uC740 \uD56D\uC0C1 \uC2E4\uC81C \uD30C\uC77C\uBA85\uC774\uB2E4.
502
508
  downloadLink.setAttribute("download", result.name);
503
- syncName();
504
509
  }
505
510
  })
506
511
  .catch(() => { });
@@ -518,7 +523,6 @@ export const PdfBlock = TiptapNode.create({
518
523
  }
519
524
  currentNode = updatedNode;
520
525
  syncPresets(newWidth ?? null);
521
- syncName();
522
526
  return true;
523
527
  },
524
528
  // 이름 입력칸 위의 이벤트는 ProseMirror 가 가로채면 안 된다(CardBlock 과 같은 방식).
@@ -1,4 +1,5 @@
1
1
  import { describe, it, expect, afterEach } from 'vitest';
2
+ import { readFileSync } from 'node:fs';
2
3
  import { Editor } from '@tiptap/core';
3
4
  import StarterKit from '@tiptap/starter-kit';
4
5
  import { PdfBlock } from './PdfBlock';
@@ -78,4 +79,118 @@ describe('PdfBlock extension', () => {
78
79
  const pdfNode = doc.content?.find((n) => n.type === 'pdfBlock');
79
80
  expect(pdfNode?.attrs?.name).toBe('PDF');
80
81
  });
82
+ /*
83
+ * 조작부는 판 위에 얹혀 있고, 위아래 바는 없다. 여기 있는 단언들은 전부 한 번씩
84
+ * 잘못 만들었다가 되돌린 것들이라 그대로 못으로 박아 둔다.
85
+ */
86
+ describe('node view — 판 위 조작부', () => {
87
+ const PDF_HTML = '<div data-pdf-src="https://example.com/doc.pdf" data-pdf-name="doc.pdf"></div>';
88
+ function mount(html = PDF_HTML, editable = true) {
89
+ const element = document.createElement('div');
90
+ document.body.appendChild(element);
91
+ editor = new Editor({
92
+ element,
93
+ editable,
94
+ extensions: [StarterKit, PdfBlock],
95
+ content: html
96
+ });
97
+ const frame = element.querySelector('.pdf-frame');
98
+ const overlay = element.querySelector('.pdf-overlay');
99
+ return { element, frame, overlay };
100
+ }
101
+ it('renders no header or footer bar — only the plate', () => {
102
+ const { frame } = mount();
103
+ expect(frame).toBeTruthy();
104
+ // 캔버스 · 상태문구 · 조작부. 바가 다시 생기면 여기서 걸린다.
105
+ const kinds = [...frame.children].map((c) => c.tagName === 'CANVAS' ? 'canvas' : c.className);
106
+ expect(kinds).toEqual(['canvas', 'pdf-status', 'pdf-overlay']);
107
+ });
108
+ it('gives editors the width presets and delete', () => {
109
+ const { overlay } = mount();
110
+ expect([...overlay.querySelectorAll('.pdf-preset')].map((b) => b.textContent)).toEqual(['25%', '50%', '75%', '100%']);
111
+ expect(overlay.querySelector('.pdf-ctl-danger')).toBeTruthy();
112
+ });
113
+ it('hides authoring controls from readers but keeps the file actions', () => {
114
+ const { overlay } = mount(PDF_HTML, false);
115
+ // 저장되는 문서 속성을 읽는 사람이 건드릴 자리는 없다.
116
+ expect(overlay.querySelector('.pdf-preset')).toBeNull();
117
+ expect(overlay.querySelector('.pdf-ctl-danger')).toBeNull();
118
+ expect(overlay.querySelectorAll('a.pdf-ctl').length).toBe(2);
119
+ });
120
+ /*
121
+ * 이름은 어느 모드에서도 그리지 않는다. 다만 `label` 을 스키마에서 빼지는 않았다 —
122
+ * 이미 이름을 지정해 둔 문서를 한 번 열었다 저장하는 것만으로 값이 사라지면 안 된다.
123
+ */
124
+ it('never paints the file name, but still round-trips the label', () => {
125
+ for (const editable of [true, false]) {
126
+ const { overlay } = mount('<div data-pdf-src="https://example.com/doc.pdf" data-pdf-name="doc.pdf" data-pdf-label="1강 자료"></div>', editable);
127
+ expect(overlay.querySelector('.pdf-cluster-start')).toBeNull();
128
+ expect(overlay.querySelector('.pdf-name')).toBeNull();
129
+ expect(overlay.textContent).not.toContain('1강 자료');
130
+ expect(overlay.textContent).not.toContain('doc.pdf');
131
+ expect(editor.getHTML()).toContain('data-pdf-label="1강 자료"');
132
+ editor.destroy();
133
+ }
134
+ });
135
+ it('marks the width preset that matches the current width', () => {
136
+ const { overlay } = mount('<div data-pdf-src="https://example.com/doc.pdf" data-pdf-width="75%"></div>');
137
+ const active = overlay.querySelector('.pdf-preset.is-active');
138
+ expect(active?.textContent).toBe('75%');
139
+ expect(active?.getAttribute('aria-pressed')).toBe('true');
140
+ });
141
+ it('writes the width attribute when a preset is clicked', () => {
142
+ const { overlay } = mount();
143
+ // 라벨로 찾는다 — 순서가 바뀌어도 무엇을 눌렀는지가 흔들리지 않게.
144
+ const preset = [...overlay.querySelectorAll('.pdf-preset')].find((b) => b.textContent === '50%');
145
+ preset.click();
146
+ const pdfNode = editor
147
+ .getJSON()
148
+ .content?.find((n) => n.type === 'pdfBlock');
149
+ expect(pdfNode?.attrs?.width).toBe('50%');
150
+ });
151
+ it('removes the block when delete is clicked', () => {
152
+ const { overlay } = mount();
153
+ overlay.querySelector('.pdf-ctl-danger').click();
154
+ const hasPdf = editor
155
+ .getJSON()
156
+ .content?.some((n) => n.type === 'pdfBlock');
157
+ expect(hasPdf).toBeFalsy();
158
+ });
159
+ /*
160
+ * ⚠️ 넘김 버튼이 판 가장자리에 붙으면 폭 조절 손잡이(`right: -12px`, 높이 전체)를
161
+ * 가려 리사이즈가 통째로 죽는다. 들여쓰기는 CSS 에 있으므로 여기서는 **그 CSS 가
162
+ * 걸리는 클래스가 실제로 붙는지**를 지킨다.
163
+ */
164
+ /*
165
+ * ⚠️ 조작부를 누르면 브라우저가 거기서 글자 선택을 시작해 프리셋·쪽수가 통째로
166
+ * 파랗게 칠해졌다(사용자 지적). 유일한 입력칸인 쪽 번호만 예외다.
167
+ */
168
+ it('does not let clicks select the control text', () => {
169
+ const css = readFileSync('src/styles/editor.css', 'utf-8');
170
+ const rule = css.match(/\.pdf-overlay\s*\{[^}]*\}/)?.[0] ?? '';
171
+ expect(rule).toContain('user-select: none');
172
+ const inputRule = css.match(/\.pdf-overlay \.pdf-page-input\s*\{[^}]*\}/)?.[0] ?? '';
173
+ expect(inputRule).toContain('user-select: text');
174
+ });
175
+ /*
176
+ * ⚠️ `.pdf-ctl` 과 `.pdf-nav` 는 특이도가 같아 **순서가 승부를 가른다.** 앞에 두면
177
+ * `.pdf-ctl { background: transparent }` 가 이겨 넘김 버튼의 판이 사라진다.
178
+ */
179
+ it('declares the nav plate after the generic control reset', () => {
180
+ const css = readFileSync('src/styles/editor.css', 'utf-8');
181
+ expect(css.indexOf('.pdf-overlay .pdf-nav {')).toBeGreaterThan(css.indexOf('.pdf-overlay .pdf-ctl,'));
182
+ });
183
+ it('keeps the page arrows away from the resize handle', () => {
184
+ const { overlay } = mount();
185
+ // 아직 문서를 못 읽었으므로 붙어 있지 않다 — 한 장짜리에 누를 수 없는
186
+ // 화살표가 남는 것을 막는 것도 같은 규칙이다.
187
+ expect(overlay.querySelector('.pdf-nav')).toBeNull();
188
+ // 손잡이는 `right: -12px` 에 `width: 14px` 이라 판 안쪽 2px 을 먹는다.
189
+ // 들여쓰기가 그보다 작아지면 리사이즈가 죽으므로 값 자체를 지킨다.
190
+ const css = readFileSync('src/styles/editor.css', 'utf-8');
191
+ const inset = (rule) => Number(css.match(new RegExp(`\\.${rule}\\s*\\{[^}]*?(\\d+)px`))?.[1] ?? '0');
192
+ expect(inset('pdf-nav-prev')).toBeGreaterThanOrEqual(12);
193
+ expect(inset('pdf-nav-next')).toBeGreaterThanOrEqual(12);
194
+ });
195
+ });
81
196
  });
package/dist/index.d.ts CHANGED
@@ -4,7 +4,6 @@ export { default as BubbleToolbar } from "./components/BubbleToolbar.svelte";
4
4
  export { default as SlashCommandMenu } from "./components/SlashCommandMenu.svelte";
5
5
  export { default as InputModal } from "./components/InputModal.svelte";
6
6
  export { default as MathModal } from "./components/MathModal.svelte";
7
- export { default as PdfViewer } from "./components/PdfViewer.svelte";
8
7
  export { default as TableBubbleMenu } from "./components/TableBubbleMenu.svelte";
9
8
  export { PdfBlock } from "./extensions/PdfBlock";
10
9
  export { Indent } from "./extensions/Indent";
@@ -24,6 +23,6 @@ export { attachResize } from "./utils/resize";
24
23
  export type { AttachResizeOptions, ResizeAxis } from "./utils/resize";
25
24
  export { cn } from "./utils/cn";
26
25
  export { lowlight, CODE_LANGUAGES } from "./utils/lowlight";
27
- export type { TipTapEditorProps, UploadHandler, ToolbarMode, ToolbarFeature, FixedToolbarProps, BlockHandleProps, SlashCommandMenuProps, PdfViewerProps, SlashMenuItem, } from "./types";
26
+ export type { TipTapEditorProps, UploadHandler, ToolbarMode, ToolbarFeature, FixedToolbarProps, BlockHandleProps, SlashCommandMenuProps, SlashMenuItem, } from "./types";
28
27
  export { TOOLBAR_PRESETS, resolveFeatures } from "./types";
29
28
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAC3E,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAC3E,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAC7E,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AACnF,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,qCAAqC,CAAC;AAGjF,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AACnF,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,YAAY,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AACrF,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC5D,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGjE,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACzF,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACtE,OAAO,EAAE,EAAE,EAAE,MAAM,YAAY,CAAC;AAahC,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAG5D,YAAY,EACV,iBAAiB,EACjB,aAAa,EACb,WAAW,EACX,cAAc,EACd,iBAAiB,EACjB,gBAAgB,EAChB,qBAAqB,EACrB,cAAc,EACd,aAAa,GACd,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAC3E,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAC3E,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAC7E,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AACnF,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,qCAAqC,CAAC;AAGjF,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AACnF,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,YAAY,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AACrF,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC5D,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGjE,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACzF,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACtE,OAAO,EAAE,EAAE,EAAE,MAAM,YAAY,CAAC;AAahC,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAG5D,YAAY,EACV,iBAAiB,EACjB,aAAa,EACb,WAAW,EACX,cAAc,EACd,iBAAiB,EACjB,gBAAgB,EAChB,qBAAqB,EACrB,aAAa,GACd,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC"}
package/dist/index.js CHANGED
@@ -5,7 +5,6 @@ export { default as BubbleToolbar } from "./components/BubbleToolbar.svelte";
5
5
  export { default as SlashCommandMenu } from "./components/SlashCommandMenu.svelte";
6
6
  export { default as InputModal } from "./components/InputModal.svelte";
7
7
  export { default as MathModal } from "./components/MathModal.svelte";
8
- export { default as PdfViewer } from "./components/PdfViewer.svelte";
9
8
  export { default as TableBubbleMenu } from "./components/TableBubbleMenu.svelte";
10
9
  // 익스텐션
11
10
  export { PdfBlock } from "./extensions/PdfBlock";
@@ -1 +1 @@
1
- .pointer-events-none{pointer-events:none}.visible{visibility:visible}.collapse{visibility:collapse}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.bottom-0{bottom:0}.bottom-full{bottom:100%}.left-0{left:0}.left-1\/2{left:50%}.top-full{top:100%}.z-30{z-index:30}.z-50{z-index:50}.mx-0\.5{margin-left:.125rem;margin-right:.125rem}.mx-auto{margin-left:auto;margin-right:auto}.my-1{margin-top:.25rem;margin-bottom:.25rem}.my-2{margin-top:.5rem;margin-bottom:.5rem}.mb-1{margin-bottom:.25rem}.mb-1\.5{margin-bottom:.375rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.ml-auto{margin-left:auto}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.block{display:block}.inline{display:inline}.flex{display:flex}.\!table{display:table!important}.table{display:table}.grid{display:grid}.hidden{display:none}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-px{height:1px}.min-h-\[52px\]{min-height:52px}.w-10{width:2.5rem}.w-4{width:1rem}.w-\[360px\]{width:360px}.w-\[420px\]{width:420px}.w-\[440px\]{width:440px}.w-fit{width:-moz-fit-content;width:fit-content}.w-full{width:100%}.w-px{width:1px}.min-w-0{min-width:0}.min-w-\[96px\]{min-width:96px}.max-w-\[90vw\]{max-width:90vw}.flex-1{flex:1 1 0%}.flex-shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.-translate-x-1\/2{--tw-translate-x:-50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.\!transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))!important}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.select-text{-webkit-user-select:text;-moz-user-select:text;user-select:text}.resize-y{resize:vertical}.columns-2{-moz-columns:2;column-count:2}.columns-3{-moz-columns:3;column-count:3}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-4{gap:1rem}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.break-all{word-break:break-all}.rounded{border-radius:var(--radius-md,4px)}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:var(--radius-xl,8px)}.rounded-md{border-radius:var(--radius-md,6px)}.rounded-xl{border-radius:var(--radius-2xl,12px)}.rounded-t-xl{border-top-left-radius:var(--radius-2xl,12px);border-top-right-radius:var(--radius-2xl,12px)}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-t{border-top-width:1px}.border-border{border-color:var(--border)}.bg-background{background-color:var(--background)}.bg-black\/40{background-color:rgba(0,0,0,.4)}.bg-border{background-color:var(--border)}.bg-muted{background-color:var(--muted)}.bg-popover{background-color:var(--popover)}.bg-primary{background-color:var(--primary)}.bg-transparent{background-color:transparent}.p-0{padding:0}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-8{padding:2rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-3\.5{padding-left:.875rem;padding-right:.875rem}.px-4{padding-left:1rem;padding-right:1rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.pt-2{padding-top:.5rem}.pt-3{padding-top:.75rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-\[11px\]{font-size:11px}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.leading-none{line-height:1}.text-destructive{color:var(--destructive)}.text-foreground{color:var(--foreground)}.text-muted-foreground{color:var(--muted-foreground)}.text-primary{color:var(--primary)}.text-primary-foreground{color:var(--primary-foreground)}.underline{text-decoration-line:underline}.opacity-30{opacity:.3}.shadow-lg{--tw-shadow:var(--shadow-dropdown,0 10px 15px -3px rgba(0,0,0,.1));--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color)}.shadow-lg,.shadow-sm{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow:var(--shadow-dropdown,0 1px 2px 0 rgba(0,0,0,.05));--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.shadow-xl{--tw-shadow:var(--shadow-popup,0 20px 25px -5px rgba(0,0,0,.1));--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid transparent;outline-offset:2px}.blur{--tw-blur:blur(8px)}.blur,.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-shadow{transition-property:box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.hce-editor-wrapper{overflow:visible!important}.hce-color-divider{border-top:1px solid var(--border)}.bubble-toolbar-container button,.hce-editor-wrapper button,.slash-menu button{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent;border:0;cursor:pointer;box-sizing:border-box}.bubble-toolbar-container button:disabled,.hce-editor-wrapper button:disabled,.slash-menu button:disabled{cursor:default}.bubble-toolbar-container .hce-btn-primary,.hce-editor-wrapper .hce-btn-primary,.slash-menu .hce-btn-primary{background-color:var(--primary);color:var(--primary-foreground,#fff)}.tiptap{font-size:1.0625rem;line-height:1.8;color:var(--foreground);padding:1.5rem}.tiptap>*+*{margin-top:.25em}.tiptap [data-type=column]>:first-child,.tiptap [data-type=detailsContent]>:first-child,.tiptap blockquote>:first-child,.tiptap li>:first-child,.tiptap td>:first-child,.tiptap th>:first-child,.tiptap>:first-child{margin-top:0}.tiptap [data-type=column]>:last-child,.tiptap [data-type=detailsContent]>:last-child,.tiptap blockquote>:last-child,.tiptap li>:last-child,.tiptap td>:last-child,.tiptap th>:last-child,.tiptap>:last-child{margin-bottom:0}.tiptap p{margin:.5em 0!important;line-height:1.5}.tiptap p:first-child{margin-top:0!important}.tiptap p:last-child{margin-bottom:0!important}.tiptap p:empty,.tiptap p:has(>br:only-child){min-height:1.5em}.tiptap h1,.tiptap h2,.tiptap h3,.tiptap li,.tiptap p{white-space:pre-wrap}.tiptap h1{font-size:2rem;font-weight:700;margin:1.5em 0 .5em;line-height:1.25}.tiptap h2{font-size:1.5rem;font-weight:600;margin:1.2em 0 .4em;line-height:1.3}.tiptap h3{font-size:1.25rem;font-weight:600;margin:1em 0 .3em;line-height:1.35}.tiptap ul{list-style:disc}.tiptap ol,.tiptap ul{padding-left:1.5em;margin:.5em 0}.tiptap ol{list-style:decimal}.tiptap ul ul{list-style:circle}.tiptap ul ul ul{list-style:square}.tiptap ol ol{list-style:lower-alpha}.tiptap ol ol ol{list-style:lower-roman}.tiptap li{margin:.4em 0;line-height:1.7}.tiptap li>p{margin:0!important;line-height:inherit!important}.tiptap ul[data-type=taskList]{list-style:none;padding-left:0}.tiptap ul[data-type=taskList] li{display:flex;align-items:baseline;gap:.5rem;margin:.3em 0;line-height:1.7;list-style:none;list-style-type:none}.tiptap ul[data-type=taskList] li>label{flex-shrink:0;display:flex;align-items:center;height:1.5em}.tiptap ul[data-type=taskList] li>label input[type=checkbox]{-moz-appearance:none;appearance:none;-webkit-appearance:none;width:1.1em;height:1.1em;margin:0;padding:0;cursor:pointer;background-color:var(--background,#fff);border:1.5px solid var(--border);border-radius:4px;transition:background-color .12s ease,border-color .12s ease,box-shadow .12s ease;display:inline-block;vertical-align:middle;flex-shrink:0}.tiptap ul[data-type=taskList] li>label input[type=checkbox]:hover{border-color:var(--muted-foreground)}.tiptap ul[data-type=taskList] li>label input[type=checkbox]:focus-visible{outline:none;box-shadow:0 0 0 2px var(--background,#fff),0 0 0 4px var(--primary)}.tiptap ul[data-type=taskList] li>label input[type=checkbox]:checked{background-color:var(--primary);border-color:var(--primary);background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2.5' viewBox='0 0 16 16'%3E%3Cpath d='m3.5 8.5 3 3 6-6.5'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:50%;background-size:100% 100%}.tiptap ul[data-type=taskList] li>div{flex:1}.tiptap ul[data-type=taskList] li[data-checked=true]>div{text-decoration:line-through;opacity:.6}.tiptap blockquote{border-left:3px solid var(--border);padding:.5rem 1rem;margin:.75em 0;color:var(--muted-foreground);font-style:italic}.tiptap hr{border:none;border-top:2px solid color-mix(in srgb,var(--foreground) 38%,transparent);margin:1.5em 0}.tiptap code{background:var(--muted);border-radius:4px;padding:.15em .35em;font-size:.9em}.tiptap pre{background:#282c34;color:#abb2bf;border-radius:8px;padding:1em;margin:.75em 0;overflow-x:auto;font-family:JetBrains Mono,Fira Code,Consolas,monospace;font-size:1em;line-height:1.6}.tiptap pre code{background:none;padding:0;border-radius:0;color:#abb2bf;font-size:inherit}.tiptap pre code .hljs-keyword,.tiptap pre code .hljs-operator{color:#c678dd}.tiptap pre code .hljs-addition,.tiptap pre code .hljs-doctag,.tiptap pre code .hljs-regexp,.tiptap pre code .hljs-string{color:#98c379}.tiptap pre code .hljs-attr,.tiptap pre code .hljs-number,.tiptap pre code .hljs-selector-class,.tiptap pre code .hljs-template-variable,.tiptap pre code .hljs-type,.tiptap pre code .hljs-variable{color:#d19a66}.tiptap pre code .hljs-function,.tiptap pre code .hljs-section,.tiptap pre code .hljs-title,.tiptap pre code .hljs-title.class_{color:#61aeee}.tiptap pre code .hljs-params,.tiptap pre code .hljs-punctuation{color:#abb2bf}.tiptap pre code .hljs-comment,.tiptap pre code .hljs-quote{color:#5c6370;font-style:italic}.tiptap pre code .hljs-built_in,.tiptap pre code .hljs-formula{color:#e6c07b}.tiptap pre code .hljs-meta,.tiptap pre code .hljs-meta .hljs-keyword{color:#61aeee}.tiptap pre code .hljs-meta .hljs-string{color:#98c379}.tiptap pre code .hljs-attribute,.tiptap pre code .hljs-deletion,.tiptap pre code .hljs-name,.tiptap pre code .hljs-property,.tiptap pre code .hljs-selector-tag,.tiptap pre code .hljs-subst,.tiptap pre code .hljs-tag{color:#e06c75}.tiptap pre code .hljs-literal{color:#56b6c2}.tiptap pre code .hljs-selector-attr,.tiptap pre code .hljs-selector-id,.tiptap pre code .hljs-selector-pseudo{color:#61aeee}.tiptap pre code .hljs-link{color:#61aeee;text-decoration:underline}.tiptap pre code .hljs-bullet,.tiptap pre code .hljs-symbol{color:#61aeee}.tiptap pre code .hljs-emphasis{font-style:italic}.tiptap pre code .hljs-strong{font-weight:700}.tiptap img{max-width:100%;height:auto;margin:.75em 0;cursor:pointer;transition:box-shadow .15s}.tiptap [data-type=pdfBlock].ProseMirror-selectednode>div,.tiptap img{border-radius:8px}.tiptap a{color:var(--primary);text-decoration:underline;text-underline-offset:2px}.tiptap a:hover{opacity:.8}.tiptap mark{background-color:#fef08a;border-radius:2px;padding:.05em .1em}.tiptap [data-type=details]{border:none;border-radius:6px;padding:0;margin:.5em 0;background:transparent;display:grid;grid-template-columns:30px 1fr;align-items:start;transition:background .12s}.tiptap [data-type=details]:hover{background:var(--muted)}.tiptap [data-type=details]>button{background:none;border:none;cursor:pointer;padding:0;width:22px;height:22px;display:inline-flex;align-items:center;justify-content:center;color:var(--muted-foreground);border-radius:4px;transition:transform .2s,color .15s,background-color .15s;flex-shrink:0;margin-top:14px;margin-left:4px}.tiptap [data-type=details]>button:hover{background:color-mix(in srgb,var(--foreground) 8%,transparent)}.tiptap [data-type=details]>button:after{content:"";display:block;width:0;height:0;border-left:7px solid;border-top:5.5px solid transparent;border-bottom:5.5px solid transparent;margin-left:2px}.tiptap [data-type=details]:hover>button{color:var(--foreground)}.tiptap [data-type=details].is-open>button{transform:rotate(90deg)}.tiptap [data-type=details]>div{flex:1;min-width:0}.tiptap [data-type=details] summary{font-weight:600;cursor:pointer;padding:10px 8px 10px 0;display:block;border-bottom:1px solid transparent;transition:border-color .15s}.tiptap [data-type=details].is-open>div>summary{border-bottom-color:var(--border,#e5e7eb)}.tiptap [data-type=details] summary[data-level="1"],.tiptap details summary[data-level="1"]{font-size:2rem;font-weight:700;line-height:1.25}.tiptap [data-type=details] summary[data-level="2"],.tiptap details summary[data-level="2"]{font-size:1.5rem;font-weight:600;line-height:1.3}.tiptap [data-type=details] summary[data-level="3"],.tiptap details summary[data-level="3"]{font-size:1.25rem;font-weight:600;line-height:1.35}.tiptap [data-type=details]:has(>div>summary[data-level="1"])>button{margin-top:19px}.tiptap [data-type=details]:has(>div>summary[data-level="2"])>button{margin-top:15px}.tiptap [data-type=details]:has(>div>summary[data-level="3"])>button{margin-top:13px}.tiptap [data-type=detailsContent]{padding:8px 8px 8px 0;border-left:none}.tiptap [data-type=detailsContent][hidden]{display:none}.tiptap details{border:none;border-radius:6px;padding:0;margin:.5em 0;background:transparent;transition:background .12s}.tiptap details:hover{background:var(--muted)}.tiptap details summary{font-weight:600;cursor:pointer;padding:10px 8px 10px 26px;-webkit-user-select:none;-moz-user-select:none;user-select:none;border-bottom:1px solid transparent;position:relative;list-style:none;transition:border-color .15s}.tiptap details[open]>summary{border-bottom-color:var(--border,#e5e7eb)}.tiptap details summary::-webkit-details-marker{display:none}.tiptap details summary::marker{content:""}.tiptap details summary:before{content:"";position:absolute;left:8px;top:50%;width:0;height:0;border-left:7px solid var(--muted-foreground);border-top:5.5px solid transparent;border-bottom:5.5px solid transparent;transform:translateY(-50%);transition:transform .2s,border-left-color .15s}.tiptap details:hover summary:before{border-left-color:var(--foreground)}.tiptap details[open]>summary:before{transform:translateY(-50%) rotate(90deg)}.tiptap details[open]>:not(summary){margin-top:8px;padding-left:26px;padding-right:8px}.tiptap table{border-collapse:collapse;margin:1em 0;table-layout:fixed;border:2px solid var(--border);max-width:100%}.tiptap .tableWrapper{overflow-x:auto;max-width:100%}.tiptap td,.tiptap th{border:1px solid var(--border);padding:.6em .8em;min-width:60px;vertical-align:middle;position:relative;box-sizing:border-box;overflow:hidden;word-break:break-word;line-height:1.5}.tiptap th{background:var(--muted);font-weight:600;text-align:left}.tiptap td{background:var(--background)}.tiptap .selectedCell:after{content:"";position:absolute;inset:0;background:var(--primary);opacity:.12;pointer-events:none;z-index:1}.tiptap .column-resize-handle{position:absolute;right:-3px;top:-1px;bottom:-1px;width:6px;background:var(--primary);opacity:0;cursor:col-resize;z-index:20;border-radius:2px;transition:opacity .15s}.tiptap table:hover .column-resize-handle{opacity:.4}.tiptap .column-resize-handle:hover{opacity:1}.tiptap.resize-cursor{cursor:col-resize}.tableWrapper{overflow-x:auto;overflow-y:hidden;margin:1em 0;max-width:100%;padding-right:4px}.tiptap table .grip-column,.tiptap table .grip-row,.tiptap table .grip-table{display:none!important}.tiptap .is-empty:before{content:attr(data-placeholder);float:left;color:var(--muted-foreground);opacity:.5;pointer-events:none;height:0}.tiptap h1.is-empty:before{font-size:2rem;font-weight:700}.tiptap h2.is-empty:before{font-size:1.5rem;font-weight:600}.tiptap h3.is-empty:before{font-size:1.25rem;font-weight:600}.ProseMirror-gapcursor{display:none;pointer-events:none;position:relative}.ProseMirror-gapcursor:after{content:"";display:block;position:absolute;top:-2px;width:20px;border-top:1px solid var(--foreground);animation:ProseMirror-cursor-blink 1.1s steps(2,start) infinite}@keyframes ProseMirror-cursor-blink{to{visibility:hidden}}.ProseMirror-focused .ProseMirror-gapcursor{display:block}.ProseMirror-dropcursor{border-top:2px solid var(--primary);margin:-1px 0}.tiptap ::-moz-selection{background-color:color-mix(in srgb,var(--primary) 15%,transparent)}.tiptap ::selection{background-color:color-mix(in srgb,var(--primary) 15%,transparent)}.ProseMirror-noderangeselection ::-moz-selection{background:transparent}.ProseMirror-noderangeselection ::selection{background:transparent}.ProseMirror-noderangeselection *{caret-color:transparent}.ProseMirror-selectednode,.ProseMirror-selectednoderange{position:relative}.ProseMirror-selectednode:not(img):not([data-type=pdfBlock]):before,.ProseMirror-selectednoderange:not([data-type=pdfBlock]):before{content:"";position:absolute;inset:-.25rem;background-color:color-mix(in srgb,var(--primary) 10%,transparent);border:2px solid color-mix(in srgb,var(--primary) 30%,transparent);border-radius:.5rem;pointer-events:none;z-index:-1}.hce-table-bubble{z-index:var(--hce-menu-z,100)}.bubble-toolbar-container{position:absolute;top:0;left:0;width:-moz-max-content;width:max-content;z-index:calc(var(--hce-menu-z, 100) + 1)}.hce-toolbar:has(.absolute)~.bubble-toolbar-container,.hce-toolbar:has(.absolute)~.hce-table-bubble{display:none}.hce-menu-surface{background-color:var(--hce-menu-surface,#333)}.hce-floating-panel{background-color:color-mix(in srgb,var(--foreground) 8%,var(--popover,var(--background,#fff)));border:1px solid color-mix(in srgb,var(--foreground) 18%,var(--border));box-shadow:0 8px 24px color-mix(in srgb,var(--foreground) 16%,transparent),0 2px 6px color-mix(in srgb,var(--foreground) 10%,transparent)}.hce-floating-plus{display:flex;align-items:center;justify-content:center;width:24px;height:24px;border-radius:6px;border:1px solid var(--border);background:var(--background);color:var(--muted-foreground);cursor:pointer;transition:all .15s}.hce-floating-plus:hover{color:var(--foreground);border-color:var(--foreground);background:var(--muted)}.hce-drag-handle{display:flex;align-items:center;gap:2px}.hce-drag-handle__plus{display:flex;align-items:center;justify-content:center;padding:4px;border-radius:4px;border:none;background:transparent;color:var(--muted-foreground);opacity:.5;cursor:pointer;transition:opacity .15s,background-color .15s}.hce-drag-handle__plus:hover{opacity:1;background:var(--muted)}.hce-drag-handle__grip{display:flex;align-items:center;justify-content:center;padding:2px;border-radius:4px;color:var(--muted-foreground);opacity:.5;cursor:grab;transition:opacity .15s,background-color .15s}.hce-drag-handle__grip:hover{opacity:1;background:var(--muted)}.hce-drag-handle__grip:active{cursor:grabbing}.tiptap div[data-type=columns]{display:grid;gap:16px;margin:1em 0}.tiptap div[data-type=columns][data-columns="2"]{grid-template-columns:1fr 1fr}.tiptap div[data-type=columns][data-columns="3"]{grid-template-columns:1fr 1fr 1fr}.tiptap div[data-type=column]{min-width:0;padding:4px 6px;border:1px dashed var(--border);border-radius:8px}.tiptap[contenteditable=true] div[data-type=column]:focus-within{border-color:var(--primary);border-style:solid}.tiptap div[data-type=columns].ProseMirror-selectednode{outline:2px solid color-mix(in srgb,var(--primary) 30%,transparent);border-radius:8px}@media (max-width:640px){.tiptap div[data-type=columns]{grid-template-columns:1fr!important}}.hce-table-btn-wrap{position:relative;display:inline-flex}.hce-table-btn-tooltip{position:absolute;bottom:100%;left:50%;transform:translateX(-50%);margin-bottom:6px;padding:4px 8px;font-size:11px;line-height:1.2;white-space:nowrap;color:#fff;background:#1e293b;border-radius:6px;pointer-events:none;opacity:0;transition:opacity .15s;z-index:60}.hce-table-btn-wrap:hover .hce-table-btn-tooltip{opacity:1}.hce-table-btn-wrap.hce-tooltip-off:hover .hce-table-btn-tooltip{opacity:0}.tiptap math-inline,math-inline.math-node{display:inline-block;vertical-align:baseline}.tiptap math-display,math-display.math-node{display:block;margin:.9em 0;text-align:center}.math-node,.tiptap .math-node{font-size:.95em}.tiptap math-inline.math-node .katex,math-inline.math-node .katex{font-size:1.0285em}.tiptap[contenteditable=true] .math-node{cursor:pointer;border-radius:4px;transition:background-color .12s}.tiptap[contenteditable=true] .math-node:hover{background-color:color-mix(in srgb,var(--primary) 10%,transparent)}.tiptap .math-node.ProseMirror-selectednode{background-color:color-mix(in srgb,var(--primary) 18%,transparent);outline:1px solid color-mix(in srgb,var(--primary) 45%,transparent)}.tiptap .math-node.empty-math .math-placeholder{font-size:.85em;padding:0 .35em;color:var(--muted-foreground);border:1px dashed var(--border);border-radius:4px}.math-node .math-error,.tiptap .math-error{color:#dc2626;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.9em}.hce-card-frame,.tiptap [data-type=card]{position:relative}.hce-card-frame{box-sizing:border-box;width:100%;margin:12px 0;padding:26px 30px;display:flex;flex-direction:column;justify-content:center;gap:12px;overflow:hidden;border:1px solid hsla(0,0%,100%,.52);border-radius:8px;box-shadow:inset 0 0 0 1px hsla(0,0%,100%,.28),0 8px 24px rgba(15,23,42,.08);overflow-wrap:anywhere;isolation:isolate}.hce-card-frame:before{content:"";position:absolute;inset:0;z-index:0;background:linear-gradient(90deg,hsla(0,0%,100%,.14),hsla(0,0%,100%,.04) 48%,rgba(15,23,42,.08));pointer-events:none}.hce-card-content{position:relative;z-index:1;box-sizing:border-box;width:100%;max-height:100%;min-height:0;padding:18px 20px;display:flex;flex-direction:column;gap:10px;overflow:hidden;border:1px solid hsla(0,0%,100%,.56);border-radius:8px;background:hsla(0,0%,100%,.82);color:#111827;box-shadow:0 14px 42px rgba(15,23,42,.12),inset 0 1px 0 hsla(0,0%,100%,.52);backdrop-filter:blur(18px) saturate(1.16);-webkit-backdrop-filter:blur(18px) saturate(1.16)}@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){.hce-card-content{background:hsla(0,0%,100%,.9)}}.hce-card-header{flex:0 0 auto}.hce-card-title{margin:0;font-size:1.65rem;font-weight:700;line-height:1.18;color:#111827}.hce-card-title[hidden]{display:none}.hce-card-title-input{box-sizing:border-box;width:100%;padding:8px 12px;border:1px solid rgba(17,24,39,.12);border-radius:6px;background:hsla(0,0%,100%,.7);color:#111827;font:inherit;font-size:1.15rem;font-weight:700;outline:none}.hce-card-title-input:focus{border-color:var(--primary,#3382f2)}.hce-card-body{flex:1 1 auto;min-height:0;overflow:auto;font-size:1rem;line-height:1.55;color:#111827}.hce-card-body>p:first-child{margin-top:0}.hce-card-body>p:last-child{margin-bottom:0}.hce-card-bg-button{position:absolute;top:8px;right:8px;z-index:3;padding:4px 10px;border:1px solid rgba(17,24,39,.12);border-radius:6px;background:hsla(0,0%,100%,.86);color:#111827;font-size:.8125rem;line-height:1.4;cursor:pointer}.hce-card-bg-button:hover{background:#fff}@media (max-width:640px){.hce-card-frame{padding:18px}.hce-card-content{padding:14px 16px}.hce-card-title{font-size:1.35rem}}.hce-active{background-color:color-mix(in srgb,var(--primary) 12%,transparent);color:var(--primary)}.hce-active:hover{background-color:color-mix(in srgb,var(--primary) 18%,transparent)}.hover\:scale-105:hover{--tw-scale-x:1.05;--tw-scale-y:1.05}.hover\:scale-105:hover,.hover\:scale-110:hover{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:scale-110:hover{--tw-scale-x:1.1;--tw-scale-y:1.1}.hover\:border-ring:hover{border-color:var(--ring)}.hover\:bg-background:hover{background-color:var(--background)}.hover\:bg-muted:hover{background-color:var(--muted)}.hover\:text-destructive:hover{color:var(--destructive)}.hover\:text-foreground:hover{color:var(--foreground)}.hover\:text-red-300:hover{--tw-text-opacity:1;color:rgb(252 165 165/var(--tw-text-opacity,1))}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-90:hover{opacity:.9}.focus\:border-primary:focus{border-color:var(--primary)}.focus\:border-ring:focus{border-color:var(--ring)}.focus\:text-foreground:focus{color:var(--foreground)}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.disabled\:opacity-30:disabled{opacity:.3}
1
+ .pointer-events-none{pointer-events:none}.visible{visibility:visible}.collapse{visibility:collapse}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.bottom-0{bottom:0}.bottom-full{bottom:100%}.left-0{left:0}.left-1\/2{left:50%}.top-full{top:100%}.z-30{z-index:30}.z-50{z-index:50}.mx-0\.5{margin-left:.125rem;margin-right:.125rem}.mx-auto{margin-left:auto;margin-right:auto}.my-1{margin-top:.25rem;margin-bottom:.25rem}.my-2{margin-top:.5rem;margin-bottom:.5rem}.mb-1{margin-bottom:.25rem}.mb-1\.5{margin-bottom:.375rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.block{display:block}.inline{display:inline}.flex{display:flex}.\!table{display:table!important}.table{display:table}.grid{display:grid}.hidden{display:none}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-px{height:1px}.min-h-\[52px\]{min-height:52px}.w-10{width:2.5rem}.w-\[360px\]{width:360px}.w-\[420px\]{width:420px}.w-\[440px\]{width:440px}.w-fit{width:-moz-fit-content;width:fit-content}.w-full{width:100%}.w-px{width:1px}.min-w-\[96px\]{min-width:96px}.max-w-\[90vw\]{max-width:90vw}.flex-shrink{flex-shrink:1}.-translate-x-1\/2{--tw-translate-x:-50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.\!transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))!important}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.cursor-pointer{cursor:pointer}.resize-y{resize:vertical}.resize{resize:both}.columns-2{-moz-columns:2;column-count:2}.columns-3{-moz-columns:3;column-count:3}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.break-all{word-break:break-all}.rounded{border-radius:var(--radius-md,4px)}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:var(--radius-xl,8px)}.rounded-md{border-radius:var(--radius-md,6px)}.rounded-xl{border-radius:var(--radius-2xl,12px)}.rounded-t-xl{border-top-left-radius:var(--radius-2xl,12px);border-top-right-radius:var(--radius-2xl,12px)}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-t{border-top-width:1px}.border-border{border-color:var(--border)}.bg-background{background-color:var(--background)}.bg-black\/40{background-color:rgba(0,0,0,.4)}.bg-border{background-color:var(--border)}.bg-popover{background-color:var(--popover)}.bg-transparent{background-color:transparent}.p-0{padding:0}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-3\.5{padding-left:.875rem;padding-right:.875rem}.px-4{padding-left:1rem;padding-right:1rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.pt-2{padding-top:.5rem}.pt-3{padding-top:.75rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-\[11px\]{font-size:11px}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.italic{font-style:italic}.text-destructive{color:var(--destructive)}.text-muted-foreground{color:var(--muted-foreground)}.underline{text-decoration-line:underline}.opacity-30{opacity:.3}.shadow-lg{--tw-shadow:var(--shadow-dropdown,0 10px 15px -3px rgba(0,0,0,.1));--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color)}.shadow-lg,.shadow-xl{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow:var(--shadow-popup,0 20px 25px -5px rgba(0,0,0,.1));--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color)}.outline-none{outline:2px solid transparent;outline-offset:2px}.blur{--tw-blur:blur(8px)}.blur,.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.hce-editor-wrapper{overflow:visible!important}.hce-color-divider{border-top:1px solid var(--border)}.bubble-toolbar-container button,.hce-editor-wrapper button,.slash-menu button{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent;border:0;cursor:pointer;box-sizing:border-box}.bubble-toolbar-container button:disabled,.hce-editor-wrapper button:disabled,.slash-menu button:disabled{cursor:default}.bubble-toolbar-container .hce-btn-primary,.hce-editor-wrapper .hce-btn-primary,.slash-menu .hce-btn-primary{background-color:var(--primary);color:var(--primary-foreground,#fff)}.tiptap{font-size:1.0625rem;line-height:1.8;color:var(--foreground);padding:1.5rem}.tiptap>*+*{margin-top:.25em}.tiptap [data-type=column]>:first-child,.tiptap [data-type=detailsContent]>:first-child,.tiptap blockquote>:first-child,.tiptap li>:first-child,.tiptap td>:first-child,.tiptap th>:first-child,.tiptap>:first-child{margin-top:0}.tiptap [data-type=column]>:last-child,.tiptap [data-type=detailsContent]>:last-child,.tiptap blockquote>:last-child,.tiptap li>:last-child,.tiptap td>:last-child,.tiptap th>:last-child,.tiptap>:last-child{margin-bottom:0}.tiptap p{margin:.5em 0!important;line-height:1.5}.tiptap p:first-child{margin-top:0!important}.tiptap p:last-child{margin-bottom:0!important}.tiptap p:empty,.tiptap p:has(>br:only-child){min-height:1.5em}.tiptap h1,.tiptap h2,.tiptap h3,.tiptap li,.tiptap p{white-space:pre-wrap}.tiptap h1{font-size:2rem;font-weight:700;margin:1.5em 0 .5em;line-height:1.25}.tiptap h2{font-size:1.5rem;font-weight:600;margin:1.2em 0 .4em;line-height:1.3}.tiptap h3{font-size:1.25rem;font-weight:600;margin:1em 0 .3em;line-height:1.35}.tiptap ul{list-style:disc}.tiptap ol,.tiptap ul{padding-left:1.5em;margin:.5em 0}.tiptap ol{list-style:decimal}.tiptap ul ul{list-style:circle}.tiptap ul ul ul{list-style:square}.tiptap ol ol{list-style:lower-alpha}.tiptap ol ol ol{list-style:lower-roman}.tiptap li{margin:.4em 0;line-height:1.7}.tiptap li>p{margin:0!important;line-height:inherit!important}.tiptap ul[data-type=taskList]{list-style:none;padding-left:0}.tiptap ul[data-type=taskList] li{display:flex;align-items:baseline;gap:.5rem;margin:.3em 0;line-height:1.7;list-style:none;list-style-type:none}.tiptap ul[data-type=taskList] li>label{flex-shrink:0;display:flex;align-items:center;height:1.5em}.tiptap ul[data-type=taskList] li>label input[type=checkbox]{-moz-appearance:none;appearance:none;-webkit-appearance:none;width:1.1em;height:1.1em;margin:0;padding:0;cursor:pointer;background-color:var(--background,#fff);border:1.5px solid var(--border);border-radius:4px;transition:background-color .12s ease,border-color .12s ease,box-shadow .12s ease;display:inline-block;vertical-align:middle;flex-shrink:0}.tiptap ul[data-type=taskList] li>label input[type=checkbox]:hover{border-color:var(--muted-foreground)}.tiptap ul[data-type=taskList] li>label input[type=checkbox]:focus-visible{outline:none;box-shadow:0 0 0 2px var(--background,#fff),0 0 0 4px var(--primary)}.tiptap ul[data-type=taskList] li>label input[type=checkbox]:checked{background-color:var(--primary);border-color:var(--primary);background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2.5' viewBox='0 0 16 16'%3E%3Cpath d='m3.5 8.5 3 3 6-6.5'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:50%;background-size:100% 100%}.tiptap ul[data-type=taskList] li>div{flex:1}.tiptap ul[data-type=taskList] li[data-checked=true]>div{text-decoration:line-through;opacity:.6}.tiptap blockquote{border-left:3px solid var(--border);padding:.5rem 1rem;margin:.75em 0;color:var(--muted-foreground);font-style:italic}.tiptap hr{border:none;border-top:2px solid color-mix(in srgb,var(--foreground) 38%,transparent);margin:1.5em 0}.tiptap code{background:var(--muted);border-radius:4px;padding:.15em .35em;font-size:.9em}.tiptap pre{background:#282c34;color:#abb2bf;border-radius:8px;padding:1em;margin:.75em 0;overflow-x:auto;font-family:JetBrains Mono,Fira Code,Consolas,monospace;font-size:1em;line-height:1.6}.tiptap pre code{background:none;padding:0;border-radius:0;color:#abb2bf;font-size:inherit}.tiptap pre code .hljs-keyword,.tiptap pre code .hljs-operator{color:#c678dd}.tiptap pre code .hljs-addition,.tiptap pre code .hljs-doctag,.tiptap pre code .hljs-regexp,.tiptap pre code .hljs-string{color:#98c379}.tiptap pre code .hljs-attr,.tiptap pre code .hljs-number,.tiptap pre code .hljs-selector-class,.tiptap pre code .hljs-template-variable,.tiptap pre code .hljs-type,.tiptap pre code .hljs-variable{color:#d19a66}.tiptap pre code .hljs-function,.tiptap pre code .hljs-section,.tiptap pre code .hljs-title,.tiptap pre code .hljs-title.class_{color:#61aeee}.tiptap pre code .hljs-params,.tiptap pre code .hljs-punctuation{color:#abb2bf}.tiptap pre code .hljs-comment,.tiptap pre code .hljs-quote{color:#5c6370;font-style:italic}.tiptap pre code .hljs-built_in,.tiptap pre code .hljs-formula{color:#e6c07b}.tiptap pre code .hljs-meta,.tiptap pre code .hljs-meta .hljs-keyword{color:#61aeee}.tiptap pre code .hljs-meta .hljs-string{color:#98c379}.tiptap pre code .hljs-attribute,.tiptap pre code .hljs-deletion,.tiptap pre code .hljs-name,.tiptap pre code .hljs-property,.tiptap pre code .hljs-selector-tag,.tiptap pre code .hljs-subst,.tiptap pre code .hljs-tag{color:#e06c75}.tiptap pre code .hljs-literal{color:#56b6c2}.tiptap pre code .hljs-selector-attr,.tiptap pre code .hljs-selector-id,.tiptap pre code .hljs-selector-pseudo{color:#61aeee}.tiptap pre code .hljs-link{color:#61aeee;text-decoration:underline}.tiptap pre code .hljs-bullet,.tiptap pre code .hljs-symbol{color:#61aeee}.tiptap pre code .hljs-emphasis{font-style:italic}.tiptap pre code .hljs-strong{font-weight:700}.tiptap img{max-width:100%;height:auto;margin:.75em 0;cursor:pointer;transition:box-shadow .15s}.tiptap [data-type=pdfBlock].ProseMirror-selectednode>div,.tiptap img{border-radius:8px}.pdf-frame{position:relative;display:flex;align-items:center;justify-content:center;border:1px solid var(--border);border-radius:8px;background:var(--muted)}.pdf-frame canvas{display:block;max-width:100%;border-radius:7px}.pdf-overlay{position:absolute;inset:0;pointer-events:none;opacity:0;transition:opacity .15s ease;-moz-user-select:none;user-select:none;-webkit-user-select:none}.pdf-overlay .pdf-page-input{-moz-user-select:text;user-select:text;-webkit-user-select:text}.pdf-frame:hover .pdf-overlay,.pdf-overlay:focus-within,[data-type=pdfBlock].ProseMirror-selectednode .pdf-overlay{opacity:1}@media (hover:none){.pdf-overlay{opacity:1}}@media (prefers-reduced-motion:reduce){.pdf-overlay{transition:none}}.pdf-cluster{position:absolute;display:flex;align-items:center;gap:1px;padding:2px;border-radius:999px;background:rgba(24,24,27,.52);backdrop-filter:blur(12px) saturate(1.2);border:1px solid hsla(0,0%,100%,.1);box-shadow:0 2px 8px rgba(0,0,0,.18);color:#fff;pointer-events:auto;max-width:calc(100% - 16px);transition:background-color .15s ease}.pdf-cluster:hover{background:rgba(24,24,27,.72)}.pdf-cluster-end{top:6px;right:6px}.pdf-cluster-page{bottom:6px;left:50%;transform:translateX(-50%)}.pdf-nav-rail{position:sticky;top:0;height:100vh;max-height:100%;pointer-events:none}.pdf-nav{position:absolute;top:50%;transform:translateY(-50%);pointer-events:auto}.pdf-nav-prev{left:12px}.pdf-nav-next{right:12px}.pdf-overlay .pdf-ctl,.pdf-overlay .pdf-page,.pdf-overlay .pdf-preset{display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;border:0;background:transparent;color:hsla(0,0%,100%,.72);font-family:inherit;cursor:pointer;border-radius:999px;transition:background-color .12s,color .12s}.pdf-overlay .pdf-ctl{width:22px;height:22px;flex:none}.pdf-overlay .pdf-ctl svg{width:13px;height:13px}.pdf-overlay .pdf-ctl:hover,.pdf-overlay .pdf-page:hover,.pdf-overlay .pdf-preset:hover{background:hsla(0,0%,100%,.16);color:#fff}.pdf-overlay .pdf-ctl:disabled{opacity:.35;cursor:default;background:transparent}.pdf-overlay .pdf-ctl-danger:hover{background:hsla(0,91%,71%,.26);color:#fff}.pdf-overlay .pdf-ctl:focus-visible,.pdf-overlay .pdf-page-input:focus-visible,.pdf-overlay .pdf-page:focus-visible,.pdf-overlay .pdf-preset:focus-visible{outline:2px solid hsla(0,0%,100%,.8);outline-offset:1px}.pdf-overlay .pdf-preset{height:20px;padding:0 6px;font-size:10px;line-height:1;font-variant-numeric:tabular-nums;flex:none}.pdf-overlay .pdf-preset.is-active,.pdf-overlay .pdf-preset.is-active:hover{background:hsla(0,0%,100%,.22);color:#fff}.pdf-overlay .pdf-page{height:20px;padding:0 8px;font-size:11px;line-height:1;font-variant-numeric:tabular-nums}.pdf-overlay .pdf-nav{width:28px;height:28px;background:rgba(24,24,27,.52);border-color:hsla(0,0%,100%,.1);box-shadow:0 2px 8px rgba(0,0,0,.18)}.pdf-overlay .pdf-nav svg{width:15px;height:15px}.pdf-overlay .pdf-nav:hover{background:rgba(24,24,27,.78);color:#fff}.pdf-overlay .pdf-nav:disabled{opacity:.5;background:rgba(24,24,27,.38)}.pdf-divider{flex:none;width:1px;align-self:stretch;margin:2px;background:hsla(0,0%,100%,.18)}.pdf-overlay .pdf-page-input{box-sizing:border-box;height:20px;width:44px;padding:0 5px;font-size:11px;text-align:center;font-variant-numeric:tabular-nums;background:hsla(0,0%,100%,.14);border:1px solid hsla(0,0%,100%,.22);color:#fff;border-radius:999px;font-family:inherit;outline:none;transition:background-color .12s,border-color .12s}.pdf-overlay .pdf-page-input:focus{background:hsla(0,0%,100%,.22);border-color:hsla(0,0%,100%,.45)}.pdf-status{display:flex;align-items:center;justify-content:center;gap:8px;padding:2.5rem 1rem;font-size:.875rem;color:var(--muted-foreground);text-align:center}.tiptap a{color:var(--primary);text-decoration:underline;text-underline-offset:2px}.tiptap a:hover{opacity:.8}.tiptap mark{background-color:#fef08a;border-radius:2px;padding:.05em .1em}.tiptap [data-type=details]{border:none;border-radius:6px;padding:0;margin:.5em 0;background:transparent;display:grid;grid-template-columns:30px 1fr;align-items:start;transition:background .12s}.tiptap [data-type=details]:hover{background:var(--muted)}.tiptap [data-type=details]>button{background:none;border:none;cursor:pointer;padding:0;width:22px;height:22px;display:inline-flex;align-items:center;justify-content:center;color:var(--muted-foreground);border-radius:4px;transition:transform .2s,color .15s,background-color .15s;flex-shrink:0;margin-top:14px;margin-left:4px}.tiptap [data-type=details]>button:hover{background:color-mix(in srgb,var(--foreground) 8%,transparent)}.tiptap [data-type=details]>button:after{content:"";display:block;width:0;height:0;border-left:7px solid;border-top:5.5px solid transparent;border-bottom:5.5px solid transparent;margin-left:2px}.tiptap [data-type=details]:hover>button{color:var(--foreground)}.tiptap [data-type=details].is-open>button{transform:rotate(90deg)}.tiptap [data-type=details]>div{flex:1;min-width:0}.tiptap [data-type=details] summary{font-weight:600;cursor:pointer;padding:10px 8px 10px 0;display:block;border-bottom:1px solid transparent;transition:border-color .15s}.tiptap [data-type=details].is-open>div>summary{border-bottom-color:var(--border,#e5e7eb)}.tiptap [data-type=details] summary[data-level="1"],.tiptap details summary[data-level="1"]{font-size:2rem;font-weight:700;line-height:1.25}.tiptap [data-type=details] summary[data-level="2"],.tiptap details summary[data-level="2"]{font-size:1.5rem;font-weight:600;line-height:1.3}.tiptap [data-type=details] summary[data-level="3"],.tiptap details summary[data-level="3"]{font-size:1.25rem;font-weight:600;line-height:1.35}.tiptap [data-type=details]:has(>div>summary[data-level="1"])>button{margin-top:19px}.tiptap [data-type=details]:has(>div>summary[data-level="2"])>button{margin-top:15px}.tiptap [data-type=details]:has(>div>summary[data-level="3"])>button{margin-top:13px}.tiptap [data-type=detailsContent]{padding:8px 8px 8px 0;border-left:none}.tiptap [data-type=detailsContent][hidden]{display:none}.tiptap details{border:none;border-radius:6px;padding:0;margin:.5em 0;background:transparent;transition:background .12s}.tiptap details:hover{background:var(--muted)}.tiptap details summary{font-weight:600;cursor:pointer;padding:10px 8px 10px 26px;-webkit-user-select:none;-moz-user-select:none;user-select:none;border-bottom:1px solid transparent;position:relative;list-style:none;transition:border-color .15s}.tiptap details[open]>summary{border-bottom-color:var(--border,#e5e7eb)}.tiptap details summary::-webkit-details-marker{display:none}.tiptap details summary::marker{content:""}.tiptap details summary:before{content:"";position:absolute;left:8px;top:50%;width:0;height:0;border-left:7px solid var(--muted-foreground);border-top:5.5px solid transparent;border-bottom:5.5px solid transparent;transform:translateY(-50%);transition:transform .2s,border-left-color .15s}.tiptap details:hover summary:before{border-left-color:var(--foreground)}.tiptap details[open]>summary:before{transform:translateY(-50%) rotate(90deg)}.tiptap details[open]>:not(summary){margin-top:8px;padding-left:26px;padding-right:8px}.tiptap table{border-collapse:collapse;margin:1em 0;table-layout:fixed;border:2px solid var(--border);max-width:100%}.tiptap .tableWrapper{overflow-x:auto;max-width:100%}.tiptap td,.tiptap th{border:1px solid var(--border);padding:.6em .8em;min-width:60px;vertical-align:middle;position:relative;box-sizing:border-box;overflow:hidden;word-break:break-word;line-height:1.5}.tiptap th{background:var(--muted);font-weight:600;text-align:left}.tiptap td{background:var(--background)}.tiptap .selectedCell:after{content:"";position:absolute;inset:0;background:var(--primary);opacity:.12;pointer-events:none;z-index:1}.tiptap .column-resize-handle{position:absolute;right:-3px;top:-1px;bottom:-1px;width:6px;background:var(--primary);opacity:0;cursor:col-resize;z-index:20;border-radius:2px;transition:opacity .15s}.tiptap table:hover .column-resize-handle{opacity:.4}.tiptap .column-resize-handle:hover{opacity:1}.tiptap.resize-cursor{cursor:col-resize}.tableWrapper{overflow-x:auto;overflow-y:hidden;margin:1em 0;max-width:100%;padding-right:4px}.tiptap table .grip-column,.tiptap table .grip-row,.tiptap table .grip-table{display:none!important}.tiptap .is-empty:before{content:attr(data-placeholder);float:left;color:var(--muted-foreground);opacity:.5;pointer-events:none;height:0}.tiptap h1.is-empty:before{font-size:2rem;font-weight:700}.tiptap h2.is-empty:before{font-size:1.5rem;font-weight:600}.tiptap h3.is-empty:before{font-size:1.25rem;font-weight:600}.ProseMirror-gapcursor{display:none;pointer-events:none;position:relative}.ProseMirror-gapcursor:after{content:"";display:block;position:absolute;top:-2px;width:20px;border-top:1px solid var(--foreground);animation:ProseMirror-cursor-blink 1.1s steps(2,start) infinite}@keyframes ProseMirror-cursor-blink{to{visibility:hidden}}.ProseMirror-focused .ProseMirror-gapcursor{display:block}.ProseMirror-dropcursor{border-top:2px solid var(--primary);margin:-1px 0}.tiptap ::-moz-selection{background-color:color-mix(in srgb,var(--primary) 15%,transparent)}.tiptap ::selection{background-color:color-mix(in srgb,var(--primary) 15%,transparent)}.ProseMirror-noderangeselection ::-moz-selection{background:transparent}.ProseMirror-noderangeselection ::selection{background:transparent}.ProseMirror-noderangeselection *{caret-color:transparent}.ProseMirror-selectednode,.ProseMirror-selectednoderange{position:relative}.ProseMirror-selectednode:not(img):not([data-type=pdfBlock]):before,.ProseMirror-selectednoderange:not([data-type=pdfBlock]):before{content:"";position:absolute;inset:-.25rem;background-color:color-mix(in srgb,var(--primary) 10%,transparent);border:2px solid color-mix(in srgb,var(--primary) 30%,transparent);border-radius:.5rem;pointer-events:none;z-index:-1}.hce-table-bubble{z-index:var(--hce-menu-z,100)}.bubble-toolbar-container{position:absolute;top:0;left:0;width:-moz-max-content;width:max-content;z-index:calc(var(--hce-menu-z, 100) + 1)}.hce-toolbar:has(.absolute)~.bubble-toolbar-container,.hce-toolbar:has(.absolute)~.hce-table-bubble{display:none}.hce-menu-surface{background-color:var(--hce-menu-surface,#333)}.hce-floating-panel{background-color:color-mix(in srgb,var(--foreground) 8%,var(--popover,var(--background,#fff)));border:1px solid color-mix(in srgb,var(--foreground) 18%,var(--border));box-shadow:0 8px 24px color-mix(in srgb,var(--foreground) 16%,transparent),0 2px 6px color-mix(in srgb,var(--foreground) 10%,transparent)}.hce-floating-plus{display:flex;align-items:center;justify-content:center;width:24px;height:24px;border-radius:6px;border:1px solid var(--border);background:var(--background);color:var(--muted-foreground);cursor:pointer;transition:all .15s}.hce-floating-plus:hover{color:var(--foreground);border-color:var(--foreground);background:var(--muted)}.hce-drag-handle{display:flex;align-items:center;gap:2px}.hce-drag-handle__plus{display:flex;align-items:center;justify-content:center;padding:4px;border-radius:4px;border:none;background:transparent;color:var(--muted-foreground);opacity:.5;cursor:pointer;transition:opacity .15s,background-color .15s}.hce-drag-handle__plus:hover{opacity:1;background:var(--muted)}.hce-drag-handle__grip{display:flex;align-items:center;justify-content:center;padding:2px;border-radius:4px;color:var(--muted-foreground);opacity:.5;cursor:grab;transition:opacity .15s,background-color .15s}.hce-drag-handle__grip:hover{opacity:1;background:var(--muted)}.hce-drag-handle__grip:active{cursor:grabbing}.tiptap div[data-type=columns]{display:grid;gap:16px;margin:1em 0}.tiptap div[data-type=columns][data-columns="2"]{grid-template-columns:1fr 1fr}.tiptap div[data-type=columns][data-columns="3"]{grid-template-columns:1fr 1fr 1fr}.tiptap div[data-type=column]{min-width:0;padding:4px 6px;border:1px dashed var(--border);border-radius:8px}.tiptap[contenteditable=true] div[data-type=column]:focus-within{border-color:var(--primary);border-style:solid}.tiptap div[data-type=columns].ProseMirror-selectednode{outline:2px solid color-mix(in srgb,var(--primary) 30%,transparent);border-radius:8px}@media (max-width:640px){.tiptap div[data-type=columns]{grid-template-columns:1fr!important}}.hce-table-btn-wrap{position:relative;display:inline-flex}.hce-table-btn-tooltip{position:absolute;bottom:100%;left:50%;transform:translateX(-50%);margin-bottom:6px;padding:4px 8px;font-size:11px;line-height:1.2;white-space:nowrap;color:#fff;background:#1e293b;border-radius:6px;pointer-events:none;opacity:0;transition:opacity .15s;z-index:60}.hce-table-btn-wrap:hover .hce-table-btn-tooltip{opacity:1}.hce-table-btn-wrap.hce-tooltip-off:hover .hce-table-btn-tooltip{opacity:0}.tiptap math-inline,math-inline.math-node{display:inline-block;vertical-align:baseline}.tiptap math-display,math-display.math-node{display:block;margin:.9em 0;text-align:center}.math-node,.tiptap .math-node{font-size:.95em}.tiptap math-inline.math-node .katex,math-inline.math-node .katex{font-size:1.0285em}.tiptap[contenteditable=true] .math-node{cursor:pointer;border-radius:4px;transition:background-color .12s}.tiptap[contenteditable=true] .math-node:hover{background-color:color-mix(in srgb,var(--primary) 10%,transparent)}.tiptap .math-node.ProseMirror-selectednode{background-color:color-mix(in srgb,var(--primary) 18%,transparent);outline:1px solid color-mix(in srgb,var(--primary) 45%,transparent)}.tiptap .math-node.empty-math .math-placeholder{font-size:.85em;padding:0 .35em;color:var(--muted-foreground);border:1px dashed var(--border);border-radius:4px}.math-node .math-error,.tiptap .math-error{color:#dc2626;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.9em}.hce-card-frame,.tiptap [data-type=card]{position:relative}.hce-card-frame{box-sizing:border-box;width:100%;margin:12px 0;padding:26px 30px;display:flex;flex-direction:column;justify-content:center;gap:12px;overflow:hidden;border:1px solid hsla(0,0%,100%,.52);border-radius:8px;box-shadow:inset 0 0 0 1px hsla(0,0%,100%,.28),0 8px 24px rgba(15,23,42,.08);overflow-wrap:anywhere;isolation:isolate}.hce-card-frame:before{content:"";position:absolute;inset:0;z-index:0;background:linear-gradient(90deg,hsla(0,0%,100%,.14),hsla(0,0%,100%,.04) 48%,rgba(15,23,42,.08));pointer-events:none}.hce-card-content{position:relative;z-index:1;box-sizing:border-box;width:100%;max-height:100%;min-height:0;padding:18px 20px;display:flex;flex-direction:column;gap:10px;overflow:hidden;border:1px solid hsla(0,0%,100%,.56);border-radius:8px;background:hsla(0,0%,100%,.82);color:#111827;box-shadow:0 14px 42px rgba(15,23,42,.12),inset 0 1px 0 hsla(0,0%,100%,.52);backdrop-filter:blur(18px) saturate(1.16);-webkit-backdrop-filter:blur(18px) saturate(1.16)}@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){.hce-card-content{background:hsla(0,0%,100%,.9)}}.hce-card-header{flex:0 0 auto}.hce-card-title{margin:0;font-size:1.65rem;font-weight:700;line-height:1.18;color:#111827}.hce-card-title[hidden]{display:none}.hce-card-title-input{box-sizing:border-box;width:100%;padding:8px 12px;border:1px solid rgba(17,24,39,.12);border-radius:6px;background:hsla(0,0%,100%,.7);color:#111827;font:inherit;font-size:1.15rem;font-weight:700;outline:none}.hce-card-title-input:focus{border-color:var(--primary,#3382f2)}.hce-card-body{flex:1 1 auto;min-height:0;overflow:auto;font-size:1rem;line-height:1.55;color:#111827}.hce-card-body>p:first-child{margin-top:0}.hce-card-body>p:last-child{margin-bottom:0}.hce-card-bg-button{position:absolute;top:8px;right:8px;z-index:3;padding:4px 10px;border:1px solid rgba(17,24,39,.12);border-radius:6px;background:hsla(0,0%,100%,.86);color:#111827;font-size:.8125rem;line-height:1.4;cursor:pointer}.hce-card-bg-button:hover{background:#fff}@media (max-width:640px){.hce-card-frame{padding:18px}.hce-card-content{padding:14px 16px}.hce-card-title{font-size:1.35rem}}.hce-active{background-color:color-mix(in srgb,var(--primary) 12%,transparent);color:var(--primary)}.hce-active:hover{background-color:color-mix(in srgb,var(--primary) 18%,transparent)}.hover\:scale-105:hover{--tw-scale-x:1.05;--tw-scale-y:1.05}.hover\:scale-105:hover,.hover\:scale-110:hover{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:scale-110:hover{--tw-scale-x:1.1;--tw-scale-y:1.1}.hover\:bg-muted:hover{background-color:var(--muted)}.hover\:text-foreground:hover{color:var(--foreground)}.hover\:text-red-300:hover{--tw-text-opacity:1;color:rgb(252 165 165/var(--tw-text-opacity,1))}.hover\:opacity-90:hover{opacity:.9}.focus\:border-primary:focus{border-color:var(--primary)}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}
package/dist/types.d.ts CHANGED
@@ -73,12 +73,6 @@ export interface SlashCommandMenuProps {
73
73
  onClose: () => void;
74
74
  onPdfUpload?: () => void;
75
75
  }
76
- export interface PdfViewerProps {
77
- src: string;
78
- fileName?: string;
79
- /** 헤더에 보여줄 이름. 없으면 `fileName` 을 쓴다. */
80
- label?: string;
81
- }
82
76
  export interface SlashMenuItem {
83
77
  label: string;
84
78
  keywords: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD,+BAA+B;AAC/B,MAAM,MAAM,aAAa,GAAG,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAE5D;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AAE7E,YAAY;AACZ,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,CAAC;AAE1D,kBAAkB;AAClB,MAAM,MAAM,cAAc,GAEvB,MAAM,GAAG,QAAQ,GAAG,WAAW,GAAG,QAAQ,GAAG,WAAW,GACxD,aAAa,GAAG,WAAW,GAAG,MAAM,GAAG,YAAY,GAEnD,YAAY,GAAG,cAAc,GAAG,aAAa,GAE7C,WAAW,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAEhC,aAAa,GAAG,cAAc,GAAG,WAAW,GAC5C,YAAY,GAAG,iBAAiB,GAAG,QAAQ,GAE3C,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAE7D,WAAW,GAAG,WAAW,GAAG,OAAO,GAAG,YAAY,GAAG,MAAM,GAE3D,MAAM,GAAG,MAAM,GAEf,eAAe,GAAG,gBAAgB,GAAG,YAAY,GACjD,YAAY,GAAG,iBAAiB,GAAG,gBAAgB,CAAC;AAEvD;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,WAAW,EAAE,cAAc,EAAE,CAwCjE,CAAC;AAEF,mEAAmE;AACnE,wBAAgB,eAAe,CAC9B,OAAO,EAAE,WAAW,EACpB,QAAQ,CAAC,EAAE,cAAc,EAAE,GACzB,GAAG,CAAC,cAAc,CAAC,CAErB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uCAAuC;IACvC,YAAY,CAAC,EAAE,aAAa,CAAC;IAC7B,uCAAuC;IACvC,aAAa,CAAC,EAAE,YAAY,CAAC;IAC7B,yFAAyF;IACzF,YAAY,CAAC,EAAE,aAAa,CAAC;IAC7B;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,oDAAoD;IACpD,YAAY,CAAC,EAAE,UAAU,CAAC;IAC1B,0DAA0D;IAC1D,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,oDAAoD;IACpD,QAAQ,CAAC,EAAE,cAAc,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uCAAuC;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,SAAS,CAAC;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnC,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD,+BAA+B;AAC/B,MAAM,MAAM,aAAa,GAAG,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAE5D;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AAE7E,YAAY;AACZ,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,CAAC;AAE1D,kBAAkB;AAClB,MAAM,MAAM,cAAc,GAEvB,MAAM,GAAG,QAAQ,GAAG,WAAW,GAAG,QAAQ,GAAG,WAAW,GACxD,aAAa,GAAG,WAAW,GAAG,MAAM,GAAG,YAAY,GAEnD,YAAY,GAAG,cAAc,GAAG,aAAa,GAE7C,WAAW,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAEhC,aAAa,GAAG,cAAc,GAAG,WAAW,GAC5C,YAAY,GAAG,iBAAiB,GAAG,QAAQ,GAE3C,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAE7D,WAAW,GAAG,WAAW,GAAG,OAAO,GAAG,YAAY,GAAG,MAAM,GAE3D,MAAM,GAAG,MAAM,GAEf,eAAe,GAAG,gBAAgB,GAAG,YAAY,GACjD,YAAY,GAAG,iBAAiB,GAAG,gBAAgB,CAAC;AAEvD;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,WAAW,EAAE,cAAc,EAAE,CAwCjE,CAAC;AAEF,mEAAmE;AACnE,wBAAgB,eAAe,CAC9B,OAAO,EAAE,WAAW,EACpB,QAAQ,CAAC,EAAE,cAAc,EAAE,GACzB,GAAG,CAAC,cAAc,CAAC,CAErB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uCAAuC;IACvC,YAAY,CAAC,EAAE,aAAa,CAAC;IAC7B,uCAAuC;IACvC,aAAa,CAAC,EAAE,YAAY,CAAC;IAC7B,yFAAyF;IACzF,YAAY,CAAC,EAAE,aAAa,CAAC;IAC7B;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,oDAAoD;IACpD,YAAY,CAAC,EAAE,UAAU,CAAC;IAC1B,0DAA0D;IAC1D,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,oDAAoD;IACpD,QAAQ,CAAC,EAAE,cAAc,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,SAAS,CAAC;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnC,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC"}
@@ -1 +1 @@
1
- {"version":3,"file":"resize.d.ts","sourceRoot":"","sources":["../../src/utils/resize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,IAAI,IAAI,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEhE;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,MAAM,UAAU,GAAG,GAAG,GAAG,GAAG,CAAC;AAEnC,MAAM,WAAW,mBAAmB;IACnC,uCAAuC;IACvC,GAAG,EAAE,WAAW,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IACjC,mDAAmD;IACnD,OAAO,EAAE,MAAM,eAAe,CAAC;IAC/B,qCAAqC;IACrC,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,sDAAsD;IACtD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,kCAAkC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;CACnC;AAUD,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,MAAM,IAAI,CAiIrE"}
1
+ {"version":3,"file":"resize.d.ts","sourceRoot":"","sources":["../../src/utils/resize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,IAAI,IAAI,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEhE;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,MAAM,UAAU,GAAG,GAAG,GAAG,GAAG,CAAC;AAEnC,MAAM,WAAW,mBAAmB;IACnC,uCAAuC;IACvC,GAAG,EAAE,WAAW,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IACjC,mDAAmD;IACnD,OAAO,EAAE,MAAM,eAAe,CAAC;IAC/B,qCAAqC;IACrC,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,sDAAsD;IACtD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,kCAAkC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;CACnC;AA4BD,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,MAAM,IAAI,CAiIrE"}
@@ -2,7 +2,24 @@ const DEFAULTS = {
2
2
  x: { min: 240, max: 1600, label: "너비 조절", cursor: "ew-resize" },
3
3
  y: { min: 120, max: 900, label: "높이 조절", cursor: "ns-resize" }
4
4
  };
5
- const IDLE = "var(--border, #d1d5db)";
5
+ /*
6
+ * ⚠️ **손잡이 색에 `--border` 를 그대로 쓰면 안 된다.** 그건 **구분선** 색이라 눈에
7
+ * 안 띄게 맞춰 둔 값이다. 다크에서 정올 실측 `#323232` 가 카드(`#1a1a1a`) 위 대비
8
+ * **1.36** — 3px 짜리 막대가 배경에 그냥 묻혔다(사용자 지적: 잘 안 보인다).
9
+ *
10
+ * 이건 선이 아니라 **잡아 끄는 물건**이라 찾을 수 있어야 한다. 글자색을 섞어 면에서
11
+ * 떼어낸다 — 라이트에서는 어두워지고 다크에서는 밝아지므로 **어느 테마에서든
12
+ * "배경에서 떨어져 나오는" 방향**이 된다(떠 있는 판 `.hce-floating-panel` 과 같은 축).
13
+ *
14
+ * 비율 45% 는 **양쪽 테마가 동시에 3:1(비문자 UI 기준)을 넘는 지점**이다. 40% 로는
15
+ * 라이트가 못 넘는다 — 실측 `#979799` 가 흰 바탕 위 **2.93**. 정올 실측 대비:
16
+ * 라이트 1.06 → 3.4, 다크 1.36 → 4.0.
17
+ *
18
+ * ⚠️ 라이트 값을 잴 때 **화면에서 테마를 토글한 직후를 믿지 말 것.** 자동화 탭에서
19
+ * `data-theme` 만 바뀌고 손잡이는 다크 값을 그대로 들고 있었다(측정 4.81 → 새로 연
20
+ * 탭에서는 2.93). 테마별 측정은 **새 탭을 열어** 한다.
21
+ */
22
+ const IDLE = "color-mix(in srgb, var(--foreground, #111827) 45%, var(--border, #d1d5db))";
6
23
  const ACTIVE = "var(--primary, #3382f2)";
7
24
  export function attachResize(options) {
8
25
  const { dom, editor, getPos, getNode, axis = "x", attr = axis === "x" ? "width" : "height", format = (v) => `${Math.round(v)}px` } = options;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teriusu/rich-editor",
3
- "version": "0.9.8",
3
+ "version": "0.9.9",
4
4
  "description": "TipTap 기반 Notion-style 리치 텍스트 에디터 (Svelte)",
5
5
  "type": "module",
6
6
  "svelte": "./dist/index.js",
@@ -1,160 +0,0 @@
1
- <script lang="ts">
2
- import { onMount } from "svelte";
3
- import { ChevronLeft, ChevronRight } from "lucide-svelte";
4
- import { getPdfJs } from "../utils/pdf";
5
-
6
- let {
7
- src,
8
- fileName,
9
- label
10
- }: { src: string; fileName?: string; label?: string } = $props();
11
-
12
- const cleanName = (raw?: string) =>
13
- (raw || "").replace(/[?#].*$/, "").trim() || "PDF 문서";
14
-
15
- /** `label` 이 있으면 그것, 없으면 파일명. */
16
- const displayName = $derived(label?.trim() || cleanName(fileName));
17
-
18
- let canvasEl: HTMLCanvasElement | undefined = $state();
19
- let containerEl: HTMLDivElement | undefined = $state();
20
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
21
- let pdf: any = $state(null);
22
- let page = $state(1);
23
- let total = $state(0);
24
- let loading = $state(true);
25
- let error = $state(false);
26
- let rendering = false;
27
-
28
- async function renderPage() {
29
- if (!pdf || !canvasEl || !containerEl || rendering) return;
30
- rendering = true;
31
- try {
32
- const pageObj = await pdf.getPage(page);
33
- const unscaledViewport = pageObj.getViewport({ scale: 1 });
34
- const availableWidth = containerEl.clientWidth;
35
- if (availableWidth <= 0) return;
36
- const scale = availableWidth / unscaledViewport.width;
37
- const viewport = pageObj.getViewport({ scale });
38
- const dpr = window.devicePixelRatio || 1;
39
- canvasEl.width = viewport.width * dpr;
40
- canvasEl.height = viewport.height * dpr;
41
- canvasEl.style.width = `${viewport.width}px`;
42
- canvasEl.style.height = `${viewport.height}px`;
43
- const ctx = canvasEl.getContext("2d");
44
- if (!ctx) return;
45
- ctx.resetTransform();
46
- ctx.clearRect(0, 0, canvasEl.width, canvasEl.height);
47
- ctx.scale(dpr, dpr);
48
- await pageObj.render({ canvasContext: ctx, viewport }).promise;
49
- } finally {
50
- rendering = false;
51
- }
52
- }
53
-
54
- $effect(() => {
55
- if (pdf && page) renderPage();
56
- });
57
-
58
- onMount(() => {
59
- let cancelled = false;
60
-
61
- (async () => {
62
- try {
63
- loading = true;
64
- error = false;
65
- const pdfjsLib = await getPdfJs();
66
- const loadingTask = pdfjsLib.getDocument(src);
67
- const pdfDoc = await loadingTask.promise;
68
- if (!cancelled) {
69
- pdf = pdfDoc;
70
- total = pdfDoc.numPages;
71
- loading = false;
72
- }
73
- } catch (e) {
74
- console.error("[PdfViewer] PDF 로드 실패:", src, e);
75
- if (!cancelled) {
76
- error = true;
77
- loading = false;
78
- }
79
- }
80
- })();
81
-
82
- // ResizeObserver
83
- let timeout: ReturnType<typeof setTimeout>;
84
- const observer = new ResizeObserver(() => {
85
- clearTimeout(timeout);
86
- timeout = setTimeout(renderPage, 100);
87
- });
88
- if (containerEl) observer.observe(containerEl);
89
-
90
- return () => {
91
- cancelled = true;
92
- observer.disconnect();
93
- clearTimeout(timeout);
94
- };
95
- });
96
- </script>
97
-
98
- {#if error}
99
- <div class="border border-border rounded-lg p-4 text-center">
100
- <p class="text-sm text-muted-foreground">PDF를 불러올 수 없습니다.</p>
101
- <a
102
- href={src}
103
- target="_blank"
104
- rel="noopener noreferrer"
105
- class="text-sm text-primary hover:underline"
106
- >
107
- 직접 다운로드
108
- </a>
109
- </div>
110
- {:else if loading}
111
- <div class="border border-border rounded-lg p-8 text-center">
112
- <p class="text-sm text-muted-foreground animate-pulse">PDF 로딩 중...</p>
113
- </div>
114
- {:else}
115
- <div class="border border-border rounded-lg overflow-hidden my-2">
116
- <div class="flex items-center gap-2 px-3 py-1 border-b border-border bg-muted">
117
- <span class="text-xs text-muted-foreground truncate min-w-0">
118
- {displayName}
119
- </span>
120
- <span class="text-xs text-muted-foreground tabular-nums ml-auto shrink-0">
121
- {page} / {total}
122
- </span>
123
- </div>
124
-
125
- <div
126
- bind:this={containerEl}
127
- class="flex items-center justify-center"
128
- >
129
- <canvas bind:this={canvasEl} class="shadow-sm"></canvas>
130
- </div>
131
-
132
- {#if total > 1}
133
- <div
134
- class="flex items-center justify-center gap-4 px-3 py-1 border-t border-border bg-muted"
135
- >
136
- <button
137
- type="button"
138
- onclick={() => (page = Math.max(1, page - 1))}
139
- disabled={page <= 1}
140
- class="p-1.5 rounded hover:bg-muted transition-colors disabled:opacity-30"
141
- title="이전 페이지"
142
- >
143
- <ChevronLeft class="w-4 h-4" />
144
- </button>
145
- <span class="text-sm tabular-nums">
146
- {page} / {total}
147
- </span>
148
- <button
149
- type="button"
150
- onclick={() => (page = Math.min(total, page + 1))}
151
- disabled={page >= total}
152
- class="p-1.5 rounded hover:bg-muted transition-colors disabled:opacity-30"
153
- title="다음 페이지"
154
- >
155
- <ChevronRight class="w-4 h-4" />
156
- </button>
157
- </div>
158
- {/if}
159
- </div>
160
- {/if}
@@ -1,9 +0,0 @@
1
- type $$ComponentProps = {
2
- src: string;
3
- fileName?: string;
4
- label?: string;
5
- };
6
- declare const PdfViewer: import("svelte").Component<$$ComponentProps, {}, "">;
7
- type PdfViewer = ReturnType<typeof PdfViewer>;
8
- export default PdfViewer;
9
- //# sourceMappingURL=PdfViewer.svelte.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"PdfViewer.svelte.d.ts","sourceRoot":"","sources":["../../src/components/PdfViewer.svelte.ts"],"names":[],"mappings":"AAOC,KAAK,gBAAgB,GAAI;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AA8I7E,QAAA,MAAM,SAAS,sDAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}