@aicut/core 0.1.0 → 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.
package/README.md CHANGED
@@ -1,9 +1,17 @@
1
1
  # @aicut/core
2
2
 
3
- Framework-agnostic video editor engine. Owns the project data model, the HTML5 playback engine, the canvas timeline (ruler, tracks, clips, thumbnails, playhead, snap, in-canvas scrollbars), and a vanilla DOM toolbar.
3
+ > Framework-agnostic engine for the AiCut video editor canvas timeline, plain-JSON projects, zero runtime deps.
4
+
5
+ [![npm](https://img.shields.io/npm/v/@aicut/core.svg)](https://www.npmjs.com/package/@aicut/core)
6
+ [![License](https://img.shields.io/npm/l/@aicut/core.svg)](./LICENSE)
7
+ [![GitHub](https://img.shields.io/badge/repo-ziqiangai/AiCut-181717?logo=github)](https://github.com/ziqiangai/AiCut)
8
+
9
+ ![AiCut editor](https://raw.githubusercontent.com/ziqiangai/AiCut/main/docs/screenshots/editor-dark.png)
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
- id: "t1",
27
- kind: "video",
28
- clips: [{ id: "c1", sourceId: "s1", in: 0, out: 5000, start: 0 }],
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 }) => localStorage.setItem("aicut", JSON.stringify(project)));
35
- editor.on("export", ({ project }) => fetch("/api/export", {
36
- method: "POST",
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
- ## API surface
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
- ### Playback & editing
53
+ ## API at a glance
47
54
 
48
55
  ```ts
49
- editor.play(); editor.pause(); editor.togglePlay(); editor.seek(timeMs);
50
- editor.split(); // split at playhead
51
- editor.trimLeft(); // trim selected clip's left edge to playhead
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
67
 
60
- ### Project, sources, tracks
61
-
62
- ```ts
63
- editor.getProject(); // deep clone of current state
64
- editor.setProject(p); // replace, preserves auto-fit etc.
65
- editor.reset(); // empty single-track project
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
- ### Viewport
76
+ // Viewport
77
+ editor.setScale(80); // px per second
78
+ editor.setSnap(false);
79
+ editor.setSelection(clipId);
74
80
 
75
- ```ts
76
- editor.getScale(); editor.setScale(80); // px per second
77
- editor.getSnap(); editor.setSnap(false);
78
- editor.getSelection(); editor.setSelection(clipId);
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
- ### Events
87
+ ## Events
83
88
 
84
89
  ```ts
85
- const off = editor.on("change", ({ project }) => /* … */);
86
- editor.on("time", ({ timeMs }) => /* playback tick */);
87
- editor.on("export", ({ project }) => /* host-triggered 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", ({ canUndo, canRedo }) => /* … */);
90
- editor.on("ready", ({ sourceId }) => /* per-source metadata loaded */);
91
- editor.on("scaleChange", ({ pxPerSec }) => /* … */);
92
- editor.on("snapChange", ({ snap }) => /* … */);
93
- editor.on("error", ({ 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
- ### Triggering export
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", // letterbox colour
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 variable is also a plain CSS custom property — `.aicut-root { --aicut-controls-bg: …; }` works too.
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
- ## Internationalisation
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,65 @@ import { Editor, localeZh } from "@aicut/core";
134
128
 
135
129
  Editor.create({ container, project, locale: localeZh });
136
130
 
137
- // Partial override + runtime swap
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 built-in toolbar and the standalone `Timeline`'s optional toolbar reserve `toolbarLeft` / `toolbarRight` slot DOM elements for host-supplied controls.
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.
145
+ ## Lighting picker (opt-in sub-entry)
156
146
 
157
- ## Standalone Timeline
147
+ A separate component for AI relighting workflows — drag a light dot around a 3D sphere wrapping a subject frame, control brightness / color / direction. Three.js is bundled only on this sub-entry, so consumers of the video editor pay zero bytes for it.
158
148
 
159
- Use the canvas timeline without the rest of the editor — useful for a frame-picker, thumbnail strip, or a read-only preview.
149
+ ```ts
150
+ import { LightingEditor } from "@aicut/core/lighting";
151
+ import "@aicut/core/styles.css";
152
+
153
+ const ed = LightingEditor.create({
154
+ container: document.getElementById("light")!,
155
+ subjectImageUrl: "/frames/subject.jpg",
156
+ smartEnabled: true, // default; false → no slot column at all
157
+ smartOpen: true, // default; user can toggle via × / header pill
158
+ onChange: (cfg) => console.log(cfg),
159
+ onGenerate: (cfg) => fetch("/relight", { method: "POST", body: JSON.stringify(cfg) }),
160
+ });
161
+
162
+ // Host appends UI into the smart slot (prompt textarea, presets, generate btn, …).
163
+ ed.smartSlot.appendChild(myAiUI);
164
+
165
+ // Runtime control:
166
+ ed.setSmartOpen(false);
167
+ ed.setSmartEnabled(false);
168
+ ed.setView("front");
169
+ ed.setConfig({ brightness: 0.8, color: "#ffaa3a" });
170
+ ```
171
+
172
+ Locale extension `LightingLocale` (separate from the video editor's `Locale`) is also exported with `lightingLocaleEn` / `lightingLocaleZh`.
173
+
174
+ ## Standalone Timeline
160
175
 
161
176
  ```ts
162
177
  import { Timeline } from "@aicut/core";
163
178
 
164
179
  const tl = Timeline.create({
165
180
  container: document.getElementById("strip")!,
166
- project: { /* one-clip project */ },
181
+ project: singleClipProject,
167
182
  showHeader: false,
168
183
  readOnly: true,
169
184
  onSeek: (ms) => console.log("picked", ms),
170
185
  });
171
186
  ```
172
187
 
188
+ Useful for a frame-picker, thumbnail strip, or read-only preview.
189
+
173
190
  ## Data model
174
191
 
175
192
  ```ts
@@ -188,14 +205,14 @@ interface Track { id: string; kind: "video" | "audio"; clips: Clip[]; }
188
205
 
189
206
  interface Clip {
190
207
  id: string; sourceId: string;
191
- in: Ms; out: Ms; // window into the source (exclusive at `out`)
192
- start: Ms; // position on the timeline
208
+ in: Ms; out: Ms; // window into the source (exclusive at `out`)
209
+ start: Ms; // position on the timeline
193
210
  speed?: number;
194
211
  }
195
212
 
196
- type Ms = number; // integer milliseconds; no frame-rate coupling
213
+ type Ms = number; // integer milliseconds; no frame-rate coupling
197
214
  ```
198
215
 
199
- ## License
216
+ ---
200
217
 
201
- MIT
218
+ [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)
@@ -0,0 +1,87 @@
1
+ // src/i18n.ts
2
+ var localeEn = {
3
+ undo: "Undo",
4
+ redo: "Redo",
5
+ split: "Split",
6
+ trimLeft: "Trim left edge",
7
+ trimRight: "Trim right edge",
8
+ speedComingSoon: "Speed (coming soon)",
9
+ playPause: "Play / Pause (Space)",
10
+ fullscreen: "Fullscreen preview",
11
+ snap: "Snap",
12
+ snapOnTitle: "Turn off snap",
13
+ snapOffTitle: "Turn on snap",
14
+ zoomOut: "Zoom out",
15
+ zoomIn: "Zoom in",
16
+ reset: "Reset edits (keep sources)",
17
+ exitFullscreen: "Exit fullscreen",
18
+ exitFullscreenTitle: "Exit fullscreen (Esc)",
19
+ newTrack: "+ New track",
20
+ videoTrackLabel: "Video {n}",
21
+ audioTrackLabel: "Audio {n}"
22
+ };
23
+ var localeZh = {
24
+ undo: "\u64A4\u9500",
25
+ redo: "\u91CD\u505A",
26
+ split: "\u5206\u5272",
27
+ trimLeft: "\u5411\u5DE6\u88C1\u526A",
28
+ trimRight: "\u5411\u53F3\u88C1\u526A",
29
+ speedComingSoon: "\u53D8\u901F\uFF08\u5373\u5C06\u5230\u6765\uFF09",
30
+ playPause: "\u64AD\u653E / \u6682\u505C (Space)",
31
+ fullscreen: "\u5168\u5C4F\u9884\u89C8",
32
+ snap: "\u5438\u9644",
33
+ snapOnTitle: "\u5173\u95ED\u5438\u9644",
34
+ snapOffTitle: "\u5F00\u542F\u5438\u9644",
35
+ zoomOut: "\u7F29\u5C0F",
36
+ zoomIn: "\u653E\u5927",
37
+ reset: "\u91CD\u7F6E\u7F16\u8F91\uFF08\u4FDD\u7559\u89C6\u9891\u6E90\uFF09",
38
+ exitFullscreen: "\u9000\u51FA\u5168\u5C4F",
39
+ exitFullscreenTitle: "\u9000\u51FA\u5168\u5C4F (Esc)",
40
+ newTrack: "+ \u65B0\u8F68\u9053",
41
+ videoTrackLabel: "\u89C6\u9891 {n}",
42
+ audioTrackLabel: "\u97F3\u9891 {n}"
43
+ };
44
+ function mergeLocale(partial) {
45
+ return partial ? { ...localeEn, ...partial } : localeEn;
46
+ }
47
+ function formatLabel(template, vars) {
48
+ return template.replace(
49
+ /\{(\w+)\}/g,
50
+ (_, k) => k in vars ? String(vars[k]) : `{${k}}`
51
+ );
52
+ }
53
+
54
+ // src/theme.ts
55
+ var THEME_VARS = {
56
+ brand: "--color-brand",
57
+ secondary: "--color-secondary",
58
+ surface: "--color-surface",
59
+ dark: "--color-dark",
60
+ muted: "--color-muted",
61
+ card: "--color-card",
62
+ success: "--color-success",
63
+ warning: "--color-warning",
64
+ info: "--color-info",
65
+ error: "--color-error",
66
+ controlsBg: "--aicut-controls-bg",
67
+ controlsBorder: "--aicut-controls-border",
68
+ controlsText: "--aicut-controls-text",
69
+ controlsHover: "--aicut-controls-hover",
70
+ controlsActive: "--aicut-controls-active",
71
+ previewBg: "--aicut-preview-bg",
72
+ radiusSm: "--aicut-radius-sm",
73
+ radiusMd: "--aicut-radius-md",
74
+ radiusLg: "--aicut-radius-lg"
75
+ };
76
+ function applyTheme(root, theme) {
77
+ if (!theme) return;
78
+ for (const key of Object.keys(theme)) {
79
+ const cssVar = THEME_VARS[key];
80
+ const value = theme[key];
81
+ if (cssVar && value) root.style.setProperty(cssVar, value);
82
+ }
83
+ }
84
+
85
+ export { applyTheme, formatLabel, localeEn, localeZh, mergeLocale };
86
+ //# sourceMappingURL=chunk-CCDON7CU.js.map
87
+ //# sourceMappingURL=chunk-CCDON7CU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/i18n.ts","../src/theme.ts"],"names":[],"mappings":"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","file":"chunk-CCDON7CU.js","sourcesContent":["/**\n * UI strings the editor paints into the DOM (toolbar tooltips, the\n * fullscreen exit button) and onto the timeline canvas (phantom new-\n * track label, track header labels). Every user-visible literal in\n * `@aicut/core` flows through this interface — there are no hidden\n * hard-coded translations elsewhere in the library.\n *\n * Defaults to English. Hosts that want Chinese (or any other locale)\n * pass `locale: localeZh` to `Editor.create` / `Timeline.create`, or\n * override individual keys with `locale: { undo: \"撤销\" }`.\n */\nexport interface Locale {\n // Toolbar tooltips\n undo: string;\n redo: string;\n split: string;\n trimLeft: string;\n trimRight: string;\n speedComingSoon: string;\n playPause: string;\n fullscreen: string;\n snap: string;\n /** Title shown on the snap button when snap is ON (clicking turns OFF). */\n snapOnTitle: string;\n /** Title shown when snap is OFF (clicking turns ON). */\n snapOffTitle: string;\n zoomOut: string;\n zoomIn: string;\n reset: string;\n\n // Fullscreen exit overlay\n exitFullscreen: string;\n exitFullscreenTitle: string;\n\n // Timeline canvas labels\n /** Phantom row that appears under the last track during a drag. */\n newTrack: string;\n /** Track header — `{n}` is replaced with the 1-based track index. */\n videoTrackLabel: string;\n /** Same template format as videoTrackLabel. */\n audioTrackLabel: string;\n}\n\n/** English. The library default — chosen over Chinese as the OSS norm. */\nexport const localeEn: Locale = {\n undo: \"Undo\",\n redo: \"Redo\",\n split: \"Split\",\n trimLeft: \"Trim left edge\",\n trimRight: \"Trim right edge\",\n speedComingSoon: \"Speed (coming soon)\",\n playPause: \"Play / Pause (Space)\",\n fullscreen: \"Fullscreen preview\",\n snap: \"Snap\",\n snapOnTitle: \"Turn off snap\",\n snapOffTitle: \"Turn on snap\",\n zoomOut: \"Zoom out\",\n zoomIn: \"Zoom in\",\n reset: \"Reset edits (keep sources)\",\n exitFullscreen: \"Exit fullscreen\",\n exitFullscreenTitle: \"Exit fullscreen (Esc)\",\n newTrack: \"+ New track\",\n videoTrackLabel: \"Video {n}\",\n audioTrackLabel: \"Audio {n}\",\n};\n\n/** Simplified Chinese. */\nexport const localeZh: Locale = {\n undo: \"撤销\",\n redo: \"重做\",\n split: \"分割\",\n trimLeft: \"向左裁剪\",\n trimRight: \"向右裁剪\",\n speedComingSoon: \"变速(即将到来)\",\n playPause: \"播放 / 暂停 (Space)\",\n fullscreen: \"全屏预览\",\n snap: \"吸附\",\n snapOnTitle: \"关闭吸附\",\n snapOffTitle: \"开启吸附\",\n zoomOut: \"缩小\",\n zoomIn: \"放大\",\n reset: \"重置编辑(保留视频源)\",\n exitFullscreen: \"退出全屏\",\n exitFullscreenTitle: \"退出全屏 (Esc)\",\n newTrack: \"+ 新轨道\",\n videoTrackLabel: \"视频 {n}\",\n audioTrackLabel: \"音频 {n}\",\n};\n\n/** Spread defaults under host overrides — host can supply a partial. */\nexport function mergeLocale(partial: Partial<Locale> | undefined): Locale {\n return partial ? { ...localeEn, ...partial } : localeEn;\n}\n\n/**\n * Replace `{key}` placeholders in a template. We only need `{n}`\n * substitution today; the implementation is generic so additional\n * keys (e.g. `{name}`) won't need a second pass.\n */\nexport function formatLabel(\n template: string,\n vars: Record<string, string | number>,\n): string {\n return template.replace(/\\{(\\w+)\\}/g, (_, k) =>\n k in vars ? String(vars[k]) : `{${k}}`,\n );\n}\n","import type { Theme } from \"./types.js\";\n\n/**\n * Map `Theme` keys to the CSS custom property they write.\n *\n * Brand/palette keys share names with iqvise's globals.css so hosts\n * that already define `--color-brand` etc. at the page level get the\n * editor in their palette for free — `theme` props ONLY needed when\n * scoping to this editor instance.\n *\n * Chrome keys keep the `--aicut-controls-*` prefix because they have\n * no analogue in the host palette.\n */\nconst THEME_VARS: Record<keyof Theme, string> = {\n brand: \"--color-brand\",\n secondary: \"--color-secondary\",\n surface: \"--color-surface\",\n dark: \"--color-dark\",\n muted: \"--color-muted\",\n card: \"--color-card\",\n success: \"--color-success\",\n warning: \"--color-warning\",\n info: \"--color-info\",\n error: \"--color-error\",\n controlsBg: \"--aicut-controls-bg\",\n controlsBorder: \"--aicut-controls-border\",\n controlsText: \"--aicut-controls-text\",\n controlsHover: \"--aicut-controls-hover\",\n controlsActive: \"--aicut-controls-active\",\n previewBg: \"--aicut-preview-bg\",\n radiusSm: \"--aicut-radius-sm\",\n radiusMd: \"--aicut-radius-md\",\n radiusLg: \"--aicut-radius-lg\",\n};\n\nexport function applyTheme(root: HTMLElement, theme: Theme | undefined): void {\n if (!theme) return;\n for (const key of Object.keys(theme) as Array<keyof Theme>) {\n const cssVar = THEME_VARS[key];\n const value = theme[key];\n if (cssVar && value) root.style.setProperty(cssVar, value);\n }\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -1,121 +1,5 @@
1
- /**
2
- * UI strings the editor paints into the DOM (toolbar tooltips, the
3
- * fullscreen exit button) and onto the timeline canvas (phantom new-
4
- * track label, track header labels). Every user-visible literal in
5
- * `@aicut/core` flows through this interface — there are no hidden
6
- * hard-coded translations elsewhere in the library.
7
- *
8
- * Defaults to English. Hosts that want Chinese (or any other locale)
9
- * pass `locale: localeZh` to `Editor.create` / `Timeline.create`, or
10
- * override individual keys with `locale: { undo: "撤销" }`.
11
- */
12
- interface Locale {
13
- undo: string;
14
- redo: string;
15
- split: string;
16
- trimLeft: string;
17
- trimRight: string;
18
- speedComingSoon: string;
19
- playPause: string;
20
- fullscreen: string;
21
- snap: string;
22
- /** Title shown on the snap button when snap is ON (clicking turns OFF). */
23
- snapOnTitle: string;
24
- /** Title shown when snap is OFF (clicking turns ON). */
25
- snapOffTitle: string;
26
- zoomOut: string;
27
- zoomIn: string;
28
- reset: string;
29
- exitFullscreen: string;
30
- exitFullscreenTitle: string;
31
- /** Phantom row that appears under the last track during a drag. */
32
- newTrack: string;
33
- /** Track header — `{n}` is replaced with the 1-based track index. */
34
- videoTrackLabel: string;
35
- /** Same template format as videoTrackLabel. */
36
- audioTrackLabel: string;
37
- }
38
- /** English. The library default — chosen over Chinese as the OSS norm. */
39
- declare const localeEn: Locale;
40
- /** Simplified Chinese. */
41
- declare const localeZh: Locale;
42
- /** Spread defaults under host overrides — host can supply a partial. */
43
- declare function mergeLocale(partial: Partial<Locale> | undefined): Locale;
44
- /**
45
- * Replace `{key}` placeholders in a template. We only need `{n}`
46
- * substitution today; the implementation is generic so additional
47
- * keys (e.g. `{name}`) won't need a second pass.
48
- */
49
- declare function formatLabel(template: string, vars: Record<string, string | number>): string;
50
-
51
- /**
52
- * Milliseconds. All timing in the project is expressed as integer ms to
53
- * keep JSON serialization unambiguous (no frame-rate coupling in the
54
- * data model — the renderer can present time as frames if it wants).
55
- */
56
- type Ms = number;
57
- interface MediaSource {
58
- id: string;
59
- url: string;
60
- kind: "video" | "audio";
61
- /** Optional — probed lazily from the <video> element if absent. */
62
- duration?: Ms;
63
- name?: string;
64
- }
65
- interface Clip {
66
- id: string;
67
- sourceId: string;
68
- /** Window into the source — `in` inclusive, `out` exclusive. */
69
- in: Ms;
70
- out: Ms;
71
- /** Position on the timeline. */
72
- start: Ms;
73
- /**
74
- * Playback rate. 1 = normal, 2 = 2× speed. Default 1.
75
- * Persisted in the project JSON so a host can restore exactly.
76
- */
77
- speed?: number;
78
- }
79
- interface Track {
80
- id: string;
81
- kind: "video" | "audio";
82
- /** Clips on this track. Must be kept sorted by `start` and non-overlapping. */
83
- clips: Clip[];
84
- }
85
- interface Project {
86
- /** Schema version — bump when breaking the JSON shape. */
87
- version: 1;
88
- sources: MediaSource[];
89
- tracks: Track[];
90
- }
91
- /**
92
- * Subset of CSS variables the editor honors. Pass any custom values
93
- * via `Editor` options; everything is forwarded as `--aicut-*` on the
94
- * editor's root container, so a host can also override via plain CSS.
95
- */
96
- interface Theme {
97
- brand?: string;
98
- secondary?: string;
99
- surface?: string;
100
- dark?: string;
101
- muted?: string;
102
- card?: string;
103
- success?: string;
104
- warning?: string;
105
- info?: string;
106
- error?: string;
107
- /** Toolbar / ruler chrome. Background of the editor frame. */
108
- controlsBg?: string;
109
- controlsBorder?: string;
110
- controlsText?: string;
111
- controlsHover?: string;
112
- controlsActive?: string;
113
- /** Letterbox color around the preview video. Defaults to black. */
114
- previewBg?: string;
115
- radiusSm?: string;
116
- radiusMd?: string;
117
- radiusLg?: string;
118
- }
1
+ import { M as Ms, T as Track, C as Clip, a as MediaSource, P as Project, b as Theme, L as Locale } from './types-C95koNwJ.cjs';
2
+ export { f as formatLabel, l as localeEn, c as localeZh, m as mergeLocale } from './types-C95koNwJ.cjs';
119
3
 
120
4
  interface EditorOptions {
121
5
  /** Host element to mount the editor into. Will be wiped on init. */
@@ -637,4 +521,4 @@ declare class Timeline {
637
521
  private applySnap;
638
522
  }
639
523
 
640
- export { type Clip, Editor, type EditorApi, type EditorEventMap, type EditorEventName, type EditorOptions, HEADER_WIDTH, type Locale, type MediaSource, type Ms, type Project, RULER_HEIGHT, TRACK_HEIGHT, type Theme, Timeline, type TimelineOptions, type Track, createEmptyProject, createId, formatLabel, localeEn, localeZh, mergeLocale, normalizeProject };
524
+ export { Clip, Editor, type EditorApi, type EditorEventMap, type EditorEventName, type EditorOptions, HEADER_WIDTH, Locale, MediaSource, Ms, Project, RULER_HEIGHT, TRACK_HEIGHT, Theme, Timeline, type TimelineOptions, Track, createEmptyProject, createId, normalizeProject };
package/dist/index.d.ts CHANGED
@@ -1,121 +1,5 @@
1
- /**
2
- * UI strings the editor paints into the DOM (toolbar tooltips, the
3
- * fullscreen exit button) and onto the timeline canvas (phantom new-
4
- * track label, track header labels). Every user-visible literal in
5
- * `@aicut/core` flows through this interface — there are no hidden
6
- * hard-coded translations elsewhere in the library.
7
- *
8
- * Defaults to English. Hosts that want Chinese (or any other locale)
9
- * pass `locale: localeZh` to `Editor.create` / `Timeline.create`, or
10
- * override individual keys with `locale: { undo: "撤销" }`.
11
- */
12
- interface Locale {
13
- undo: string;
14
- redo: string;
15
- split: string;
16
- trimLeft: string;
17
- trimRight: string;
18
- speedComingSoon: string;
19
- playPause: string;
20
- fullscreen: string;
21
- snap: string;
22
- /** Title shown on the snap button when snap is ON (clicking turns OFF). */
23
- snapOnTitle: string;
24
- /** Title shown when snap is OFF (clicking turns ON). */
25
- snapOffTitle: string;
26
- zoomOut: string;
27
- zoomIn: string;
28
- reset: string;
29
- exitFullscreen: string;
30
- exitFullscreenTitle: string;
31
- /** Phantom row that appears under the last track during a drag. */
32
- newTrack: string;
33
- /** Track header — `{n}` is replaced with the 1-based track index. */
34
- videoTrackLabel: string;
35
- /** Same template format as videoTrackLabel. */
36
- audioTrackLabel: string;
37
- }
38
- /** English. The library default — chosen over Chinese as the OSS norm. */
39
- declare const localeEn: Locale;
40
- /** Simplified Chinese. */
41
- declare const localeZh: Locale;
42
- /** Spread defaults under host overrides — host can supply a partial. */
43
- declare function mergeLocale(partial: Partial<Locale> | undefined): Locale;
44
- /**
45
- * Replace `{key}` placeholders in a template. We only need `{n}`
46
- * substitution today; the implementation is generic so additional
47
- * keys (e.g. `{name}`) won't need a second pass.
48
- */
49
- declare function formatLabel(template: string, vars: Record<string, string | number>): string;
50
-
51
- /**
52
- * Milliseconds. All timing in the project is expressed as integer ms to
53
- * keep JSON serialization unambiguous (no frame-rate coupling in the
54
- * data model — the renderer can present time as frames if it wants).
55
- */
56
- type Ms = number;
57
- interface MediaSource {
58
- id: string;
59
- url: string;
60
- kind: "video" | "audio";
61
- /** Optional — probed lazily from the <video> element if absent. */
62
- duration?: Ms;
63
- name?: string;
64
- }
65
- interface Clip {
66
- id: string;
67
- sourceId: string;
68
- /** Window into the source — `in` inclusive, `out` exclusive. */
69
- in: Ms;
70
- out: Ms;
71
- /** Position on the timeline. */
72
- start: Ms;
73
- /**
74
- * Playback rate. 1 = normal, 2 = 2× speed. Default 1.
75
- * Persisted in the project JSON so a host can restore exactly.
76
- */
77
- speed?: number;
78
- }
79
- interface Track {
80
- id: string;
81
- kind: "video" | "audio";
82
- /** Clips on this track. Must be kept sorted by `start` and non-overlapping. */
83
- clips: Clip[];
84
- }
85
- interface Project {
86
- /** Schema version — bump when breaking the JSON shape. */
87
- version: 1;
88
- sources: MediaSource[];
89
- tracks: Track[];
90
- }
91
- /**
92
- * Subset of CSS variables the editor honors. Pass any custom values
93
- * via `Editor` options; everything is forwarded as `--aicut-*` on the
94
- * editor's root container, so a host can also override via plain CSS.
95
- */
96
- interface Theme {
97
- brand?: string;
98
- secondary?: string;
99
- surface?: string;
100
- dark?: string;
101
- muted?: string;
102
- card?: string;
103
- success?: string;
104
- warning?: string;
105
- info?: string;
106
- error?: string;
107
- /** Toolbar / ruler chrome. Background of the editor frame. */
108
- controlsBg?: string;
109
- controlsBorder?: string;
110
- controlsText?: string;
111
- controlsHover?: string;
112
- controlsActive?: string;
113
- /** Letterbox color around the preview video. Defaults to black. */
114
- previewBg?: string;
115
- radiusSm?: string;
116
- radiusMd?: string;
117
- radiusLg?: string;
118
- }
1
+ import { M as Ms, T as Track, C as Clip, a as MediaSource, P as Project, b as Theme, L as Locale } from './types-C95koNwJ.js';
2
+ export { f as formatLabel, l as localeEn, c as localeZh, m as mergeLocale } from './types-C95koNwJ.js';
119
3
 
120
4
  interface EditorOptions {
121
5
  /** Host element to mount the editor into. Will be wiped on init. */
@@ -637,4 +521,4 @@ declare class Timeline {
637
521
  private applySnap;
638
522
  }
639
523
 
640
- export { type Clip, Editor, type EditorApi, type EditorEventMap, type EditorEventName, type EditorOptions, HEADER_WIDTH, type Locale, type MediaSource, type Ms, type Project, RULER_HEIGHT, TRACK_HEIGHT, type Theme, Timeline, type TimelineOptions, type Track, createEmptyProject, createId, formatLabel, localeEn, localeZh, mergeLocale, normalizeProject };
524
+ export { Clip, Editor, type EditorApi, type EditorEventMap, type EditorEventName, type EditorOptions, HEADER_WIDTH, Locale, MediaSource, Ms, Project, RULER_HEIGHT, TRACK_HEIGHT, Theme, Timeline, type TimelineOptions, Track, createEmptyProject, createId, normalizeProject };