@servantcdh/ez-planet-labeling 0.3.3 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +299 -121
- package/dist/index.d.ts +830 -1239
- package/dist/index.js +18028 -3745
- package/package.json +15 -5
- package/dist/index-DDn_Fjhx.js +0 -788
- package/dist/style.css +0 -1
package/README.md
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
# @servantcdh/ez-planet-labeling
|
|
2
2
|
|
|
3
|
-
Fabric.js
|
|
3
|
+
Fabric.js 기반 이미지 라벨링 워크스페이스 라이브러리.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
[](https://stackblitz.com/github/servantcdh/ez-planet/tree/master/examples/labeling-demo?file=src/App.tsx)
|
|
5
|
+
호스트 앱이 데이터와 뮤테이션을 Context로 주입하면, 라이브러리는 순수 프레젠테이션 레이어로 동작합니다. CSS는 JS 번들에 자동 주입되어 별도 import가 필요 없습니다.
|
|
8
6
|
|
|
9
7
|
## 설치
|
|
10
8
|
|
|
@@ -20,160 +18,340 @@ npm install @servantcdh/ez-planet-labeling
|
|
|
20
18
|
|---------|---------|------|
|
|
21
19
|
| `react` | `^18.0.0` | UI 런타임 |
|
|
22
20
|
| `react-dom` | `^18.0.0` | DOM 렌더링 |
|
|
23
|
-
| `fabric` | `^
|
|
21
|
+
| `fabric` | `^5.0.0` | 캔버스 엔진 |
|
|
24
22
|
|
|
25
23
|
### Bundled Dependencies (라이브러리에 포함)
|
|
26
24
|
|
|
27
25
|
| Package | 용도 |
|
|
28
26
|
|---------|------|
|
|
29
|
-
| `zustand` | 내부 상태 관리
|
|
30
|
-
| `zundo` |
|
|
31
|
-
| `@erase2d/fabric` |
|
|
27
|
+
| `zustand` | 내부 상태 관리 |
|
|
28
|
+
| `zundo` | Undo/Redo |
|
|
29
|
+
| `@erase2d/fabric` | EraserBrush |
|
|
30
|
+
| `@tanstack/react-table` | 가상화 테이블 |
|
|
31
|
+
| `@tanstack/react-virtual` | 가상화 스크롤 |
|
|
32
|
+
| `echarts` | 차트 라벨 렌더링 |
|
|
32
33
|
|
|
33
|
-
##
|
|
34
|
+
## 기본 사용법
|
|
34
35
|
|
|
35
|
-
|
|
36
|
+
```tsx
|
|
37
|
+
import {
|
|
38
|
+
LabelingProviders,
|
|
39
|
+
LabelingWorkspace,
|
|
40
|
+
staticData,
|
|
41
|
+
loadingData,
|
|
42
|
+
} from '@servantcdh/ez-planet-labeling';
|
|
43
|
+
// CSS import 불필요 — JS 번들에 자동 포함
|
|
44
|
+
|
|
45
|
+
function App() {
|
|
46
|
+
const dataCtx = useMyLabelingData(); // 호스트가 구현
|
|
47
|
+
const mutationCtx = useMyLabelingMutations(); // 호스트가 구현
|
|
48
|
+
const datasetCtx = useMyDatasetData(); // 호스트가 구현
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<LabelingProviders
|
|
52
|
+
data={dataCtx}
|
|
53
|
+
mutations={mutationCtx}
|
|
54
|
+
dataset={datasetCtx}
|
|
55
|
+
>
|
|
56
|
+
<LabelingWorkspace />
|
|
57
|
+
</LabelingProviders>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
```
|
|
36
61
|
|
|
37
|
-
|
|
62
|
+
## Architecture
|
|
38
63
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
activeRecordId={currentId}
|
|
49
|
-
onRecordSelect={handleSelect}
|
|
50
|
-
classes={classes}
|
|
51
|
-
onSave={handleSave}
|
|
52
|
-
tools={['selection', 'blankRect', 'polygon', 'brush', 'eraser']}
|
|
53
|
-
mode="labeling"
|
|
54
|
-
/>
|
|
64
|
+
### Provider 구조
|
|
65
|
+
|
|
66
|
+
라이브러리는 3개의 Context를 통해 호스트로부터 데이터를 주입받습니다. 변경 빈도별로 분리하여 불필요한 리렌더를 방지합니다.
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
LabelingProviders
|
|
70
|
+
├── LabelingDataProvider — 라벨/정책/컨텍스트 데이터 (자주 변경)
|
|
71
|
+
├── LabelingMutationProvider — Mutation 콜백 함수 (참조 안정적)
|
|
72
|
+
└── LabelingDatasetProvider — 데이터셋/콘텐츠 (레코드 선택 시만 변경)
|
|
55
73
|
```
|
|
56
74
|
|
|
57
|
-
###
|
|
75
|
+
### AsyncData\<T\>
|
|
58
76
|
|
|
59
|
-
|
|
77
|
+
모든 쿼리 데이터는 `AsyncData<T>` 래퍼로 전달합니다. react-query의 반환 타입과 동일한 인터페이스입니다.
|
|
60
78
|
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
<LabelingProvider image={image} annotations={annotations} onChange={onChange} ...>
|
|
72
|
-
<LabelingNavigation records={records} activeRecordId={id} onRecordSelect={onSelect} />
|
|
73
|
-
<LabelingCanvas image={image} annotations={annotations} onChange={onChange} />
|
|
74
|
-
<LabelingToolbar tools={['selection', 'brush', 'eraser']} />
|
|
75
|
-
<LabelingInfoPanel classes={classes} annotations={annotations} />
|
|
76
|
-
</LabelingProvider>
|
|
79
|
+
```ts
|
|
80
|
+
interface AsyncData<T> {
|
|
81
|
+
data: T | undefined;
|
|
82
|
+
isLoading: boolean;
|
|
83
|
+
isFetching: boolean;
|
|
84
|
+
isError: boolean;
|
|
85
|
+
error: unknown;
|
|
86
|
+
refetch: () => void;
|
|
87
|
+
dataUpdatedAt: number;
|
|
88
|
+
}
|
|
77
89
|
```
|
|
78
90
|
|
|
79
|
-
|
|
91
|
+
헬퍼 함수:
|
|
80
92
|
|
|
81
|
-
|
|
93
|
+
| 함수 | 용도 |
|
|
94
|
+
|------|------|
|
|
95
|
+
| `staticData(value)` | 이미 로드된 값을 래핑 |
|
|
96
|
+
| `loadingData()` | 로딩 상태 플레이스홀더 |
|
|
97
|
+
| `errorData(error)` | 에러 상태 플레이스홀더 |
|
|
82
98
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
99
|
+
### LabelingDataContextValue
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
interface LabelingDataContextValue {
|
|
103
|
+
policiesBatch: AsyncData<PolicyDetail[]>;
|
|
104
|
+
labelContext: AsyncData<LabelContextResponse>;
|
|
105
|
+
labelContextStatus: AsyncData<ContentsetStatusResponse>;
|
|
106
|
+
labelContextInLabeling: AsyncData<InLabelingStatusResponse>;
|
|
107
|
+
labelContextEnable: AsyncData<LabelContextEnableResponse>;
|
|
108
|
+
labelSearch: AsyncData<LabelSearchResult>;
|
|
109
|
+
previousLabelContexts: AsyncData<PreviousLabelContextWithLabelsResponse[]>;
|
|
110
|
+
validResultSearch: AsyncData<ValidResultSearchResult>;
|
|
111
|
+
}
|
|
93
112
|
```
|
|
94
113
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
| Prop | Type | Required | Description |
|
|
100
|
-
|------|------|----------|-------------|
|
|
101
|
-
| `image` | `string \| { url, width, height }` | Yes | 라벨링 대상 이미지 |
|
|
102
|
-
| `annotations` | `Annotation[]` | Yes | 현재 어노테이션 목록 |
|
|
103
|
-
| `onChange` | `(event: CanvasChangeEvent) => void` | Yes | 어노테이션 변경 콜백 |
|
|
104
|
-
| `records` | `WorkspaceRecord[]` | Yes | 레코드 목록 (Navigation) |
|
|
105
|
-
| `activeRecordId` | `string` | Yes | 현재 활성 레코드 ID |
|
|
106
|
-
| `onRecordSelect` | `(record: WorkspaceRecord) => void` | Yes | 레코드 선택 콜백 |
|
|
107
|
-
| `classes` | `LabelingClass[]` | Yes | 라벨 클래스 목록 |
|
|
108
|
-
| `onSave` | `(state: CanvasState) => void` | Yes | 저장 콜백 |
|
|
109
|
-
| `tools` | `ToolType[]` | No | 사용할 도구 목록 |
|
|
110
|
-
| `mode` | `'labeling' \| 'validation' \| 'readonly'` | No | 워크스페이스 모드 |
|
|
111
|
-
| `theme` | `Partial<LabelingTheme>` | No | 테마 커스터마이징 |
|
|
112
|
-
| `layout` | `WorkspaceLayout` | No | 레이아웃 설정 |
|
|
113
|
-
| `extensions` | `LabelingExtension[]` | No | 확장 기능 |
|
|
114
|
-
| `validationResults` | `ValidationResult[]` | No | 검증 결과 (validation 모드) |
|
|
115
|
-
| `indicator` | `WorkspaceIndicator` | No | 진행 표시기 |
|
|
116
|
-
|
|
117
|
-
### Annotation
|
|
114
|
+
### LabelingMutationContextValue
|
|
115
|
+
|
|
116
|
+
각 뮤테이션은 `async function` + `MutationState` 쌍으로 구성됩니다.
|
|
118
117
|
|
|
119
118
|
```ts
|
|
120
|
-
interface
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
119
|
+
interface LabelingMutationContextValue {
|
|
120
|
+
batchUpdateLabels: (vars: LabelBatchUpdateVariables) => Promise<LabelBatchUpdateResponse>;
|
|
121
|
+
batchUpdateLabelsState: MutationState;
|
|
122
|
+
|
|
123
|
+
bulkCreateLabels: (vars: LabelBulkCreateVariables) => Promise<BulkLabelCreateResponse>;
|
|
124
|
+
bulkCreateLabelsState: MutationState;
|
|
125
|
+
|
|
126
|
+
createLabelContext: (body: LabelContextCreateRequest) => Promise<LabelContextResponse>;
|
|
127
|
+
createLabelContextState: MutationState;
|
|
128
|
+
|
|
129
|
+
updateLabelContext: (vars: LabelContextUpdateVariables) => Promise<LabelContextResponse>;
|
|
130
|
+
updateLabelContextState: MutationState;
|
|
131
|
+
|
|
132
|
+
createLabelStatus: (body: LabelStatusCreateRequest) => Promise<ApiResponse<LabelStatusResponse>>;
|
|
133
|
+
createLabelStatusState: MutationState;
|
|
134
|
+
|
|
135
|
+
uploadFileLabel: (vars: FileLabelUploadVariables) => Promise<LabelResponse>;
|
|
136
|
+
uploadFileLabelState: MutationState;
|
|
137
|
+
|
|
138
|
+
copyLabels: (body: LabelCopyRequest) => Promise<LabelCopyResponse>;
|
|
139
|
+
copyLabelsState: MutationState;
|
|
140
|
+
|
|
141
|
+
createValidResult: (body: ValidResultCreateRequest) => Promise<ValidResultResponse>;
|
|
142
|
+
createValidResultState: MutationState;
|
|
143
|
+
|
|
144
|
+
updateValidResult: (vars: ValidResultUpdateVariables) => Promise<ValidResultResponse>;
|
|
145
|
+
updateValidResultState: MutationState;
|
|
146
|
+
|
|
147
|
+
bulkDeleteValidResults: (body: ValidResultBulkDeleteRequest) => Promise<ValidResultBulkDeleteResponse>;
|
|
148
|
+
bulkDeleteValidResultsState: MutationState;
|
|
149
|
+
|
|
150
|
+
onMutationSuccess: (hint: MutationSuccessHint) => void;
|
|
126
151
|
}
|
|
127
152
|
```
|
|
128
153
|
|
|
129
|
-
|
|
154
|
+
`onMutationSuccess`는 라이브러리가 뮤테이션 성공 후 호출합니다. 호스트는 `hint.type`에 따라 적절한 캐시 무효화/리페치를 수행하면 됩니다.
|
|
130
155
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
156
|
+
```ts
|
|
157
|
+
type MutationSuccessHint =
|
|
158
|
+
| { type: "labels-saved"; labelContextId: string | null }
|
|
159
|
+
| { type: "labels-copied" }
|
|
160
|
+
| { type: "labels-bulk-created"; labelContextId: string }
|
|
161
|
+
| { type: "label-context-created"; labelContextId: string }
|
|
162
|
+
| { type: "label-context-updated"; labelContextId: string }
|
|
163
|
+
| { type: "label-status-created" }
|
|
164
|
+
| { type: "valid-result-created" }
|
|
165
|
+
| { type: "valid-result-updated" }
|
|
166
|
+
| { type: "valid-results-deleted" }
|
|
167
|
+
| { type: "file-uploaded"; labelContextId: string };
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### LabelingDatasetContextValue
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
interface LabelingDatasetContextValue {
|
|
174
|
+
datasetDetail: AsyncData<DatasetDTO>;
|
|
175
|
+
datasetContents: AsyncData<DatasetApiResponse<DatasetContentSearchResponse>>;
|
|
176
|
+
datasetContentDetail: AsyncData<DatasetContentRecord | null>;
|
|
177
|
+
}
|
|
178
|
+
```
|
|
137
179
|
|
|
138
180
|
## Extension System
|
|
139
181
|
|
|
182
|
+
`LabelingWorkspace`는 `extensions` prop을 통해 호스트가 확장 기능을 등록할 수 있습니다. 라이브러리는 렌더 슬롯만 제공하며, 모든 UI와 로직은 호스트가 소유합니다.
|
|
183
|
+
|
|
140
184
|
```tsx
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
185
|
+
<LabelingWorkspace extensions={[samExtension, autoLabelingExtension]} />
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### LabelingExtension
|
|
189
|
+
|
|
190
|
+
```ts
|
|
191
|
+
interface LabelingExtension {
|
|
192
|
+
id: string;
|
|
193
|
+
name: string;
|
|
194
|
+
enabled?: boolean;
|
|
195
|
+
renderInfoPanelAction?: (ctx: ExtensionRenderContext) => ReactNode;
|
|
196
|
+
renderOverlay?: (ctx: ExtensionRenderContext) => ReactNode;
|
|
197
|
+
renderToolbarAction?: (ctx: ExtensionRenderContext) => ReactNode;
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Render Slots
|
|
202
|
+
|
|
203
|
+
| Slot | 위치 | 용도 |
|
|
204
|
+
|------|------|------|
|
|
205
|
+
| `renderInfoPanelAction` | InfoPanel 하단 | 액션 버튼 (예: Auto Labeling 실행) |
|
|
206
|
+
| `renderOverlay` | 워크스페이스 루트 위 | 모달, 패널 등 오버레이 UI |
|
|
207
|
+
| `renderToolbarAction` | 플로팅 툴바 끝 | 캔버스 도구 버튼 (예: SAM) |
|
|
208
|
+
|
|
209
|
+
### ExtensionRenderContext
|
|
210
|
+
|
|
211
|
+
각 렌더 함수는 현재 워크스페이스 상태와 캔버스 접근 API를 포함한 컨텍스트를 받습니다.
|
|
212
|
+
|
|
213
|
+
```ts
|
|
214
|
+
interface ExtensionRenderContext {
|
|
215
|
+
// 워크스페이스 상태
|
|
216
|
+
contentSetId: string | null;
|
|
217
|
+
labelContextId: string | null;
|
|
218
|
+
policyIds: string[];
|
|
219
|
+
datasetId: string;
|
|
220
|
+
datasetVersion: string;
|
|
221
|
+
requestDataRefresh: (hint: MutationSuccessHint) => void;
|
|
222
|
+
|
|
223
|
+
// 캔버스 접근
|
|
224
|
+
canvasRef: RefObject<unknown | null>; // fabric.Canvas 인스턴스
|
|
225
|
+
imageInfo: WorkspaceImageInfo | null; // { url, width, height }
|
|
226
|
+
addCanvasObjects: (objects: unknown[]) => void;
|
|
227
|
+
removeCanvasObjects: (predicate: (obj: unknown) => boolean) => void;
|
|
154
228
|
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Canvas Access
|
|
155
232
|
|
|
156
|
-
|
|
233
|
+
캔버스와 직접 상호작용해야 하는 확장은 다음 export를 사용할 수 있습니다:
|
|
234
|
+
|
|
235
|
+
```ts
|
|
236
|
+
import {
|
|
237
|
+
getCanvasInstance, // fabric.Canvas 싱글턴 반환
|
|
238
|
+
addCanvasObjects, // fabric 오브젝트 추가
|
|
239
|
+
removeCanvasObjects, // 조건에 맞는 오브젝트 제거
|
|
240
|
+
} from '@servantcdh/ez-planet-labeling';
|
|
157
241
|
```
|
|
158
242
|
|
|
159
|
-
|
|
243
|
+
### Extension 구현 예시 (SAM)
|
|
160
244
|
|
|
161
245
|
```tsx
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
246
|
+
import type { LabelingExtension } from '@servantcdh/ez-planet-labeling';
|
|
247
|
+
|
|
248
|
+
function createSAMExtension(api: SAMApi): LabelingExtension {
|
|
249
|
+
return {
|
|
250
|
+
id: 'sam',
|
|
251
|
+
name: 'Segment Anything',
|
|
252
|
+
renderToolbarAction: (ctx) => (
|
|
253
|
+
<SAMToolButton
|
|
254
|
+
canvasRef={ctx.canvasRef}
|
|
255
|
+
imageInfo={ctx.imageInfo}
|
|
256
|
+
onResult={(polygons) => ctx.addCanvasObjects(polygons)}
|
|
257
|
+
/>
|
|
258
|
+
),
|
|
259
|
+
renderOverlay: (ctx) => (
|
|
260
|
+
<SAMResultPanel
|
|
261
|
+
api={api}
|
|
262
|
+
contentSetId={ctx.contentSetId}
|
|
263
|
+
onApply={() => ctx.requestDataRefresh({
|
|
264
|
+
type: 'labels-saved',
|
|
265
|
+
labelContextId: ctx.labelContextId,
|
|
266
|
+
})}
|
|
267
|
+
/>
|
|
268
|
+
),
|
|
269
|
+
};
|
|
270
|
+
}
|
|
175
271
|
```
|
|
176
272
|
|
|
273
|
+
### Extension 구현 예시 (Automated Labeling)
|
|
274
|
+
|
|
275
|
+
```tsx
|
|
276
|
+
function createAutoLabelingExtension(api: AutoLabelingApi): LabelingExtension {
|
|
277
|
+
return {
|
|
278
|
+
id: 'auto-labeling',
|
|
279
|
+
name: 'Automated Labeling',
|
|
280
|
+
renderInfoPanelAction: (ctx) => (
|
|
281
|
+
<AutoLabelingButton
|
|
282
|
+
contentSetId={ctx.contentSetId}
|
|
283
|
+
policyIds={ctx.policyIds}
|
|
284
|
+
/>
|
|
285
|
+
),
|
|
286
|
+
renderOverlay: (ctx) => (
|
|
287
|
+
<AutoLabelingModal
|
|
288
|
+
api={api}
|
|
289
|
+
datasetId={ctx.datasetId}
|
|
290
|
+
onComplete={() => ctx.requestDataRefresh({
|
|
291
|
+
type: 'labels-saved',
|
|
292
|
+
labelContextId: ctx.labelContextId,
|
|
293
|
+
})}
|
|
294
|
+
/>
|
|
295
|
+
),
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
## 주요 기능
|
|
301
|
+
|
|
302
|
+
- **도구**: Selection, Bounding Box, Polygon, Brush, Eraser, Magic Brush, Superpixel
|
|
303
|
+
- **Extension System**: 렌더 슬롯 + 캔버스 접근 기반 플러그인 아키텍처
|
|
304
|
+
- **Validation Mode**: 검증 워크플로우 지원
|
|
305
|
+
- **Undo/Redo**: zundo 기반 temporal history
|
|
306
|
+
- **CSS Scoping**: `.lc-root` 컨테이너 스코핑, `--lc-*` 네임스페이스 CSS 변수
|
|
307
|
+
- **CSS-in-JS 번들**: `vite-plugin-css-injected-by-js`로 별도 CSS import 불필요
|
|
308
|
+
|
|
309
|
+
## Exports
|
|
310
|
+
|
|
311
|
+
### Components
|
|
312
|
+
|
|
313
|
+
| Export | 설명 |
|
|
314
|
+
|--------|------|
|
|
315
|
+
| `LabelingWorkspace` | 메인 워크스페이스 컴포넌트 |
|
|
316
|
+
| `LabelingProviders` | 3개 Provider 합성 래퍼 |
|
|
317
|
+
|
|
318
|
+
### Types
|
|
319
|
+
|
|
320
|
+
| Export | 설명 |
|
|
321
|
+
|--------|------|
|
|
322
|
+
| `LabelingWorkspaceProps` | 워크스페이스 props |
|
|
323
|
+
| `LabelingProvidersProps` | Provider props |
|
|
324
|
+
| `LabelingDataContextValue` | 데이터 Context 인터페이스 |
|
|
325
|
+
| `LabelingMutationContextValue` | 뮤테이션 Context 인터페이스 |
|
|
326
|
+
| `LabelingDatasetContextValue` | 데이터셋 Context 인터페이스 |
|
|
327
|
+
| `AsyncData<T>` | 비동기 데이터 래퍼 |
|
|
328
|
+
| `MutationState` | 뮤테이션 상태 |
|
|
329
|
+
| `MutationSuccessHint` | 뮤테이션 성공 힌트 |
|
|
330
|
+
| `LabelingExtension` | 확장 인터페이스 |
|
|
331
|
+
| `ExtensionRenderContext` | 확장 렌더 컨텍스트 |
|
|
332
|
+
| `WorkspaceImageInfo` | 이미지 정보 |
|
|
333
|
+
|
|
334
|
+
### Helpers
|
|
335
|
+
|
|
336
|
+
| Export | 설명 |
|
|
337
|
+
|--------|------|
|
|
338
|
+
| `staticData(value)` | 로드 완료 상태의 `AsyncData` 생성 |
|
|
339
|
+
| `loadingData()` | 로딩 상태의 `AsyncData` 생성 |
|
|
340
|
+
| `errorData(error)` | 에러 상태의 `AsyncData` 생성 |
|
|
341
|
+
| `IDLE_MUTATION` | 대기 상태의 `MutationState` |
|
|
342
|
+
|
|
343
|
+
### Canvas Access
|
|
344
|
+
|
|
345
|
+
| Export | 설명 |
|
|
346
|
+
|--------|------|
|
|
347
|
+
| `getCanvasInstance()` | fabric.Canvas 싱글턴 반환 |
|
|
348
|
+
| `addCanvasObjects(objects)` | 캔버스에 fabric 오브젝트 추가 |
|
|
349
|
+
| `removeCanvasObjects(predicate)` | 조건에 맞는 오브젝트 제거 |
|
|
350
|
+
|
|
351
|
+
### Domain Types
|
|
352
|
+
|
|
353
|
+
라벨, 정책, 데이터셋 관련 도메인 타입은 `index.ts`에서 re-export됩니다. 전체 목록은 소스를 참조하세요.
|
|
354
|
+
|
|
177
355
|
## License
|
|
178
356
|
|
|
179
357
|
MIT
|