@kokoa/clotho-editor 0.1.4 → 0.2.0

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.
@@ -2051,3 +2051,132 @@ body.studio-player-popout #clotho-player-preview {
2051
2051
  margin-top: 0.4rem;
2052
2052
  }
2053
2053
  }
2054
+
2055
+ .studio-plugin-toolbar {
2056
+ display: contents;
2057
+ }
2058
+
2059
+ .studio-plugin-panels,
2060
+ .studio-plugin-inspectors {
2061
+ display: grid;
2062
+ gap: 0.55rem;
2063
+ }
2064
+
2065
+ .studio-plugin-panels > section,
2066
+ .studio-plugin-inspectors > section {
2067
+ padding: 0.65rem;
2068
+ border: 1px solid var(--color-border);
2069
+ border-radius: 0.55rem;
2070
+ background: var(--color-surface);
2071
+ }
2072
+
2073
+ .studio-template-panel {
2074
+ display: grid;
2075
+ gap: 0.55rem;
2076
+ }
2077
+
2078
+ .studio-template-select {
2079
+ width: 100%;
2080
+ padding: 0.35rem 0.45rem;
2081
+ border: 1px solid var(--color-border);
2082
+ border-radius: 0.3rem;
2083
+ background: var(--color-surface);
2084
+ color: var(--color-fg);
2085
+ }
2086
+
2087
+ .studio-template-status[data-error="true"] {
2088
+ color: var(--color-danger);
2089
+ }
2090
+
2091
+ .studio-checkpoint-card {
2092
+ margin-bottom: 0.7rem;
2093
+ padding: 0.65rem;
2094
+ border: 1px solid var(--color-border);
2095
+ border-radius: 0.5rem;
2096
+ background: var(--color-surface-raised, var(--color-surface));
2097
+ }
2098
+
2099
+ .studio-responsive-grid {
2100
+ display: grid;
2101
+ gap: 0.6rem;
2102
+ margin-top: 0.65rem;
2103
+ }
2104
+
2105
+ .studio-responsive-grid figure {
2106
+ margin: 0;
2107
+ padding: 0.4rem;
2108
+ border: 1px solid var(--color-border);
2109
+ border-radius: 0.45rem;
2110
+ overflow: hidden;
2111
+ background: var(--color-canvas);
2112
+ }
2113
+
2114
+ .studio-responsive-grid figcaption {
2115
+ margin-bottom: 0.35rem;
2116
+ color: var(--color-fg-muted);
2117
+ font-size: 0.7rem;
2118
+ }
2119
+
2120
+ .studio-responsive-grid svg {
2121
+ display: block;
2122
+ width: 100%;
2123
+ height: auto;
2124
+ }
2125
+
2126
+ .studio-profile {
2127
+ display: grid;
2128
+ gap: 0.35rem;
2129
+ margin: 0.55rem 0;
2130
+ }
2131
+ .studio-profile > div {
2132
+ display: flex;
2133
+ justify-content: space-between;
2134
+ gap: 0.75rem;
2135
+ }
2136
+ .studio-profile dt {
2137
+ color: var(--color-fg-muted);
2138
+ }
2139
+ .studio-profile dd {
2140
+ margin: 0;
2141
+ font-family: var(--font-mono);
2142
+ }
2143
+
2144
+ .studio-lint-list {
2145
+ display: grid;
2146
+ gap: 0.35rem;
2147
+ padding: 0;
2148
+ list-style: none;
2149
+ }
2150
+ .studio-lint-list button {
2151
+ width: 100%;
2152
+ display: grid;
2153
+ gap: 0.2rem;
2154
+ padding: 0.45rem;
2155
+ border: 1px solid var(--color-border);
2156
+ border-radius: 0.4rem;
2157
+ background: var(--color-surface);
2158
+ color: var(--color-fg);
2159
+ text-align: left;
2160
+ }
2161
+ .studio-lint-list span {
2162
+ color: var(--color-fg-muted);
2163
+ font-size: 0.72rem;
2164
+ }
2165
+ .studio-lint-list em {
2166
+ color: var(--color-accent);
2167
+ font-size: 0.68rem;
2168
+ font-style: normal;
2169
+ }
2170
+ .studio-lint-compare {
2171
+ display: grid;
2172
+ grid-template-columns: 1fr 1fr;
2173
+ gap: 0.4rem;
2174
+ }
2175
+ .studio-lint-compare figure {
2176
+ min-width: 0;
2177
+ margin: 0;
2178
+ }
2179
+ .studio-lint-compare svg {
2180
+ width: 100%;
2181
+ height: auto;
2182
+ }
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { AnimationDocument, SnapshotMap, Appearance, AnimationElement, TrackKeyframe, Chapter, AnimationEffect, GroupElement } from '@kokoa/clotho';
1
+ import { AnimationDocument, SnapshotMap, Appearance, AnimationElement, TrackKeyframe, Chapter, AnimationEffect, DataValue, LayoutMode, Checkpoint, StoryManifest, StoryEdge, AnimationTemplate, GroupElement, ParameterSchema } from '@kokoa/clotho';
2
2
 
3
3
  interface StudioProps {
4
4
  initial: AnimationDocument;
@@ -29,22 +29,12 @@ declare function createLocalStorageRepository(options?: LocalStorageRepositoryOp
29
29
 
30
30
  type ImageUploadResolver = (file: File) => Promise<string> | string;
31
31
 
32
- interface StudioMountProps {
33
- initialId?: string;
34
- repository?: AnimationRepository;
35
- editorTitle?: string;
36
- resolveImage?: ImageUploadResolver;
32
+ interface PaletteCommand {
33
+ id: string;
34
+ label: string;
35
+ hint?: string;
36
+ run: () => void;
37
37
  }
38
- declare function StudioMount({ initialId, repository, editorTitle, resolveImage, }: StudioMountProps): React.JSX.Element;
39
-
40
- /** 검증된 애니메이션 문서를 읽기 쉬운 JSON으로 변환한다. */
41
- declare function animationDocumentToJson(def: AnimationDocument): string;
42
- /** 어떤 image 요소에서도 참조하지 않는 asset을 제거한 새 문서를 반환한다. */
43
- declare function garbageCollectAnimationAssets(def: AnimationDocument): AnimationDocument;
44
- /** 문서 ID를 운영체제에서 안전하게 사용할 수 있는 파일 이름으로 바꾼다. */
45
- declare function animationDocumentFileName(def: AnimationDocument): string;
46
- /** 현재 문서를 브라우저에서 JSON 파일로 내려받는다. */
47
- declare function downloadAnimationJson(def: AnimationDocument): void;
48
38
 
49
39
  type Selection = {
50
40
  kind: "none";
@@ -61,7 +51,7 @@ type Selection = {
61
51
  kind: "effect";
62
52
  effectId: string;
63
53
  };
64
- type HistoryKind = "meta" | "canvas" | "settings" | "add" | "delete" | "move" | "style" | "rotate" | "resize" | "reorder" | "track" | "appearance" | "chapter" | "effect" | "group" | "asset" | "other";
54
+ type HistoryKind = "meta" | "canvas" | "settings" | "add" | "delete" | "move" | "style" | "rotate" | "resize" | "reorder" | "track" | "appearance" | "chapter" | "effect" | "group" | "layout" | "checkpoint" | "asset" | "other";
65
55
  interface HistoryEntry {
66
56
  snap: string;
67
57
  label: string;
@@ -127,6 +117,8 @@ declare function updateDuration(ms: number): void;
127
117
  declare function updateMeta(patch: Partial<Pick<AnimationDocument, "title" | "description">>): void;
128
118
  /** Update the locales offered to localized text elements in this document. */
129
119
  declare function updateLocales(locales: string[]): void;
120
+ declare function updateData(data: Record<string, DataValue>): void;
121
+ declare function updateResponsive(responsive: NonNullable<AnimationDocument["responsive"]>): void;
130
122
  declare function updateCanvas(patch: Partial<AnimationDocument["canvas"]>): void;
131
123
  declare function updateSettings(patch: Partial<AnimationDocument["settings"]>): void;
132
124
  declare function uniqueElementId(type: string): string;
@@ -152,6 +144,107 @@ declare function registerInlineAsset(bytes: Uint8Array, mime: string): string;
152
144
  */
153
145
  declare function registerDataUriAsset(dataUri: string): string;
154
146
 
147
+ interface LayoutCollision {
148
+ readonly firstId: string;
149
+ readonly secondId: string;
150
+ }
151
+ declare function createLayout(elementIds: readonly string[], mode: LayoutMode): void;
152
+ declare function detachFromLayout(elementIds: readonly string[]): void;
153
+ declare function layoutIdsFor(elementIds: readonly string[]): string[];
154
+ declare function findLayoutCollisions(def: AnimationDocument): LayoutCollision[];
155
+
156
+ declare function addCheckpoint(checkpoint: Checkpoint): void;
157
+ declare function updateCheckpoint(id: string, patch: Partial<Checkpoint>): void;
158
+ declare function deleteCheckpoint(id: string): void;
159
+ declare function uniqueCheckpointId(): string;
160
+
161
+ type EditorPluginSlot = "toolbar" | "panel" | "inspector";
162
+ interface EditorPluginManifestLike {
163
+ id: string;
164
+ name: string;
165
+ capabilities: readonly string[];
166
+ editor?: {
167
+ toolbarItems?: readonly string[];
168
+ panels?: readonly string[];
169
+ inspectors?: readonly string[];
170
+ };
171
+ }
172
+ interface EditorPluginPermissions {
173
+ ui?: boolean;
174
+ documentRead?: boolean;
175
+ documentWrite?: boolean;
176
+ }
177
+ interface EditorPluginContext {
178
+ readonly pluginId: string;
179
+ getDocument(): AnimationDocument;
180
+ replaceDocument(document: AnimationDocument): void;
181
+ getSelection(): Selection;
182
+ setSelection(selection: Selection): void;
183
+ }
184
+ interface EditorPluginView {
185
+ id: string;
186
+ label: string;
187
+ mount(container: HTMLElement, context: EditorPluginContext): void | (() => void);
188
+ }
189
+ interface EditorPluginDefinition {
190
+ manifest: EditorPluginManifestLike;
191
+ toolbarItems?: Record<string, EditorPluginView>;
192
+ panels?: Record<string, EditorPluginView>;
193
+ inspectors?: Record<string, EditorPluginView>;
194
+ commands?: readonly PaletteCommand[];
195
+ }
196
+ type EditorPluginPermissionResolver = (plugin: EditorPluginManifestLike) => EditorPluginPermissions;
197
+ type DocumentImporter = (input: unknown) => AnimationDocument | Promise<AnimationDocument>;
198
+ interface EditorPluginHostState {
199
+ getDocument(): AnimationDocument | null;
200
+ replaceDocument(document: AnimationDocument): void;
201
+ getSelection(): Selection;
202
+ setSelection(selection: Selection): void;
203
+ }
204
+ interface EditorPluginMountResult {
205
+ commands: PaletteCommand[];
206
+ dispose(): void;
207
+ }
208
+ interface EditorPluginIssue {
209
+ pluginId: string;
210
+ message: string;
211
+ }
212
+ declare function validateEditorPlugin(plugin: EditorPluginDefinition): EditorPluginIssue[];
213
+ declare function createEditorPluginContext(pluginId: string, permissions: EditorPluginPermissions, state: EditorPluginHostState): EditorPluginContext;
214
+ declare function mountEditorPlugins(root: HTMLElement, plugins: readonly EditorPluginDefinition[], resolvePermissions: EditorPluginPermissionResolver, state: EditorPluginHostState): EditorPluginMountResult;
215
+
216
+ interface StoryEditorOptions {
217
+ manifest: StoryManifest;
218
+ onChange?(manifest: StoryManifest): void;
219
+ }
220
+ declare function replaceStoryNodeDocument(manifest: StoryManifest, nodeId: string, document: AnimationDocument): StoryManifest;
221
+ declare function appendStoryEdge(manifest: StoryManifest, edge: StoryEdge): StoryManifest;
222
+ declare function createStoryEditorPlugin(options: StoryEditorOptions): EditorPluginDefinition;
223
+
224
+ interface StudioMountProps {
225
+ initialId?: string;
226
+ repository?: AnimationRepository;
227
+ editorTitle?: string;
228
+ resolveImage?: ImageUploadResolver;
229
+ importDocument?: DocumentImporter;
230
+ plugins?: readonly EditorPluginDefinition[];
231
+ resolvePluginPermissions?: EditorPluginPermissionResolver;
232
+ /** Trusted build-time templates shown as a parameter panel. */
233
+ templates?: readonly AnimationTemplate[];
234
+ /** Optional multi-document Story Graph edited alongside the active node timeline. */
235
+ story?: StoryEditorOptions;
236
+ }
237
+ declare function StudioMount({ initialId, repository, editorTitle, resolveImage, importDocument, plugins, resolvePluginPermissions, templates, story, }: StudioMountProps): React.JSX.Element;
238
+
239
+ /** 검증된 애니메이션 문서를 읽기 쉬운 JSON으로 변환한다. */
240
+ declare function animationDocumentToJson(def: AnimationDocument): string;
241
+ /** 어떤 image 요소에서도 참조하지 않는 asset을 제거한 새 문서를 반환한다. */
242
+ declare function garbageCollectAnimationAssets(def: AnimationDocument): AnimationDocument;
243
+ /** 문서 ID를 운영체제에서 안전하게 사용할 수 있는 파일 이름으로 바꾼다. */
244
+ declare function animationDocumentFileName(def: AnimationDocument): string;
245
+ /** 현재 문서를 브라우저에서 JSON 파일로 내려받는다. */
246
+ declare function downloadAnimationJson(def: AnimationDocument): void;
247
+
155
248
  declare function isGroup(el: AnimationElement): el is GroupElement;
156
249
  /** Direct children of a group, in document order (which is also paint order). */
157
250
  declare function childIdsOf(groupId: string): string[];
@@ -173,4 +266,42 @@ interface HostOptions {
173
266
  declare function configureHost(options: HostOptions): void;
174
267
  declare function placeholderImageUrl(): string;
175
268
 
176
- export { type AnimationRepository, type AnimationSummary, type HistoryEntry, type HistoryKind, type HostOptions, type ImageUploadResolver, type LocalStorageRepositoryOptions, type Selection, Studio, StudioMount, type StudioMountProps, type StudioProps, addAppearance, addChapter, addEffect, addElement, animationDocumentFileName, animationDocumentToJson, apiBaseUrl, beginTransient, canRedo, canUndo, childIdsOf, configureAnimationRepository, configureApi, configureHost, createLocalStorageRepository, deleteChapter, deleteEffect, deleteElement, downloadAnimationJson, endTransient, garbageCollectAnimationAssets, getCurrentSnapshot, getCurrentTime, getDef, getHistory, getSelectedElementIds, getSelection, groupElements, isDirty, isDraft, isElementSelected, isGroup, jumpBack, jumpForward, markClean, moveElementToEnd, moveElementToFront, placeholderImageUrl, promoteDraftToSaved, redo, registerDataUriAsset, registerExternalAsset, registerInlineAsset, removeAppearance, removeTrack, removeTrackKeyframe, reorderElement, resetHistory, setCurrentTime, setDef, setDraft, setElementValueAtTime, setSelection, setTrackKeyframe, subscribe, toggleSelectionFor, undo, ungroupElement, uniqueChapterId, uniqueEffectId, uniqueElementId, updateAppearance, updateCanvas, updateChapter, updateDuration, updateEffect, updateElementBase, updateLocales, updateMeta, updateSettings };
269
+ declare function initialParameterValue(schema: ParameterSchema): unknown;
270
+ declare function parameterInputValue(schema: ParameterSchema, raw: string | boolean): unknown;
271
+ /** Built-in authoring panel for trusted templates registered by the host. */
272
+ declare function createTemplateEditorPlugin(templates: readonly AnimationTemplate[]): EditorPluginDefinition;
273
+
274
+ declare function createVisualRegressionPlugin(): EditorPluginDefinition;
275
+
276
+ declare const RESPONSIVE_VIEWPORTS: readonly [{
277
+ readonly id: "compact";
278
+ readonly label: "모바일";
279
+ readonly width: 375;
280
+ }, {
281
+ readonly id: "regular";
282
+ readonly label: "본문";
283
+ readonly width: 768;
284
+ }, {
285
+ readonly id: "wide";
286
+ readonly label: "넓은 화면";
287
+ readonly width: 1280;
288
+ }];
289
+ declare function addDefaultResponsiveVariants(document: AnimationDocument): AnimationDocument;
290
+ declare function createResponsiveInspectorPlugin(): EditorPluginDefinition;
291
+
292
+ interface SceneProfile {
293
+ elementCount: number;
294
+ trackCount: number;
295
+ keyframeCount: number;
296
+ sampleCount: number;
297
+ totalMs: number;
298
+ averageMs: number;
299
+ maxMs: number;
300
+ overBudget: boolean;
301
+ }
302
+ declare function profileAnimation(document: AnimationDocument, sampleCount?: number, frameBudgetMs?: number): SceneProfile;
303
+ declare function createPerformanceProfilerPlugin(): EditorPluginDefinition;
304
+
305
+ declare function createLinterPlugin(): EditorPluginDefinition;
306
+
307
+ export { type AnimationRepository, type AnimationSummary, type DocumentImporter, type EditorPluginContext, type EditorPluginDefinition, type EditorPluginHostState, type EditorPluginManifestLike, type EditorPluginMountResult, type EditorPluginPermissionResolver, type EditorPluginPermissions, type EditorPluginSlot, type EditorPluginView, type HistoryEntry, type HistoryKind, type HostOptions, type ImageUploadResolver, type LocalStorageRepositoryOptions, RESPONSIVE_VIEWPORTS, type SceneProfile, type Selection, type StoryEditorOptions, Studio, StudioMount, type StudioMountProps, type StudioProps, addAppearance, addChapter, addCheckpoint, addDefaultResponsiveVariants, addEffect, addElement, animationDocumentFileName, animationDocumentToJson, apiBaseUrl, appendStoryEdge, beginTransient, canRedo, canUndo, childIdsOf, configureAnimationRepository, configureApi, configureHost, createEditorPluginContext, createLayout, createLinterPlugin, createLocalStorageRepository, createPerformanceProfilerPlugin, createResponsiveInspectorPlugin, createStoryEditorPlugin, createTemplateEditorPlugin, createVisualRegressionPlugin, deleteChapter, deleteCheckpoint, deleteEffect, deleteElement, detachFromLayout, downloadAnimationJson, endTransient, findLayoutCollisions, garbageCollectAnimationAssets, getCurrentSnapshot, getCurrentTime, getDef, getHistory, getSelectedElementIds, getSelection, groupElements, initialParameterValue, isDirty, isDraft, isElementSelected, isGroup, jumpBack, jumpForward, layoutIdsFor, markClean, mountEditorPlugins, moveElementToEnd, moveElementToFront, parameterInputValue, placeholderImageUrl, profileAnimation, promoteDraftToSaved, redo, registerDataUriAsset, registerExternalAsset, registerInlineAsset, removeAppearance, removeTrack, removeTrackKeyframe, reorderElement, replaceStoryNodeDocument, resetHistory, setCurrentTime, setDef, setDraft, setElementValueAtTime, setSelection, setTrackKeyframe, subscribe, toggleSelectionFor, undo, ungroupElement, uniqueChapterId, uniqueCheckpointId, uniqueEffectId, uniqueElementId, updateAppearance, updateCanvas, updateChapter, updateCheckpoint, updateData, updateDuration, updateEffect, updateElementBase, updateLocales, updateMeta, updateResponsive, updateSettings, validateEditorPlugin };