@lumir-company/editor 0.4.0 โ 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +456 -66
- package/dist/index.d.mts +878 -7
- package/dist/index.d.ts +878 -7
- package/dist/index.js +2779 -53
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2769 -42
- package/dist/index.mjs.map +1 -1
- package/dist/style.css +878 -0
- package/package.json +14 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# LumirEditor
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**์ด๋ฏธ์ง ์ ์ฉ** BlockNote ๊ธฐ๋ฐ Rich Text ์๋ํฐ
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@lumir-company/editor)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
@@ -9,32 +9,35 @@
|
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
12
|
-
##
|
|
12
|
+
## ๋ชฉ์ฐจ
|
|
13
13
|
|
|
14
|
-
- [ํน์ง](
|
|
15
|
-
- [๋น ๋ฅธ ์์](
|
|
16
|
-
- [์ด๋ฏธ์ง ์
๋ก๋](
|
|
14
|
+
- [ํน์ง](#ํน์ง)
|
|
15
|
+
- [๋น ๋ฅธ ์์](#๋น ๋ฅธ-์์)
|
|
16
|
+
- [์ด๋ฏธ์ง ์
๋ก๋](#์ด๋ฏธ์ง-์
๋ก๋)
|
|
17
17
|
- [S3 ์
๋ก๋ ์ค์ ](#1-s3-์
๋ก๋-๊ถ์ฅ)
|
|
18
|
-
- [ํ์ผ๋ช
์ปค์คํฐ๋ง์ด์ง](
|
|
18
|
+
- [ํ์ผ๋ช
์ปค์คํฐ๋ง์ด์ง](#ํ์ผ๋ช
-์ปค์คํฐ๋ง์ด์ง)
|
|
19
19
|
- [์ปค์คํ
์
๋ก๋](#2-์ปค์คํ
-์
๋ก๋)
|
|
20
|
-
- [
|
|
21
|
-
- [
|
|
22
|
-
- [
|
|
23
|
-
- [
|
|
20
|
+
- [์ด๋ฏธ์ง ์ญ์ ](#์ด๋ฏธ์ง-์ญ์ )
|
|
21
|
+
- [HTML ๋ฏธ๋ฆฌ๋ณด๊ธฐ](#html-๋ฏธ๋ฆฌ๋ณด๊ธฐ)
|
|
22
|
+
- [Props API](#props-api)
|
|
23
|
+
- [์ฌ์ฉ ์์ ](#์ฌ์ฉ-์์ )
|
|
24
|
+
- [์คํ์ผ๋ง](#์คํ์ผ๋ง)
|
|
25
|
+
- [ํธ๋ฌ๋ธ์ํ
](#ํธ๋ฌ๋ธ์ํ
)
|
|
24
26
|
|
|
25
27
|
---
|
|
26
28
|
|
|
27
|
-
##
|
|
29
|
+
## ํน์ง
|
|
28
30
|
|
|
29
|
-
| ํน์ง
|
|
30
|
-
|
|
|
31
|
-
|
|
|
32
|
-
|
|
|
33
|
-
|
|
|
34
|
-
|
|
|
35
|
-
|
|
|
36
|
-
|
|
|
37
|
-
|
|
|
31
|
+
| ํน์ง | ์ค๋ช
|
|
|
32
|
+
| ----------------------- | ------------------------------------------------------ |
|
|
33
|
+
| **์ด๋ฏธ์ง ์ ์ฉ** | ์ด๋ฏธ์ง ์
๋ก๋/๋๋๊ทธ์ค๋๋กญ๋ง ์ง์ (๋น๋์ค/์ค๋์ค ์ ๊ฑฐ) |
|
|
34
|
+
| **HTML ๋ฏธ๋ฆฌ๋ณด๊ธฐ** | HTML ํ์ผ์ ๋๋๊ทธ ์ค ๋๋กญํ์ฌ iframe์ผ๋ก ๋ฏธ๋ฆฌ๋ณด๊ธฐ |
|
|
35
|
+
| **S3 ์ฐ๋** | Presigned URL ๊ธฐ๋ฐ S3 ์
๋ก๋ ๋ด์ฅ |
|
|
36
|
+
| **ํ์ผ๋ช
์ปค์คํฐ๋ง์ด์ง** | ์
๋ก๋ ํ์ผ๋ช
๋ณ๊ฒฝ ์ฝ๋ฐฑ + UUID ์๋ ์ถ๊ฐ ์ง์ |
|
|
37
|
+
| **๋ก๋ฉ ์คํผ๋** | ์ด๋ฏธ์ง ์
๋ก๋ ์ค ์๋ ์คํผ๋ ํ์ |
|
|
38
|
+
| **์ฑ๋ฅ ์ต์ ํ** | ์ ๋๋ฉ์ด์
๋นํ์ฑํ๋ก ๋น ๋ฅธ ๋ ๋๋ง |
|
|
39
|
+
| **TypeScript** | ์์ ํ ํ์
์์ ์ฑ |
|
|
40
|
+
| **ํ
๋ง ์ง์** | ๋ผ์ดํธ/๋คํฌ ํ
๋ง ๋ฐ ์ปค์คํ
ํ
๋ง |
|
|
38
41
|
|
|
39
42
|
### ์ง์ ์ด๋ฏธ์ง ํ์
|
|
40
43
|
|
|
@@ -44,7 +47,7 @@ PNG, JPEG/JPG, GIF, WebP, BMP, SVG
|
|
|
44
47
|
|
|
45
48
|
---
|
|
46
49
|
|
|
47
|
-
##
|
|
50
|
+
## ๋น ๋ฅธ ์์
|
|
48
51
|
|
|
49
52
|
### 1. ์ค์น
|
|
50
53
|
|
|
@@ -74,7 +77,7 @@ export default function App() {
|
|
|
74
77
|
}
|
|
75
78
|
```
|
|
76
79
|
|
|
77
|
-
>
|
|
80
|
+
> **์ค์**: `style.css`๋ฅผ ์ํฌํธํ์ง ์์ผ๋ฉด ์๋ํฐ๊ฐ ์ ์ ์๋ํ์ง ์์ต๋๋ค.
|
|
78
81
|
|
|
79
82
|
### 3. Next.js์์ ์ฌ์ฉ
|
|
80
83
|
|
|
@@ -102,7 +105,7 @@ export default function EditorPage() {
|
|
|
102
105
|
|
|
103
106
|
---
|
|
104
107
|
|
|
105
|
-
##
|
|
108
|
+
## ์ด๋ฏธ์ง ์
๋ก๋
|
|
106
109
|
|
|
107
110
|
### 1. S3 ์
๋ก๋ (๊ถ์ฅ)
|
|
108
111
|
|
|
@@ -140,10 +143,12 @@ production/blog/images/my-photo.png
|
|
|
140
143
|
|
|
141
144
|
---
|
|
142
145
|
|
|
143
|
-
###
|
|
146
|
+
### ํ์ผ๋ช
์ปค์คํฐ๋ง์ด์ง
|
|
144
147
|
|
|
145
148
|
์ฌ๋ฌ ์ด๋ฏธ์ง๋ฅผ ๋์์ ์
๋ก๋ํ ๋ ํ์ผ๋ช
์ค๋ณต์ ๋ฐฉ์งํ๊ณ ๊ด๋ฆฌํ๊ธฐ ์ฝ๊ฒ ๋ง๋๋ ๊ธฐ๋ฅ์
๋๋ค.
|
|
146
149
|
|
|
150
|
+
> **์ฐธ๊ณ **: ๊ธฐ๋ณธ์ ์ผ๋ก ํ์ฅ์๋ ์๋์ผ๋ก ๋ถ์ต๋๋ค. `preserveExtension: false`๋ก ์ค์ ํ๋ฉด ํ์ฅ์๋ฅผ ๋ถ์ด์ง ์์ต๋๋ค.
|
|
151
|
+
|
|
147
152
|
#### ์ต์
1: UUID ์๋ ์ถ๊ฐ
|
|
148
153
|
|
|
149
154
|
```tsx
|
|
@@ -172,10 +177,11 @@ production/blog/images/my-photo.png
|
|
|
172
177
|
apiEndpoint: "/api/s3/presigned",
|
|
173
178
|
env: "production",
|
|
174
179
|
path: "uploads",
|
|
175
|
-
fileNameTransform: (
|
|
176
|
-
//
|
|
180
|
+
fileNameTransform: (nameWithoutExt, file) => {
|
|
181
|
+
// nameWithoutExt๋ ํ์ฅ์๊ฐ ์ ๊ฑฐ๋ ํ์ผ๋ช
(์: "photo")
|
|
182
|
+
// ํ์ฅ์๋ ์๋์ผ๋ก ๋ถ์ต๋๋ค
|
|
177
183
|
const userId = getCurrentUserId();
|
|
178
|
-
return `${userId}_${
|
|
184
|
+
return `${userId}_${nameWithoutExt}`;
|
|
179
185
|
},
|
|
180
186
|
}}
|
|
181
187
|
/>
|
|
@@ -185,7 +191,9 @@ production/blog/images/my-photo.png
|
|
|
185
191
|
|
|
186
192
|
```
|
|
187
193
|
์๋ณธ: photo.png
|
|
188
|
-
|
|
194
|
+
โ nameWithoutExt: "photo"
|
|
195
|
+
โ ๋ณํ ํ: "user123_photo"
|
|
196
|
+
โ ์ต์ข
: user123_photo.png
|
|
189
197
|
```
|
|
190
198
|
|
|
191
199
|
#### ์ต์
3: ์กฐํฉ ์ฌ์ฉ (๊ถ์ฅ)
|
|
@@ -196,7 +204,7 @@ production/blog/images/my-photo.png
|
|
|
196
204
|
apiEndpoint: "/api/s3/presigned",
|
|
197
205
|
env: "production",
|
|
198
206
|
path: "uploads",
|
|
199
|
-
fileNameTransform: (
|
|
207
|
+
fileNameTransform: (nameWithoutExt) => `user123_${nameWithoutExt}`,
|
|
200
208
|
appendUUID: true, // ๋ณํ ํ UUID ์ถ๊ฐ
|
|
201
209
|
}}
|
|
202
210
|
/>
|
|
@@ -206,8 +214,10 @@ production/blog/images/my-photo.png
|
|
|
206
214
|
|
|
207
215
|
```
|
|
208
216
|
์๋ณธ: photo.png
|
|
209
|
-
|
|
210
|
-
|
|
217
|
+
โ nameWithoutExt: "photo"
|
|
218
|
+
1. fileNameTransform ์ ์ฉ: "user123_photo"
|
|
219
|
+
2. appendUUID ์ ์ฉ: "user123_photo_550e8400-e29b-41d4"
|
|
220
|
+
3. ํ์ฅ์ ๋ถ์ด๊ธฐ: user123_photo_550e8400-e29b-41d4.png
|
|
211
221
|
```
|
|
212
222
|
|
|
213
223
|
#### ์ค์ ์์ : ํ์์คํฌํ + UUID
|
|
@@ -220,11 +230,10 @@ function MyEditor() {
|
|
|
220
230
|
apiEndpoint: "/api/s3/presigned",
|
|
221
231
|
env: "production",
|
|
222
232
|
path: "uploads",
|
|
223
|
-
fileNameTransform: (
|
|
233
|
+
fileNameTransform: (nameWithoutExt, file) => {
|
|
234
|
+
// nameWithoutExt๋ ์ด๋ฏธ ํ์ฅ์๊ฐ ์ ๊ฑฐ๋จ
|
|
224
235
|
const timestamp = new Date().toISOString().split("T")[0]; // 2024-01-15
|
|
225
|
-
|
|
226
|
-
const nameWithoutExt = originalName.replace(`.${ext}`, "");
|
|
227
|
-
return `${timestamp}_${nameWithoutExt}.${ext}`;
|
|
236
|
+
return `${timestamp}_${nameWithoutExt}`;
|
|
228
237
|
},
|
|
229
238
|
appendUUID: true,
|
|
230
239
|
}}
|
|
@@ -236,7 +245,41 @@ function MyEditor() {
|
|
|
236
245
|
**๊ฒฐ๊ณผ:**
|
|
237
246
|
|
|
238
247
|
```
|
|
239
|
-
|
|
248
|
+
์๋ณธ: photo.png
|
|
249
|
+
โ nameWithoutExt: "photo"
|
|
250
|
+
1. fileNameTransform: "2024-01-15_photo"
|
|
251
|
+
2. appendUUID: "2024-01-15_photo_550e8400-e29b-41d4"
|
|
252
|
+
3. ํ์ฅ์ ๋ถ์ด๊ธฐ: 2024-01-15_photo_550e8400-e29b-41d4.png
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
#### ์ต์
4: ํ์ฅ์ ์ ๊ฑฐ (preserveExtension: false)
|
|
256
|
+
|
|
257
|
+
```tsx
|
|
258
|
+
<LumirEditor
|
|
259
|
+
s3Upload={{
|
|
260
|
+
apiEndpoint: "/api/s3/presigned",
|
|
261
|
+
env: "production",
|
|
262
|
+
path: "uploads",
|
|
263
|
+
fileNameTransform: (nameWithoutExt) => `${nameWithoutExt}_custom`,
|
|
264
|
+
preserveExtension: false, // ํ์ฅ์ ์ ๋ถ์
|
|
265
|
+
}}
|
|
266
|
+
/>
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
**๊ฒฐ๊ณผ:**
|
|
270
|
+
|
|
271
|
+
```
|
|
272
|
+
์๋ณธ: photo.png
|
|
273
|
+
โ nameWithoutExt: "photo"
|
|
274
|
+
โ ๋ณํ ํ: "photo_custom"
|
|
275
|
+
โ ์ต์ข
: photo_custom (ํ์ฅ์ ์์)
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
**์ฌ์ฉ ์ฌ๋ก**: WebP ๋ณํ ๋ฑ ์๋ฒ์์ ํ์ฅ์๋ฅผ ๋ณ๊ฒฝํ๋ ๊ฒฝ์ฐ
|
|
279
|
+
|
|
280
|
+
```tsx
|
|
281
|
+
fileNameTransform: (nameWithoutExt) => `${nameWithoutExt}.webp`,
|
|
282
|
+
preserveExtension: false,
|
|
240
283
|
```
|
|
241
284
|
|
|
242
285
|
---
|
|
@@ -289,19 +332,290 @@ const imageUrl = await s3Uploader(imageFile);
|
|
|
289
332
|
|
|
290
333
|
---
|
|
291
334
|
|
|
292
|
-
##
|
|
335
|
+
## ์ด๋ฏธ์ง ์ญ์
|
|
336
|
+
|
|
337
|
+
์๋ํฐ์์ ์ด๋ฏธ์ง๊ฐ ์ญ์ ๋ ๋ S3 ๋ฑ ์ธ๋ถ ์คํ ๋ฆฌ์ง์์๋ ์๋์ผ๋ก ์ญ์ ํ๊ณ ์ถ๋ค๋ฉด `onImageDelete` ์ฝ๋ฐฑ์ ์ฌ์ฉํ์ธ์.
|
|
338
|
+
|
|
339
|
+
### ๊ธฐ๋ณธ ์ฌ์ฉ
|
|
340
|
+
|
|
341
|
+
```tsx
|
|
342
|
+
<LumirEditor
|
|
343
|
+
s3Upload={{
|
|
344
|
+
apiEndpoint: "/api/s3/presigned",
|
|
345
|
+
env: "production",
|
|
346
|
+
path: "images",
|
|
347
|
+
}}
|
|
348
|
+
onImageDelete={(imageUrl) => {
|
|
349
|
+
console.log("์ด๋ฏธ์ง ์ญ์ ๋จ:", imageUrl);
|
|
350
|
+
// S3์์ ์ญ์ ๋ก์ง ๊ตฌํ
|
|
351
|
+
}}
|
|
352
|
+
/>
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### ๊ถ์ฅ: ์ง์ฐ ์ญ์ (Undo/Redo ๋์)
|
|
356
|
+
|
|
357
|
+
Undo๋ก ์ด๋ฏธ์ง๋ฅผ ๋ณต์ํ ์ ์๋๋ก **์ง์ฐ ์ญ์ **๋ฅผ ๊ถ์ฅํฉ๋๋ค.
|
|
358
|
+
|
|
359
|
+
```tsx
|
|
360
|
+
"use client";
|
|
361
|
+
|
|
362
|
+
import { useState, useRef, useCallback } from "react";
|
|
363
|
+
|
|
364
|
+
function Editor() {
|
|
365
|
+
const pendingDeletes = useRef(new Map());
|
|
366
|
+
|
|
367
|
+
const handleImageDelete = useCallback((imageUrl: string) => {
|
|
368
|
+
// ์ด๋ฏธ ์์ฝ๋ ์ญ์ ๊ฐ ์์ผ๋ฉด ๋ฌด์
|
|
369
|
+
if (pendingDeletes.current.has(imageUrl)) return;
|
|
370
|
+
|
|
371
|
+
// 5๋ถ ํ ์ญ์ ์์ฝ
|
|
372
|
+
const timeoutId = setTimeout(async () => {
|
|
373
|
+
pendingDeletes.current.delete(imageUrl);
|
|
374
|
+
|
|
375
|
+
// S3์์ ์ค์ ์ญ์
|
|
376
|
+
await fetch(`/api/s3/delete?url=${encodeURIComponent(imageUrl)}`, {
|
|
377
|
+
method: "DELETE",
|
|
378
|
+
});
|
|
379
|
+
}, 5 * 60 * 1000); // 5๋ถ
|
|
380
|
+
|
|
381
|
+
pendingDeletes.current.set(imageUrl, timeoutId);
|
|
382
|
+
}, []);
|
|
383
|
+
|
|
384
|
+
return (
|
|
385
|
+
<LumirEditor
|
|
386
|
+
s3Upload={{ /* ... */ }}
|
|
387
|
+
onImageDelete={handleImageDelete}
|
|
388
|
+
/>
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
### S3 ์ญ์ API ์์
|
|
394
|
+
|
|
395
|
+
> **์ฐธ๊ณ **: `onImageDelete`๋ **ํ๋ ์์ํฌ ๋
๋ฆฝ์ **์
๋๋ค. ์๋๋ ๊ฐ ํ๊ฒฝ๋ณ ๊ตฌํ ์์์
๋๋ค.
|
|
396
|
+
|
|
397
|
+
#### Next.js API Route
|
|
398
|
+
|
|
399
|
+
**ํ์ผ**: `app/api/s3/delete/route.ts`
|
|
400
|
+
|
|
401
|
+
```typescript
|
|
402
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
403
|
+
import { S3Client, DeleteObjectCommand } from "@aws-sdk/client-s3";
|
|
404
|
+
|
|
405
|
+
const s3 = new S3Client({
|
|
406
|
+
region: process.env.AWS_REGION!,
|
|
407
|
+
credentials: {
|
|
408
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
|
|
409
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
|
|
410
|
+
},
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
export async function DELETE(req: NextRequest) {
|
|
414
|
+
const { searchParams } = new URL(req.url);
|
|
415
|
+
const imageUrl = searchParams.get("url");
|
|
416
|
+
|
|
417
|
+
if (!imageUrl) {
|
|
418
|
+
return NextResponse.json({ error: "url is required" }, { status: 400 });
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// URL์์ S3 ํค ์ถ์ถ
|
|
422
|
+
const key = extractKeyFromUrl(imageUrl);
|
|
423
|
+
|
|
424
|
+
await s3.send(
|
|
425
|
+
new DeleteObjectCommand({
|
|
426
|
+
Bucket: process.env.AWS_S3_BUCKET!,
|
|
427
|
+
Key: key,
|
|
428
|
+
})
|
|
429
|
+
);
|
|
430
|
+
|
|
431
|
+
return NextResponse.json({ success: true });
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function extractKeyFromUrl(url: string): string {
|
|
435
|
+
const urlObj = new URL(url);
|
|
436
|
+
return decodeURIComponent(urlObj.pathname.slice(1));
|
|
437
|
+
}
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
**ํด๋ผ์ด์ธํธ ๊ตฌํ**:
|
|
441
|
+
|
|
442
|
+
```tsx
|
|
443
|
+
const handleImageDelete = (imageUrl: string) => {
|
|
444
|
+
fetch(`/api/s3/delete?url=${encodeURIComponent(imageUrl)}`, {
|
|
445
|
+
method: "DELETE",
|
|
446
|
+
});
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
<LumirEditor onImageDelete={handleImageDelete} />
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
#### React + Express
|
|
453
|
+
|
|
454
|
+
**์๋ฒ** (`server.js`):
|
|
455
|
+
|
|
456
|
+
```javascript
|
|
457
|
+
app.delete('/api/images', async (req, res) => {
|
|
458
|
+
const { imageUrl } = req.body;
|
|
459
|
+
const key = extractKeyFromS3Url(imageUrl);
|
|
460
|
+
|
|
461
|
+
await s3Client.send(new DeleteObjectCommand({
|
|
462
|
+
Bucket: process.env.S3_BUCKET,
|
|
463
|
+
Key: key
|
|
464
|
+
}));
|
|
465
|
+
|
|
466
|
+
res.json({ success: true });
|
|
467
|
+
});
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
**ํด๋ผ์ด์ธํธ**:
|
|
471
|
+
|
|
472
|
+
```tsx
|
|
473
|
+
const handleImageDelete = async (imageUrl: string) => {
|
|
474
|
+
await fetch('https://api.myapp.com/api/images', {
|
|
475
|
+
method: 'DELETE',
|
|
476
|
+
headers: { 'Content-Type': 'application/json' },
|
|
477
|
+
body: JSON.stringify({ imageUrl })
|
|
478
|
+
});
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
<LumirEditor onImageDelete={handleImageDelete} />
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
#### React Native + Firebase Storage
|
|
485
|
+
|
|
486
|
+
```tsx
|
|
487
|
+
import storage from '@react-native-firebase/storage';
|
|
488
|
+
|
|
489
|
+
const handleImageDelete = async (imageUrl: string) => {
|
|
490
|
+
const ref = storage().refFromURL(imageUrl);
|
|
491
|
+
await ref.delete();
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
<LumirEditor onImageDelete={handleImageDelete} />
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
#### Vue + Axios + FastAPI
|
|
498
|
+
|
|
499
|
+
```typescript
|
|
500
|
+
const handleImageDelete = async (imageUrl: string) => {
|
|
501
|
+
await axios.delete('https://api.myapp.com/v1/images', {
|
|
502
|
+
data: { imageUrl }
|
|
503
|
+
});
|
|
504
|
+
};
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
### ์ฃผ์์ฌํญ
|
|
508
|
+
|
|
509
|
+
| ํญ๋ชฉ | ์ค๋ช
|
|
|
510
|
+
|------|------|
|
|
511
|
+
| **Undo/Redo** | ์ง์ฐ ์ญ์ ๋ก ๋ณต์ ๊ฐ๋ฅํ๊ฒ ๊ตฌํ (๊ถ์ฅ: 5-10๋ถ) |
|
|
512
|
+
| **๊ถํ ๊ฒ์ฆ** | ํ๋ก๋์
์์๋ ์ธ์ฆ/์ธ๊ฐ ํ์ |
|
|
513
|
+
| **์ฐธ์กฐ ์นด์ดํธ** | ๊ฐ์ ์ด๋ฏธ์ง๋ฅผ ์ฌ๋ฌ ๋ฌธ์์์ ์ฌ์ฉํ๋์ง ํ์ธ |
|
|
514
|
+
| **์ญ์ ๋ก๊ทธ** | ๊ฐ์ฌ ์ถ์ ์ ์ํ ์ญ์ ๊ธฐ๋ก ์ ์ฅ ๊ถ์ฅ |
|
|
515
|
+
|
|
516
|
+
---
|
|
517
|
+
|
|
518
|
+
## HTML ๋ฏธ๋ฆฌ๋ณด๊ธฐ
|
|
519
|
+
|
|
520
|
+
LumirEditor๋ HTML ํ์ผ์ iframe์ ์ฌ์ฉํ์ฌ ๋ฏธ๋ฆฌ๋ณด๊ธฐํ ์ ์๋ ์ปค์คํ
๋ธ๋ก์ ์ ๊ณตํฉ๋๋ค. ํธ์ง ๋ถ๊ฐ๋ฅํ ์์ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ๊ธฐ๋ฅ์ผ๋ก, HTML ๋ฌธ์๋ฅผ ์์ ํ๊ฒ ํ์ํ ์ ์์ต๋๋ค.
|
|
521
|
+
|
|
522
|
+
### ์ฌ์ฉ ๋ฐฉ๋ฒ
|
|
523
|
+
|
|
524
|
+
#### 1. ๋๋๊ทธ ์ค ๋๋กญ
|
|
525
|
+
|
|
526
|
+
HTML ํ์ผ(`.html`, `.htm`)์ ์๋ํฐ์ ๋๋๊ทธ ์ค ๋๋กญํ๋ฉด ์๋์ผ๋ก iframe ๋ฏธ๋ฆฌ๋ณด๊ธฐ ๋ธ๋ก์ด ์ฝ์
๋ฉ๋๋ค.
|
|
527
|
+
|
|
528
|
+
```tsx
|
|
529
|
+
<LumirEditor />
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
- **์ง์ ํ์ผ ํ์**: `.html`, `.htm`
|
|
533
|
+
- **ํน์ง**:
|
|
534
|
+
- ํธ์ง ๋ถ๊ฐ๋ฅํ ์์ ๋ฏธ๋ฆฌ๋ณด๊ธฐ
|
|
535
|
+
- ์ ๊ธฐ/ํผ์น๊ธฐ ๊ธฐ๋ฅ
|
|
536
|
+
- ์์ ํ sandbox ์ฒ๋ฆฌ (`allow-same-origin`, JavaScript ์คํ ๋นํ์ฑํ)
|
|
537
|
+
- ํ์ผ๋ช
ํ์
|
|
538
|
+
|
|
539
|
+
#### 2. ์ฌ๋์ ๋ฉ๋ด
|
|
540
|
+
|
|
541
|
+
์๋ํฐ์์ `/`๋ฅผ ์
๋ ฅํ๊ณ "HTML Preview"๋ฅผ ์ ํํ๋ฉด ์์ HTML ๋ฏธ๋ฆฌ๋ณด๊ธฐ ๋ธ๋ก์ด ์ฝ์
๋ฉ๋๋ค.
|
|
542
|
+
|
|
543
|
+
```
|
|
544
|
+
/ โ HTML Preview
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
### ํน์ง
|
|
548
|
+
|
|
549
|
+
- **iframe ๊ธฐ๋ฐ**: HTML ๋ฌธ์๋ฅผ ๋
๋ฆฝ๋ iframe์์ ์์ ํ๊ฒ ๋ ๋๋ง
|
|
550
|
+
- **Sandbox ๋ณด์**: `sandbox="allow-same-origin"` ์์ฑ์ผ๋ก ๋ณด์ ๊ฐํ (JavaScript ์คํ ์๋์ ๋นํ์ฑํ)
|
|
551
|
+
- **์ ๊ธฐ/ํผ์น๊ธฐ**: ํค๋ ํด๋ฆญ์ผ๋ก ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์์ญ ํ ๊ธ
|
|
552
|
+
- **๋๋๊ทธ ๋ฆฌ์ฌ์ด์ฆ**: ํ๋จ ํธ๋ค์ ๋๋๊ทธํ์ฌ ๋์ด ์กฐ์ ๊ฐ๋ฅ (100px ~ 1200px)
|
|
553
|
+
- **์ ์ฐฝ ์ด๊ธฐ**: HTML ๋ฌธ์๋ฅผ ์ ์ฐฝ์์ ์ ์ฒด ํ๋ฉด์ผ๋ก ํ์ธ
|
|
554
|
+
- **๋ค์ด๋ก๋**: HTML ํ์ผ๋ก ๋ค์ด๋ก๋
|
|
555
|
+
- **ํธ์ง ๋ถ๊ฐ**: ์์ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์ ์ฉ
|
|
556
|
+
|
|
557
|
+
### ์ฌ์ฉ ์์
|
|
558
|
+
|
|
559
|
+
```tsx
|
|
560
|
+
import { LumirEditor } from "@lumir-company/editor";
|
|
561
|
+
import "@lumir-company/editor/style.css";
|
|
562
|
+
|
|
563
|
+
function App() {
|
|
564
|
+
return (
|
|
565
|
+
<div className="w-full h-[600px]">
|
|
566
|
+
<LumirEditor
|
|
567
|
+
onContentChange={(blocks) => {
|
|
568
|
+
// HTML ๋ฏธ๋ฆฌ๋ณด๊ธฐ ๋ธ๋ก๋ ์ผ๋ฐ ๋ธ๋ก๊ณผ ๋์ผํ๊ฒ ์ฒ๋ฆฌ๋จ
|
|
569
|
+
console.log(blocks);
|
|
570
|
+
}}
|
|
571
|
+
/>
|
|
572
|
+
</div>
|
|
573
|
+
);
|
|
574
|
+
}
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
### ํ๋ก๊ทธ๋๋ฐ ๋ฐฉ์์ผ๋ก ๋ธ๋ก ์ฝ์
|
|
578
|
+
|
|
579
|
+
```tsx
|
|
580
|
+
import { HtmlPreview } from "@lumir-company/editor";
|
|
581
|
+
|
|
582
|
+
// ์๋ํฐ ์ธ์คํด์ค์์ ์ง์ ๋ธ๋ก ์ฝ์
|
|
583
|
+
editor.insertBlocks([
|
|
584
|
+
{
|
|
585
|
+
type: "htmlPreview",
|
|
586
|
+
props: {
|
|
587
|
+
htmlContent: "<h1>Hello World</h1><p>This is HTML content</p>",
|
|
588
|
+
fileName: "example.html",
|
|
589
|
+
height: "400px",
|
|
590
|
+
},
|
|
591
|
+
},
|
|
592
|
+
]);
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
### ์ฃผ์์ฌํญ
|
|
596
|
+
|
|
597
|
+
- HTML ๋ด์ฉ์ iframe์ `sandbox="allow-same-origin"` ์์ฑ์ผ๋ก ๋ณด์์ด ๊ฐํ๋์ด ์์ต๋๋ค
|
|
598
|
+
- **JavaScript๋ ์๋์ ์ผ๋ก ๋นํ์ฑํ**๋์ด ์์ต๋๋ค (๋ณด์์ ์ด์ )
|
|
599
|
+
- ์ธ๋ถ ๋ฆฌ์์ค(CSS, JS, ์ด๋ฏธ์ง ๋ฑ)๋ ์๋ ๊ฒฝ๋ก๊ฐ ์๋ํ์ง ์์ ์ ์์ต๋๋ค
|
|
600
|
+
- ์ธ๋ผ์ธ CSS ์คํ์ผ์ ๊ถ์ฅํฉ๋๋ค
|
|
601
|
+
|
|
602
|
+
---
|
|
603
|
+
|
|
604
|
+
## Props API
|
|
293
605
|
|
|
294
606
|
### ํต์ฌ Props
|
|
295
607
|
|
|
296
|
-
| Prop | ํ์
|
|
297
|
-
| ----------------- |
|
|
298
|
-
| `s3Upload` | `S3UploaderConfig`
|
|
299
|
-
| `uploadFile` | `(file: File) => Promise<string>`
|
|
300
|
-
| `onContentChange` | `(blocks) => void`
|
|
301
|
-
| `
|
|
302
|
-
| `
|
|
303
|
-
| `
|
|
304
|
-
| `
|
|
608
|
+
| Prop | ํ์
| ๊ธฐ๋ณธ๊ฐ | ์ค๋ช
|
|
|
609
|
+
| ----------------- | ------------------------------------- | ----------- | ----------------------- |
|
|
610
|
+
| `s3Upload` | `S3UploaderConfig` | `undefined` | S3 ์
๋ก๋ ์ค์ |
|
|
611
|
+
| `uploadFile` | `(file: File) => Promise<string>` | `undefined` | ์ปค์คํ
์
๋ก๋ ํจ์ |
|
|
612
|
+
| `onContentChange` | `(blocks) => void` | `undefined` | ์ฝํ
์ธ ๋ณ๊ฒฝ ์ฝ๋ฐฑ |
|
|
613
|
+
| `onImageDelete` | `(imageUrl: string) => void` | `undefined` | ์ด๋ฏธ์ง ์ญ์ ์ ์ฝ๋ฐฑ |
|
|
614
|
+
| `onError` | `(error: LumirEditorError) => void` | `undefined` | ์๋ฌ ๋ฐ์ ์ ์ฝ๋ฐฑ |
|
|
615
|
+
| `initialContent` | `Block[] \| string` | `undefined` | ์ด๊ธฐ ์ฝํ
์ธ |
|
|
616
|
+
| `editable` | `boolean` | `true` | ํธ์ง ๊ฐ๋ฅ ์ฌ๋ถ |
|
|
617
|
+
| `theme` | `"light" \| "dark"` | `"light"` | ํ
๋ง |
|
|
618
|
+
| `className` | `string` | `""` | CSS ํด๋์ค |
|
|
305
619
|
|
|
306
620
|
### S3UploaderConfig
|
|
307
621
|
|
|
@@ -313,8 +627,9 @@ interface S3UploaderConfig {
|
|
|
313
627
|
path: string; // S3 ์ ์ฅ ๊ฒฝ๋ก
|
|
314
628
|
|
|
315
629
|
// ์ ํ (ํ์ผ๋ช
์ปค์คํฐ๋ง์ด์ง)
|
|
316
|
-
fileNameTransform?: (
|
|
317
|
-
appendUUID?: boolean; // true: ํ์ผ๋ช
๋ค์ UUID ์ถ๊ฐ
|
|
630
|
+
fileNameTransform?: (nameWithoutExt: string, file: File) => string; // ํ์ฅ์ ์ ์ธํ ์ด๋ฆ ๋ณํ
|
|
631
|
+
appendUUID?: boolean; // true: ํ์ผ๋ช
๋ค์ UUID ์ถ๊ฐ (ํ์ฅ์ ์์ ์ฝ์
)
|
|
632
|
+
preserveExtension?: boolean; // false: ํ์ฅ์๋ฅผ ๋ถ์ด์ง ์์ (๊ธฐ๋ณธ: true)
|
|
318
633
|
}
|
|
319
634
|
```
|
|
320
635
|
|
|
@@ -328,12 +643,22 @@ interface LumirEditorProps {
|
|
|
328
643
|
// === ์๋ํฐ ์ค์ ===
|
|
329
644
|
initialContent?: DefaultPartialBlock[] | string; // ์ด๊ธฐ ์ฝํ
์ธ (๋ธ๋ก ๋ฐฐ์ด ๋๋ JSON ๋ฌธ์์ด)
|
|
330
645
|
initialEmptyBlocks?: number; // ์ด๊ธฐ ๋น ๋ธ๋ก ๊ฐ์ (๊ธฐ๋ณธ: 3)
|
|
646
|
+
placeholder?: string; // ๋น ๋ธ๋ก์ ํ์ํ ์๋ด ํ
์คํธ (์: "๋ด์ฉ์ ์
๋ ฅํ์ธ์...")
|
|
331
647
|
uploadFile?: (file: File) => Promise<string>; // ์ปค์คํ
ํ์ผ ์
๋ก๋ ํจ์
|
|
332
|
-
s3Upload?:
|
|
648
|
+
s3Upload?: {
|
|
649
|
+
apiEndpoint: string;
|
|
650
|
+
env: "development" | "production";
|
|
651
|
+
path: string;
|
|
652
|
+
fileNameTransform?: (nameWithoutExt: string, file: File) => string; // ํ์ฅ์ ์ ์ธํ ์ด๋ฆ ๋ณํ
|
|
653
|
+
appendUUID?: boolean; // UUID ์๋ ์ถ๊ฐ (ํ์ฅ์ ์)
|
|
654
|
+
preserveExtension?: boolean; // ํ์ฅ์ ์๋ ๋ถ์ด๊ธฐ (๊ธฐ๋ณธ: true)
|
|
655
|
+
};
|
|
333
656
|
|
|
334
657
|
// === ์ฝ๋ฐฑ ===
|
|
335
658
|
onContentChange?: (blocks: DefaultPartialBlock[]) => void; // ์ฝํ
์ธ ๋ณ๊ฒฝ ์ ํธ์ถ
|
|
659
|
+
onImageDelete?: (imageUrl: string) => void; // ์ด๋ฏธ์ง ์ญ์ ์ ํธ์ถ (S3 ์ญ์ ๋ฑ)
|
|
336
660
|
onSelectionChange?: () => void; // ์ ํ ์์ญ ๋ณ๊ฒฝ ์ ํธ์ถ
|
|
661
|
+
onError?: (error: LumirEditorError) => void; // ์๋ฌ ๋ฐ์ ์ ํธ์ถ
|
|
337
662
|
|
|
338
663
|
// ๊ธฐ๋ฅ ์ค์
|
|
339
664
|
tables?: TableConfig; // ํ
์ด๋ธ ๊ธฐ๋ฅ ์ค์ (splitCells, cellBackgroundColor ๋ฑ)
|
|
@@ -355,6 +680,11 @@ interface LumirEditorProps {
|
|
|
355
680
|
tableHandles?: boolean; // ํ
์ด๋ธ ํธ๋ค ํ์ (๊ธฐ๋ณธ: true)
|
|
356
681
|
className?: string; // ์ปจํ
์ด๋ CSS ํด๋์ค
|
|
357
682
|
|
|
683
|
+
// === ๋งํฌ ํ๋ฆฌ๋ทฐ ์ค์ ===
|
|
684
|
+
linkPreview?: {
|
|
685
|
+
apiEndpoint: string; // ๋งํฌ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ API ์๋ํฌ์ธํธ (์: "/api/link-preview")
|
|
686
|
+
};
|
|
687
|
+
|
|
358
688
|
// ๋ฏธ๋์ด ์
๋ก๋ ํ์ฉ ์ฌ๋ถ (๊ธฐ๋ณธ: ๋ชจ๋ ๋นํ์ฑ)
|
|
359
689
|
allowVideoUpload?: boolean; // ๋น๋์ค ์
๋ก๋ ํ์ฉ (๊ธฐ๋ณธ: false)
|
|
360
690
|
allowAudioUpload?: boolean; // ์ค๋์ค ์
๋ก๋ ํ์ฉ (๊ธฐ๋ณธ: false)
|
|
@@ -366,7 +696,7 @@ interface LumirEditorProps {
|
|
|
366
696
|
|
|
367
697
|
---
|
|
368
698
|
|
|
369
|
-
##
|
|
699
|
+
## ์ฌ์ฉ ์์
|
|
370
700
|
|
|
371
701
|
### ์ฝ๊ธฐ ์ ์ฉ ๋ชจ๋
|
|
372
702
|
|
|
@@ -416,7 +746,7 @@ function EditorWithSave() {
|
|
|
416
746
|
|
|
417
747
|
---
|
|
418
748
|
|
|
419
|
-
##
|
|
749
|
+
## ์คํ์ผ๋ง
|
|
420
750
|
|
|
421
751
|
### Tailwind CSS์ ํจ๊ป ์ฌ์ฉ
|
|
422
752
|
|
|
@@ -454,7 +784,7 @@ import { LumirEditor, cn } from "@lumir-company/editor";
|
|
|
454
784
|
|
|
455
785
|
---
|
|
456
786
|
|
|
457
|
-
##
|
|
787
|
+
## ํธ๋ฌ๋ธ์ํ
|
|
458
788
|
|
|
459
789
|
### ํ์ ์ฒดํฌ๋ฆฌ์คํธ
|
|
460
790
|
|
|
@@ -468,10 +798,10 @@ import { LumirEditor, cn } from "@lumir-company/editor";
|
|
|
468
798
|
#### 1. ์๋ํฐ๊ฐ ๋ณด์ด์ง ์์
|
|
469
799
|
|
|
470
800
|
```tsx
|
|
471
|
-
//
|
|
801
|
+
// ์๋ชป๋จ
|
|
472
802
|
<LumirEditor />;
|
|
473
803
|
|
|
474
|
-
//
|
|
804
|
+
// ์ฌ๋ฐ๋ฆ
|
|
475
805
|
import "@lumir-company/editor/style.css";
|
|
476
806
|
<div className="h-[400px]">
|
|
477
807
|
<LumirEditor />
|
|
@@ -481,10 +811,10 @@ import "@lumir-company/editor/style.css";
|
|
|
481
811
|
#### 2. Next.js Hydration ์ค๋ฅ
|
|
482
812
|
|
|
483
813
|
```tsx
|
|
484
|
-
//
|
|
814
|
+
// ์๋ชป๋จ
|
|
485
815
|
import { LumirEditor } from "@lumir-company/editor";
|
|
486
816
|
|
|
487
|
-
//
|
|
817
|
+
// ์ฌ๋ฐ๋ฆ
|
|
488
818
|
const LumirEditor = dynamic(
|
|
489
819
|
() =>
|
|
490
820
|
import("@lumir-company/editor").then((m) => ({ default: m.LumirEditor })),
|
|
@@ -508,7 +838,7 @@ const LumirEditor = dynamic(
|
|
|
508
838
|
#### 4. ์ฌ๋ฌ ์ด๋ฏธ์ง ์
๋ก๋ ์ ์ค๋ณต ๋ฌธ์
|
|
509
839
|
|
|
510
840
|
```tsx
|
|
511
|
-
//
|
|
841
|
+
// ํด๊ฒฐ: appendUUID ์ฌ์ฉ
|
|
512
842
|
<LumirEditor
|
|
513
843
|
s3Upload={{
|
|
514
844
|
apiEndpoint: "/api/s3/presigned",
|
|
@@ -521,7 +851,7 @@ const LumirEditor = dynamic(
|
|
|
521
851
|
|
|
522
852
|
---
|
|
523
853
|
|
|
524
|
-
##
|
|
854
|
+
## ์ ํธ๋ฆฌํฐ API
|
|
525
855
|
|
|
526
856
|
### ContentUtils
|
|
527
857
|
|
|
@@ -554,23 +884,83 @@ const uploader = createS3Uploader({
|
|
|
554
884
|
const url = await uploader(imageFile);
|
|
555
885
|
```
|
|
556
886
|
|
|
557
|
-
##
|
|
887
|
+
## ๊ด๋ จ ๋งํฌ
|
|
558
888
|
|
|
559
889
|
- [npm Package](https://www.npmjs.com/package/@lumir-company/editor)
|
|
560
890
|
- [BlockNote Documentation](https://www.blocknotejs.org/)
|
|
561
891
|
|
|
562
892
|
---
|
|
563
893
|
|
|
564
|
-
##
|
|
894
|
+
## ๋ณ๊ฒฝ ๋ก๊ทธ
|
|
895
|
+
|
|
896
|
+
### v0.4.3
|
|
897
|
+
|
|
898
|
+
- **๋งํฌ ํ๋ฆฌ๋ทฐ ๊ธฐ๋ฅ ์ถ๊ฐ**
|
|
899
|
+
- `linkPreview` prop์ผ๋ก ๋งํฌ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ํ์ฑํ (์นด์นด์คํก ์คํ์ผ OG ์นด๋)
|
|
900
|
+
- URL ๋ถ์ฌ๋ฃ๊ธฐ ์ ์๋ ๋งํฌ ํ๋ฆฌ๋ทฐ ๋ธ๋ก ์์ฑ (๋น ๋ธ๋ก์ด๋ฉด ๊ต์ฒด, ํ
์คํธ ์์ผ๋ฉด ํ๋จ ์ฝ์
)
|
|
901
|
+
- ์ฌ๋์ ๋ฉ๋ด(`/`)์์ Link Preview ํญ๋ชฉ ์ถ๊ฐ
|
|
902
|
+
- ๋๋๊ทธ ๋ฆฌ์ฌ์ด์ฆ ์ง์ (์ข์ฐ ๋๋น, ํ๋จ ์ด๋ฏธ์ง ๋์ด ์กฐ์ )
|
|
903
|
+
- ๋ฉํ๋ฐ์ดํฐ์ ์ด๋ฏธ์ง ์์ ๊ฒฝ์ฐ ์ด๋ฏธ์ง ์์ญ ์๋ต
|
|
904
|
+
- ์๋ฌ ์นด๋ ํด๋ฆญ ์ ๋งํฌ ์ด๋ ์ง์
|
|
905
|
+
- `fetchLinkMetadata`, `clearMetadataCache`, `LinkMetadata` ํ์
export
|
|
906
|
+
- **๋งํฌ ํด๋ฐ ์ปค์คํ
**
|
|
907
|
+
- ํ
์คํธ ๋งํฌ๋ฅผ ๋งํฌ ํ๋ฆฌ๋ทฐ ๋ธ๋ก์ผ๋ก ์ ํํ๋ ๋ฒํผ ์ถ๊ฐ (`replaceBlocks` ์ฌ์ฉ)
|
|
908
|
+
- `linkPreview.apiEndpoint` ์ค์ ์์๋ง ์ ํ ๋ฒํผ ํ์
|
|
909
|
+
- **placeholder prop ์ถ๊ฐ**
|
|
910
|
+
- `placeholder` prop์ผ๋ก ์๋ํฐ ๋น ๋ธ๋ก ์๋ด ํ
์คํธ ์ค์
|
|
911
|
+
- **์ด๋ฏธ์ง ์ญ์ ๊ธฐ๋ฅ ์ถ๊ฐ**
|
|
912
|
+
- `onImageDelete` ์ฝ๋ฐฑ prop ์ถ๊ฐ - ์๋ํฐ์์ ์ด๋ฏธ์ง ์ญ์ ์ ํธ์ถ
|
|
913
|
+
- S3 ๋ฑ ์ธ๋ถ ์คํ ๋ฆฌ์ง์์ ์ด๋ฏธ์ง ์๋ ์ญ์ ์ง์
|
|
914
|
+
- ์ง์ฐ ์ญ์ ํจํด์ผ๋ก Undo/Redo ๋์ ๊ฐ๋ฅ
|
|
915
|
+
- ์ด๋ฏธ์ง URL ์ถ์ถ ๋ฐ ์ญ์ ๊ฐ์ง ํฌํผ ํจ์ ๋ด์ฅ
|
|
916
|
+
- **๋ณด์ ๊ฐํ**
|
|
917
|
+
- URL ์ด์ค์ผ์ดํ ์ฒ๋ฆฌ ์ถ๊ฐ (XSS ๋ฐฉ์ง)
|
|
918
|
+
- LinkButton: `javascript:`, `data:`, `vbscript:`, `file:` ํ๋กํ ์ฝ ์ฐจ๋จ
|
|
919
|
+
- ์ํํ URL ์
๋ ฅ ์ ์๋ฌ ๋ฉ์์ง ํ์
|
|
920
|
+
- **๋งํฌ ์ฝ์
๋ฒ๊ทธ ์์ **
|
|
921
|
+
- ํ๋กํ
๋ฉ๋ด ๋งํฌ ๋ฒํผ: ํ
์คํธ ๋ฏธ์ ํ ์์๋ URL ํ
์คํธ๋ก ๋งํฌ ์ฝ์
์ง์
|
|
922
|
+
- `editor.focus()` ํธ์ถ๋ก ์ ํ ์ํ ๋ณต์
|
|
923
|
+
- **README ๊ฐ์ **
|
|
924
|
+
- ๋งํฌ ํ๋ฆฌ๋ทฐ ์ฌ์ฉ ๊ฐ์ด๋ ๋ฐ API ๋ผ์ฐํธ ์์ ์ถ๊ฐ
|
|
925
|
+
- ์ด๋ฏธ์ง ์ญ์ ์น์
์ถ๊ฐ (์ง์ฐ ์ญ์ ์์ ํฌํจ)
|
|
926
|
+
- S3 ์ญ์ API ๊ตฌํ ์์ ์ถ๊ฐ
|
|
927
|
+
- Props API ๋ฌธ์ ์
๋ฐ์ดํธ
|
|
928
|
+
|
|
929
|
+
### v0.4.2
|
|
930
|
+
|
|
931
|
+
- **์ฝ๋ ๊ตฌ์กฐ ๋ฆฌํฉํ ๋ง**
|
|
932
|
+
- FloatingMenu ์ปดํฌ๋ํธ ๋ถ๋ฆฌ (Icons, ๊ฐ๋ณ ๋ฒํผ ์ปดํฌ๋ํธ)
|
|
933
|
+
- ์์ ์์ ๋ณ๋ ํ์ผ๋ก ๋ถ๋ฆฌ (`constants/colors.ts`)
|
|
934
|
+
- ๋ฏธ์ฌ์ฉ ๊ธฐ๋ฅ ์ ๊ฑฐ (FontSelect, FontSizeControl)
|
|
935
|
+
- **์๋ฌ ์ฒ๋ฆฌ ๊ฐ์ **
|
|
936
|
+
- `LumirEditorError` ์ปค์คํ
์๋ฌ ํด๋์ค ์ถ๊ฐ
|
|
937
|
+
- `onError` ์ฝ๋ฐฑ prop ์ถ๊ฐ - ์๋ฌ ๋ฐ์ ์ ์ฌ์ฉ์ ์ ์ ํธ๋ค๋ง ๊ฐ๋ฅ
|
|
938
|
+
- ์๋ฌ ๋ฐ์ ์ ์ฌ์ฉ์ ์นํ์ ํ ์คํธ ๋ฉ์์ง ์๋ ํ์
|
|
939
|
+
- **HTML ๋ฏธ๋ฆฌ๋ณด๊ธฐ ๊ฐ์ **
|
|
940
|
+
- sandbox ์ค์ ๋ช
ํํ (JavaScript ์๋์ ๋นํ์ฑํ)
|
|
941
|
+
- ๋๋๊ทธ ๋ฆฌ์ฌ์ด์ฆ, ์ ์ฐฝ ์ด๊ธฐ, ๋ค์ด๋ก๋ ๊ธฐ๋ฅ ๋ฌธ์ํ
|
|
942
|
+
- **ํ์
๊ฐ์ **
|
|
943
|
+
- `LumirErrorCode`, `LumirErrorDetails` ํ์
export
|
|
944
|
+
- `ColorItem` ํ์
export
|
|
945
|
+
|
|
946
|
+
### v0.4.1
|
|
947
|
+
|
|
948
|
+
- `preserveExtension` prop ์ถ๊ฐ - ํ์ฅ์ ์๋ ๋ถ์ด๊ธฐ ์ ์ด (๊ธฐ๋ณธ: true)
|
|
949
|
+
- **์ค์**: ํ์ผ๋ช
๋ณํ ์ ํ์ฅ์ ์์น ์์ (ํ์ฅ์๊ฐ ํญ์ ๋งจ ๋ค์ ์ค๋๋ก)
|
|
950
|
+
- **Breaking Change**: `fileNameTransform` ํ๋ผ๋ฏธํฐ ๋ณ๊ฒฝ - ์ด์ ํ์ฅ์ ์ ์ธํ ํ์ผ๋ช
๋ง ์ ๋ฌ๋จ
|
|
951
|
+
- ์ด์ : `fileNameTransform: (originalName, file) => ...` โ originalName์ ํ์ฅ์ ํฌํจ
|
|
952
|
+
- ๋ณ๊ฒฝ: `fileNameTransform: (nameWithoutExt, file) => ...` โ nameWithoutExt์ ํ์ฅ์ ์ ์ธ
|
|
953
|
+
- ํ์ฅ์ ์ ๊ฑฐ ์ฌ์ฉ ์ฌ๋ก ๋ฌธ์ํ
|
|
954
|
+
- README ์์ ๋ฐ ์ค๋ช
๊ฐ์
|
|
565
955
|
|
|
566
956
|
### v0.4.0
|
|
567
957
|
|
|
568
|
-
-
|
|
569
|
-
-
|
|
570
|
-
-
|
|
571
|
-
-
|
|
958
|
+
- ํ์ผ๋ช
๋ณํ ์ฝ๋ฐฑ (`fileNameTransform`) ์ถ๊ฐ
|
|
959
|
+
- UUID ์๋ ์ถ๊ฐ ์ต์
(`appendUUID`) ์ถ๊ฐ
|
|
960
|
+
- ์ฌ๋ฌ ์ด๋ฏธ์ง ๋์ ์
๋ก๋ ์ ์ค๋ณต ๋ฌธ์ ํด๊ฒฐ
|
|
961
|
+
- ๋ฌธ์ ๋ํญ ๊ฐ์
|
|
572
962
|
|
|
573
963
|
### v0.3.3
|
|
574
964
|
|
|
575
|
-
-
|
|
576
|
-
-
|
|
965
|
+
- ์๋ํฐ ์ฌ์์ฑ ๋ฐฉ์ง ์ต์ ํ
|
|
966
|
+
- ํ์
์ ์ ๊ฐ์
|