@aicut/core 0.2.0 → 0.4.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 +12 -11
- package/dist/lighting/index.cjs +29 -170
- package/dist/lighting/index.cjs.map +1 -1
- package/dist/lighting/index.d.cts +15 -67
- package/dist/lighting/index.d.ts +15 -67
- package/dist/lighting/index.js +29 -170
- package/dist/lighting/index.js.map +1 -1
- package/package.json +1 -1
- package/styles/theme.css +23 -95
package/README.md
CHANGED
|
@@ -144,7 +144,9 @@ editor.toolbarRight.appendChild(exportBtn);
|
|
|
144
144
|
|
|
145
145
|
## Lighting picker (opt-in sub-entry)
|
|
146
146
|
|
|
147
|
-
A separate component for AI
|
|
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.
|
|
148
|
+
|
|
149
|
+
The library renders **only the picker** (scene + controls). Smart-mode UI (prompt textarea, preset thumbnails, Generate button, close handling) is host code laid out beside `<LightingEditor>`.
|
|
148
150
|
|
|
149
151
|
```ts
|
|
150
152
|
import { LightingEditor } from "@aicut/core/lighting";
|
|
@@ -153,23 +155,22 @@ import "@aicut/core/styles.css";
|
|
|
153
155
|
const ed = LightingEditor.create({
|
|
154
156
|
container: document.getElementById("light")!,
|
|
155
157
|
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
158
|
onChange: (cfg) => console.log(cfg),
|
|
159
|
-
onGenerate: (cfg) => fetch("/relight", { method: "POST", body: JSON.stringify(cfg) }),
|
|
160
159
|
});
|
|
161
160
|
|
|
162
|
-
//
|
|
163
|
-
ed.smartSlot.appendChild(myAiUI);
|
|
164
|
-
|
|
165
|
-
// Runtime control:
|
|
166
|
-
ed.setSmartOpen(false);
|
|
167
|
-
ed.setSmartEnabled(false);
|
|
161
|
+
// Runtime control
|
|
168
162
|
ed.setView("front");
|
|
169
163
|
ed.setConfig({ brightness: 0.8, color: "#ffaa3a" });
|
|
164
|
+
ed.setSubjectImage("/frames/another-subject.jpg");
|
|
165
|
+
|
|
166
|
+
// Host snapshot for "Generate" — call from your own button
|
|
167
|
+
function onGenerate() {
|
|
168
|
+
const cfg = ed.getConfig();
|
|
169
|
+
fetch("/relight", { method: "POST", body: JSON.stringify(cfg) });
|
|
170
|
+
}
|
|
170
171
|
```
|
|
171
172
|
|
|
172
|
-
Locale extension `LightingLocale` (separate from the video editor's `Locale`) is
|
|
173
|
+
Locale extension `LightingLocale` (separate from the video editor's `Locale`) is exported with `lightingLocaleEn` / `lightingLocaleZh`.
|
|
173
174
|
|
|
174
175
|
## Standalone Timeline
|
|
175
176
|
|
package/dist/lighting/index.cjs
CHANGED
|
@@ -90,6 +90,13 @@ var LightingControls = class {
|
|
|
90
90
|
/** Exposed so the editor can append host-level chrome (e.g. the
|
|
91
91
|
* Smart Mode toggle) without LightingControls knowing about it. */
|
|
92
92
|
headerSlot;
|
|
93
|
+
/**
|
|
94
|
+
* Footer slot — sits where the built-in Reset used to live. Host
|
|
95
|
+
* appends any action buttons (Reset, Generate, Save preset, etc.)
|
|
96
|
+
* via the React wrapper's `controlsFooter` prop / Vue's
|
|
97
|
+
* `<slot name="controlsFooter">`. Library renders nothing here.
|
|
98
|
+
*/
|
|
99
|
+
footerSlot;
|
|
93
100
|
brightnessInput;
|
|
94
101
|
colorInput;
|
|
95
102
|
dirButtons;
|
|
@@ -102,7 +109,6 @@ var LightingControls = class {
|
|
|
102
109
|
colorLabelEl;
|
|
103
110
|
keyLabelEl;
|
|
104
111
|
rimLabelEl;
|
|
105
|
-
resetBtn;
|
|
106
112
|
locale;
|
|
107
113
|
lastConfig = null;
|
|
108
114
|
constructor(locale, cb) {
|
|
@@ -192,15 +198,12 @@ var LightingControls = class {
|
|
|
192
198
|
});
|
|
193
199
|
rimSection.appendChild(this.rimToggle);
|
|
194
200
|
this.root.appendChild(rimSection);
|
|
195
|
-
|
|
196
|
-
this.
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
this.
|
|
201
|
-
this.resetBtn.addEventListener("click", () => cb.onReset());
|
|
202
|
-
footer.appendChild(this.resetBtn);
|
|
203
|
-
this.root.appendChild(footer);
|
|
201
|
+
this.footerSlot = mkDiv("aicut-lighting-controls-footer");
|
|
202
|
+
this.footerSlot.setAttribute(
|
|
203
|
+
"data-testid",
|
|
204
|
+
"aicut-lighting-controls-footer"
|
|
205
|
+
);
|
|
206
|
+
this.root.appendChild(this.footerSlot);
|
|
204
207
|
}
|
|
205
208
|
/** Idempotent — mirror the given config into all visible controls. */
|
|
206
209
|
render(cfg) {
|
|
@@ -229,7 +232,6 @@ var LightingControls = class {
|
|
|
229
232
|
this.colorLabelEl.textContent = locale.lightingColor;
|
|
230
233
|
this.keyLabelEl.textContent = locale.lightingKeyTitle;
|
|
231
234
|
this.rimLabelEl.textContent = locale.lightingRim;
|
|
232
|
-
this.resetBtn.textContent = locale.lightingResetParams;
|
|
233
235
|
const dirLabels = {
|
|
234
236
|
left: "lightingDirLeft",
|
|
235
237
|
right: "lightingDirRight",
|
|
@@ -265,9 +267,6 @@ var lightingLocaleEn = {
|
|
|
265
267
|
lightingRim: "Rim light",
|
|
266
268
|
lightingViewPerspective: "Perspective",
|
|
267
269
|
lightingViewFront: "Front",
|
|
268
|
-
lightingResetParams: "Reset",
|
|
269
|
-
lightingSmartClose: "Close Smart mode",
|
|
270
|
-
lightingSmartToggle: "Smart mode",
|
|
271
270
|
lightingDirLeft: "Left",
|
|
272
271
|
lightingDirRight: "Right",
|
|
273
272
|
lightingDirTop: "Top",
|
|
@@ -284,9 +283,6 @@ var lightingLocaleZh = {
|
|
|
284
283
|
lightingRim: "\u8F6E\u5ED3\u5149",
|
|
285
284
|
lightingViewPerspective: "\u900F\u89C6",
|
|
286
285
|
lightingViewFront: "\u6B63\u9762",
|
|
287
|
-
lightingResetParams: "\u91CD\u7F6E\u53C2\u6570",
|
|
288
|
-
lightingSmartClose: "\u5173\u95ED\u667A\u80FD\u6A21\u5F0F",
|
|
289
|
-
lightingSmartToggle: "\u667A\u80FD\u6A21\u5F0F",
|
|
290
286
|
lightingDirLeft: "\u5DE6\u4FA7",
|
|
291
287
|
lightingDirRight: "\u53F3\u4FA7",
|
|
292
288
|
lightingDirTop: "\u9876\u90E8",
|
|
@@ -19041,22 +19037,14 @@ var LightingEditor = class _LightingEditor {
|
|
|
19041
19037
|
config;
|
|
19042
19038
|
view;
|
|
19043
19039
|
locale;
|
|
19044
|
-
smartEnabled;
|
|
19045
|
-
smartOpen;
|
|
19046
19040
|
scene;
|
|
19047
19041
|
controls;
|
|
19048
19042
|
sceneViewport;
|
|
19049
19043
|
viewToggleEl;
|
|
19050
|
-
|
|
19051
|
-
|
|
19052
|
-
|
|
19053
|
-
|
|
19054
|
-
smartToggleThumb = null;
|
|
19055
|
-
/** Host slot the React/Vue wrapper portals/teleports into. Stable
|
|
19056
|
-
* reference across smartOpen toggles — only the wrapper around it
|
|
19057
|
-
* collapses/expands. Always present even when `smartEnabled: false`
|
|
19058
|
-
* so portals don't blow up; just detached from the visible tree. */
|
|
19059
|
-
smartSlot;
|
|
19044
|
+
/** Footer slot in the controls column. Host appends Reset/Generate/
|
|
19045
|
+
* preset-save/etc. buttons here. The library renders nothing into
|
|
19046
|
+
* it — it's the same convention as the video editor's toolbar slots. */
|
|
19047
|
+
controlsFooter;
|
|
19060
19048
|
resizeObs = null;
|
|
19061
19049
|
destroyed = false;
|
|
19062
19050
|
static create(opts) {
|
|
@@ -19067,8 +19055,6 @@ var LightingEditor = class _LightingEditor {
|
|
|
19067
19055
|
this.root = opts.container;
|
|
19068
19056
|
this.config = { ...DEFAULT_LIGHTING_CONFIG, ...opts.config };
|
|
19069
19057
|
this.view = opts.view ?? "perspective";
|
|
19070
|
-
this.smartEnabled = opts.smartEnabled !== false;
|
|
19071
|
-
this.smartOpen = opts.smartOpen !== false;
|
|
19072
19058
|
this.locale = {
|
|
19073
19059
|
...mergeLocale(opts.locale),
|
|
19074
19060
|
...mergeLightingLocale(opts.locale)
|
|
@@ -19077,9 +19063,9 @@ var LightingEditor = class _LightingEditor {
|
|
|
19077
19063
|
this.root.innerHTML = "";
|
|
19078
19064
|
if (!this.root.style.position) this.root.style.position = "relative";
|
|
19079
19065
|
applyTheme(this.root, opts.theme);
|
|
19080
|
-
|
|
19081
|
-
|
|
19082
|
-
this.root.appendChild(
|
|
19066
|
+
const body = document.createElement("div");
|
|
19067
|
+
body.className = "aicut-lighting-body";
|
|
19068
|
+
this.root.appendChild(body);
|
|
19083
19069
|
const sceneCol = document.createElement("div");
|
|
19084
19070
|
sceneCol.className = "aicut-lighting-scene-col";
|
|
19085
19071
|
this.viewToggleEl = this.buildViewToggle();
|
|
@@ -19088,7 +19074,7 @@ var LightingEditor = class _LightingEditor {
|
|
|
19088
19074
|
this.sceneViewport.className = "aicut-lighting-scene-viewport";
|
|
19089
19075
|
this.sceneViewport.setAttribute("data-testid", "aicut-lighting-scene");
|
|
19090
19076
|
sceneCol.appendChild(this.sceneViewport);
|
|
19091
|
-
|
|
19077
|
+
body.appendChild(sceneCol);
|
|
19092
19078
|
this.controls = new LightingControls(this.locale, {
|
|
19093
19079
|
onBrightnessChange: (level) => this.applyMutation({ brightness: level }),
|
|
19094
19080
|
onColorChange: (hex) => this.applyMutation({ color: hex }),
|
|
@@ -19096,20 +19082,10 @@ var LightingEditor = class _LightingEditor {
|
|
|
19096
19082
|
keyDirection: PRESET_DIRECTIONS[preset],
|
|
19097
19083
|
keyPreset: preset
|
|
19098
19084
|
}),
|
|
19099
|
-
onRimToggle: (on) => this.applyMutation({ rim: on })
|
|
19100
|
-
onReset: () => this.setConfig(DEFAULT_LIGHTING_CONFIG, "reset")
|
|
19085
|
+
onRimToggle: (on) => this.applyMutation({ rim: on })
|
|
19101
19086
|
});
|
|
19102
|
-
|
|
19103
|
-
this.
|
|
19104
|
-
this.smartSlot.className = "aicut-lighting-smart-slot";
|
|
19105
|
-
this.smartSlot.setAttribute("data-testid", "aicut-lighting-smart");
|
|
19106
|
-
if (this.smartEnabled) {
|
|
19107
|
-
this.smartWrapper = this.buildSmartWrapper();
|
|
19108
|
-
this.body.appendChild(this.smartWrapper);
|
|
19109
|
-
this.smartToggleEl = this.buildSmartToggle();
|
|
19110
|
-
this.controls.headerSlot.appendChild(this.smartToggleEl);
|
|
19111
|
-
}
|
|
19112
|
-
this.syncSmartState();
|
|
19087
|
+
body.appendChild(this.controls.root);
|
|
19088
|
+
this.controlsFooter = this.controls.footerSlot;
|
|
19113
19089
|
this.scene = new LightingScene(this.sceneViewport, this.view);
|
|
19114
19090
|
this.scene.setLightDirection(this.config.keyDirection);
|
|
19115
19091
|
this.scene.setBrightness(this.config.brightness);
|
|
@@ -19153,6 +19129,11 @@ var LightingEditor = class _LightingEditor {
|
|
|
19153
19129
|
this.opts.subjectImageUrl = url;
|
|
19154
19130
|
this.scene.setSubjectImage(url);
|
|
19155
19131
|
}
|
|
19132
|
+
/** Restore config to the safe defaults. Convenience for host's
|
|
19133
|
+
* "Reset" button — equivalent to `setConfig(DEFAULT_LIGHTING_CONFIG)`. */
|
|
19134
|
+
reset() {
|
|
19135
|
+
this.setConfig(DEFAULT_LIGHTING_CONFIG, "reset");
|
|
19136
|
+
}
|
|
19156
19137
|
setView(v) {
|
|
19157
19138
|
if (v === this.view) return;
|
|
19158
19139
|
this.view = v;
|
|
@@ -19162,42 +19143,6 @@ var LightingEditor = class _LightingEditor {
|
|
|
19162
19143
|
getView() {
|
|
19163
19144
|
return this.view;
|
|
19164
19145
|
}
|
|
19165
|
-
/** Enable/disable the entire Smart Mode feature at runtime. */
|
|
19166
|
-
setSmartEnabled(enabled) {
|
|
19167
|
-
if (enabled === this.smartEnabled) return;
|
|
19168
|
-
this.smartEnabled = enabled;
|
|
19169
|
-
if (enabled) {
|
|
19170
|
-
if (!this.smartWrapper) {
|
|
19171
|
-
this.smartWrapper = this.buildSmartWrapper();
|
|
19172
|
-
this.body.appendChild(this.smartWrapper);
|
|
19173
|
-
}
|
|
19174
|
-
if (!this.smartToggleEl) {
|
|
19175
|
-
this.smartToggleEl = this.buildSmartToggle();
|
|
19176
|
-
this.controls.headerSlot.appendChild(this.smartToggleEl);
|
|
19177
|
-
}
|
|
19178
|
-
} else {
|
|
19179
|
-
this.smartWrapper?.remove();
|
|
19180
|
-
this.smartWrapper = null;
|
|
19181
|
-
this.smartCloseBtn = null;
|
|
19182
|
-
this.smartToggleEl?.remove();
|
|
19183
|
-
this.smartToggleEl = null;
|
|
19184
|
-
this.smartToggleThumb = null;
|
|
19185
|
-
}
|
|
19186
|
-
this.syncSmartState();
|
|
19187
|
-
}
|
|
19188
|
-
isSmartEnabled() {
|
|
19189
|
-
return this.smartEnabled;
|
|
19190
|
-
}
|
|
19191
|
-
/** Open/close the smart slot drawer when enabled. No-op when disabled. */
|
|
19192
|
-
setSmartOpen(open) {
|
|
19193
|
-
if (!this.smartEnabled || open === this.smartOpen) return;
|
|
19194
|
-
this.smartOpen = open;
|
|
19195
|
-
this.syncSmartState();
|
|
19196
|
-
this.opts.onSmartOpenChange?.(open);
|
|
19197
|
-
}
|
|
19198
|
-
isSmartOpen() {
|
|
19199
|
-
return this.smartOpen;
|
|
19200
|
-
}
|
|
19201
19146
|
setTheme(theme) {
|
|
19202
19147
|
applyTheme(this.root, theme);
|
|
19203
19148
|
}
|
|
@@ -19208,10 +19153,6 @@ var LightingEditor = class _LightingEditor {
|
|
|
19208
19153
|
};
|
|
19209
19154
|
this.controls.setLocale(this.locale);
|
|
19210
19155
|
this.syncViewToggle();
|
|
19211
|
-
this.syncSmartLocale();
|
|
19212
|
-
}
|
|
19213
|
-
requestGenerate() {
|
|
19214
|
-
this.opts.onGenerate?.(this.getConfig());
|
|
19215
19156
|
}
|
|
19216
19157
|
destroy() {
|
|
19217
19158
|
if (this.destroyed) return;
|
|
@@ -19220,8 +19161,6 @@ var LightingEditor = class _LightingEditor {
|
|
|
19220
19161
|
this.scene.destroy();
|
|
19221
19162
|
this.root.innerHTML = "";
|
|
19222
19163
|
this.root.classList.remove("aicut-root", "aicut-lighting-editor");
|
|
19223
|
-
this.root.removeAttribute("data-smart-enabled");
|
|
19224
|
-
this.root.removeAttribute("data-smart-open");
|
|
19225
19164
|
}
|
|
19226
19165
|
// ---- Internal ------------------------------------------------------
|
|
19227
19166
|
applyMutation(partial) {
|
|
@@ -19271,86 +19210,6 @@ var LightingEditor = class _LightingEditor {
|
|
|
19271
19210
|
if (v === "front") btn.textContent = this.locale.lightingViewFront;
|
|
19272
19211
|
}
|
|
19273
19212
|
}
|
|
19274
|
-
/**
|
|
19275
|
-
* Build the smart slot column wrapper: × close button + the actual
|
|
19276
|
-
* host slot. Wrapper handles the drawer animation; slot reference
|
|
19277
|
-
* stays stable so portals don't have to relocate.
|
|
19278
|
-
*/
|
|
19279
|
-
buildSmartWrapper() {
|
|
19280
|
-
const wrap = document.createElement("div");
|
|
19281
|
-
wrap.className = "aicut-lighting-smart-wrapper";
|
|
19282
|
-
this.smartCloseBtn = document.createElement("button");
|
|
19283
|
-
this.smartCloseBtn.type = "button";
|
|
19284
|
-
this.smartCloseBtn.className = "aicut-lighting-smart-close";
|
|
19285
|
-
this.smartCloseBtn.title = this.locale.lightingSmartClose;
|
|
19286
|
-
this.smartCloseBtn.setAttribute("aria-label", this.locale.lightingSmartClose);
|
|
19287
|
-
this.smartCloseBtn.setAttribute("data-testid", "aicut-lighting-smart-close");
|
|
19288
|
-
this.smartCloseBtn.innerHTML = "×";
|
|
19289
|
-
this.smartCloseBtn.addEventListener("click", () => this.setSmartOpen(false));
|
|
19290
|
-
wrap.appendChild(this.smartCloseBtn);
|
|
19291
|
-
wrap.appendChild(this.smartSlot);
|
|
19292
|
-
return wrap;
|
|
19293
|
-
}
|
|
19294
|
-
/** Pill-style toggle that lives in the controls header. Re-opens
|
|
19295
|
-
* the smart drawer when the host has closed it via ×. */
|
|
19296
|
-
buildSmartToggle() {
|
|
19297
|
-
const row = document.createElement("div");
|
|
19298
|
-
row.className = "aicut-lighting-smart-toggle-row";
|
|
19299
|
-
const label = document.createElement("span");
|
|
19300
|
-
label.className = "aicut-lighting-smart-toggle-label";
|
|
19301
|
-
label.textContent = this.locale.lightingSmartToggle;
|
|
19302
|
-
row.appendChild(label);
|
|
19303
|
-
const toggle = document.createElement("div");
|
|
19304
|
-
toggle.className = "aicut-lighting-toggle aicut-lighting-smart-toggle";
|
|
19305
|
-
toggle.setAttribute("role", "switch");
|
|
19306
|
-
toggle.setAttribute("tabindex", "0");
|
|
19307
|
-
toggle.setAttribute("data-testid", "aicut-lighting-smart-toggle");
|
|
19308
|
-
toggle.title = this.locale.lightingSmartToggle;
|
|
19309
|
-
this.smartToggleThumb = document.createElement("div");
|
|
19310
|
-
this.smartToggleThumb.className = "aicut-lighting-toggle-thumb";
|
|
19311
|
-
toggle.appendChild(this.smartToggleThumb);
|
|
19312
|
-
toggle.addEventListener("click", () => this.setSmartOpen(!this.smartOpen));
|
|
19313
|
-
row.appendChild(toggle);
|
|
19314
|
-
return row;
|
|
19315
|
-
}
|
|
19316
|
-
/** Re-translate smart-related labels after a locale swap. */
|
|
19317
|
-
syncSmartLocale() {
|
|
19318
|
-
if (this.smartCloseBtn) {
|
|
19319
|
-
this.smartCloseBtn.title = this.locale.lightingSmartClose;
|
|
19320
|
-
this.smartCloseBtn.setAttribute("aria-label", this.locale.lightingSmartClose);
|
|
19321
|
-
}
|
|
19322
|
-
if (this.smartToggleEl) {
|
|
19323
|
-
const label = this.smartToggleEl.querySelector(
|
|
19324
|
-
".aicut-lighting-smart-toggle-label"
|
|
19325
|
-
);
|
|
19326
|
-
if (label) label.textContent = this.locale.lightingSmartToggle;
|
|
19327
|
-
const toggle = this.smartToggleEl.querySelector(
|
|
19328
|
-
".aicut-lighting-smart-toggle"
|
|
19329
|
-
);
|
|
19330
|
-
if (toggle) toggle.title = this.locale.lightingSmartToggle;
|
|
19331
|
-
}
|
|
19332
|
-
}
|
|
19333
|
-
/** Mirror smart state to data-* attrs + toggle thumb position.
|
|
19334
|
-
* Drives the CSS that collapses the column when closed. */
|
|
19335
|
-
syncSmartState() {
|
|
19336
|
-
this.root.setAttribute(
|
|
19337
|
-
"data-smart-enabled",
|
|
19338
|
-
this.smartEnabled ? "true" : "false"
|
|
19339
|
-
);
|
|
19340
|
-
this.root.setAttribute(
|
|
19341
|
-
"data-smart-open",
|
|
19342
|
-
this.smartOpen ? "true" : "false"
|
|
19343
|
-
);
|
|
19344
|
-
if (this.smartToggleEl) {
|
|
19345
|
-
const toggle = this.smartToggleEl.querySelector(
|
|
19346
|
-
".aicut-lighting-smart-toggle"
|
|
19347
|
-
);
|
|
19348
|
-
if (toggle) {
|
|
19349
|
-
toggle.classList.toggle("active", this.smartOpen);
|
|
19350
|
-
toggle.setAttribute("aria-checked", this.smartOpen ? "true" : "false");
|
|
19351
|
-
}
|
|
19352
|
-
}
|
|
19353
|
-
}
|
|
19354
19213
|
};
|
|
19355
19214
|
/*! Bundled license information:
|
|
19356
19215
|
|