@aicut/core 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -80
- package/package.json +35 -2
package/README.md
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
# @aicut/core
|
|
2
2
|
|
|
3
|
-
Framework-agnostic
|
|
3
|
+
> Framework-agnostic engine for the AiCut video editor — canvas timeline, plain-JSON projects, zero runtime deps.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@aicut/core)
|
|
6
|
+
[](./LICENSE)
|
|
7
|
+
[](https://github.com/ziqiangai/AiCut)
|
|
8
|
+
|
|
9
|
+

|
|
4
10
|
|
|
5
11
|
For React or Vue apps, prefer **[@aicut/react](https://www.npmjs.com/package/@aicut/react)** or **[@aicut/vue](https://www.npmjs.com/package/@aicut/vue)** — they wrap this same engine.
|
|
6
12
|
|
|
13
|
+
## Install
|
|
14
|
+
|
|
7
15
|
```bash
|
|
8
16
|
pnpm add @aicut/core
|
|
9
17
|
```
|
|
@@ -21,91 +29,79 @@ const editor = Editor.create({
|
|
|
21
29
|
sources: [
|
|
22
30
|
{ id: "s1", url: "/media/a.mp4", kind: "video", name: "a.mp4" },
|
|
23
31
|
],
|
|
24
|
-
tracks: [
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
},
|
|
30
|
-
],
|
|
32
|
+
tracks: [{
|
|
33
|
+
id: "t1",
|
|
34
|
+
kind: "video",
|
|
35
|
+
clips: [{ id: "c1", sourceId: "s1", in: 0, out: 5000, start: 0 }],
|
|
36
|
+
}],
|
|
31
37
|
},
|
|
32
38
|
});
|
|
33
39
|
|
|
34
|
-
editor.on("change", ({ project }) =>
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
headers: { "content-type": "application/json" },
|
|
38
|
-
body: JSON.stringify({ project }),
|
|
39
|
-
}));
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
The editor renders into your `container`. Call methods on the returned `editor` instance to drive it imperatively.
|
|
40
|
+
editor.on("change", ({ project }) => {
|
|
41
|
+
localStorage.setItem("aicut", JSON.stringify(project));
|
|
42
|
+
});
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
editor.on("export", ({ project }) => {
|
|
45
|
+
fetch("/api/export", {
|
|
46
|
+
method: "POST",
|
|
47
|
+
headers: { "content-type": "application/json" },
|
|
48
|
+
body: JSON.stringify({ project }),
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
```
|
|
45
52
|
|
|
46
|
-
|
|
53
|
+
## API at a glance
|
|
47
54
|
|
|
48
55
|
```ts
|
|
49
|
-
|
|
50
|
-
editor.
|
|
51
|
-
editor.
|
|
56
|
+
// Playback
|
|
57
|
+
editor.play(); editor.pause(); editor.togglePlay();
|
|
58
|
+
editor.seek(timeMs);
|
|
59
|
+
|
|
60
|
+
// Editing
|
|
61
|
+
editor.split(); // at playhead
|
|
62
|
+
editor.trimLeft();
|
|
52
63
|
editor.trimRight();
|
|
53
64
|
editor.removeClip(clipId);
|
|
54
65
|
editor.setClipSpeed(clipId, 2);
|
|
55
|
-
|
|
56
66
|
editor.undo(); editor.redo();
|
|
57
|
-
editor.canUndo(); editor.canRedo();
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
### Project, sources, tracks
|
|
61
67
|
|
|
62
|
-
|
|
63
|
-
editor.getProject();
|
|
64
|
-
editor.setProject(
|
|
65
|
-
editor.reset();
|
|
68
|
+
// Project state
|
|
69
|
+
editor.getProject();
|
|
70
|
+
editor.setProject(project);
|
|
71
|
+
editor.reset();
|
|
66
72
|
editor.addSource({ id, url, kind: "video" });
|
|
67
73
|
editor.addTrack("video");
|
|
68
|
-
editor.removeTrack(trackId);
|
|
69
74
|
editor.moveClip(clipId, { start, trackId, newTrack });
|
|
70
|
-
editor.resizeClip(clipId, { in, out, start });
|
|
71
|
-
```
|
|
72
75
|
|
|
73
|
-
|
|
76
|
+
// Viewport
|
|
77
|
+
editor.setScale(80); // px per second
|
|
78
|
+
editor.setSnap(false);
|
|
79
|
+
editor.setSelection(clipId);
|
|
74
80
|
|
|
75
|
-
|
|
76
|
-
editor.
|
|
77
|
-
editor.
|
|
78
|
-
editor.
|
|
79
|
-
editor.enterFullscreen(); editor.exitFullscreen();
|
|
81
|
+
// UI
|
|
82
|
+
editor.setTheme({ controlsBg: "#fff" });
|
|
83
|
+
editor.setLocale({ undo: "Annuler" });
|
|
84
|
+
editor.requestExport(); // → fires "export" event
|
|
80
85
|
```
|
|
81
86
|
|
|
82
|
-
|
|
87
|
+
## Events
|
|
83
88
|
|
|
84
89
|
```ts
|
|
85
|
-
|
|
86
|
-
editor.on("time",
|
|
87
|
-
editor.on("export",
|
|
90
|
+
editor.on("change", ({ project }) => /* … */);
|
|
91
|
+
editor.on("time", ({ timeMs }) => /* … */);
|
|
92
|
+
editor.on("export", ({ project }) => /* … */);
|
|
88
93
|
editor.on("selectionChange", ({ clipId }) => /* … */);
|
|
89
|
-
editor.on("historyChange",
|
|
90
|
-
editor.on("ready",
|
|
91
|
-
editor.on("scaleChange",
|
|
92
|
-
editor.on("snapChange",
|
|
93
|
-
editor.on("error",
|
|
94
|
-
off(); // unsubscribe
|
|
94
|
+
editor.on("historyChange", ({ canUndo, canRedo }) => /* … */);
|
|
95
|
+
editor.on("ready", ({ sourceId }) => /* … */);
|
|
96
|
+
editor.on("scaleChange", ({ pxPerSec }) => /* … */);
|
|
97
|
+
editor.on("snapChange", ({ snap }) => /* … */);
|
|
98
|
+
editor.on("error", ({ error }) => /* … */);
|
|
95
99
|
```
|
|
96
100
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
The library never calls a backend on its own — `requestExport()` fires the `export` event with the current project JSON; your handler decides what to do.
|
|
100
|
-
|
|
101
|
-
```ts
|
|
102
|
-
editor.requestExport(); // → emits "export" with project
|
|
103
|
-
```
|
|
101
|
+
Each `on` returns an unsubscribe function.
|
|
104
102
|
|
|
105
103
|
## Theming
|
|
106
104
|
|
|
107
|
-
CSS variables; pass any subset via `theme` and the rest fall back to the defaults.
|
|
108
|
-
|
|
109
105
|
```ts
|
|
110
106
|
Editor.create({
|
|
111
107
|
container,
|
|
@@ -116,16 +112,14 @@ Editor.create({
|
|
|
116
112
|
controlsBorder: "rgba(255, 255, 255, 0.08)",
|
|
117
113
|
controlsHover: "rgba(255, 255, 255, 0.08)",
|
|
118
114
|
controlsActive: "rgba(255, 255, 255, 0.12)",
|
|
119
|
-
previewBg: "#000",
|
|
115
|
+
previewBg: "#000", // letterbox colour
|
|
120
116
|
},
|
|
121
117
|
});
|
|
122
|
-
|
|
123
|
-
editor.setTheme({ controlsBg: "#f6f6f8", previewBg: "#e4e4e7" }); // runtime swap
|
|
124
118
|
```
|
|
125
119
|
|
|
126
|
-
Every
|
|
120
|
+
Every key is also a plain CSS custom property — `.aicut-root { --aicut-controls-bg: …; }` works too. Call `editor.setTheme(…)` to swap at runtime.
|
|
127
121
|
|
|
128
|
-
##
|
|
122
|
+
## i18n
|
|
129
123
|
|
|
130
124
|
English by default. Bundled `localeZh` covers the whole editor (toolbar tooltips, exit-fullscreen overlay, canvas track headers).
|
|
131
125
|
|
|
@@ -134,42 +128,36 @@ import { Editor, localeZh } from "@aicut/core";
|
|
|
134
128
|
|
|
135
129
|
Editor.create({ container, project, locale: localeZh });
|
|
136
130
|
|
|
137
|
-
|
|
138
|
-
editor.setLocale({ undo: "Annuler", redo: "Refaire" });
|
|
131
|
+
editor.setLocale({ undo: "Annuler" }); // partial override
|
|
139
132
|
```
|
|
140
133
|
|
|
141
|
-
`Locale` is exported as a type if you need to typecheck a custom pack.
|
|
142
|
-
|
|
143
134
|
## Toolbar slots
|
|
144
135
|
|
|
145
|
-
Both the editor's
|
|
136
|
+
Both the editor's toolbar and the standalone `Timeline`'s optional toolbar expose `toolbarLeft` / `toolbarRight` slot DOM elements. The library renders nothing into them — append your own buttons.
|
|
146
137
|
|
|
147
138
|
```ts
|
|
148
|
-
const editor = Editor.create({ container, project });
|
|
149
139
|
const exportBtn = document.createElement("button");
|
|
150
140
|
exportBtn.textContent = "Export";
|
|
151
141
|
exportBtn.onclick = () => editor.requestExport();
|
|
152
142
|
editor.toolbarRight.appendChild(exportBtn);
|
|
153
143
|
```
|
|
154
144
|
|
|
155
|
-
The library paints nothing into either slot and renders no separator until they're populated.
|
|
156
|
-
|
|
157
145
|
## Standalone Timeline
|
|
158
146
|
|
|
159
|
-
Use the canvas timeline without the rest of the editor — useful for a frame-picker, thumbnail strip, or a read-only preview.
|
|
160
|
-
|
|
161
147
|
```ts
|
|
162
148
|
import { Timeline } from "@aicut/core";
|
|
163
149
|
|
|
164
150
|
const tl = Timeline.create({
|
|
165
151
|
container: document.getElementById("strip")!,
|
|
166
|
-
project:
|
|
152
|
+
project: singleClipProject,
|
|
167
153
|
showHeader: false,
|
|
168
154
|
readOnly: true,
|
|
169
155
|
onSeek: (ms) => console.log("picked", ms),
|
|
170
156
|
});
|
|
171
157
|
```
|
|
172
158
|
|
|
159
|
+
Useful for a frame-picker, thumbnail strip, or read-only preview.
|
|
160
|
+
|
|
173
161
|
## Data model
|
|
174
162
|
|
|
175
163
|
```ts
|
|
@@ -188,14 +176,14 @@ interface Track { id: string; kind: "video" | "audio"; clips: Clip[]; }
|
|
|
188
176
|
|
|
189
177
|
interface Clip {
|
|
190
178
|
id: string; sourceId: string;
|
|
191
|
-
in: Ms; out: Ms;
|
|
192
|
-
start: Ms;
|
|
179
|
+
in: Ms; out: Ms; // window into the source (exclusive at `out`)
|
|
180
|
+
start: Ms; // position on the timeline
|
|
193
181
|
speed?: number;
|
|
194
182
|
}
|
|
195
183
|
|
|
196
|
-
type Ms = number;
|
|
184
|
+
type Ms = number; // integer milliseconds; no frame-rate coupling
|
|
197
185
|
```
|
|
198
186
|
|
|
199
|
-
|
|
187
|
+
---
|
|
200
188
|
|
|
201
|
-
|
|
189
|
+
[Full docs & demo](https://github.com/ziqiangai/AiCut) · [@aicut/react](https://www.npmjs.com/package/@aicut/react) · [@aicut/vue](https://www.npmjs.com/package/@aicut/vue)
|
package/package.json
CHANGED
|
@@ -1,8 +1,41 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aicut/core",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Framework-agnostic core for the AiCut video editor — data model,
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Framework-agnostic core for the AiCut video editor — canvas timeline, data model, HTML5 playback engine.",
|
|
5
5
|
"license": "MIT",
|
|
6
|
+
"author": "ziqiang <ziqiangytu@gmail.com>",
|
|
7
|
+
"homepage": "https://github.com/ziqiangai/AiCut#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/ziqiangai/AiCut.git",
|
|
11
|
+
"directory": "packages/core"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/ziqiangai/AiCut/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"video-editor",
|
|
18
|
+
"video-editing",
|
|
19
|
+
"timeline",
|
|
20
|
+
"timeline-editor",
|
|
21
|
+
"nle",
|
|
22
|
+
"video-cutter",
|
|
23
|
+
"video-clip",
|
|
24
|
+
"canvas",
|
|
25
|
+
"ffmpeg",
|
|
26
|
+
"video",
|
|
27
|
+
"editor",
|
|
28
|
+
"mp4",
|
|
29
|
+
"component",
|
|
30
|
+
"framework-agnostic",
|
|
31
|
+
"capcut",
|
|
32
|
+
"premiere",
|
|
33
|
+
"final-cut",
|
|
34
|
+
"davinci",
|
|
35
|
+
"imovie",
|
|
36
|
+
"veed",
|
|
37
|
+
"filmora"
|
|
38
|
+
],
|
|
6
39
|
"type": "module",
|
|
7
40
|
"sideEffects": [
|
|
8
41
|
"./styles/*.css"
|