@kokoa/clotho-editor 0.1.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.
- package/LICENSE +21 -0
- package/README.md +54 -0
- package/dist/chunk-UE32Q6U5.js +943 -0
- package/dist/chunk-UE32Q6U5.js.map +1 -0
- package/dist/clotho-editor.css +1921 -0
- package/dist/index.d.ts +138 -0
- package/dist/index.js +425 -0
- package/dist/index.js.map +1 -0
- package/dist/main-SWHHYTLM.js +16214 -0
- package/dist/main-SWHHYTLM.js.map +1 -0
- package/docs/PORTING.md +136 -0
- package/package.json +64 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/Studio.tsx","../src/StudioMount.tsx"],"names":["setDef","addElement","deleteElement","useEffect","jsx"],"mappings":"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","file":"index.js","sourcesContent":["\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport type { AnimationDocument } from \"@kokoa/clotho\";\nimport { AnimationStage, usePlayer } from \"@kokoa/clotho/react\";\n\ntype EditableElement = Record<string, unknown> & { id: string; type: string };\ntype ElementList = AnimationDocument[\"elements\"];\n\nexport interface StudioProps {\n initial: AnimationDocument;\n onSave: (def: AnimationDocument) => void | Promise<void>;\n}\n\nfunction elementsOf(def: AnimationDocument): EditableElement[] {\n return def.elements as unknown as EditableElement[];\n}\n\nexport function Studio({ initial, onSave }: StudioProps): React.JSX.Element {\n const [def, setDef] = useState<AnimationDocument>(initial);\n const [selectedId, setSelectedId] = useState<string | null>(\n elementsOf(initial)[0]?.id ?? null,\n );\n const [playing, setPlaying] = useState(false);\n const [time, setTime] = useState(0);\n const [saving, setSaving] = useState(false);\n const [saved, setSaved] = useState(false);\n\n // The clock for preview playback. It lives outside React (core/player), so the\n // editor subscribes rather than owning a rAF loop of its own.\n const { player, state: playerState } = usePlayer(def, { autoplay: false });\n\n useEffect(() => {\n if (playing) player.play();\n else {\n player.pause();\n player.seek(time);\n }\n }, [playing, player, time]);\n\n const elements = elementsOf(def);\n const selected = elements.find((e) => e.id === selectedId) ?? null;\n const scalarProps = selected\n ? Object.entries(selected).filter(\n ([key, value]) =>\n (typeof value === \"string\" || typeof value === \"number\") &&\n key !== \"id\" &&\n key !== \"type\",\n )\n : [];\n\n function setElements(next: EditableElement[]): void {\n setDef((current) => ({\n ...current,\n elements: next as unknown as ElementList,\n }));\n setSaved(false);\n }\n\n function updateElement(\n id: string,\n key: string,\n raw: string,\n numeric: boolean,\n ): void {\n setElements(\n elements.map((e) =>\n e.id === id ? { ...e, [key]: numeric ? Number(raw) : raw } : e,\n ),\n );\n }\n\n function addElement(): void {\n const id = `el-${Date.now().toString(36)}`;\n const el: EditableElement = {\n type: \"text\",\n id,\n x: 100,\n y: 100,\n rotation: 0,\n content: \"New\",\n fontSize: 24,\n color: \"#4f46e5\",\n textAnchor: \"start\",\n appearances: [{ start: 0, end: def.duration }],\n tracks: [],\n };\n setElements([...elements, el]);\n setSelectedId(id);\n }\n\n function deleteElement(id: string): void {\n setElements(elements.filter((e) => e.id !== id));\n if (selectedId === id) setSelectedId(null);\n }\n\n async function handleSave(): Promise<void> {\n setSaving(true);\n try {\n await onSave(def);\n setSaved(true);\n } finally {\n setSaving(false);\n }\n }\n\n return (\n <div className=\"studio\">\n <aside className=\"studio-panel studio-left\">\n <div className=\"studio-panel-head\">\n <span>요소 ({elements.length})</span>\n <button type=\"button\" className=\"studio-btn\" onClick={addElement}>\n + 추가\n </button>\n </div>\n <ul className=\"studio-el-list\">\n {elements.map((e) => (\n <li key={e.id}>\n <button\n type=\"button\"\n className={\n e.id === selectedId ? \"studio-el active\" : \"studio-el\"\n }\n onClick={() => setSelectedId(e.id)}\n >\n <span className=\"studio-el-type\">{e.type}</span>\n <span className=\"studio-el-id\">{e.id}</span>\n </button>\n </li>\n ))}\n </ul>\n </aside>\n\n <div className=\"studio-preview\">\n <div className=\"studio-preview-bar\">\n <button\n type=\"button\"\n className=\"studio-btn\"\n onClick={() => setPlaying((p) => !p)}\n >\n {playing ? \"⏸ 정지\" : \"▶ 재생\"}\n </button>\n <input\n type=\"range\"\n min={0}\n max={def.duration}\n step={50}\n value={time}\n disabled={playing}\n onChange={(e) => {\n setTime(Number(e.target.value));\n setPlaying(false);\n }}\n />\n <span className=\"studio-time\">\n {time} / {def.duration}ms\n </span>\n <button\n type=\"button\"\n className=\"studio-btn studio-save\"\n onClick={() => void handleSave()}\n disabled={saving}\n >\n {saving ? \"저장 중…\" : saved ? \"✓ 저장됨\" : \"저장\"}\n </button>\n </div>\n <div className=\"studio-canvas\">\n {/* One frame at a time: the editor's scrubber is the source of truth, and the\n player only supplies the clock while previewing. Same buildScene path as\n the published animation, so preview and production cannot diverge. */}\n <AnimationStage doc={def} time={playing ? playerState.time : time} />\n </div>\n </div>\n\n <aside className=\"studio-panel studio-right\">\n <div className=\"studio-panel-head\">메타</div>\n <label className=\"studio-field\">\n <span>제목</span>\n <input\n value={def.title}\n onChange={(e) => {\n setDef((d) => ({ ...d, title: e.target.value }));\n setSaved(false);\n }}\n />\n </label>\n <label className=\"studio-field\">\n <span>duration (ms)</span>\n <input\n type=\"number\"\n value={def.duration}\n onChange={(e) => {\n setDef((d) => ({ ...d, duration: Number(e.target.value) }));\n setSaved(false);\n }}\n />\n </label>\n <div className=\"studio-panel-head\">\n 속성 {selected ? `(${selected.type})` : \"\"}\n </div>\n {selected ? (\n <>\n {scalarProps.map(([key, value]) => (\n <label key={key} className=\"studio-field\">\n <span>{key}</span>\n <input\n value={String(value)}\n onChange={(e) =>\n updateElement(\n selected.id,\n key,\n e.target.value,\n typeof value === \"number\",\n )\n }\n />\n </label>\n ))}\n <button\n type=\"button\"\n className=\"studio-btn studio-del\"\n onClick={() => deleteElement(selected.id)}\n >\n 요소 삭제\n </button>\n </>\n ) : (\n <p className=\"studio-empty\">요소를 선택하세요.</p>\n )}\n </aside>\n </div>\n );\n}\n","\"use client\";\n\nimport { useEffect, useRef } from \"react\";\n// The stylesheet ships as a separate entry rather than an import, so a consumer that\n// only wants the headless pieces does not pull CSS into its bundle:\n// import \"@kokoa/clotho-editor/styles.css\";\n\nexport interface StudioMountProps {\n initialId?: string;\n}\n\nconst CANVAS_PRESETS: {\n id: string;\n label: string;\n sub: string;\n w: number;\n h: number;\n}[] = [\n { id: \"800x500\", label: \"8:5\", sub: \"800×500\", w: 32, h: 20 },\n { id: \"1280x720\", label: \"16:9\", sub: \"HD\", w: 32, h: 18 },\n { id: \"1024x768\", label: \"4:3\", sub: \"1024×768\", w: 28, h: 21 },\n { id: \"600x600\", label: \"1:1\", sub: \"정사각\", w: 22, h: 22 },\n { id: \"1200x400\", label: \"3:1\", sub: \"와이드\", w: 36, h: 12 },\n { id: \"375x812\", label: \"9:19\", sub: \"모바일\", w: 14, h: 30 },\n];\n\nconst PRESET_HTML = CANVAS_PRESETS.map(\n (p) => `\n <button type=\"button\" class=\"studio-preset-btn\" aria-label=\"캔버스 크기 ${p.id} (${p.sub})\" title=\"${p.id} · ${p.sub}\" data-canvas-preset=\"${p.id}\">\n <svg class=\"studio-preset-thumb\" viewBox=\"0 0 ${p.w + 2} ${p.h + 2}\" width=\"${p.w + 2}\" height=\"${p.h + 2}\" aria-hidden=\"true\">\n <rect x=\"1\" y=\"1\" width=\"${p.w}\" height=\"${p.h}\" rx=\"2\" />\n </svg>\n <span class=\"studio-preset-label\">${p.label}</span>\n <span class=\"studio-preset-sub\">${p.sub}</span>\n </button>`,\n).join(\"\");\n\nconst SKELETON = `\n<div id=\"studio-app\" class=\"studio-app\">\n <header class=\"studio-header\">\n <h1 class=\"studio-header-title\">🎬 애니메이션 스튜디오</h1>\n <div class=\"studio-header-actions\">\n <button type=\"button\" id=\"studio-open\" class=\"studio-btn\" aria-label=\"저장된 애니메이션 열기\">📁 열기</button>\n <button type=\"button\" id=\"studio-new\" class=\"studio-btn\" aria-label=\"새 애니메이션 만들기\">+ 새 애니메이션</button>\n <button type=\"button\" id=\"studio-undo\" class=\"studio-btn studio-btn-icon\" aria-label=\"실행 취소\" title=\"실행 취소 (⌘Z)\" disabled>↶</button>\n <button type=\"button\" id=\"studio-redo\" class=\"studio-btn studio-btn-icon\" aria-label=\"다시 실행\" title=\"다시 실행 (⌘⇧Z / ⌘Y)\" disabled>↷</button>\n <button type=\"button\" id=\"studio-grid-toggle\" class=\"studio-btn studio-btn-grid\" aria-label=\"격자 + 스냅\" title=\"격자 + 스냅 (G)\" aria-pressed=\"false\"><span class=\"studio-btn-grid-icon\">⊞</span><span class=\"studio-btn-grid-label\" id=\"studio-grid-label\">격자 끔</span></button>\n <input type=\"text\" id=\"studio-title\" class=\"studio-title-input\" placeholder=\"제목\" aria-label=\"애니메이션 제목\" disabled />\n <span id=\"studio-id-display\" class=\"studio-id-display\"></span>\n <span id=\"studio-status\" class=\"studio-status\" aria-live=\"polite\">대기 중</span>\n <button type=\"button\" id=\"studio-save\" class=\"studio-btn studio-btn-primary\" aria-label=\"저장\" disabled>💾 저장 (⌘S)</button>\n <button type=\"button\" id=\"studio-delete\" class=\"studio-btn studio-btn-danger\" aria-label=\"삭제\" disabled>🗑 삭제</button>\n </div>\n </header>\n\n <div class=\"studio-body\">\n <aside class=\"studio-tools\" aria-label=\"요소 추가\">\n <div class=\"studio-tools-section\">\n <div class=\"studio-tools-title\">요소 추가</div>\n <button type=\"button\" class=\"studio-tool-btn\" data-add-element=\"rect\" title=\"사각형\" aria-label=\"사각형 추가\">□ Rect</button>\n <button type=\"button\" class=\"studio-tool-btn\" data-add-element=\"circle\" title=\"원\" aria-label=\"원 추가\">○ Circle</button>\n <button type=\"button\" class=\"studio-tool-btn\" data-add-element=\"line\" title=\"선\" aria-label=\"선 추가\">/ Line</button>\n <button type=\"button\" class=\"studio-tool-btn\" data-add-element=\"arrow\" title=\"화살표\" aria-label=\"화살표 추가\">↗ Arrow</button>\n <button type=\"button\" class=\"studio-tool-btn\" data-add-element=\"text\" title=\"텍스트\" aria-label=\"텍스트 추가\">T Text</button>\n <button type=\"button\" class=\"studio-tool-btn\" data-add-element=\"image\" title=\"이미지\" aria-label=\"이미지 추가\">🖼 Image</button>\n <button type=\"button\" class=\"studio-tool-btn\" data-add-element=\"path\" title=\"SVG path\" aria-label=\"SVG path 추가\">✎ Path</button>\n <button type=\"button\" class=\"studio-tool-btn\" data-add-element=\"polygon\" title=\"다각형\" aria-label=\"다각형 추가\">⬢ Polygon</button>\n <button type=\"button\" class=\"studio-tool-btn\" id=\"studio-open-icons\" title=\"아이콘 라이브러리\" aria-label=\"아이콘 라이브러리 추가\">🎨 Icons</button>\n </div>\n <div class=\"studio-tools-section\">\n <div class=\"studio-tools-title\">캔버스 크기</div>\n <div class=\"studio-canvas-size-row\">\n <input type=\"number\" id=\"studio-canvas-width\" class=\"studio-canvas-size-input\" min=\"100\" max=\"8000\" step=\"10\" placeholder=\"width\" />\n <span class=\"studio-canvas-size-x\">×</span>\n <input type=\"number\" id=\"studio-canvas-height\" class=\"studio-canvas-size-input\" min=\"100\" max=\"8000\" step=\"10\" placeholder=\"height\" />\n </div>\n <div class=\"studio-canvas-presets\">${PRESET_HTML}</div>\n </div>\n <div class=\"studio-tools-section\">\n <div class=\"studio-tools-title\">이미지</div>\n <button type=\"button\" class=\"studio-tool-btn\" id=\"studio-image-upload\">📤 이미지 업로드</button>\n <input type=\"file\" id=\"studio-image-file\" accept=\"image/*\" hidden />\n <div class=\"studio-tools-hint\">이미지를 캔버스에 직접 드래그하거나 ⌘V 로 붙여넣어도 됩니다.</div>\n </div>\n <div class=\"studio-tools-section\">\n <div class=\"studio-tools-title\">단축키</div>\n <button type=\"button\" class=\"studio-tool-btn\" id=\"studio-help\">⌨ 단축키 보기 (?)</button>\n </div>\n <div class=\"studio-tools-section\">\n <div class=\"studio-tools-title\">요소 목록</div>\n <input type=\"text\" id=\"studio-element-search\" class=\"studio-element-search\" placeholder=\"🔍 검색 (name / label / id / type)\" autocomplete=\"off\" />\n <ul id=\"studio-element-list\" class=\"studio-element-list\"></ul>\n </div>\n </aside>\n\n <main class=\"studio-canvas-wrap\">\n <div class=\"studio-canvas-frame\">\n <svg id=\"studio-canvas\" class=\"studio-canvas\" xmlns=\"http://www.w3.org/2000/svg\"></svg>\n </div>\n <button type=\"button\" id=\"studio-floating-help\" class=\"studio-floating-help\" aria-label=\"단축키 보기\" title=\"단축키 보기 (? / Shift+/)\">?</button>\n <div id=\"studio-help-hint\" class=\"studio-help-hint\" hidden>\n <span>💡 단축키는 <kbd>?</kbd> 또는 우측 하단 버튼으로 확인하세요</span>\n <button type=\"button\" id=\"studio-help-hint-close\" aria-label=\"닫기\">✕</button>\n </div>\n </main>\n\n <aside class=\"studio-props\" aria-label=\"속성 패널\">\n <div id=\"studio-props-content\" class=\"studio-props-content\">\n <p class=\"studio-props-empty\">요소 또는 step 을 선택하세요.</p>\n </div>\n </aside>\n </div>\n\n <div id=\"studio-timeline-resizer\" class=\"studio-timeline-resizer\" role=\"separator\" aria-orientation=\"horizontal\" aria-label=\"타임라인 영역 크기 조정\"></div>\n <footer class=\"studio-timeline-wrap\">\n <div class=\"studio-timeline-header\">\n <button type=\"button\" id=\"studio-play\" class=\"studio-btn\">▶ Play</button>\n <button type=\"button\" id=\"studio-restart\" class=\"studio-btn\">⟲ Reset</button>\n <label class=\"studio-speed\">속도\n <input type=\"range\" id=\"studio-speed\" min=\"0.25\" max=\"3\" step=\"0.25\" value=\"1\" />\n <span id=\"studio-speed-value\">1.00x</span>\n </label>\n <span class=\"studio-timeline-spacer\"></span>\n <button type=\"button\" id=\"studio-add-step\" class=\"studio-btn\">+ Chapter 추가</button>\n </div>\n <div class=\"studio-timeline-tracks-wrap\">\n <div id=\"studio-timeline-tracks\" class=\"studio-timeline-tracks\"></div>\n </div>\n <div class=\"studio-element-tracks-wrap\">\n <div id=\"studio-element-tracks\" class=\"studio-element-tracks\"></div>\n </div>\n </footer>\n\n <dialog id=\"studio-library-dialog\" class=\"studio-dialog\">\n <div class=\"studio-dialog-header\">\n <h2>저장된 애니메이션</h2>\n <button type=\"button\" class=\"studio-dialog-close\" data-studio-dialog-close>✕</button>\n </div>\n <div class=\"studio-dialog-body\">\n <ul id=\"studio-library-list\" class=\"studio-library-list\"></ul>\n </div>\n </dialog>\n\n <dialog id=\"studio-icon-dialog\" class=\"studio-dialog\">\n <div class=\"studio-dialog-header\">\n <h2>🎨 아이콘 라이브러리</h2>\n <button type=\"button\" class=\"studio-dialog-close\" data-icon-dialog-close>✕</button>\n </div>\n <div class=\"studio-dialog-body\">\n <input type=\"search\" id=\"studio-icon-search\" placeholder=\"아이콘 검색…\" class=\"studio-icon-search-input\" />\n <div id=\"studio-icon-list\"></div>\n </div>\n </dialog>\n\n <dialog id=\"studio-help-dialog\" class=\"studio-dialog studio-dialog-small\">\n <div class=\"studio-dialog-header\">\n <h2>⌨ 단축키</h2>\n <button type=\"button\" class=\"studio-dialog-close\" data-studio-dialog-close>✕</button>\n </div>\n <div class=\"studio-dialog-body\">\n <table class=\"studio-help-table\">\n <tbody>\n <tr><th>⌘ S</th><td>저장</td></tr>\n <tr><th>⌘ Z / ⌘ ⇧ Z</th><td>Undo / Redo</td></tr>\n <tr><th>⌘ C / V / X</th><td>요소 복사 / 붙여넣기 / 잘라내기</td></tr>\n <tr><th>Delete / Backspace</th><td>선택된 요소 또는 step 삭제</td></tr>\n <tr><th>?</th><td>이 화면 열기</td></tr>\n <tr><th>Esc</th><td>선택 해제 / 다이얼로그 닫기</td></tr>\n <tr><th>드래그 (캔버스)</th><td>요소 이동</td></tr>\n <tr><th>드래그 (요소 위 dot)</th><td>화살표 연결</td></tr>\n <tr><th>드래그 (선/화살표 끝점)</th><td>끝점 재배치 + 다른 요소에 sticky</td></tr>\n <tr><th>드래그 (회전 핸들)</th><td>요소 회전</td></tr>\n <tr><th>드래그 (타임라인 바 우측)</th><td>step duration 조정</td></tr>\n <tr><th>이미지 드래그 in</th><td>이미지 업로드 + 배치</td></tr>\n </tbody>\n </table>\n </div>\n </dialog>\n\n <dialog id=\"studio-new-dialog\" class=\"studio-dialog studio-dialog-small\">\n <div class=\"studio-dialog-header\">\n <h2 id=\"studio-new-dialog-title\">새 애니메이션 만들기</h2>\n <button type=\"button\" class=\"studio-dialog-close\" data-studio-dialog-close aria-label=\"닫기\">✕</button>\n </div>\n <div class=\"studio-dialog-body\">\n <label class=\"studio-field\"><span>ID (영문 소문자 / 숫자 / - / _)</span>\n <input type=\"text\" id=\"studio-new-id\" placeholder=\"예: user-login-flow\" />\n </label>\n <label class=\"studio-field\"><span>제목</span>\n <input type=\"text\" id=\"studio-new-title\" placeholder=\"사용자 로그인 흐름\" />\n </label>\n <div id=\"studio-new-error\" class=\"studio-new-error\"></div>\n </div>\n <footer class=\"studio-dialog-footer\">\n <button type=\"button\" class=\"studio-btn\" data-studio-dialog-close>취소</button>\n <button type=\"button\" id=\"studio-new-create\" class=\"studio-btn studio-btn-primary\">만들기</button>\n </footer>\n </dialog>\n\n <dialog id=\"studio-palette-dialog\" class=\"studio-palette-dialog\">\n <input type=\"text\" id=\"studio-palette-input\" class=\"studio-palette-input\" placeholder=\"🔍 명령 또는 요소 검색…\" autocomplete=\"off\" spellcheck=\"false\" />\n <ul id=\"studio-palette-list\" class=\"studio-palette-list\"></ul>\n <div class=\"studio-palette-footer\">↑↓ 이동 · Enter 실행 · Esc 닫기</div>\n </dialog>\n\n <dialog id=\"studio-history-dialog\" class=\"studio-history-dialog\">\n <div class=\"studio-history-header\">\n <h2>📜 작업 이력</h2>\n <button type=\"button\" class=\"studio-dialog-close\" data-studio-dialog-close aria-label=\"닫기\">✕</button>\n </div>\n <ul id=\"studio-history-list\" class=\"studio-history-list\"></ul>\n <div class=\"studio-history-footer\">항목을 클릭하면 해당 시점으로 이동합니다 · ⌘Z / ⌘⇧Z</div>\n </dialog>\n</div>\n`;\n\nexport function StudioMount({\n initialId,\n}: StudioMountProps): React.JSX.Element {\n const inited = useRef(false);\n\n useEffect(() => {\n if (inited.current) return;\n inited.current = true;\n let disposed = false;\n void import(\"./legacy/main\").then(({ initStudio }) => {\n if (disposed) return;\n initStudio(initialId ? { initialId } : undefined);\n });\n return () => {\n disposed = true;\n document.body.classList.remove(\"editor-active\");\n document.documentElement.classList.remove(\"editor-active\");\n };\n }, [initialId]);\n\n return (\n <section className=\"studio-shell w-full\" data-pagefind-ignore=\"all\">\n <div dangerouslySetInnerHTML={{ __html: SKELETON }} />\n </section>\n );\n}\n"]}
|