@aicut/vue 0.4.2 → 0.5.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/README.md +105 -2
- package/dist/VideoEditor.vue.d.ts +22 -1
- package/dist/VideoEditor.vue.d.ts.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +87 -62
- package/dist/index.js.map +1 -1
- package/dist/webcodecs.cjs +2 -0
- package/dist/webcodecs.cjs.map +1 -0
- package/dist/webcodecs.d.ts +10 -0
- package/dist/webcodecs.d.ts.map +1 -0
- package/dist/webcodecs.js +7 -0
- package/dist/webcodecs.js.map +1 -0
- package/package.json +7 -2
package/README.md
CHANGED
|
@@ -74,11 +74,35 @@ editor.value?.api()?.setProject(saved);
|
|
|
74
74
|
```ts
|
|
75
75
|
interface VideoEditorProps {
|
|
76
76
|
defaultProject?: Project;
|
|
77
|
-
theme?: Theme;
|
|
78
|
-
locale?: Partial<Locale>;
|
|
77
|
+
theme?: Theme; // CSS-var overrides; reactive
|
|
78
|
+
locale?: Partial<Locale>; // EN default; pass localeZh for ZH; reactive
|
|
79
|
+
|
|
80
|
+
playbackEngine?: PlaybackEngineFactory; // pluggable playback; default
|
|
81
|
+
// HtmlVideoEngine. Bound at mount.
|
|
82
|
+
timelineHeight?: number; // outer height of bottom area
|
|
83
|
+
// (default 240). Reactive.
|
|
84
|
+
trackHeight?: number; // per-row height (default 56);
|
|
85
|
+
// process-wide, initial-only.
|
|
86
|
+
rulerHeight?: number; // time-label strip (default 24).
|
|
79
87
|
}
|
|
80
88
|
```
|
|
81
89
|
|
|
90
|
+
## Slots
|
|
91
|
+
|
|
92
|
+
Two named slots — `headerLeft` and `headerRight` — fill the optional header bar above the preview. Empty by default; the header collapses entirely when both are unused, so the default layout is identical to before they existed.
|
|
93
|
+
|
|
94
|
+
```vue
|
|
95
|
+
<VideoEditor :default-project="project">
|
|
96
|
+
<template #headerLeft>
|
|
97
|
+
<strong>Untitled project</strong>
|
|
98
|
+
</template>
|
|
99
|
+
<template #headerRight>
|
|
100
|
+
<button @click="share">Share</button>
|
|
101
|
+
<button @click="editor?.api()?.requestExport()">Export</button>
|
|
102
|
+
</template>
|
|
103
|
+
</VideoEditor>
|
|
104
|
+
```
|
|
105
|
+
|
|
82
106
|
## Events
|
|
83
107
|
|
|
84
108
|
```ts
|
|
@@ -130,6 +154,85 @@ const locale = computed<Locale>(() =>
|
|
|
130
154
|
|
|
131
155
|
`locale` swap re-titles the toolbar and re-paints canvas labels in place.
|
|
132
156
|
|
|
157
|
+
## Compact viewports
|
|
158
|
+
|
|
159
|
+
Default chrome is sized for desktop. For laptop side panels or embedded editors, shrink the bottom area to reclaim preview height:
|
|
160
|
+
|
|
161
|
+
```vue
|
|
162
|
+
<script setup lang="ts">
|
|
163
|
+
import { ref } from "vue";
|
|
164
|
+
const timelineHeight = ref(160);
|
|
165
|
+
</script>
|
|
166
|
+
|
|
167
|
+
<template>
|
|
168
|
+
<VideoEditor
|
|
169
|
+
:default-project="project"
|
|
170
|
+
:timeline-height="timelineHeight"
|
|
171
|
+
:track-height="40"
|
|
172
|
+
/>
|
|
173
|
+
</template>
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
`timelineHeight` is reactive — bind it to a slider and the editor recompacts in place. `trackHeight` / `rulerHeight` are initial-only (process-wide via `setTimelineMetrics`); change + remount to re-apply. Range guidance: `timelineHeight` ∈ [120, 480], `trackHeight` ∈ [28, 96], `rulerHeight` ∈ [18, 36].
|
|
177
|
+
|
|
178
|
+
## Custom playback engine
|
|
179
|
+
|
|
180
|
+
The editor talks to playback through a single interface. The default is
|
|
181
|
+
`HtmlVideoEngine` (one hidden `<video>` per source, swap on clip
|
|
182
|
+
boundaries). To plug in a different one — WebCodecs, WebGL compositor,
|
|
183
|
+
desktop-wrapper IPC bridge — pass a factory:
|
|
184
|
+
|
|
185
|
+
```vue
|
|
186
|
+
<script setup lang="ts">
|
|
187
|
+
import { VideoEditor, type PlaybackEngineFactory } from "@aicut/vue";
|
|
188
|
+
|
|
189
|
+
const myEngine: PlaybackEngineFactory = ({ host, project }) =>
|
|
190
|
+
new MyCustomEngine(host, project); // implements PlaybackEngine
|
|
191
|
+
</script>
|
|
192
|
+
|
|
193
|
+
<template>
|
|
194
|
+
<VideoEditor
|
|
195
|
+
:default-project="project"
|
|
196
|
+
:playback-engine="myEngine"
|
|
197
|
+
/* initial-only — bound at mount */
|
|
198
|
+
/>
|
|
199
|
+
</template>
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
`PlaybackEngine`, `PlaybackEngineFactory`, `PlaybackEngineOptions`, and
|
|
203
|
+
the built-in `HtmlVideoEngine` are re-exported from `@aicut/vue` so
|
|
204
|
+
you don't need a separate `@aicut/core` import to write one.
|
|
205
|
+
|
|
206
|
+
See [@aicut/core's playback section](https://www.npmjs.com/package/@aicut/core#playback-engine)
|
|
207
|
+
for the full interface contract.
|
|
208
|
+
|
|
209
|
+
### WebCodecs engine (opt-in sub-entry)
|
|
210
|
+
|
|
211
|
+
For frame-accurate playback via the browser's `VideoDecoder` API, import from the sub-entry so mp4box.js (~200 KB) only loads when you ask for it:
|
|
212
|
+
|
|
213
|
+
```vue
|
|
214
|
+
<script setup lang="ts">
|
|
215
|
+
import { computed } from "vue";
|
|
216
|
+
import { VideoEditor } from "@aicut/vue";
|
|
217
|
+
import {
|
|
218
|
+
WebCodecsEngine,
|
|
219
|
+
isWebCodecsSupported,
|
|
220
|
+
} from "@aicut/vue/webcodecs";
|
|
221
|
+
|
|
222
|
+
const factory = computed(() =>
|
|
223
|
+
isWebCodecsSupported()
|
|
224
|
+
? (opts) => new WebCodecsEngine({ ...opts, debug: true })
|
|
225
|
+
: undefined,
|
|
226
|
+
);
|
|
227
|
+
</script>
|
|
228
|
+
|
|
229
|
+
<template>
|
|
230
|
+
<VideoEditor :playback-engine="factory" /* … */ />
|
|
231
|
+
</template>
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
`WebCodecsEngine` v1 covers single-track MP4/MOV playback (H.264 / HEVC / VP9 / AV1 — whatever the browser's `VideoDecoder` supports). Multi-track compositing, audio, transitions land in follow-up releases.
|
|
235
|
+
|
|
133
236
|
## `<LightingEditor>` (opt-in sub-entry)
|
|
134
237
|
|
|
135
238
|
A 3D lighting director for AI relighting flows — separate sub-entry; three.js bundles only here.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type EditorApi, type Locale, type Project, type Theme } from "@aicut/core";
|
|
1
|
+
import { type EditorApi, type Locale, type PlaybackEngineFactory, type Project, type Theme } from "@aicut/core";
|
|
2
2
|
/**
|
|
3
3
|
* Vue 3 wrapper around `@aicut/core`. Same shape as `@aicut/react`:
|
|
4
4
|
* uncontrolled for project state, theme is reactive, API exposed via
|
|
@@ -9,6 +9,27 @@ type __VLS_Props = {
|
|
|
9
9
|
theme?: Theme;
|
|
10
10
|
/** UI string overrides (English default). Reactive — swap to `localeZh` for Chinese. */
|
|
11
11
|
locale?: Partial<Locale>;
|
|
12
|
+
/**
|
|
13
|
+
* Initial-only factory for a custom playback engine. Defaults to the
|
|
14
|
+
* built-in `HtmlVideoEngine`. Pass `WebCodecsEngine` (v0.6+) or your
|
|
15
|
+
* own engine to override. Bound at mount; later prop changes are
|
|
16
|
+
* ignored.
|
|
17
|
+
*/
|
|
18
|
+
playbackEngine?: PlaybackEngineFactory;
|
|
19
|
+
/**
|
|
20
|
+
* Initial-only — pixel height of each track row (default 56). Lower
|
|
21
|
+
* values (~32–40) shrink the timeline for small viewports. Applied
|
|
22
|
+
* process-wide at construction time.
|
|
23
|
+
*/
|
|
24
|
+
trackHeight?: number;
|
|
25
|
+
/** Initial-only — pixel height of the timeline ruler (default 24). */
|
|
26
|
+
rulerHeight?: number;
|
|
27
|
+
/**
|
|
28
|
+
* Pixel height of the whole bottom timeline area (default 240).
|
|
29
|
+
* Reactive — swap any time to recompact. The canvas inside fills
|
|
30
|
+
* 100% and shows an internal scrollbar when track count overflows.
|
|
31
|
+
*/
|
|
32
|
+
timelineHeight?: number;
|
|
12
33
|
};
|
|
13
34
|
declare var __VLS_5: {}, __VLS_11: {};
|
|
14
35
|
type __VLS_Slots = {} & {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VideoEditor.vue.d.ts","sourceRoot":"","sources":["../src/VideoEditor.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"VideoEditor.vue.d.ts","sourceRoot":"","sources":["../src/VideoEditor.vue"],"names":[],"mappings":"AA6JA,OAAO,EAEL,KAAK,SAAS,EACd,KAAK,MAAM,EAEX,KAAK,qBAAqB,EAC1B,KAAK,OAAO,EACZ,KAAK,KAAK,EACX,MAAM,aAAa,CAAC;AAErB;;;;GAIG;AACH,KAAK,WAAW,GAAG;IACjB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,wFAAwF;IACxF,MAAM,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IACzB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,qBAAqB,CAAC;IACvC;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sEAAsE;IACtE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAmJF,QAAA,IAAI,OAAO,IAAU,EAAE,QAAQ,IAAY,CAAE;AAC7C,KAAK,WAAW,GAAG,EAAE,GACnB;IAAE,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,OAAO,KAAK,GAAG,CAAA;CAAE,GAC/C;IAAE,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,QAAQ,KAAK,GAAG,CAAA;CAAE,CAAC;AAwBpD,QAAA,MAAM,eAAe;IA5EnB,kEAAkE;eACzD,SAAS,GAAG,IAAI;;;;;;;;;;;;;;;;;;;kFAmFzB,CAAC;wBACkB,eAAe,CAAC,OAAO,eAAe,EAAE,WAAW,CAAC;AAAzE,wBAA0E;AAQ1E,KAAK,eAAe,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IAChC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KAEV,CAAA;CACD,CAAC"}
|
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("vue"),
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("vue"),a=require("@aicut/core"),h=t.defineComponent({__name:"VideoEditor",props:{defaultProject:{},theme:{},locale:{},playbackEngine:{type:Function},trackHeight:{},rulerHeight:{},timelineHeight:{}},emits:["ready","change","export","timeUpdate","play","pause","selectionChange","error"],setup(p,{expose:d,emit:m}){const o=p,l=m,c=t.ref(null);let e=null;const r=[],i=t.ref(null),u=t.ref(null);return t.onMounted(()=>{c.value&&(e=a.Editor.create({container:c.value,project:o.defaultProject,theme:o.theme,locale:o.locale,playbackEngine:o.playbackEngine,...o.trackHeight!=null?{trackHeight:o.trackHeight}:{},...o.rulerHeight!=null?{rulerHeight:o.rulerHeight}:{},...o.timelineHeight!=null?{timelineHeight:o.timelineHeight}:{}}),r.push(e.on("change",({project:n})=>l("change",n)),e.on("export",({project:n})=>l("export",n)),e.on("time",({timeMs:n})=>l("timeUpdate",n)),e.on("play",()=>l("play")),e.on("pause",()=>l("pause")),e.on("selectionChange",({clipId:n})=>l("selectionChange",n)),e.on("error",({error:n})=>l("error",n))),i.value=e.headerLeft,u.value=e.headerRight,l("ready",e))}),t.watch(()=>o.theme,n=>{n&&e&&e.setTheme(n)}),t.watch(()=>o.locale,n=>{n&&e&&e.setLocale(n)}),t.watch(()=>o.timelineHeight,n=>{const s=c.value;s&&(n!=null&&n>0?s.style.setProperty("--aicut-timeline-height",`${Math.round(n)}px`):s.style.removeProperty("--aicut-timeline-height"))}),t.onBeforeUnmount(()=>{for(const n of r)n();r.length=0,e==null||e.destroy(),e=null,i.value=null,u.value=null}),d({api:()=>e}),(n,s)=>(t.openBlock(),t.createElementBlock("div",{ref_key:"host",ref:c,"data-aicut-host":""},[i.value?(t.openBlock(),t.createBlock(t.Teleport,{key:0,to:i.value},[t.renderSlot(n.$slots,"headerLeft")],8,["to"])):t.createCommentVNode("",!0),u.value?(t.openBlock(),t.createBlock(t.Teleport,{key:1,to:u.value},[t.renderSlot(n.$slots,"headerRight")],8,["to"])):t.createCommentVNode("",!0)],512))}}),g=t.defineComponent({__name:"Timeline",props:{defaultProject:{},defaultScale:{},defaultTime:{},defaultSelectedClipId:{},showHeader:{type:Boolean},readOnly:{type:Boolean},snap:{type:Boolean},autoFit:{type:Boolean},locale:{}},emits:["seek","selectClip","scaleChange","moveClip","resizeClip","change"],setup(p,{expose:d,emit:m}){const o=p,l=m,c=t.ref(null);let e=null;return t.onMounted(()=>{c.value&&(e=a.Timeline.create({container:c.value,project:o.defaultProject,pxPerSec:o.defaultScale,time:o.defaultTime,selectedClipId:o.defaultSelectedClipId??null,showHeader:o.showHeader,readOnly:o.readOnly,snap:o.snap,autoFit:o.autoFit,locale:o.locale,onSeek:r=>l("seek",r),onSelectClip:r=>l("selectClip",r),onScaleChange:r=>l("scaleChange",r),onMoveClip:(r,i)=>l("moveClip",r,i),onResizeClip:(r,i)=>l("resizeClip",r,i),onChange:r=>l("change",r)}))}),t.watch(()=>o.locale,r=>{r&&e&&e.setLocale(r)}),t.onBeforeUnmount(()=>{e==null||e.destroy(),e=null}),d({api:()=>e}),(r,i)=>(t.openBlock(),t.createElementBlock("div",{ref_key:"host",ref:c,"data-aicut-timeline-host":"",style:{width:"100%",height:"240px"}},null,512))}});Object.defineProperty(exports,"CanvasCompositorEngine",{enumerable:!0,get:()=>a.CanvasCompositorEngine});Object.defineProperty(exports,"HEADER_WIDTH",{enumerable:!0,get:()=>a.HEADER_WIDTH});Object.defineProperty(exports,"HtmlVideoEngine",{enumerable:!0,get:()=>a.HtmlVideoEngine});Object.defineProperty(exports,"RULER_HEIGHT",{enumerable:!0,get:()=>a.RULER_HEIGHT});Object.defineProperty(exports,"TRACK_HEIGHT",{enumerable:!0,get:()=>a.TRACK_HEIGHT});Object.defineProperty(exports,"canvasCompositorEngineFactory",{enumerable:!0,get:()=>a.canvasCompositorEngineFactory});Object.defineProperty(exports,"createEmptyProject",{enumerable:!0,get:()=>a.createEmptyProject});Object.defineProperty(exports,"createId",{enumerable:!0,get:()=>a.createId});Object.defineProperty(exports,"htmlVideoEngineFactory",{enumerable:!0,get:()=>a.htmlVideoEngineFactory});Object.defineProperty(exports,"localeEn",{enumerable:!0,get:()=>a.localeEn});Object.defineProperty(exports,"localeZh",{enumerable:!0,get:()=>a.localeZh});Object.defineProperty(exports,"setTimelineMetrics",{enumerable:!0,get:()=>a.setTimelineMetrics});exports.Timeline=g;exports.VideoEditor=h;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../src/VideoEditor.vue","../src/Timeline.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { onBeforeUnmount, onMounted, ref, watch } from \"vue\";\nimport {\n Editor,\n type EditorApi,\n type Locale,\n type Ms,\n type Project,\n type Theme,\n} from \"@aicut/core\";\n\n/**\n * Vue 3 wrapper around `@aicut/core`. Same shape as `@aicut/react`:\n * uncontrolled for project state, theme is reactive, API exposed via\n * `defineExpose` so a parent `ref` can call cut/seek/setProject/etc.\n */\nconst props = defineProps<{\n defaultProject?: Project;\n theme?: Theme;\n /** UI string overrides (English default). Reactive — swap to `localeZh` for Chinese. */\n locale?: Partial<Locale>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"ready\", api: EditorApi): void;\n (e: \"change\", project: Project): void;\n (e: \"export\", project: Project): void;\n (e: \"timeUpdate\", timeMs: Ms): void;\n (e: \"play\"): void;\n (e: \"pause\"): void;\n (e: \"selectionChange\", clipId: string | null): void;\n (e: \"error\", error: Error): void;\n}>();\n\nconst host = ref<HTMLDivElement | null>(null);\nlet editor: Editor | null = null;\nconst offs: Array<() => void> = [];\n/** Header slot DOM nodes — set after editor mount so Vue Teleports\n * have a valid target. Library renders nothing here; named slots\n * `#headerLeft` / `#headerRight` portal whatever the host provides. */\nconst headerLeftSlot = ref<HTMLElement | null>(null);\nconst headerRightSlot = ref<HTMLElement | null>(null);\n\nonMounted(() => {\n if (!host.value) return;\n editor = Editor.create({\n container: host.value,\n project: props.defaultProject,\n theme: props.theme,\n locale: props.locale,\n });\n\n offs.push(\n editor.on(\"change\", ({ project }) => emit(\"change\", project)),\n editor.on(\"export\", ({ project }) => emit(\"export\", project)),\n editor.on(\"time\", ({ timeMs }) => emit(\"timeUpdate\", timeMs)),\n editor.on(\"play\", () => emit(\"play\")),\n editor.on(\"pause\", () => emit(\"pause\")),\n editor.on(\"selectionChange\", ({ clipId }) =>\n emit(\"selectionChange\", clipId),\n ),\n editor.on(\"error\", ({ error }) => emit(\"error\", error)),\n );\n\n headerLeftSlot.value = editor.headerLeft;\n headerRightSlot.value = editor.headerRight;\n emit(\"ready\", editor);\n});\n\nwatch(\n () => props.theme,\n (theme) => {\n if (theme && editor) editor.setTheme(theme);\n },\n);\n\nwatch(\n () => props.locale,\n (locale) => {\n if (locale && editor) editor.setLocale(locale);\n },\n);\n\nonBeforeUnmount(() => {\n for (const off of offs) off();\n offs.length = 0;\n editor?.destroy();\n editor = null;\n headerLeftSlot.value = null;\n headerRightSlot.value = null;\n});\n\ndefineExpose({\n /** Returns the underlying core API or null if not yet mounted. */\n api: (): EditorApi | null => editor,\n});\n</script>\n\n<template>\n <div ref=\"host\" data-aicut-host=\"\">\n <Teleport v-if=\"headerLeftSlot\" :to=\"headerLeftSlot\">\n <slot name=\"headerLeft\" />\n </Teleport>\n <Teleport v-if=\"headerRightSlot\" :to=\"headerRightSlot\">\n <slot name=\"headerRight\" />\n </Teleport>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { onBeforeUnmount, onMounted, ref, watch } from \"vue\";\nimport {\n Timeline as CoreTimeline,\n type Clip,\n type Locale,\n type Ms,\n type Project,\n} from \"@aicut/core\";\n\n/**\n * Standalone canvas Timeline wrapped for Vue 3. Same surface as the\n * React `<Timeline>`: pass `defaultProject`, drive imperatively via\n * the exposed `api()` ref.\n */\nconst props = defineProps<{\n defaultProject: Project;\n defaultScale?: number;\n defaultTime?: Ms;\n defaultSelectedClipId?: string | null;\n showHeader?: boolean;\n readOnly?: boolean;\n snap?: boolean;\n autoFit?: boolean;\n locale?: Partial<Locale>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"seek\", timeMs: Ms): void;\n (e: \"selectClip\", clipId: string | null): void;\n (e: \"scaleChange\", pxPerSec: number): void;\n (e: \"moveClip\", clipId: string, opts: { start?: Ms; trackId?: string }): void;\n (\n e: \"resizeClip\",\n clipId: string,\n edits: Partial<Pick<Clip, \"in\" | \"out\" | \"start\">>,\n ): void;\n (e: \"change\", project: Project): void;\n}>();\n\nconst host = ref<HTMLDivElement | null>(null);\nlet timeline: CoreTimeline | null = null;\n\nonMounted(() => {\n if (!host.value) return;\n timeline = CoreTimeline.create({\n container: host.value,\n project: props.defaultProject,\n pxPerSec: props.defaultScale,\n time: props.defaultTime,\n selectedClipId: props.defaultSelectedClipId ?? null,\n showHeader: props.showHeader,\n readOnly: props.readOnly,\n snap: props.snap,\n autoFit: props.autoFit,\n locale: props.locale,\n onSeek: (t) => emit(\"seek\", t),\n onSelectClip: (id) => emit(\"selectClip\", id),\n onScaleChange: (s) => emit(\"scaleChange\", s),\n onMoveClip: (id, opts) => emit(\"moveClip\", id, opts),\n onResizeClip: (id, edits) => emit(\"resizeClip\", id, edits),\n onChange: (p) => emit(\"change\", p),\n });\n});\n\nwatch(\n () => props.locale,\n (locale) => {\n if (locale && timeline) timeline.setLocale(locale);\n },\n);\n\nonBeforeUnmount(() => {\n timeline?.destroy();\n timeline = null;\n});\n\ndefineExpose({\n api: (): CoreTimeline | null => timeline,\n});\n</script>\n\n<template>\n <div ref=\"host\" data-aicut-timeline-host=\"\" :style=\"{ width: '100%', height: '240px' }\" />\n</template>\n"],"names":["props","__props","emit","__emit","host","ref","editor","offs","headerLeftSlot","headerRightSlot","onMounted","Editor","project","timeMs","clipId","error","watch","theme","locale","onBeforeUnmount","off","__expose","_createElementBlock","_createBlock","_Teleport","_renderSlot","_ctx","timeline","CoreTimeline","t","id","s","opts","edits","p"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/VideoEditor.vue","../src/Timeline.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { onBeforeUnmount, onMounted, ref, watch } from \"vue\";\nimport {\n Editor,\n type EditorApi,\n type Locale,\n type Ms,\n type PlaybackEngineFactory,\n type Project,\n type Theme,\n} from \"@aicut/core\";\n\n/**\n * Vue 3 wrapper around `@aicut/core`. Same shape as `@aicut/react`:\n * uncontrolled for project state, theme is reactive, API exposed via\n * `defineExpose` so a parent `ref` can call cut/seek/setProject/etc.\n */\nconst props = defineProps<{\n defaultProject?: Project;\n theme?: Theme;\n /** UI string overrides (English default). Reactive — swap to `localeZh` for Chinese. */\n locale?: Partial<Locale>;\n /**\n * Initial-only factory for a custom playback engine. Defaults to the\n * built-in `HtmlVideoEngine`. Pass `WebCodecsEngine` (v0.6+) or your\n * own engine to override. Bound at mount; later prop changes are\n * ignored.\n */\n playbackEngine?: PlaybackEngineFactory;\n /**\n * Initial-only — pixel height of each track row (default 56). Lower\n * values (~32–40) shrink the timeline for small viewports. Applied\n * process-wide at construction time.\n */\n trackHeight?: number;\n /** Initial-only — pixel height of the timeline ruler (default 24). */\n rulerHeight?: number;\n /**\n * Pixel height of the whole bottom timeline area (default 240).\n * Reactive — swap any time to recompact. The canvas inside fills\n * 100% and shows an internal scrollbar when track count overflows.\n */\n timelineHeight?: number;\n}>();\n\nconst emit = defineEmits<{\n (e: \"ready\", api: EditorApi): void;\n (e: \"change\", project: Project): void;\n (e: \"export\", project: Project): void;\n (e: \"timeUpdate\", timeMs: Ms): void;\n (e: \"play\"): void;\n (e: \"pause\"): void;\n (e: \"selectionChange\", clipId: string | null): void;\n (e: \"error\", error: Error): void;\n}>();\n\nconst host = ref<HTMLDivElement | null>(null);\nlet editor: Editor | null = null;\nconst offs: Array<() => void> = [];\n/** Header slot DOM nodes — set after editor mount so Vue Teleports\n * have a valid target. Library renders nothing here; named slots\n * `#headerLeft` / `#headerRight` portal whatever the host provides. */\nconst headerLeftSlot = ref<HTMLElement | null>(null);\nconst headerRightSlot = ref<HTMLElement | null>(null);\n\nonMounted(() => {\n if (!host.value) return;\n editor = Editor.create({\n container: host.value,\n project: props.defaultProject,\n theme: props.theme,\n locale: props.locale,\n playbackEngine: props.playbackEngine,\n ...(props.trackHeight != null ? { trackHeight: props.trackHeight } : {}),\n ...(props.rulerHeight != null ? { rulerHeight: props.rulerHeight } : {}),\n ...(props.timelineHeight != null\n ? { timelineHeight: props.timelineHeight }\n : {}),\n });\n\n offs.push(\n editor.on(\"change\", ({ project }) => emit(\"change\", project)),\n editor.on(\"export\", ({ project }) => emit(\"export\", project)),\n editor.on(\"time\", ({ timeMs }) => emit(\"timeUpdate\", timeMs)),\n editor.on(\"play\", () => emit(\"play\")),\n editor.on(\"pause\", () => emit(\"pause\")),\n editor.on(\"selectionChange\", ({ clipId }) =>\n emit(\"selectionChange\", clipId),\n ),\n editor.on(\"error\", ({ error }) => emit(\"error\", error)),\n );\n\n headerLeftSlot.value = editor.headerLeft;\n headerRightSlot.value = editor.headerRight;\n emit(\"ready\", editor);\n});\n\nwatch(\n () => props.theme,\n (theme) => {\n if (theme && editor) editor.setTheme(theme);\n },\n);\n\nwatch(\n () => props.locale,\n (locale) => {\n if (locale && editor) editor.setLocale(locale);\n },\n);\n\n// Reactive — sets the CSS custom property directly so the timeline\n// height can be tweaked without remounting.\nwatch(\n () => props.timelineHeight,\n (timelineHeight) => {\n const root = host.value;\n if (!root) return;\n if (timelineHeight != null && timelineHeight > 0) {\n root.style.setProperty(\n \"--aicut-timeline-height\",\n `${Math.round(timelineHeight)}px`,\n );\n } else {\n root.style.removeProperty(\"--aicut-timeline-height\");\n }\n },\n);\n\nonBeforeUnmount(() => {\n for (const off of offs) off();\n offs.length = 0;\n editor?.destroy();\n editor = null;\n headerLeftSlot.value = null;\n headerRightSlot.value = null;\n});\n\ndefineExpose({\n /** Returns the underlying core API or null if not yet mounted. */\n api: (): EditorApi | null => editor,\n});\n</script>\n\n<template>\n <div ref=\"host\" data-aicut-host=\"\">\n <Teleport v-if=\"headerLeftSlot\" :to=\"headerLeftSlot\">\n <slot name=\"headerLeft\" />\n </Teleport>\n <Teleport v-if=\"headerRightSlot\" :to=\"headerRightSlot\">\n <slot name=\"headerRight\" />\n </Teleport>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { onBeforeUnmount, onMounted, ref, watch } from \"vue\";\nimport {\n Timeline as CoreTimeline,\n type Clip,\n type Locale,\n type Ms,\n type Project,\n} from \"@aicut/core\";\n\n/**\n * Standalone canvas Timeline wrapped for Vue 3. Same surface as the\n * React `<Timeline>`: pass `defaultProject`, drive imperatively via\n * the exposed `api()` ref.\n */\nconst props = defineProps<{\n defaultProject: Project;\n defaultScale?: number;\n defaultTime?: Ms;\n defaultSelectedClipId?: string | null;\n showHeader?: boolean;\n readOnly?: boolean;\n snap?: boolean;\n autoFit?: boolean;\n locale?: Partial<Locale>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"seek\", timeMs: Ms): void;\n (e: \"selectClip\", clipId: string | null): void;\n (e: \"scaleChange\", pxPerSec: number): void;\n (e: \"moveClip\", clipId: string, opts: { start?: Ms; trackId?: string }): void;\n (\n e: \"resizeClip\",\n clipId: string,\n edits: Partial<Pick<Clip, \"in\" | \"out\" | \"start\">>,\n ): void;\n (e: \"change\", project: Project): void;\n}>();\n\nconst host = ref<HTMLDivElement | null>(null);\nlet timeline: CoreTimeline | null = null;\n\nonMounted(() => {\n if (!host.value) return;\n timeline = CoreTimeline.create({\n container: host.value,\n project: props.defaultProject,\n pxPerSec: props.defaultScale,\n time: props.defaultTime,\n selectedClipId: props.defaultSelectedClipId ?? null,\n showHeader: props.showHeader,\n readOnly: props.readOnly,\n snap: props.snap,\n autoFit: props.autoFit,\n locale: props.locale,\n onSeek: (t) => emit(\"seek\", t),\n onSelectClip: (id) => emit(\"selectClip\", id),\n onScaleChange: (s) => emit(\"scaleChange\", s),\n onMoveClip: (id, opts) => emit(\"moveClip\", id, opts),\n onResizeClip: (id, edits) => emit(\"resizeClip\", id, edits),\n onChange: (p) => emit(\"change\", p),\n });\n});\n\nwatch(\n () => props.locale,\n (locale) => {\n if (locale && timeline) timeline.setLocale(locale);\n },\n);\n\nonBeforeUnmount(() => {\n timeline?.destroy();\n timeline = null;\n});\n\ndefineExpose({\n api: (): CoreTimeline | null => timeline,\n});\n</script>\n\n<template>\n <div ref=\"host\" data-aicut-timeline-host=\"\" :style=\"{ width: '100%', height: '240px' }\" />\n</template>\n"],"names":["props","__props","emit","__emit","host","ref","editor","offs","headerLeftSlot","headerRightSlot","onMounted","Editor","project","timeMs","clipId","error","watch","theme","locale","timelineHeight","root","onBeforeUnmount","off","__expose","_createElementBlock","_createBlock","_Teleport","_renderSlot","_ctx","timeline","CoreTimeline","t","id","s","opts","edits","p"],"mappings":"yZAiBA,MAAMA,EAAQC,EA4BRC,EAAOC,EAWPC,EAAOC,EAAAA,IAA2B,IAAI,EAC5C,IAAIC,EAAwB,KAC5B,MAAMC,EAA0B,CAAA,EAI1BC,EAAiBH,EAAAA,IAAwB,IAAI,EAC7CI,EAAkBJ,EAAAA,IAAwB,IAAI,EAEpDK,OAAAA,EAAAA,UAAU,IAAM,CACTN,EAAK,QACVE,EAASK,EAAAA,OAAO,OAAO,CACrB,UAAWP,EAAK,MAChB,QAASJ,EAAM,eACf,MAAOA,EAAM,MACb,OAAQA,EAAM,OACd,eAAgBA,EAAM,eACtB,GAAIA,EAAM,aAAe,KAAO,CAAE,YAAaA,EAAM,WAAA,EAAgB,CAAA,EACrE,GAAIA,EAAM,aAAe,KAAO,CAAE,YAAaA,EAAM,WAAA,EAAgB,CAAA,EACrE,GAAIA,EAAM,gBAAkB,KACxB,CAAE,eAAgBA,EAAM,gBACxB,CAAA,CAAC,CACN,EAEDO,EAAK,KACHD,EAAO,GAAG,SAAU,CAAC,CAAE,QAAAM,KAAcV,EAAK,SAAUU,CAAO,CAAC,EAC5DN,EAAO,GAAG,SAAU,CAAC,CAAE,QAAAM,KAAcV,EAAK,SAAUU,CAAO,CAAC,EAC5DN,EAAO,GAAG,OAAQ,CAAC,CAAE,OAAAO,KAAaX,EAAK,aAAcW,CAAM,CAAC,EAC5DP,EAAO,GAAG,OAAQ,IAAMJ,EAAK,MAAM,CAAC,EACpCI,EAAO,GAAG,QAAS,IAAMJ,EAAK,OAAO,CAAC,EACtCI,EAAO,GAAG,kBAAmB,CAAC,CAAE,OAAAQ,CAAA,IAC9BZ,EAAK,kBAAmBY,CAAM,CAAA,EAEhCR,EAAO,GAAG,QAAS,CAAC,CAAE,MAAAS,KAAYb,EAAK,QAASa,CAAK,CAAC,CAAA,EAGxDP,EAAe,MAAQF,EAAO,WAC9BG,EAAgB,MAAQH,EAAO,YAC/BJ,EAAK,QAASI,CAAM,EACtB,CAAC,EAEDU,EAAAA,MACE,IAAMhB,EAAM,MACXiB,GAAU,CACLA,GAASX,GAAQA,EAAO,SAASW,CAAK,CAC5C,CAAA,EAGFD,EAAAA,MACE,IAAMhB,EAAM,OACXkB,GAAW,CACNA,GAAUZ,GAAQA,EAAO,UAAUY,CAAM,CAC/C,CAAA,EAKFF,EAAAA,MACE,IAAMhB,EAAM,eACXmB,GAAmB,CAClB,MAAMC,EAAOhB,EAAK,MACbgB,IACDD,GAAkB,MAAQA,EAAiB,EAC7CC,EAAK,MAAM,YACT,0BACA,GAAG,KAAK,MAAMD,CAAc,CAAC,IAAA,EAG/BC,EAAK,MAAM,eAAe,yBAAyB,EAEvD,CAAA,EAGFC,EAAAA,gBAAgB,IAAM,CACpB,UAAWC,KAAOf,EAAMe,EAAA,EACxBf,EAAK,OAAS,EACdD,GAAA,MAAAA,EAAQ,UACRA,EAAS,KACTE,EAAe,MAAQ,KACvBC,EAAgB,MAAQ,IAC1B,CAAC,EAEDc,EAAa,CAEX,IAAK,IAAwBjB,CAAA,CAC9B,wBAICkB,EAAAA,mBAOM,MAAA,SAPG,OAAJ,IAAIpB,EAAO,kBAAgB,EAAA,GACdI,EAAA,qBAAhBiB,EAAAA,YAEWC,EAAAA,SAAA,OAFsB,GAAIlB,EAAA,KAAA,GACnCmB,aAA0BC,EAAA,OAAA,YAAA,CAAA,yCAEZnB,EAAA,qBAAhBgB,EAAAA,YAEWC,EAAAA,SAAA,OAFuB,GAAIjB,EAAA,KAAA,GACpCkB,aAA2BC,EAAA,OAAA,aAAA,CAAA,yXCvIjC,MAAM5B,EAAQC,EAYRC,EAAOC,EAaPC,EAAOC,EAAAA,IAA2B,IAAI,EAC5C,IAAIwB,EAAgC,KAEpCnB,OAAAA,EAAAA,UAAU,IAAM,CACTN,EAAK,QACVyB,EAAWC,EAAAA,SAAa,OAAO,CAC7B,UAAW1B,EAAK,MAChB,QAASJ,EAAM,eACf,SAAUA,EAAM,aAChB,KAAMA,EAAM,YACZ,eAAgBA,EAAM,uBAAyB,KAC/C,WAAYA,EAAM,WAClB,SAAUA,EAAM,SAChB,KAAMA,EAAM,KACZ,QAASA,EAAM,QACf,OAAQA,EAAM,OACd,OAAS+B,GAAM7B,EAAK,OAAQ6B,CAAC,EAC7B,aAAeC,GAAO9B,EAAK,aAAc8B,CAAE,EAC3C,cAAgBC,GAAM/B,EAAK,cAAe+B,CAAC,EAC3C,WAAY,CAACD,EAAIE,IAAShC,EAAK,WAAY8B,EAAIE,CAAI,EACnD,aAAc,CAACF,EAAIG,IAAUjC,EAAK,aAAc8B,EAAIG,CAAK,EACzD,SAAWC,GAAMlC,EAAK,SAAUkC,CAAC,CAAA,CAClC,EACH,CAAC,EAEDpB,EAAAA,MACE,IAAMhB,EAAM,OACXkB,GAAW,CACNA,GAAUW,GAAUA,EAAS,UAAUX,CAAM,CACnD,CAAA,EAGFG,EAAAA,gBAAgB,IAAM,CACpBQ,GAAA,MAAAA,EAAU,UACVA,EAAW,IACb,CAAC,EAEDN,EAAa,CACX,IAAK,IAA2BM,CAAA,CACjC,wBAICL,EAAAA,mBAA0F,MAAA,SAAjF,OAAJ,IAAIpB,EAAO,2BAAyB,GAAI,MAAO,CAAA,MAAA,OAAA,OAAA,OAAA,CAAA"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { default as VideoEditor } from "./VideoEditor.vue";
|
|
2
2
|
export { default as Timeline } from "./Timeline.vue";
|
|
3
|
-
export type { Project, MediaSource, Track, Clip, Ms, Theme, EditorApi, TimelineOptions, Locale, } from "@aicut/core";
|
|
4
|
-
export { createEmptyProject, createId, localeEn, localeZh } from "@aicut/core";
|
|
3
|
+
export type { Project, MediaSource, Track, Clip, Ms, Theme, EditorApi, TimelineOptions, Locale, PlaybackEngine, PlaybackEngineFactory, PlaybackEngineOptions, CanvasCompositorEngineOptions, } from "@aicut/core";
|
|
4
|
+
export { createEmptyProject, createId, localeEn, localeZh, HtmlVideoEngine, htmlVideoEngineFactory, CanvasCompositorEngine, canvasCompositorEngineFactory, TRACK_HEIGHT, RULER_HEIGHT, HEADER_WIDTH, setTimelineMetrics, } from "@aicut/core";
|
|
5
5
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AACrD,YAAY,EACV,OAAO,EACP,WAAW,EACX,KAAK,EACL,IAAI,EACJ,EAAE,EACF,KAAK,EACL,SAAS,EACT,eAAe,EACf,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AACrD,YAAY,EACV,OAAO,EACP,WAAW,EACX,KAAK,EACL,IAAI,EACJ,EAAE,EACF,KAAK,EACL,SAAS,EACT,eAAe,EACf,MAAM,EACN,cAAc,EACd,qBAAqB,EACrB,qBAAqB,EACrB,6BAA6B,GAC9B,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,kBAAkB,EAClB,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,eAAe,EACf,sBAAsB,EACtB,sBAAsB,EACtB,6BAA6B,EAC7B,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,kBAAkB,GACnB,MAAM,aAAa,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,72 +1,89 @@
|
|
|
1
|
-
import { defineComponent as
|
|
2
|
-
import { Editor as
|
|
3
|
-
import {
|
|
4
|
-
const
|
|
1
|
+
import { defineComponent as v, ref as s, onMounted as H, watch as u, onBeforeUnmount as E, openBlock as p, createElementBlock as k, createBlock as f, Teleport as g, renderSlot as y, createCommentVNode as C } from "vue";
|
|
2
|
+
import { Editor as _, Timeline as T } from "@aicut/core";
|
|
3
|
+
import { CanvasCompositorEngine as I, HEADER_WIDTH as F, HtmlVideoEngine as L, RULER_HEIGHT as w, TRACK_HEIGHT as V, canvasCompositorEngineFactory as M, createEmptyProject as U, createId as $, htmlVideoEngineFactory as b, localeEn as z, localeZh as O, setTimelineMetrics as A } from "@aicut/core";
|
|
4
|
+
const B = /* @__PURE__ */ v({
|
|
5
5
|
__name: "VideoEditor",
|
|
6
6
|
props: {
|
|
7
7
|
defaultProject: {},
|
|
8
8
|
theme: {},
|
|
9
|
-
locale: {}
|
|
9
|
+
locale: {},
|
|
10
|
+
playbackEngine: { type: Function },
|
|
11
|
+
trackHeight: {},
|
|
12
|
+
rulerHeight: {},
|
|
13
|
+
timelineHeight: {}
|
|
10
14
|
},
|
|
11
15
|
emits: ["ready", "change", "export", "timeUpdate", "play", "pause", "selectionChange", "error"],
|
|
12
|
-
setup(
|
|
13
|
-
const
|
|
16
|
+
setup(h, { expose: d, emit: m }) {
|
|
17
|
+
const t = h, a = m, r = s(null);
|
|
14
18
|
let e = null;
|
|
15
|
-
const o = [], n =
|
|
16
|
-
return
|
|
17
|
-
r.value && (e =
|
|
19
|
+
const o = [], n = s(null), i = s(null);
|
|
20
|
+
return H(() => {
|
|
21
|
+
r.value && (e = _.create({
|
|
18
22
|
container: r.value,
|
|
19
|
-
project:
|
|
20
|
-
theme:
|
|
21
|
-
locale:
|
|
23
|
+
project: t.defaultProject,
|
|
24
|
+
theme: t.theme,
|
|
25
|
+
locale: t.locale,
|
|
26
|
+
playbackEngine: t.playbackEngine,
|
|
27
|
+
...t.trackHeight != null ? { trackHeight: t.trackHeight } : {},
|
|
28
|
+
...t.rulerHeight != null ? { rulerHeight: t.rulerHeight } : {},
|
|
29
|
+
...t.timelineHeight != null ? { timelineHeight: t.timelineHeight } : {}
|
|
22
30
|
}), o.push(
|
|
23
|
-
e.on("change", ({ project:
|
|
24
|
-
e.on("export", ({ project:
|
|
25
|
-
e.on("time", ({ timeMs:
|
|
31
|
+
e.on("change", ({ project: l }) => a("change", l)),
|
|
32
|
+
e.on("export", ({ project: l }) => a("export", l)),
|
|
33
|
+
e.on("time", ({ timeMs: l }) => a("timeUpdate", l)),
|
|
26
34
|
e.on("play", () => a("play")),
|
|
27
35
|
e.on("pause", () => a("pause")),
|
|
28
36
|
e.on(
|
|
29
37
|
"selectionChange",
|
|
30
|
-
({ clipId:
|
|
38
|
+
({ clipId: l }) => a("selectionChange", l)
|
|
31
39
|
),
|
|
32
|
-
e.on("error", ({ error:
|
|
33
|
-
), n.value = e.headerLeft,
|
|
34
|
-
}),
|
|
35
|
-
() =>
|
|
36
|
-
(
|
|
37
|
-
|
|
40
|
+
e.on("error", ({ error: l }) => a("error", l))
|
|
41
|
+
), n.value = e.headerLeft, i.value = e.headerRight, a("ready", e));
|
|
42
|
+
}), u(
|
|
43
|
+
() => t.theme,
|
|
44
|
+
(l) => {
|
|
45
|
+
l && e && e.setTheme(l);
|
|
46
|
+
}
|
|
47
|
+
), u(
|
|
48
|
+
() => t.locale,
|
|
49
|
+
(l) => {
|
|
50
|
+
l && e && e.setLocale(l);
|
|
38
51
|
}
|
|
39
|
-
),
|
|
40
|
-
() =>
|
|
41
|
-
(
|
|
42
|
-
|
|
52
|
+
), u(
|
|
53
|
+
() => t.timelineHeight,
|
|
54
|
+
(l) => {
|
|
55
|
+
const c = r.value;
|
|
56
|
+
c && (l != null && l > 0 ? c.style.setProperty(
|
|
57
|
+
"--aicut-timeline-height",
|
|
58
|
+
`${Math.round(l)}px`
|
|
59
|
+
) : c.style.removeProperty("--aicut-timeline-height"));
|
|
43
60
|
}
|
|
44
|
-
),
|
|
45
|
-
for (const
|
|
46
|
-
o.length = 0, e == null || e.destroy(), e = null, n.value = null,
|
|
47
|
-
}),
|
|
61
|
+
), E(() => {
|
|
62
|
+
for (const l of o) l();
|
|
63
|
+
o.length = 0, e == null || e.destroy(), e = null, n.value = null, i.value = null;
|
|
64
|
+
}), d({
|
|
48
65
|
/** Returns the underlying core API or null if not yet mounted. */
|
|
49
66
|
api: () => e
|
|
50
|
-
}), (
|
|
67
|
+
}), (l, c) => (p(), k("div", {
|
|
51
68
|
ref_key: "host",
|
|
52
69
|
ref: r,
|
|
53
70
|
"data-aicut-host": ""
|
|
54
71
|
}, [
|
|
55
|
-
n.value ? (
|
|
72
|
+
n.value ? (p(), f(g, {
|
|
56
73
|
key: 0,
|
|
57
74
|
to: n.value
|
|
58
75
|
}, [
|
|
59
|
-
|
|
60
|
-
], 8, ["to"])) :
|
|
61
|
-
|
|
76
|
+
y(l.$slots, "headerLeft")
|
|
77
|
+
], 8, ["to"])) : C("", !0),
|
|
78
|
+
i.value ? (p(), f(g, {
|
|
62
79
|
key: 1,
|
|
63
|
-
to:
|
|
80
|
+
to: i.value
|
|
64
81
|
}, [
|
|
65
|
-
|
|
66
|
-
], 8, ["to"])) :
|
|
82
|
+
y(l.$slots, "headerRight")
|
|
83
|
+
], 8, ["to"])) : C("", !0)
|
|
67
84
|
], 512));
|
|
68
85
|
}
|
|
69
|
-
}), P = /* @__PURE__ */
|
|
86
|
+
}), P = /* @__PURE__ */ v({
|
|
70
87
|
__name: "Timeline",
|
|
71
88
|
props: {
|
|
72
89
|
defaultProject: {},
|
|
@@ -80,21 +97,21 @@ const E = /* @__PURE__ */ g({
|
|
|
80
97
|
locale: {}
|
|
81
98
|
},
|
|
82
99
|
emits: ["seek", "selectClip", "scaleChange", "moveClip", "resizeClip", "change"],
|
|
83
|
-
setup(
|
|
84
|
-
const
|
|
100
|
+
setup(h, { expose: d, emit: m }) {
|
|
101
|
+
const t = h, a = m, r = s(null);
|
|
85
102
|
let e = null;
|
|
86
|
-
return
|
|
87
|
-
r.value && (e =
|
|
103
|
+
return H(() => {
|
|
104
|
+
r.value && (e = T.create({
|
|
88
105
|
container: r.value,
|
|
89
|
-
project:
|
|
90
|
-
pxPerSec:
|
|
91
|
-
time:
|
|
92
|
-
selectedClipId:
|
|
93
|
-
showHeader:
|
|
94
|
-
readOnly:
|
|
95
|
-
snap:
|
|
96
|
-
autoFit:
|
|
97
|
-
locale:
|
|
106
|
+
project: t.defaultProject,
|
|
107
|
+
pxPerSec: t.defaultScale,
|
|
108
|
+
time: t.defaultTime,
|
|
109
|
+
selectedClipId: t.defaultSelectedClipId ?? null,
|
|
110
|
+
showHeader: t.showHeader,
|
|
111
|
+
readOnly: t.readOnly,
|
|
112
|
+
snap: t.snap,
|
|
113
|
+
autoFit: t.autoFit,
|
|
114
|
+
locale: t.locale,
|
|
98
115
|
onSeek: (o) => a("seek", o),
|
|
99
116
|
onSelectClip: (o) => a("selectClip", o),
|
|
100
117
|
onScaleChange: (o) => a("scaleChange", o),
|
|
@@ -102,16 +119,16 @@ const E = /* @__PURE__ */ g({
|
|
|
102
119
|
onResizeClip: (o, n) => a("resizeClip", o, n),
|
|
103
120
|
onChange: (o) => a("change", o)
|
|
104
121
|
}));
|
|
105
|
-
}),
|
|
106
|
-
() =>
|
|
122
|
+
}), u(
|
|
123
|
+
() => t.locale,
|
|
107
124
|
(o) => {
|
|
108
125
|
o && e && e.setLocale(o);
|
|
109
126
|
}
|
|
110
|
-
),
|
|
127
|
+
), E(() => {
|
|
111
128
|
e == null || e.destroy(), e = null;
|
|
112
|
-
}),
|
|
129
|
+
}), d({
|
|
113
130
|
api: () => e
|
|
114
|
-
}), (o, n) => (
|
|
131
|
+
}), (o, n) => (p(), k("div", {
|
|
115
132
|
ref_key: "host",
|
|
116
133
|
ref: r,
|
|
117
134
|
"data-aicut-timeline-host": "",
|
|
@@ -120,11 +137,19 @@ const E = /* @__PURE__ */ g({
|
|
|
120
137
|
}
|
|
121
138
|
});
|
|
122
139
|
export {
|
|
140
|
+
I as CanvasCompositorEngine,
|
|
141
|
+
F as HEADER_WIDTH,
|
|
142
|
+
L as HtmlVideoEngine,
|
|
143
|
+
w as RULER_HEIGHT,
|
|
144
|
+
V as TRACK_HEIGHT,
|
|
123
145
|
P as Timeline,
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
146
|
+
B as VideoEditor,
|
|
147
|
+
M as canvasCompositorEngineFactory,
|
|
148
|
+
U as createEmptyProject,
|
|
149
|
+
$ as createId,
|
|
150
|
+
b as htmlVideoEngineFactory,
|
|
127
151
|
z as localeEn,
|
|
128
|
-
|
|
152
|
+
O as localeZh,
|
|
153
|
+
A as setTimelineMetrics
|
|
129
154
|
};
|
|
130
155
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/VideoEditor.vue","../src/Timeline.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { onBeforeUnmount, onMounted, ref, watch } from \"vue\";\nimport {\n Editor,\n type EditorApi,\n type Locale,\n type Ms,\n type Project,\n type Theme,\n} from \"@aicut/core\";\n\n/**\n * Vue 3 wrapper around `@aicut/core`. Same shape as `@aicut/react`:\n * uncontrolled for project state, theme is reactive, API exposed via\n * `defineExpose` so a parent `ref` can call cut/seek/setProject/etc.\n */\nconst props = defineProps<{\n defaultProject?: Project;\n theme?: Theme;\n /** UI string overrides (English default). Reactive — swap to `localeZh` for Chinese. */\n locale?: Partial<Locale>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"ready\", api: EditorApi): void;\n (e: \"change\", project: Project): void;\n (e: \"export\", project: Project): void;\n (e: \"timeUpdate\", timeMs: Ms): void;\n (e: \"play\"): void;\n (e: \"pause\"): void;\n (e: \"selectionChange\", clipId: string | null): void;\n (e: \"error\", error: Error): void;\n}>();\n\nconst host = ref<HTMLDivElement | null>(null);\nlet editor: Editor | null = null;\nconst offs: Array<() => void> = [];\n/** Header slot DOM nodes — set after editor mount so Vue Teleports\n * have a valid target. Library renders nothing here; named slots\n * `#headerLeft` / `#headerRight` portal whatever the host provides. */\nconst headerLeftSlot = ref<HTMLElement | null>(null);\nconst headerRightSlot = ref<HTMLElement | null>(null);\n\nonMounted(() => {\n if (!host.value) return;\n editor = Editor.create({\n container: host.value,\n project: props.defaultProject,\n theme: props.theme,\n locale: props.locale,\n });\n\n offs.push(\n editor.on(\"change\", ({ project }) => emit(\"change\", project)),\n editor.on(\"export\", ({ project }) => emit(\"export\", project)),\n editor.on(\"time\", ({ timeMs }) => emit(\"timeUpdate\", timeMs)),\n editor.on(\"play\", () => emit(\"play\")),\n editor.on(\"pause\", () => emit(\"pause\")),\n editor.on(\"selectionChange\", ({ clipId }) =>\n emit(\"selectionChange\", clipId),\n ),\n editor.on(\"error\", ({ error }) => emit(\"error\", error)),\n );\n\n headerLeftSlot.value = editor.headerLeft;\n headerRightSlot.value = editor.headerRight;\n emit(\"ready\", editor);\n});\n\nwatch(\n () => props.theme,\n (theme) => {\n if (theme && editor) editor.setTheme(theme);\n },\n);\n\nwatch(\n () => props.locale,\n (locale) => {\n if (locale && editor) editor.setLocale(locale);\n },\n);\n\nonBeforeUnmount(() => {\n for (const off of offs) off();\n offs.length = 0;\n editor?.destroy();\n editor = null;\n headerLeftSlot.value = null;\n headerRightSlot.value = null;\n});\n\ndefineExpose({\n /** Returns the underlying core API or null if not yet mounted. */\n api: (): EditorApi | null => editor,\n});\n</script>\n\n<template>\n <div ref=\"host\" data-aicut-host=\"\">\n <Teleport v-if=\"headerLeftSlot\" :to=\"headerLeftSlot\">\n <slot name=\"headerLeft\" />\n </Teleport>\n <Teleport v-if=\"headerRightSlot\" :to=\"headerRightSlot\">\n <slot name=\"headerRight\" />\n </Teleport>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { onBeforeUnmount, onMounted, ref, watch } from \"vue\";\nimport {\n Timeline as CoreTimeline,\n type Clip,\n type Locale,\n type Ms,\n type Project,\n} from \"@aicut/core\";\n\n/**\n * Standalone canvas Timeline wrapped for Vue 3. Same surface as the\n * React `<Timeline>`: pass `defaultProject`, drive imperatively via\n * the exposed `api()` ref.\n */\nconst props = defineProps<{\n defaultProject: Project;\n defaultScale?: number;\n defaultTime?: Ms;\n defaultSelectedClipId?: string | null;\n showHeader?: boolean;\n readOnly?: boolean;\n snap?: boolean;\n autoFit?: boolean;\n locale?: Partial<Locale>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"seek\", timeMs: Ms): void;\n (e: \"selectClip\", clipId: string | null): void;\n (e: \"scaleChange\", pxPerSec: number): void;\n (e: \"moveClip\", clipId: string, opts: { start?: Ms; trackId?: string }): void;\n (\n e: \"resizeClip\",\n clipId: string,\n edits: Partial<Pick<Clip, \"in\" | \"out\" | \"start\">>,\n ): void;\n (e: \"change\", project: Project): void;\n}>();\n\nconst host = ref<HTMLDivElement | null>(null);\nlet timeline: CoreTimeline | null = null;\n\nonMounted(() => {\n if (!host.value) return;\n timeline = CoreTimeline.create({\n container: host.value,\n project: props.defaultProject,\n pxPerSec: props.defaultScale,\n time: props.defaultTime,\n selectedClipId: props.defaultSelectedClipId ?? null,\n showHeader: props.showHeader,\n readOnly: props.readOnly,\n snap: props.snap,\n autoFit: props.autoFit,\n locale: props.locale,\n onSeek: (t) => emit(\"seek\", t),\n onSelectClip: (id) => emit(\"selectClip\", id),\n onScaleChange: (s) => emit(\"scaleChange\", s),\n onMoveClip: (id, opts) => emit(\"moveClip\", id, opts),\n onResizeClip: (id, edits) => emit(\"resizeClip\", id, edits),\n onChange: (p) => emit(\"change\", p),\n });\n});\n\nwatch(\n () => props.locale,\n (locale) => {\n if (locale && timeline) timeline.setLocale(locale);\n },\n);\n\nonBeforeUnmount(() => {\n timeline?.destroy();\n timeline = null;\n});\n\ndefineExpose({\n api: (): CoreTimeline | null => timeline,\n});\n</script>\n\n<template>\n <div ref=\"host\" data-aicut-timeline-host=\"\" :style=\"{ width: '100%', height: '240px' }\" />\n</template>\n"],"names":["props","__props","emit","__emit","host","ref","editor","offs","headerLeftSlot","headerRightSlot","onMounted","Editor","project","timeMs","clipId","error","watch","theme","locale","onBeforeUnmount","off","__expose","_createElementBlock","_createBlock","_Teleport","_renderSlot","_ctx","timeline","CoreTimeline","t","id","s","opts","edits","p"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/VideoEditor.vue","../src/Timeline.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { onBeforeUnmount, onMounted, ref, watch } from \"vue\";\nimport {\n Editor,\n type EditorApi,\n type Locale,\n type Ms,\n type PlaybackEngineFactory,\n type Project,\n type Theme,\n} from \"@aicut/core\";\n\n/**\n * Vue 3 wrapper around `@aicut/core`. Same shape as `@aicut/react`:\n * uncontrolled for project state, theme is reactive, API exposed via\n * `defineExpose` so a parent `ref` can call cut/seek/setProject/etc.\n */\nconst props = defineProps<{\n defaultProject?: Project;\n theme?: Theme;\n /** UI string overrides (English default). Reactive — swap to `localeZh` for Chinese. */\n locale?: Partial<Locale>;\n /**\n * Initial-only factory for a custom playback engine. Defaults to the\n * built-in `HtmlVideoEngine`. Pass `WebCodecsEngine` (v0.6+) or your\n * own engine to override. Bound at mount; later prop changes are\n * ignored.\n */\n playbackEngine?: PlaybackEngineFactory;\n /**\n * Initial-only — pixel height of each track row (default 56). Lower\n * values (~32–40) shrink the timeline for small viewports. Applied\n * process-wide at construction time.\n */\n trackHeight?: number;\n /** Initial-only — pixel height of the timeline ruler (default 24). */\n rulerHeight?: number;\n /**\n * Pixel height of the whole bottom timeline area (default 240).\n * Reactive — swap any time to recompact. The canvas inside fills\n * 100% and shows an internal scrollbar when track count overflows.\n */\n timelineHeight?: number;\n}>();\n\nconst emit = defineEmits<{\n (e: \"ready\", api: EditorApi): void;\n (e: \"change\", project: Project): void;\n (e: \"export\", project: Project): void;\n (e: \"timeUpdate\", timeMs: Ms): void;\n (e: \"play\"): void;\n (e: \"pause\"): void;\n (e: \"selectionChange\", clipId: string | null): void;\n (e: \"error\", error: Error): void;\n}>();\n\nconst host = ref<HTMLDivElement | null>(null);\nlet editor: Editor | null = null;\nconst offs: Array<() => void> = [];\n/** Header slot DOM nodes — set after editor mount so Vue Teleports\n * have a valid target. Library renders nothing here; named slots\n * `#headerLeft` / `#headerRight` portal whatever the host provides. */\nconst headerLeftSlot = ref<HTMLElement | null>(null);\nconst headerRightSlot = ref<HTMLElement | null>(null);\n\nonMounted(() => {\n if (!host.value) return;\n editor = Editor.create({\n container: host.value,\n project: props.defaultProject,\n theme: props.theme,\n locale: props.locale,\n playbackEngine: props.playbackEngine,\n ...(props.trackHeight != null ? { trackHeight: props.trackHeight } : {}),\n ...(props.rulerHeight != null ? { rulerHeight: props.rulerHeight } : {}),\n ...(props.timelineHeight != null\n ? { timelineHeight: props.timelineHeight }\n : {}),\n });\n\n offs.push(\n editor.on(\"change\", ({ project }) => emit(\"change\", project)),\n editor.on(\"export\", ({ project }) => emit(\"export\", project)),\n editor.on(\"time\", ({ timeMs }) => emit(\"timeUpdate\", timeMs)),\n editor.on(\"play\", () => emit(\"play\")),\n editor.on(\"pause\", () => emit(\"pause\")),\n editor.on(\"selectionChange\", ({ clipId }) =>\n emit(\"selectionChange\", clipId),\n ),\n editor.on(\"error\", ({ error }) => emit(\"error\", error)),\n );\n\n headerLeftSlot.value = editor.headerLeft;\n headerRightSlot.value = editor.headerRight;\n emit(\"ready\", editor);\n});\n\nwatch(\n () => props.theme,\n (theme) => {\n if (theme && editor) editor.setTheme(theme);\n },\n);\n\nwatch(\n () => props.locale,\n (locale) => {\n if (locale && editor) editor.setLocale(locale);\n },\n);\n\n// Reactive — sets the CSS custom property directly so the timeline\n// height can be tweaked without remounting.\nwatch(\n () => props.timelineHeight,\n (timelineHeight) => {\n const root = host.value;\n if (!root) return;\n if (timelineHeight != null && timelineHeight > 0) {\n root.style.setProperty(\n \"--aicut-timeline-height\",\n `${Math.round(timelineHeight)}px`,\n );\n } else {\n root.style.removeProperty(\"--aicut-timeline-height\");\n }\n },\n);\n\nonBeforeUnmount(() => {\n for (const off of offs) off();\n offs.length = 0;\n editor?.destroy();\n editor = null;\n headerLeftSlot.value = null;\n headerRightSlot.value = null;\n});\n\ndefineExpose({\n /** Returns the underlying core API or null if not yet mounted. */\n api: (): EditorApi | null => editor,\n});\n</script>\n\n<template>\n <div ref=\"host\" data-aicut-host=\"\">\n <Teleport v-if=\"headerLeftSlot\" :to=\"headerLeftSlot\">\n <slot name=\"headerLeft\" />\n </Teleport>\n <Teleport v-if=\"headerRightSlot\" :to=\"headerRightSlot\">\n <slot name=\"headerRight\" />\n </Teleport>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { onBeforeUnmount, onMounted, ref, watch } from \"vue\";\nimport {\n Timeline as CoreTimeline,\n type Clip,\n type Locale,\n type Ms,\n type Project,\n} from \"@aicut/core\";\n\n/**\n * Standalone canvas Timeline wrapped for Vue 3. Same surface as the\n * React `<Timeline>`: pass `defaultProject`, drive imperatively via\n * the exposed `api()` ref.\n */\nconst props = defineProps<{\n defaultProject: Project;\n defaultScale?: number;\n defaultTime?: Ms;\n defaultSelectedClipId?: string | null;\n showHeader?: boolean;\n readOnly?: boolean;\n snap?: boolean;\n autoFit?: boolean;\n locale?: Partial<Locale>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"seek\", timeMs: Ms): void;\n (e: \"selectClip\", clipId: string | null): void;\n (e: \"scaleChange\", pxPerSec: number): void;\n (e: \"moveClip\", clipId: string, opts: { start?: Ms; trackId?: string }): void;\n (\n e: \"resizeClip\",\n clipId: string,\n edits: Partial<Pick<Clip, \"in\" | \"out\" | \"start\">>,\n ): void;\n (e: \"change\", project: Project): void;\n}>();\n\nconst host = ref<HTMLDivElement | null>(null);\nlet timeline: CoreTimeline | null = null;\n\nonMounted(() => {\n if (!host.value) return;\n timeline = CoreTimeline.create({\n container: host.value,\n project: props.defaultProject,\n pxPerSec: props.defaultScale,\n time: props.defaultTime,\n selectedClipId: props.defaultSelectedClipId ?? null,\n showHeader: props.showHeader,\n readOnly: props.readOnly,\n snap: props.snap,\n autoFit: props.autoFit,\n locale: props.locale,\n onSeek: (t) => emit(\"seek\", t),\n onSelectClip: (id) => emit(\"selectClip\", id),\n onScaleChange: (s) => emit(\"scaleChange\", s),\n onMoveClip: (id, opts) => emit(\"moveClip\", id, opts),\n onResizeClip: (id, edits) => emit(\"resizeClip\", id, edits),\n onChange: (p) => emit(\"change\", p),\n });\n});\n\nwatch(\n () => props.locale,\n (locale) => {\n if (locale && timeline) timeline.setLocale(locale);\n },\n);\n\nonBeforeUnmount(() => {\n timeline?.destroy();\n timeline = null;\n});\n\ndefineExpose({\n api: (): CoreTimeline | null => timeline,\n});\n</script>\n\n<template>\n <div ref=\"host\" data-aicut-timeline-host=\"\" :style=\"{ width: '100%', height: '240px' }\" />\n</template>\n"],"names":["props","__props","emit","__emit","host","ref","editor","offs","headerLeftSlot","headerRightSlot","onMounted","Editor","project","timeMs","clipId","error","watch","theme","locale","timelineHeight","root","onBeforeUnmount","off","__expose","_createElementBlock","_createBlock","_Teleport","_renderSlot","_ctx","timeline","CoreTimeline","t","id","s","opts","edits","p"],"mappings":";;;;;;;;;;;;;;;;AAiBA,UAAMA,IAAQC,GA4BRC,IAAOC,GAWPC,IAAOC,EAA2B,IAAI;AAC5C,QAAIC,IAAwB;AAC5B,UAAMC,IAA0B,CAAA,GAI1BC,IAAiBH,EAAwB,IAAI,GAC7CI,IAAkBJ,EAAwB,IAAI;AAEpD,WAAAK,EAAU,MAAM;AACd,MAAKN,EAAK,UACVE,IAASK,EAAO,OAAO;AAAA,QACrB,WAAWP,EAAK;AAAA,QAChB,SAASJ,EAAM;AAAA,QACf,OAAOA,EAAM;AAAA,QACb,QAAQA,EAAM;AAAA,QACd,gBAAgBA,EAAM;AAAA,QACtB,GAAIA,EAAM,eAAe,OAAO,EAAE,aAAaA,EAAM,YAAA,IAAgB,CAAA;AAAA,QACrE,GAAIA,EAAM,eAAe,OAAO,EAAE,aAAaA,EAAM,YAAA,IAAgB,CAAA;AAAA,QACrE,GAAIA,EAAM,kBAAkB,OACxB,EAAE,gBAAgBA,EAAM,mBACxB,CAAA;AAAA,MAAC,CACN,GAEDO,EAAK;AAAA,QACHD,EAAO,GAAG,UAAU,CAAC,EAAE,SAAAM,QAAcV,EAAK,UAAUU,CAAO,CAAC;AAAA,QAC5DN,EAAO,GAAG,UAAU,CAAC,EAAE,SAAAM,QAAcV,EAAK,UAAUU,CAAO,CAAC;AAAA,QAC5DN,EAAO,GAAG,QAAQ,CAAC,EAAE,QAAAO,QAAaX,EAAK,cAAcW,CAAM,CAAC;AAAA,QAC5DP,EAAO,GAAG,QAAQ,MAAMJ,EAAK,MAAM,CAAC;AAAA,QACpCI,EAAO,GAAG,SAAS,MAAMJ,EAAK,OAAO,CAAC;AAAA,QACtCI,EAAO;AAAA,UAAG;AAAA,UAAmB,CAAC,EAAE,QAAAQ,EAAA,MAC9BZ,EAAK,mBAAmBY,CAAM;AAAA,QAAA;AAAA,QAEhCR,EAAO,GAAG,SAAS,CAAC,EAAE,OAAAS,QAAYb,EAAK,SAASa,CAAK,CAAC;AAAA,MAAA,GAGxDP,EAAe,QAAQF,EAAO,YAC9BG,EAAgB,QAAQH,EAAO,aAC/BJ,EAAK,SAASI,CAAM;AAAA,IACtB,CAAC,GAEDU;AAAA,MACE,MAAMhB,EAAM;AAAA,MACZ,CAACiB,MAAU;AACT,QAAIA,KAASX,KAAQA,EAAO,SAASW,CAAK;AAAA,MAC5C;AAAA,IAAA,GAGFD;AAAA,MACE,MAAMhB,EAAM;AAAA,MACZ,CAACkB,MAAW;AACV,QAAIA,KAAUZ,KAAQA,EAAO,UAAUY,CAAM;AAAA,MAC/C;AAAA,IAAA,GAKFF;AAAA,MACE,MAAMhB,EAAM;AAAA,MACZ,CAACmB,MAAmB;AAClB,cAAMC,IAAOhB,EAAK;AAClB,QAAKgB,MACDD,KAAkB,QAAQA,IAAiB,IAC7CC,EAAK,MAAM;AAAA,UACT;AAAA,UACA,GAAG,KAAK,MAAMD,CAAc,CAAC;AAAA,QAAA,IAG/BC,EAAK,MAAM,eAAe,yBAAyB;AAAA,MAEvD;AAAA,IAAA,GAGFC,EAAgB,MAAM;AACpB,iBAAWC,KAAOf,EAAM,CAAAe,EAAA;AACxB,MAAAf,EAAK,SAAS,GACdD,KAAA,QAAAA,EAAQ,WACRA,IAAS,MACTE,EAAe,QAAQ,MACvBC,EAAgB,QAAQ;AAAA,IAC1B,CAAC,GAEDc,EAAa;AAAA;AAAA,MAEX,KAAK,MAAwBjB;AAAA,IAAA,CAC9B,mBAICkB,EAOM,OAAA;AAAA,eAPG;AAAA,MAAJ,KAAIpB;AAAA,MAAO,mBAAgB;AAAA,IAAA;MACdI,EAAA,cAAhBiB,EAEWC,GAAA;AAAA;QAFsB,IAAIlB,EAAA;AAAA,MAAA;QACnCmB,EAA0BC,EAAA,QAAA,YAAA;AAAA,MAAA;MAEZnB,EAAA,cAAhBgB,EAEWC,GAAA;AAAA;QAFuB,IAAIjB,EAAA;AAAA,MAAA;QACpCkB,EAA2BC,EAAA,QAAA,aAAA;AAAA,MAAA;;;;;;;;;;;;;;;;;;ACvIjC,UAAM5B,IAAQC,GAYRC,IAAOC,GAaPC,IAAOC,EAA2B,IAAI;AAC5C,QAAIwB,IAAgC;AAEpC,WAAAnB,EAAU,MAAM;AACd,MAAKN,EAAK,UACVyB,IAAWC,EAAa,OAAO;AAAA,QAC7B,WAAW1B,EAAK;AAAA,QAChB,SAASJ,EAAM;AAAA,QACf,UAAUA,EAAM;AAAA,QAChB,MAAMA,EAAM;AAAA,QACZ,gBAAgBA,EAAM,yBAAyB;AAAA,QAC/C,YAAYA,EAAM;AAAA,QAClB,UAAUA,EAAM;AAAA,QAChB,MAAMA,EAAM;AAAA,QACZ,SAASA,EAAM;AAAA,QACf,QAAQA,EAAM;AAAA,QACd,QAAQ,CAAC+B,MAAM7B,EAAK,QAAQ6B,CAAC;AAAA,QAC7B,cAAc,CAACC,MAAO9B,EAAK,cAAc8B,CAAE;AAAA,QAC3C,eAAe,CAACC,MAAM/B,EAAK,eAAe+B,CAAC;AAAA,QAC3C,YAAY,CAACD,GAAIE,MAAShC,EAAK,YAAY8B,GAAIE,CAAI;AAAA,QACnD,cAAc,CAACF,GAAIG,MAAUjC,EAAK,cAAc8B,GAAIG,CAAK;AAAA,QACzD,UAAU,CAACC,MAAMlC,EAAK,UAAUkC,CAAC;AAAA,MAAA,CAClC;AAAA,IACH,CAAC,GAEDpB;AAAA,MACE,MAAMhB,EAAM;AAAA,MACZ,CAACkB,MAAW;AACV,QAAIA,KAAUW,KAAUA,EAAS,UAAUX,CAAM;AAAA,MACnD;AAAA,IAAA,GAGFG,EAAgB,MAAM;AACpB,MAAAQ,KAAA,QAAAA,EAAU,WACVA,IAAW;AAAA,IACb,CAAC,GAEDN,EAAa;AAAA,MACX,KAAK,MAA2BM;AAAA,IAAA,CACjC,mBAICL,EAA0F,OAAA;AAAA,eAAjF;AAAA,MAAJ,KAAIpB;AAAA,MAAO,4BAAyB;AAAA,MAAI,OAAO,EAAA,OAAA,QAAA,QAAA,QAAA;AAAA,IAAA;;;"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("@aicut/core/webcodecs");Object.defineProperty(exports,"WebCodecsEngine",{enumerable:!0,get:()=>e.WebCodecsEngine});Object.defineProperty(exports,"isWebCodecsSupported",{enumerable:!0,get:()=>e.isWebCodecsSupported});Object.defineProperty(exports,"webCodecsEngineFactory",{enumerable:!0,get:()=>e.webCodecsEngineFactory});
|
|
2
|
+
//# sourceMappingURL=webcodecs.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webcodecs.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @aicut/vue/webcodecs — separate entry that pulls mp4box.js for
|
|
3
|
+
* frame-accurate WebCodecs playback. Users who never import this
|
|
4
|
+
* path don't pay the mp4box bundle cost.
|
|
5
|
+
*
|
|
6
|
+
* Pass the factory directly to `<VideoEditor :playback-engine="…" />`,
|
|
7
|
+
* or wrap it in a closure to flip `debug: true`.
|
|
8
|
+
*/
|
|
9
|
+
export { WebCodecsEngine, webCodecsEngineFactory, isWebCodecsSupported, type WebCodecsEngineOptions, type DemuxedTrack, } from "@aicut/core/webcodecs";
|
|
10
|
+
//# sourceMappingURL=webcodecs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webcodecs.d.ts","sourceRoot":"","sources":["../src/webcodecs.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,oBAAoB,EACpB,KAAK,sBAAsB,EAC3B,KAAK,YAAY,GAClB,MAAM,uBAAuB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webcodecs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aicut/vue",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Vue 3 wrapper for the AiCut video editor + lighting picker — thin declarative shells over @aicut/core.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "ziqiang <ziqiangytu@gmail.com>",
|
|
@@ -53,6 +53,11 @@
|
|
|
53
53
|
"types": "./dist/lighting.d.ts",
|
|
54
54
|
"import": "./dist/lighting.js",
|
|
55
55
|
"require": "./dist/lighting.cjs"
|
|
56
|
+
},
|
|
57
|
+
"./webcodecs": {
|
|
58
|
+
"types": "./dist/webcodecs.d.ts",
|
|
59
|
+
"import": "./dist/webcodecs.js",
|
|
60
|
+
"require": "./dist/webcodecs.cjs"
|
|
56
61
|
}
|
|
57
62
|
},
|
|
58
63
|
"files": [
|
|
@@ -60,7 +65,7 @@
|
|
|
60
65
|
"README.md"
|
|
61
66
|
],
|
|
62
67
|
"dependencies": {
|
|
63
|
-
"@aicut/core": "0.
|
|
68
|
+
"@aicut/core": "0.5.0"
|
|
64
69
|
},
|
|
65
70
|
"peerDependencies": {
|
|
66
71
|
"vue": "^3.4.0"
|