@aicut/react 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,6 +1,14 @@
1
1
  # @aicut/react
2
2
 
3
- React 18 / 19 wrapper around **[@aicut/core](https://www.npmjs.com/package/@aicut/core)** — a canvas-rendered video editor component you can drop into any host app. Import the core stylesheet once and you're done.
3
+ > React wrapper for the **AiCut** video editor canvas timeline, custom toolbar slots, theming, i18n, drop-in `<VideoEditor>`.
4
+
5
+ [![npm](https://img.shields.io/npm/v/@aicut/react.svg)](https://www.npmjs.com/package/@aicut/react)
6
+ [![License](https://img.shields.io/npm/l/@aicut/react.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)
10
+
11
+ ## Install
4
12
 
5
13
  ```bash
6
14
  pnpm add @aicut/react @aicut/core
@@ -19,12 +27,14 @@ import "@aicut/core/styles.css";
19
27
 
20
28
  const project: Project = {
21
29
  version: 1,
22
- sources: [{ id: "s1", url: "/media/a.mp4", kind: "video", name: "a.mp4" }],
23
- tracks: [
24
- { id: "t1", kind: "video", clips: [
25
- { id: "c1", sourceId: "s1", in: 0, out: 5000, start: 0 },
26
- ]},
30
+ sources: [
31
+ { id: "s1", url: "/media/a.mp4", kind: "video", name: "a.mp4" },
27
32
  ],
33
+ tracks: [{
34
+ id: "t1",
35
+ kind: "video",
36
+ clips: [{ id: "c1", sourceId: "s1", in: 0, out: 5000, start: 0 }],
37
+ }],
28
38
  };
29
39
 
30
40
  export function Editor() {
@@ -44,32 +54,45 @@ export function Editor() {
44
54
  }
45
55
  ```
46
56
 
47
- The component is **uncontrolled for project state** — the editor owns the current project. To restore from JSON later, `apiRef.current?.setProject(saved)`.
57
+ The component is **uncontrolled for project state** — the editor owns the current project. To restore from JSON later:
58
+
59
+ ```ts
60
+ apiRef.current?.setProject(savedJson);
61
+ ```
48
62
 
49
63
  ## Props
50
64
 
51
- | Prop | Type | Notes |
52
- | --- | --- | --- |
53
- | `defaultProject` | `Project` | Initial project. Read once on mount. |
54
- | `theme` | `Theme` | CSS variable overrides. Reactive — mirrors to `editor.setTheme`. |
55
- | `locale` | `Partial<Locale>` | UI strings. English by default; pass `localeZh` for Chinese. Reactive. |
56
- | `toolbarLeft` | `ReactNode` | Portaled into the editor toolbar's left bookend slot. |
57
- | `toolbarRight` | `ReactNode` | Portaled into the right slot — host's Export button lives here. |
58
- | `apiRef` | `Ref<VideoEditorApi \| null>` | Imperative API handle. |
59
- | `onReady` | `(api) => void` | Fires synchronously on mount. |
60
- | `onChange` | `(project) => void` | Any model mutation. |
61
- | `onExport` | `(project) => void` | Fired by `api.requestExport()`. |
62
- | `onTimeUpdate` | `(ms) => void` | Playback tick. |
63
- | `onPlay` / `onPause` | `() => void` | |
64
- | `onSelectionChange` | `(clipId \| null) => void` | |
65
- | `onError` | `(err) => void` | |
66
- | `className` / `style` | `string` / `CSSProperties` | Forwarded to the host `<div>`. |
67
-
68
- The `apiRef` value implements **every method on `EditorApi`** — `play`, `pause`, `seek`, `split`, `trimLeft`, `trimRight`, `setProject`, `getProject`, `addSource`, `addTrack`, `removeClip`, `setSelection`, `undo`, `redo`, `setTheme`, `setLocale`, `requestExport`, etc. See [`@aicut/core`](https://www.npmjs.com/package/@aicut/core) for the full surface.
65
+ ```ts
66
+ interface VideoEditorProps {
67
+ defaultProject?: Project;
68
+
69
+ theme?: Theme; // CSS-var overrides; reactive
70
+ locale?: Partial<Locale>; // EN default; pass localeZh for ZH
71
+
72
+ toolbarLeft?: ReactNode; // host controls left bookend
73
+ toolbarRight?: ReactNode; // right bookend
74
+
75
+ apiRef?: Ref<VideoEditorApi | null>;
76
+
77
+ onReady?: (api: VideoEditorApi) => void;
78
+ onChange?: (project: Project) => void;
79
+ onExport?: (project: Project) => void; // fired by api.requestExport()
80
+ onTimeUpdate?: (ms: number) => void;
81
+ onPlay?: () => void;
82
+ onPause?: () => void;
83
+ onSelectionChange?: (clipId: string | null) => void;
84
+ onError?: (err: Error) => void;
85
+
86
+ className?: string;
87
+ style?: CSSProperties;
88
+ }
89
+ ```
90
+
91
+ The `apiRef` value exposes the full **`EditorApi`** — `play`, `pause`, `seek`, `split`, `trimLeft`, `trimRight`, `setProject`, `getProject`, `addSource`, `addTrack`, `removeClip`, `undo`, `redo`, `setTheme`, `setLocale`, `requestExport`, and more. See [@aicut/core](https://www.npmjs.com/package/@aicut/core) for the complete surface.
69
92
 
70
93
  ## Custom toolbar controls
71
94
 
72
- Drop any React node into `toolbarLeft` / `toolbarRight`. The library renders nothing into the slots and hides the separator until they're populated.
95
+ The editor's top toolbar reserves bookend slots for any React node. The library hides the visual separator until you put something in them.
73
96
 
74
97
  ```tsx
75
98
  <VideoEditor
@@ -88,7 +111,7 @@ Drop any React node into `toolbarLeft` / `toolbarRight`. The library renders not
88
111
  />
89
112
  ```
90
113
 
91
- `api.requestExport()` fires the `export` event with the current project JSON, which flows back through your `onExport` prop. From there, POST it to your own backend.
114
+ `api.requestExport()` fires the `export` event with the current project JSON, which flows back through your `onExport` prop. Your handler decides whether to POST to a backend, download locally, etc.
92
115
 
93
116
  ## Theming
94
117
 
@@ -100,7 +123,7 @@ Drop any React node into `toolbarLeft` / `toolbarRight`. The library renders not
100
123
  controlsBorder: "rgba(0, 0, 0, 0.08)",
101
124
  controlsHover: "rgba(0, 0, 0, 0.06)",
102
125
  controlsActive: "rgba(0, 0, 0, 0.08)",
103
- previewBg: "#e4e4e7", // letterbox colour around the video
126
+ previewBg: "#e4e4e7", // letterbox colour around the video
104
127
  }}
105
128
  /* … */
106
129
  />
@@ -110,18 +133,63 @@ The `theme` prop is reactive — swap it any time and the editor calls `setTheme
110
133
 
111
134
  ## i18n
112
135
 
113
- English is default. Pass the bundled `localeZh` for Chinese, or a partial object to override specific keys.
114
-
115
136
  ```tsx
116
137
  import { VideoEditor, localeZh } from "@aicut/react";
117
138
 
139
+ // Whole-locale swap
118
140
  <VideoEditor locale={localeZh} /* … */ />
141
+
142
+ // Partial override
119
143
  <VideoEditor locale={{ undo: "Annuler" }} /* … */ />
120
144
  ```
121
145
 
146
+ `locale` is reactive too — runtime swap re-titles the toolbar and re-paints canvas labels in place.
147
+
148
+ ## `<LightingEditor>` (opt-in sub-entry)
149
+
150
+ A 3D lighting director for AI relighting flows — separate component that doesn't pull three.js into the rest of your bundle.
151
+
152
+ ```tsx
153
+ import { useRef } from "react";
154
+ import {
155
+ LightingEditor,
156
+ type LightingEditorApi,
157
+ type LightingConfig,
158
+ } from "@aicut/react/lighting";
159
+ import "@aicut/core/styles.css";
160
+
161
+ function Relight() {
162
+ const apiRef = useRef<LightingEditorApi | null>(null);
163
+ return (
164
+ <LightingEditor
165
+ apiRef={apiRef}
166
+ subjectImageUrl="/frames/subject.jpg"
167
+ smartEnabled
168
+ smartPanel={
169
+ <>
170
+ <textarea placeholder="Describe the mood…" />
171
+ <button onClick={() => apiRef.current?.requestGenerate()}>
172
+ Generate
173
+ </button>
174
+ </>
175
+ }
176
+ onChange={(cfg: LightingConfig) => console.log(cfg)}
177
+ onGenerate={(cfg) =>
178
+ fetch("/relight", {
179
+ method: "POST",
180
+ body: JSON.stringify(cfg),
181
+ })
182
+ }
183
+ />
184
+ );
185
+ }
186
+ ```
187
+
188
+ Props: `subjectImageUrl`, `defaultConfig`, `defaultView`, `theme`, `locale`, `smartEnabled`, `smartOpen`, `smartPanel`, `onChange`, `onGenerate`, `onSmartOpenChange`. The host's `smartPanel` is portaled into the editor's slot; the library renders the × close button + a "Smart mode" header pill to re-open it.
189
+
122
190
  ## Standalone `<Timeline>`
123
191
 
124
- Use the canvas timeline without the rest of the editor.
192
+ Use the canvas timeline without the rest of the editor — frame-pickers, thumbnail strips, read-only previews.
125
193
 
126
194
  ```tsx
127
195
  import { Timeline, type TimelineApi } from "@aicut/react";
@@ -138,6 +206,6 @@ import { Timeline, type TimelineApi } from "@aicut/react";
138
206
  />
139
207
  ```
140
208
 
141
- ## License
209
+ ---
142
210
 
143
- MIT
211
+ [Full docs & demo](https://github.com/ziqiangai/AiCut) · [@aicut/core](https://www.npmjs.com/package/@aicut/core) · [@aicut/vue](https://www.npmjs.com/package/@aicut/vue)
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/lighting.ts
21
+ var lighting_exports = {};
22
+ __export(lighting_exports, {
23
+ DEFAULT_LIGHTING_CONFIG: () => import_lighting2.DEFAULT_LIGHTING_CONFIG,
24
+ LightingEditor: () => LightingEditor,
25
+ PRESET_DIRECTIONS: () => import_lighting2.PRESET_DIRECTIONS,
26
+ lightingLocaleEn: () => import_lighting2.lightingLocaleEn,
27
+ lightingLocaleZh: () => import_lighting2.lightingLocaleZh,
28
+ mergeLightingLocale: () => import_lighting2.mergeLightingLocale,
29
+ snapToPreset: () => import_lighting2.snapToPreset
30
+ });
31
+ module.exports = __toCommonJS(lighting_exports);
32
+
33
+ // src/LightingEditor.tsx
34
+ var import_react = require("react");
35
+ var import_react_dom = require("react-dom");
36
+ var import_lighting = require("@aicut/core/lighting");
37
+ var import_jsx_runtime = require("react/jsx-runtime");
38
+ function LightingEditor(props) {
39
+ const hostRef = (0, import_react.useRef)(null);
40
+ const editorRef = (0, import_react.useRef)(null);
41
+ const [slot, setSlot] = (0, import_react.useState)(null);
42
+ const cbRef = (0, import_react.useRef)(props);
43
+ cbRef.current = props;
44
+ (0, import_react.useEffect)(() => {
45
+ const host = hostRef.current;
46
+ if (!host) return;
47
+ const editor = import_lighting.LightingEditor.create({
48
+ container: host,
49
+ subjectImageUrl: cbRef.current.subjectImageUrl,
50
+ config: cbRef.current.defaultConfig,
51
+ view: cbRef.current.defaultView,
52
+ smartEnabled: cbRef.current.smartEnabled,
53
+ smartOpen: cbRef.current.smartOpen,
54
+ theme: cbRef.current.theme,
55
+ locale: cbRef.current.locale,
56
+ onChange: (cfg) => cbRef.current.onChange?.(cfg),
57
+ onGenerate: (cfg) => cbRef.current.onGenerate?.(cfg),
58
+ onSmartOpenChange: (open) => cbRef.current.onSmartOpenChange?.(open)
59
+ });
60
+ editorRef.current = editor;
61
+ setSlot(editor.smartSlot);
62
+ return () => {
63
+ editor.destroy();
64
+ editorRef.current = null;
65
+ setSlot(null);
66
+ };
67
+ }, []);
68
+ (0, import_react.useEffect)(() => {
69
+ if (props.theme) editorRef.current?.setTheme(props.theme);
70
+ }, [props.theme]);
71
+ (0, import_react.useEffect)(() => {
72
+ editorRef.current?.setLocale(props.locale ?? {});
73
+ }, [props.locale]);
74
+ (0, import_react.useEffect)(() => {
75
+ if (props.subjectImageUrl)
76
+ editorRef.current?.setSubjectImage(props.subjectImageUrl);
77
+ }, [props.subjectImageUrl]);
78
+ (0, import_react.useEffect)(() => {
79
+ if (props.smartEnabled !== void 0)
80
+ editorRef.current?.setSmartEnabled(props.smartEnabled);
81
+ }, [props.smartEnabled]);
82
+ (0, import_react.useEffect)(() => {
83
+ if (props.smartOpen !== void 0)
84
+ editorRef.current?.setSmartOpen(props.smartOpen);
85
+ }, [props.smartOpen]);
86
+ (0, import_react.useImperativeHandle)(
87
+ props.apiRef,
88
+ () => {
89
+ const ed = editorRef.current;
90
+ if (!ed) return null;
91
+ return {
92
+ setConfig: (p) => ed.setConfig(p),
93
+ getConfig: () => ed.getConfig(),
94
+ setSubjectImage: (url) => ed.setSubjectImage(url),
95
+ setView: (v) => ed.setView(v),
96
+ getView: () => ed.getView(),
97
+ setSmartEnabled: (en) => ed.setSmartEnabled(en),
98
+ isSmartEnabled: () => ed.isSmartEnabled(),
99
+ setSmartOpen: (open) => ed.setSmartOpen(open),
100
+ isSmartOpen: () => ed.isSmartOpen(),
101
+ requestGenerate: () => ed.requestGenerate()
102
+ };
103
+ },
104
+ [slot]
105
+ );
106
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
107
+ "div",
108
+ {
109
+ ref: hostRef,
110
+ className: props.className,
111
+ style: props.style,
112
+ "data-aicut-lighting-host": "",
113
+ children: slot && props.smartPanel != null ? (0, import_react_dom.createPortal)(props.smartPanel, slot) : null
114
+ }
115
+ );
116
+ }
117
+
118
+ // src/lighting.ts
119
+ var import_lighting2 = require("@aicut/core/lighting");
120
+ // Annotate the CommonJS export names for ESM import in node:
121
+ 0 && (module.exports = {
122
+ DEFAULT_LIGHTING_CONFIG,
123
+ LightingEditor,
124
+ PRESET_DIRECTIONS,
125
+ lightingLocaleEn,
126
+ lightingLocaleZh,
127
+ mergeLightingLocale,
128
+ snapToPreset
129
+ });
130
+ //# sourceMappingURL=lighting.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lighting.ts","../src/LightingEditor.tsx"],"sourcesContent":["/**\n * @aicut/react/lighting — separate entry that pulls three.js. Users\n * who never import this path don't pay the three.js bundle cost.\n */\nexport { LightingEditor } from \"./LightingEditor.js\";\nexport type {\n LightingEditorProps,\n LightingEditorApi,\n} from \"./LightingEditor.js\";\n\n// Re-export the data + locale exports from the core sub-entry so\n// hosts only need a single import line for everything lighting-related.\nexport {\n DEFAULT_LIGHTING_CONFIG,\n PRESET_DIRECTIONS,\n lightingLocaleEn,\n lightingLocaleZh,\n mergeLightingLocale,\n snapToPreset,\n} from \"@aicut/core/lighting\";\nexport type {\n KeyPreset,\n LightingConfig,\n LightingEditorOptions,\n LightingLocale,\n LightingView,\n} from \"@aicut/core/lighting\";\n","import {\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n type CSSProperties,\n type ReactNode,\n type Ref,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\nimport {\n LightingEditor as CoreLightingEditor,\n type LightingConfig,\n type LightingEditorOptions,\n type LightingView,\n} from \"@aicut/core/lighting\";\nimport type { Theme } from \"@aicut/core\";\n\n/**\n * Imperative handle. Mirrors the core class's mutating surface, plus\n * a `requestGenerate()` shortcut so host buttons inside the smart slot\n * don't need to thread the api ref to fire onGenerate.\n */\nexport interface LightingEditorApi {\n setConfig(partial: Partial<LightingConfig>): void;\n getConfig(): LightingConfig;\n setSubjectImage(url: string): void;\n setView(v: LightingView): void;\n getView(): LightingView;\n setSmartEnabled(enabled: boolean): void;\n isSmartEnabled(): boolean;\n setSmartOpen(open: boolean): void;\n isSmartOpen(): boolean;\n requestGenerate(): void;\n}\n\nexport interface LightingEditorProps {\n /** Initial subject image (URL or data URI). */\n subjectImageUrl?: string;\n /** Initial config. */\n defaultConfig?: Partial<LightingConfig>;\n /** Initial view. Default `\"perspective\"`. */\n defaultView?: LightingView;\n /** Theme — reactive (calls editor.setTheme). */\n theme?: Theme;\n /** Locale partial — reactive (calls editor.setLocale). */\n locale?: LightingEditorOptions[\"locale\"];\n\n /**\n * Any React node — portaled into the editor's smart slot. Host uses\n * this for prompt textarea, preset grid, generate button, anything.\n * The library renders nothing into the slot until you populate it.\n */\n smartPanel?: ReactNode;\n /**\n * Whether the Smart mode feature is wired in at all. When false,\n * the column AND the controls-header toggle disappear — leaving a\n * clean 2-col scene + controls layout. Default `true`. Reactive.\n */\n smartEnabled?: boolean;\n /**\n * When `smartEnabled`, whether the slot drawer starts open. Reactive\n * — flips re-fire the editor's `setSmartOpen` so the panel slides\n * in/out. Default `true`.\n */\n smartOpen?: boolean;\n\n className?: string;\n style?: CSSProperties;\n apiRef?: Ref<LightingEditorApi | null>;\n\n onChange?: (cfg: LightingConfig) => void;\n onGenerate?: (cfg: LightingConfig) => void;\n /** Fires when the user clicks × or the Smart mode header toggle. */\n onSmartOpenChange?: (open: boolean) => void;\n}\n\nexport function LightingEditor(props: LightingEditorProps) {\n const hostRef = useRef<HTMLDivElement | null>(null);\n const editorRef = useRef<CoreLightingEditor | null>(null);\n // smart slot DOM node — set once the editor mounts; gates the portal.\n const [slot, setSlot] = useState<HTMLElement | null>(null);\n\n // Stable closure for callbacks the core only ever subscribes to once.\n const cbRef = useRef(props);\n cbRef.current = props;\n\n useEffect(() => {\n const host = hostRef.current;\n if (!host) return;\n const editor = CoreLightingEditor.create({\n container: host,\n subjectImageUrl: cbRef.current.subjectImageUrl,\n config: cbRef.current.defaultConfig,\n view: cbRef.current.defaultView,\n smartEnabled: cbRef.current.smartEnabled,\n smartOpen: cbRef.current.smartOpen,\n theme: cbRef.current.theme,\n locale: cbRef.current.locale,\n onChange: (cfg) => cbRef.current.onChange?.(cfg),\n onGenerate: (cfg) => cbRef.current.onGenerate?.(cfg),\n onSmartOpenChange: (open) =>\n cbRef.current.onSmartOpenChange?.(open),\n });\n editorRef.current = editor;\n setSlot(editor.smartSlot);\n return () => {\n editor.destroy();\n editorRef.current = null;\n setSlot(null);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // Reactive prop mirror — theme + locale + subject only. Config /\n // view are owned by the editor instance after mount; host should\n // use `apiRef.current?.setConfig(…)` to push them.\n useEffect(() => {\n if (props.theme) editorRef.current?.setTheme(props.theme);\n }, [props.theme]);\n useEffect(() => {\n // Always push — `undefined` is the \"reset to English defaults\"\n // signal. Previously the `if (props.locale)` guard meant that\n // toggling ZH→EN in the host kept the ZH labels in place.\n editorRef.current?.setLocale(props.locale ?? {});\n }, [props.locale]);\n useEffect(() => {\n if (props.subjectImageUrl)\n editorRef.current?.setSubjectImage(props.subjectImageUrl);\n }, [props.subjectImageUrl]);\n\n useEffect(() => {\n if (props.smartEnabled !== undefined)\n editorRef.current?.setSmartEnabled(props.smartEnabled);\n }, [props.smartEnabled]);\n useEffect(() => {\n if (props.smartOpen !== undefined)\n editorRef.current?.setSmartOpen(props.smartOpen);\n }, [props.smartOpen]);\n\n // Keyed on `slot` for the same reason VideoEditor's apiRef is —\n // useImperativeHandle's factory runs in the commit phase BEFORE\n // useEffect, so with `[]` deps the ref locks to null forever.\n useImperativeHandle<LightingEditorApi | null, LightingEditorApi | null>(\n props.apiRef,\n () => {\n const ed = editorRef.current;\n if (!ed) return null;\n return {\n setConfig: (p) => ed.setConfig(p),\n getConfig: () => ed.getConfig(),\n setSubjectImage: (url) => ed.setSubjectImage(url),\n setView: (v) => ed.setView(v),\n getView: () => ed.getView(),\n setSmartEnabled: (en) => ed.setSmartEnabled(en),\n isSmartEnabled: () => ed.isSmartEnabled(),\n setSmartOpen: (open) => ed.setSmartOpen(open),\n isSmartOpen: () => ed.isSmartOpen(),\n requestGenerate: () => ed.requestGenerate(),\n };\n },\n [slot],\n );\n\n return (\n <div\n ref={hostRef}\n className={props.className}\n style={props.style}\n data-aicut-lighting-host=\"\"\n >\n {slot && props.smartPanel != null\n ? createPortal(props.smartPanel, slot)\n : null}\n </div>\n );\n}\n"],"mappings":"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","names":["CoreLightingEditor","import_lighting"]}
@@ -0,0 +1,63 @@
1
+ import * as react from 'react';
2
+ import { ReactNode, CSSProperties, Ref } from 'react';
3
+ import { LightingConfig, LightingView, LightingEditorOptions } from '@aicut/core/lighting';
4
+ export { DEFAULT_LIGHTING_CONFIG, KeyPreset, LightingConfig, LightingEditorOptions, LightingLocale, LightingView, PRESET_DIRECTIONS, lightingLocaleEn, lightingLocaleZh, mergeLightingLocale, snapToPreset } from '@aicut/core/lighting';
5
+ import { Theme } from '@aicut/core';
6
+
7
+ /**
8
+ * Imperative handle. Mirrors the core class's mutating surface, plus
9
+ * a `requestGenerate()` shortcut so host buttons inside the smart slot
10
+ * don't need to thread the api ref to fire onGenerate.
11
+ */
12
+ interface LightingEditorApi {
13
+ setConfig(partial: Partial<LightingConfig>): void;
14
+ getConfig(): LightingConfig;
15
+ setSubjectImage(url: string): void;
16
+ setView(v: LightingView): void;
17
+ getView(): LightingView;
18
+ setSmartEnabled(enabled: boolean): void;
19
+ isSmartEnabled(): boolean;
20
+ setSmartOpen(open: boolean): void;
21
+ isSmartOpen(): boolean;
22
+ requestGenerate(): void;
23
+ }
24
+ interface LightingEditorProps {
25
+ /** Initial subject image (URL or data URI). */
26
+ subjectImageUrl?: string;
27
+ /** Initial config. */
28
+ defaultConfig?: Partial<LightingConfig>;
29
+ /** Initial view. Default `"perspective"`. */
30
+ defaultView?: LightingView;
31
+ /** Theme — reactive (calls editor.setTheme). */
32
+ theme?: Theme;
33
+ /** Locale partial — reactive (calls editor.setLocale). */
34
+ locale?: LightingEditorOptions["locale"];
35
+ /**
36
+ * Any React node — portaled into the editor's smart slot. Host uses
37
+ * this for prompt textarea, preset grid, generate button, anything.
38
+ * The library renders nothing into the slot until you populate it.
39
+ */
40
+ smartPanel?: ReactNode;
41
+ /**
42
+ * Whether the Smart mode feature is wired in at all. When false,
43
+ * the column AND the controls-header toggle disappear — leaving a
44
+ * clean 2-col scene + controls layout. Default `true`. Reactive.
45
+ */
46
+ smartEnabled?: boolean;
47
+ /**
48
+ * When `smartEnabled`, whether the slot drawer starts open. Reactive
49
+ * — flips re-fire the editor's `setSmartOpen` so the panel slides
50
+ * in/out. Default `true`.
51
+ */
52
+ smartOpen?: boolean;
53
+ className?: string;
54
+ style?: CSSProperties;
55
+ apiRef?: Ref<LightingEditorApi | null>;
56
+ onChange?: (cfg: LightingConfig) => void;
57
+ onGenerate?: (cfg: LightingConfig) => void;
58
+ /** Fires when the user clicks × or the Smart mode header toggle. */
59
+ onSmartOpenChange?: (open: boolean) => void;
60
+ }
61
+ declare function LightingEditor(props: LightingEditorProps): react.JSX.Element;
62
+
63
+ export { LightingEditor, type LightingEditorApi, type LightingEditorProps };
@@ -0,0 +1,63 @@
1
+ import * as react from 'react';
2
+ import { ReactNode, CSSProperties, Ref } from 'react';
3
+ import { LightingConfig, LightingView, LightingEditorOptions } from '@aicut/core/lighting';
4
+ export { DEFAULT_LIGHTING_CONFIG, KeyPreset, LightingConfig, LightingEditorOptions, LightingLocale, LightingView, PRESET_DIRECTIONS, lightingLocaleEn, lightingLocaleZh, mergeLightingLocale, snapToPreset } from '@aicut/core/lighting';
5
+ import { Theme } from '@aicut/core';
6
+
7
+ /**
8
+ * Imperative handle. Mirrors the core class's mutating surface, plus
9
+ * a `requestGenerate()` shortcut so host buttons inside the smart slot
10
+ * don't need to thread the api ref to fire onGenerate.
11
+ */
12
+ interface LightingEditorApi {
13
+ setConfig(partial: Partial<LightingConfig>): void;
14
+ getConfig(): LightingConfig;
15
+ setSubjectImage(url: string): void;
16
+ setView(v: LightingView): void;
17
+ getView(): LightingView;
18
+ setSmartEnabled(enabled: boolean): void;
19
+ isSmartEnabled(): boolean;
20
+ setSmartOpen(open: boolean): void;
21
+ isSmartOpen(): boolean;
22
+ requestGenerate(): void;
23
+ }
24
+ interface LightingEditorProps {
25
+ /** Initial subject image (URL or data URI). */
26
+ subjectImageUrl?: string;
27
+ /** Initial config. */
28
+ defaultConfig?: Partial<LightingConfig>;
29
+ /** Initial view. Default `"perspective"`. */
30
+ defaultView?: LightingView;
31
+ /** Theme — reactive (calls editor.setTheme). */
32
+ theme?: Theme;
33
+ /** Locale partial — reactive (calls editor.setLocale). */
34
+ locale?: LightingEditorOptions["locale"];
35
+ /**
36
+ * Any React node — portaled into the editor's smart slot. Host uses
37
+ * this for prompt textarea, preset grid, generate button, anything.
38
+ * The library renders nothing into the slot until you populate it.
39
+ */
40
+ smartPanel?: ReactNode;
41
+ /**
42
+ * Whether the Smart mode feature is wired in at all. When false,
43
+ * the column AND the controls-header toggle disappear — leaving a
44
+ * clean 2-col scene + controls layout. Default `true`. Reactive.
45
+ */
46
+ smartEnabled?: boolean;
47
+ /**
48
+ * When `smartEnabled`, whether the slot drawer starts open. Reactive
49
+ * — flips re-fire the editor's `setSmartOpen` so the panel slides
50
+ * in/out. Default `true`.
51
+ */
52
+ smartOpen?: boolean;
53
+ className?: string;
54
+ style?: CSSProperties;
55
+ apiRef?: Ref<LightingEditorApi | null>;
56
+ onChange?: (cfg: LightingConfig) => void;
57
+ onGenerate?: (cfg: LightingConfig) => void;
58
+ /** Fires when the user clicks × or the Smart mode header toggle. */
59
+ onSmartOpenChange?: (open: boolean) => void;
60
+ }
61
+ declare function LightingEditor(props: LightingEditorProps): react.JSX.Element;
62
+
63
+ export { LightingEditor, type LightingEditorApi, type LightingEditorProps };
@@ -0,0 +1,111 @@
1
+ // src/LightingEditor.tsx
2
+ import {
3
+ useEffect,
4
+ useImperativeHandle,
5
+ useRef,
6
+ useState
7
+ } from "react";
8
+ import { createPortal } from "react-dom";
9
+ import {
10
+ LightingEditor as CoreLightingEditor
11
+ } from "@aicut/core/lighting";
12
+ import { jsx } from "react/jsx-runtime";
13
+ function LightingEditor(props) {
14
+ const hostRef = useRef(null);
15
+ const editorRef = useRef(null);
16
+ const [slot, setSlot] = useState(null);
17
+ const cbRef = useRef(props);
18
+ cbRef.current = props;
19
+ useEffect(() => {
20
+ const host = hostRef.current;
21
+ if (!host) return;
22
+ const editor = CoreLightingEditor.create({
23
+ container: host,
24
+ subjectImageUrl: cbRef.current.subjectImageUrl,
25
+ config: cbRef.current.defaultConfig,
26
+ view: cbRef.current.defaultView,
27
+ smartEnabled: cbRef.current.smartEnabled,
28
+ smartOpen: cbRef.current.smartOpen,
29
+ theme: cbRef.current.theme,
30
+ locale: cbRef.current.locale,
31
+ onChange: (cfg) => cbRef.current.onChange?.(cfg),
32
+ onGenerate: (cfg) => cbRef.current.onGenerate?.(cfg),
33
+ onSmartOpenChange: (open) => cbRef.current.onSmartOpenChange?.(open)
34
+ });
35
+ editorRef.current = editor;
36
+ setSlot(editor.smartSlot);
37
+ return () => {
38
+ editor.destroy();
39
+ editorRef.current = null;
40
+ setSlot(null);
41
+ };
42
+ }, []);
43
+ useEffect(() => {
44
+ if (props.theme) editorRef.current?.setTheme(props.theme);
45
+ }, [props.theme]);
46
+ useEffect(() => {
47
+ editorRef.current?.setLocale(props.locale ?? {});
48
+ }, [props.locale]);
49
+ useEffect(() => {
50
+ if (props.subjectImageUrl)
51
+ editorRef.current?.setSubjectImage(props.subjectImageUrl);
52
+ }, [props.subjectImageUrl]);
53
+ useEffect(() => {
54
+ if (props.smartEnabled !== void 0)
55
+ editorRef.current?.setSmartEnabled(props.smartEnabled);
56
+ }, [props.smartEnabled]);
57
+ useEffect(() => {
58
+ if (props.smartOpen !== void 0)
59
+ editorRef.current?.setSmartOpen(props.smartOpen);
60
+ }, [props.smartOpen]);
61
+ useImperativeHandle(
62
+ props.apiRef,
63
+ () => {
64
+ const ed = editorRef.current;
65
+ if (!ed) return null;
66
+ return {
67
+ setConfig: (p) => ed.setConfig(p),
68
+ getConfig: () => ed.getConfig(),
69
+ setSubjectImage: (url) => ed.setSubjectImage(url),
70
+ setView: (v) => ed.setView(v),
71
+ getView: () => ed.getView(),
72
+ setSmartEnabled: (en) => ed.setSmartEnabled(en),
73
+ isSmartEnabled: () => ed.isSmartEnabled(),
74
+ setSmartOpen: (open) => ed.setSmartOpen(open),
75
+ isSmartOpen: () => ed.isSmartOpen(),
76
+ requestGenerate: () => ed.requestGenerate()
77
+ };
78
+ },
79
+ [slot]
80
+ );
81
+ return /* @__PURE__ */ jsx(
82
+ "div",
83
+ {
84
+ ref: hostRef,
85
+ className: props.className,
86
+ style: props.style,
87
+ "data-aicut-lighting-host": "",
88
+ children: slot && props.smartPanel != null ? createPortal(props.smartPanel, slot) : null
89
+ }
90
+ );
91
+ }
92
+
93
+ // src/lighting.ts
94
+ import {
95
+ DEFAULT_LIGHTING_CONFIG,
96
+ PRESET_DIRECTIONS,
97
+ lightingLocaleEn,
98
+ lightingLocaleZh,
99
+ mergeLightingLocale,
100
+ snapToPreset
101
+ } from "@aicut/core/lighting";
102
+ export {
103
+ DEFAULT_LIGHTING_CONFIG,
104
+ LightingEditor,
105
+ PRESET_DIRECTIONS,
106
+ lightingLocaleEn,
107
+ lightingLocaleZh,
108
+ mergeLightingLocale,
109
+ snapToPreset
110
+ };
111
+ //# sourceMappingURL=lighting.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/LightingEditor.tsx","../src/lighting.ts"],"sourcesContent":["import {\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n type CSSProperties,\n type ReactNode,\n type Ref,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\nimport {\n LightingEditor as CoreLightingEditor,\n type LightingConfig,\n type LightingEditorOptions,\n type LightingView,\n} from \"@aicut/core/lighting\";\nimport type { Theme } from \"@aicut/core\";\n\n/**\n * Imperative handle. Mirrors the core class's mutating surface, plus\n * a `requestGenerate()` shortcut so host buttons inside the smart slot\n * don't need to thread the api ref to fire onGenerate.\n */\nexport interface LightingEditorApi {\n setConfig(partial: Partial<LightingConfig>): void;\n getConfig(): LightingConfig;\n setSubjectImage(url: string): void;\n setView(v: LightingView): void;\n getView(): LightingView;\n setSmartEnabled(enabled: boolean): void;\n isSmartEnabled(): boolean;\n setSmartOpen(open: boolean): void;\n isSmartOpen(): boolean;\n requestGenerate(): void;\n}\n\nexport interface LightingEditorProps {\n /** Initial subject image (URL or data URI). */\n subjectImageUrl?: string;\n /** Initial config. */\n defaultConfig?: Partial<LightingConfig>;\n /** Initial view. Default `\"perspective\"`. */\n defaultView?: LightingView;\n /** Theme — reactive (calls editor.setTheme). */\n theme?: Theme;\n /** Locale partial — reactive (calls editor.setLocale). */\n locale?: LightingEditorOptions[\"locale\"];\n\n /**\n * Any React node — portaled into the editor's smart slot. Host uses\n * this for prompt textarea, preset grid, generate button, anything.\n * The library renders nothing into the slot until you populate it.\n */\n smartPanel?: ReactNode;\n /**\n * Whether the Smart mode feature is wired in at all. When false,\n * the column AND the controls-header toggle disappear — leaving a\n * clean 2-col scene + controls layout. Default `true`. Reactive.\n */\n smartEnabled?: boolean;\n /**\n * When `smartEnabled`, whether the slot drawer starts open. Reactive\n * — flips re-fire the editor's `setSmartOpen` so the panel slides\n * in/out. Default `true`.\n */\n smartOpen?: boolean;\n\n className?: string;\n style?: CSSProperties;\n apiRef?: Ref<LightingEditorApi | null>;\n\n onChange?: (cfg: LightingConfig) => void;\n onGenerate?: (cfg: LightingConfig) => void;\n /** Fires when the user clicks × or the Smart mode header toggle. */\n onSmartOpenChange?: (open: boolean) => void;\n}\n\nexport function LightingEditor(props: LightingEditorProps) {\n const hostRef = useRef<HTMLDivElement | null>(null);\n const editorRef = useRef<CoreLightingEditor | null>(null);\n // smart slot DOM node — set once the editor mounts; gates the portal.\n const [slot, setSlot] = useState<HTMLElement | null>(null);\n\n // Stable closure for callbacks the core only ever subscribes to once.\n const cbRef = useRef(props);\n cbRef.current = props;\n\n useEffect(() => {\n const host = hostRef.current;\n if (!host) return;\n const editor = CoreLightingEditor.create({\n container: host,\n subjectImageUrl: cbRef.current.subjectImageUrl,\n config: cbRef.current.defaultConfig,\n view: cbRef.current.defaultView,\n smartEnabled: cbRef.current.smartEnabled,\n smartOpen: cbRef.current.smartOpen,\n theme: cbRef.current.theme,\n locale: cbRef.current.locale,\n onChange: (cfg) => cbRef.current.onChange?.(cfg),\n onGenerate: (cfg) => cbRef.current.onGenerate?.(cfg),\n onSmartOpenChange: (open) =>\n cbRef.current.onSmartOpenChange?.(open),\n });\n editorRef.current = editor;\n setSlot(editor.smartSlot);\n return () => {\n editor.destroy();\n editorRef.current = null;\n setSlot(null);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // Reactive prop mirror — theme + locale + subject only. Config /\n // view are owned by the editor instance after mount; host should\n // use `apiRef.current?.setConfig(…)` to push them.\n useEffect(() => {\n if (props.theme) editorRef.current?.setTheme(props.theme);\n }, [props.theme]);\n useEffect(() => {\n // Always push — `undefined` is the \"reset to English defaults\"\n // signal. Previously the `if (props.locale)` guard meant that\n // toggling ZH→EN in the host kept the ZH labels in place.\n editorRef.current?.setLocale(props.locale ?? {});\n }, [props.locale]);\n useEffect(() => {\n if (props.subjectImageUrl)\n editorRef.current?.setSubjectImage(props.subjectImageUrl);\n }, [props.subjectImageUrl]);\n\n useEffect(() => {\n if (props.smartEnabled !== undefined)\n editorRef.current?.setSmartEnabled(props.smartEnabled);\n }, [props.smartEnabled]);\n useEffect(() => {\n if (props.smartOpen !== undefined)\n editorRef.current?.setSmartOpen(props.smartOpen);\n }, [props.smartOpen]);\n\n // Keyed on `slot` for the same reason VideoEditor's apiRef is —\n // useImperativeHandle's factory runs in the commit phase BEFORE\n // useEffect, so with `[]` deps the ref locks to null forever.\n useImperativeHandle<LightingEditorApi | null, LightingEditorApi | null>(\n props.apiRef,\n () => {\n const ed = editorRef.current;\n if (!ed) return null;\n return {\n setConfig: (p) => ed.setConfig(p),\n getConfig: () => ed.getConfig(),\n setSubjectImage: (url) => ed.setSubjectImage(url),\n setView: (v) => ed.setView(v),\n getView: () => ed.getView(),\n setSmartEnabled: (en) => ed.setSmartEnabled(en),\n isSmartEnabled: () => ed.isSmartEnabled(),\n setSmartOpen: (open) => ed.setSmartOpen(open),\n isSmartOpen: () => ed.isSmartOpen(),\n requestGenerate: () => ed.requestGenerate(),\n };\n },\n [slot],\n );\n\n return (\n <div\n ref={hostRef}\n className={props.className}\n style={props.style}\n data-aicut-lighting-host=\"\"\n >\n {slot && props.smartPanel != null\n ? createPortal(props.smartPanel, slot)\n : null}\n </div>\n );\n}\n","/**\n * @aicut/react/lighting — separate entry that pulls three.js. Users\n * who never import this path don't pay the three.js bundle cost.\n */\nexport { LightingEditor } from \"./LightingEditor.js\";\nexport type {\n LightingEditorProps,\n LightingEditorApi,\n} from \"./LightingEditor.js\";\n\n// Re-export the data + locale exports from the core sub-entry so\n// hosts only need a single import line for everything lighting-related.\nexport {\n DEFAULT_LIGHTING_CONFIG,\n PRESET_DIRECTIONS,\n lightingLocaleEn,\n lightingLocaleZh,\n mergeLightingLocale,\n snapToPreset,\n} from \"@aicut/core/lighting\";\nexport type {\n KeyPreset,\n LightingConfig,\n LightingEditorOptions,\n LightingLocale,\n LightingView,\n} from \"@aicut/core/lighting\";\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,8 +1,43 @@
1
1
  {
2
2
  "name": "@aicut/react",
3
- "version": "0.1.0",
4
- "description": "React wrapper for the AiCut video editor — thin declarative shell over @aicut/core.",
3
+ "version": "0.2.0",
4
+ "description": "React wrapper for the AiCut video editor + lighting picker — thin declarative shells over @aicut/core.",
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/react"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/ziqiangai/AiCut/issues"
15
+ },
16
+ "keywords": [
17
+ "video-editor",
18
+ "video-editing",
19
+ "react",
20
+ "react-component",
21
+ "timeline",
22
+ "timeline-editor",
23
+ "nle",
24
+ "video-cutter",
25
+ "video-clip",
26
+ "canvas",
27
+ "video",
28
+ "editor",
29
+ "mp4",
30
+ "component",
31
+ "capcut",
32
+ "premiere",
33
+ "final-cut",
34
+ "davinci",
35
+ "imovie",
36
+ "veed",
37
+ "filmora",
38
+ "lighting",
39
+ "three.js"
40
+ ],
6
41
  "type": "module",
7
42
  "main": "./dist/index.cjs",
8
43
  "module": "./dist/index.js",
@@ -12,6 +47,11 @@
12
47
  "types": "./dist/index.d.ts",
13
48
  "import": "./dist/index.js",
14
49
  "require": "./dist/index.cjs"
50
+ },
51
+ "./lighting": {
52
+ "types": "./dist/lighting.d.ts",
53
+ "import": "./dist/lighting.js",
54
+ "require": "./dist/lighting.cjs"
15
55
  }
16
56
  },
17
57
  "files": [
@@ -19,7 +59,7 @@
19
59
  "README.md"
20
60
  ],
21
61
  "dependencies": {
22
- "@aicut/core": "0.1.0"
62
+ "@aicut/core": "0.2.0"
23
63
  },
24
64
  "peerDependencies": {
25
65
  "react": "^18.0.0 || ^19.0.0",