@teriusu/rich-editor 0.5.0 → 0.5.2

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.
@@ -229,13 +229,19 @@
229
229
  uploading = true;
230
230
  const size = file.size;
231
231
  onUploadFile(file)
232
- .then((url) => {
232
+ .then((result) => {
233
+ const isFileId = result && !result.includes("/") && !result.includes(":");
233
234
  editor!
234
235
  .chain()
235
236
  .focus()
236
237
  .insertContent({
237
238
  type: "fileAttachment",
238
- attrs: { src: url, name: file.name, size },
239
+ attrs: {
240
+ src: isFileId ? null : result,
241
+ fileId: isFileId ? result : null,
242
+ name: file.name,
243
+ size,
244
+ },
239
245
  })
240
246
  .run();
241
247
  })
@@ -486,7 +492,10 @@
486
492
 
487
493
  // File resolver를 storage에 등록
488
494
  if (onResolveFile) {
489
- editor.storage.fileAttachment = { resolver: onResolveFile };
495
+ editor.storage.fileAttachment = {
496
+ ...editor.storage.fileAttachment,
497
+ resolver: onResolveFile,
498
+ };
490
499
  }
491
500
 
492
501
  editor.on("update", handleUpdate);
@@ -1 +1 @@
1
- {"version":3,"file":"TipTapEditor.svelte.d.ts","sourceRoot":"","sources":["../../src/components/TipTapEditor.svelte.ts"],"names":[],"mappings":"AAgCA,OAAO,EAAa,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AAa5D,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAEhE,KAAK,gBAAgB,GAAI;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,aAAa,CAAC;IAC7B,aAAa,CAAC,EAAE,YAAY,CAAC;IAC7B,UAAU,CAAC,EAAE,YAAY,EAAE,CAAC;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,QAAQ,CAAC,EAAE,cAAc,EAAE,CAAC;CAC7B,CAAC;AAkmBJ,QAAA,MAAM,YAAY,sDAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"TipTapEditor.svelte.d.ts","sourceRoot":"","sources":["../../src/components/TipTapEditor.svelte.ts"],"names":[],"mappings":"AAgCA,OAAO,EAAa,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AAa5D,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAEhE,KAAK,gBAAgB,GAAI;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,aAAa,CAAC;IAC7B,aAAa,CAAC,EAAE,YAAY,CAAC;IAC7B,UAAU,CAAC,EAAE,YAAY,EAAE,CAAC;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,QAAQ,CAAC,EAAE,cAAc,EAAE,CAAC;CAC7B,CAAC;AA2mBJ,QAAA,MAAM,YAAY,sDAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"FileAttachment.d.ts","sourceRoot":"","sources":["../../src/extensions/FileAttachment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAmB,MAAM,cAAc,CAAC;AAErD,MAAM,WAAW,qBAAqB;IACpC,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACzC;AAED,MAAM,MAAM,iBAAiB,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAC9E,MAAM,MAAM,YAAY,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAE1E,OAAO,QAAQ,cAAc,CAAC;IAC5B,UAAU,QAAQ,CAAC,UAAU;QAC3B,cAAc,EAAE;YACd,iBAAiB,EAAE,CAAC,KAAK,EAAE;gBACzB,GAAG,CAAC,EAAE,MAAM,CAAC;gBACb,MAAM,CAAC,EAAE,MAAM,CAAC;gBAChB,IAAI,EAAE,MAAM,CAAC;gBACb,IAAI,CAAC,EAAE,MAAM,CAAC;aACf,KAAK,UAAU,CAAC;SAClB,CAAC;KACH;CACF;AA6BD,eAAO,MAAM,cAAc,kCAkMzB,CAAC"}
1
+ {"version":3,"file":"FileAttachment.d.ts","sourceRoot":"","sources":["../../src/extensions/FileAttachment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAmB,MAAM,cAAc,CAAC;AAErD,MAAM,WAAW,qBAAqB;IACpC,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACzC;AAED,MAAM,MAAM,iBAAiB,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAC9E,MAAM,MAAM,YAAY,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAE1E,OAAO,QAAQ,cAAc,CAAC;IAC5B,UAAU,QAAQ,CAAC,UAAU;QAC3B,cAAc,EAAE;YACd,iBAAiB,EAAE,CAAC,KAAK,EAAE;gBACzB,GAAG,CAAC,EAAE,MAAM,CAAC;gBACb,MAAM,CAAC,EAAE,MAAM,CAAC;gBAChB,IAAI,EAAE,MAAM,CAAC;gBACb,IAAI,CAAC,EAAE,MAAM,CAAC;aACf,KAAK,UAAU,CAAC;SAClB,CAAC;KACH;CACF;AAkCD,eAAO,MAAM,cAAc,kCAmNzB,CAAC"}
@@ -6,6 +6,10 @@ function formatFileSize(bytes) {
6
6
  return `${(bytes / 1024).toFixed(1)} KB`;
7
7
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
8
8
  }
9
+ function isInlineable(name) {
10
+ const ext = name.split(".").pop()?.toLowerCase() || "";
11
+ return ["pdf", "png", "jpg", "jpeg", "gif", "webp", "svg"].includes(ext);
12
+ }
9
13
  function getFileIcon(name) {
10
14
  const ext = name.split(".").pop()?.toLowerCase() || "";
11
15
  const icons = {
@@ -116,22 +120,36 @@ export const FileAttachment = Node.create({
116
120
  "display:block;font-size:13px;font-weight:600;color:var(--primary, #4A7DAC);text-decoration:none;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;cursor:pointer;";
117
121
  let resolvedSrc = null;
118
122
  let resolvedName = node.attrs.name;
119
- function triggerDownload(e) {
123
+ function getProxyUrl() {
124
+ const fileId = node.attrs.fileId;
125
+ if (!fileId)
126
+ return null;
127
+ const baseUrl = editor.storage.fileAttachment?.downloadBaseUrl || "/api/upload";
128
+ return `${baseUrl}/${fileId}/download`;
129
+ }
130
+ function handleClick(e) {
120
131
  e.preventDefault();
121
- if (!resolvedSrc)
132
+ const proxyUrl = getProxyUrl();
133
+ const targetUrl = proxyUrl || resolvedSrc;
134
+ if (!targetUrl)
122
135
  return;
123
- fetch(resolvedSrc)
124
- .then((res) => res.blob())
125
- .then((blob) => {
126
- const url = URL.createObjectURL(blob);
127
- const a = document.createElement("a");
128
- a.href = url;
129
- a.download = resolvedName;
130
- a.click();
131
- URL.revokeObjectURL(url);
132
- });
136
+ if (isInlineable(resolvedName)) {
137
+ window.open(targetUrl, "_blank");
138
+ }
139
+ else {
140
+ fetch(targetUrl)
141
+ .then((res) => res.blob())
142
+ .then((blob) => {
143
+ const url = URL.createObjectURL(blob);
144
+ const a = document.createElement("a");
145
+ a.href = url;
146
+ a.download = resolvedName;
147
+ a.click();
148
+ URL.revokeObjectURL(url);
149
+ });
150
+ }
133
151
  }
134
- nameEl.addEventListener("click", triggerDownload);
152
+ nameEl.addEventListener("click", handleClick);
135
153
  const sizeEl = document.createElement("span");
136
154
  sizeEl.style.cssText =
137
155
  "font-size:11px;color:var(--muted-foreground, #718096);";
@@ -197,7 +215,10 @@ export const FileAttachment = Node.create({
197
215
  };
198
216
  },
199
217
  addStorage() {
200
- return { resolver: null };
218
+ return {
219
+ resolver: null,
220
+ downloadBaseUrl: "/api/upload",
221
+ };
201
222
  },
202
223
  addCommands() {
203
224
  return {
@@ -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;AAInE,eAAO,MAAM,QAAQ,sBAwTnB,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;AAInE,eAAO,MAAM,QAAQ,sBA8TnB,CAAC"}
@@ -245,31 +245,36 @@ export const PdfBlock = TiptapNode.create({
245
245
  }
246
246
  }
247
247
  // URL 해결 후 PDF 로드
248
+ function getProxyUrl(fileId) {
249
+ const baseUrl = editor.storage.fileAttachment?.downloadBaseUrl || "/api/upload";
250
+ return `${baseUrl}/${fileId}/download`;
251
+ }
248
252
  if (node.attrs.src) {
249
- openLink.href = node.attrs.src;
250
- loadPdf(node.attrs.src);
253
+ if (node.attrs.fileId) {
254
+ // fileId 있으면 프록시 URL 사용 (경로 숨김)
255
+ const proxyUrl = getProxyUrl(node.attrs.fileId);
256
+ openLink.href = proxyUrl;
257
+ loadPdf(proxyUrl);
258
+ }
259
+ else {
260
+ openLink.href = node.attrs.src;
261
+ loadPdf(node.attrs.src);
262
+ }
251
263
  }
252
264
  else if (node.attrs.fileId) {
253
- // resolver로 URL 획득
254
- openLink.href = "#";
265
+ const proxyUrl = getProxyUrl(node.attrs.fileId);
266
+ openLink.href = proxyUrl;
267
+ // resolver로 이름 획득
255
268
  const resolver = editor.storage.fileAttachment?.resolver;
256
269
  if (resolver) {
257
270
  resolver(node.attrs.fileId)
258
271
  .then((result) => {
259
- openLink.href = result.src;
260
272
  if (result.name)
261
273
  nameSpan.textContent = result.name;
262
- loadPdf(result.src);
263
274
  })
264
- .catch(() => {
265
- loadingDiv.innerHTML =
266
- '<p class="text-sm text-muted-foreground">PDF URL\uC744 \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.</p>';
267
- });
268
- }
269
- else {
270
- loadingDiv.innerHTML =
271
- `<p class="text-sm text-muted-foreground">PDF ID: ${node.attrs.fileId}</p>`;
275
+ .catch(() => { });
272
276
  }
277
+ loadPdf(proxyUrl);
273
278
  }
274
279
  return {
275
280
  dom,
package/dist/types.js CHANGED
@@ -20,7 +20,7 @@ export const TOOLBAR_PRESETS = {
20
20
  'blockquote', 'horizontal-rule', 'toggle',
21
21
  'link', 'image', 'pdf', 'youtube', 'video', 'file',
22
22
  'columns-2', 'columns-3', 'table', 'code-block',
23
- 'bubble-toolbar', 'slash-menu', 'table-menu'
23
+ 'fixed-toolbar', 'bubble-toolbar', 'slash-menu', 'table-menu'
24
24
  ],
25
25
  minimal: [
26
26
  'bold', 'italic', 'strike', 'code',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teriusu/rich-editor",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "TipTap 기반 Notion-style 리치 텍스트 에디터 (Svelte)",
5
5
  "type": "module",
6
6
  "svelte": "./dist/index.js",