reframe-video 0.1.0 → 0.1.2

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.
@@ -200,6 +200,39 @@ export function buildPanel(store: EditorStore, root: HTMLElement) {
200
200
  }
201
201
  }
202
202
 
203
+ // --- beats (semantic groups) ---
204
+ const beats: Extract<TimelineIR, { kind: "beat" }>[] = [];
205
+ const beatOf = new Map<string, string>();
206
+ const walkBeats = (tl: TimelineIR, owner?: string) => {
207
+ if (tl.kind === "beat") {
208
+ beats.push(tl);
209
+ tl.children.forEach((c) => walkBeats(c, tl.name));
210
+ return;
211
+ }
212
+ if ("label" in tl && tl.label !== undefined && owner) beatOf.set(tl.label, owner);
213
+ if ("children" in tl) tl.children.forEach((c) => walkBeats(c, owner));
214
+ };
215
+ if (ir.timeline) walkBeats(ir.timeline);
216
+ if (beats.length > 0) {
217
+ root.append(el("h3", {}, "Beats"));
218
+ for (const b of beats) {
219
+ const card = el("div", { class: "step-card beat-card" },
220
+ el("div", {}, `${b.name} `, el("span", { class: "kind" }, "(beat)")),
221
+ );
222
+ const gapRow = makeControl("gap", b.gap ?? 0, store.hasTimelineEdit(b.name, "gap"),
223
+ (v) => store.setTimelineParam(b.name, "gap", Number(v)),
224
+ () => store.unsetTimelineParam(b.name, "gap"));
225
+ gapRow.prepend(el("label", {}, "gap"));
226
+ card.append(gapRow);
227
+ const scaleRow = makeControl("scale", b.scale ?? 1, store.hasTimelineEdit(b.name, "scale"),
228
+ (v) => store.setTimelineParam(b.name, "scale", Number(v)),
229
+ () => store.unsetTimelineParam(b.name, "scale"));
230
+ scaleRow.prepend(el("label", {}, "scale"));
231
+ card.append(scaleRow);
232
+ root.append(card);
233
+ }
234
+ }
235
+
203
236
  // --- labeled timeline steps ---
204
237
  const steps: Extract<TimelineIR, { label?: string }>[] = [];
205
238
  const walkTl = (tl: TimelineIR) => {
@@ -211,9 +244,10 @@ export function buildPanel(store: EditorStore, root: HTMLElement) {
211
244
  root.append(el("h3", {}, "Timeline"));
212
245
  for (const step of steps) {
213
246
  const label = step.label!;
214
- const card = el("div", { class: "step-card" },
215
- el("div", {}, `${label} `, el("span", { class: "kind" }, `(${step.kind})`)),
216
- );
247
+ const beatName = beatOf.get(label);
248
+ const head = el("div", {}, `${label} `, el("span", { class: "kind" }, `(${step.kind})`));
249
+ if (beatName) head.append(el("span", { class: "badge" }, ` ↳ ${beatName}`));
250
+ const card = el("div", { class: "step-card" }, head);
217
251
  const durRow = makeControl(
218
252
  "duration",
219
253
  "duration" in step ? (step.duration ?? 0.5) : 0.5,
@@ -15,6 +15,7 @@ import {
15
15
  type OverlayDoc,
16
16
  type PropValue,
17
17
  type SceneIR,
18
+ type TimelineIR,
18
19
  } from "@reframe/core";
19
20
 
20
21
  export type ChangeKind = "value" | "structure";
@@ -91,12 +92,37 @@ export class EditorStore {
91
92
  this.recompose("structure");
92
93
  }
93
94
 
94
- setTimelineParam(label: string, key: "duration" | "ease" | "stagger", value: number | string) {
95
+ setTimelineParam(
96
+ label: string,
97
+ key: "duration" | "ease" | "stagger" | "at" | "gap" | "scale" | "order",
98
+ value: number | string,
99
+ ) {
95
100
  ((this.draft.timeline ??= {})[label] ??= {})[key] = value as never;
96
101
  this.recompose("value");
97
102
  }
98
103
 
99
- unsetTimelineParam(label: string, key: "duration" | "ease" | "stagger") {
104
+ /** Labeled motionPath steps with their (possibly overlay-edited) waypoints —
105
+ * drives the preview's draggable handles and is exposed on window.__store. */
106
+ motionPaths(): { label: string; points: [number, number][] }[] {
107
+ const out: { label: string; points: [number, number][] }[] = [];
108
+ const walk = (tl: TimelineIR) => {
109
+ if (tl.kind === "motionPath" && tl.label) out.push({ label: tl.label, points: tl.points });
110
+ if ("children" in tl) tl.children.forEach(walk);
111
+ };
112
+ if (this.compiled.ir.timeline) walk(this.compiled.ir.timeline);
113
+ return out;
114
+ }
115
+
116
+ /** A dragged waypoint writes the whole points array as a timeline patch. */
117
+ setMotionPathPoints(label: string, points: [number, number][]) {
118
+ ((this.draft.timeline ??= {})[label] ??= {}).points = points;
119
+ this.recompose("value");
120
+ }
121
+
122
+ unsetTimelineParam(
123
+ label: string,
124
+ key: "duration" | "ease" | "stagger" | "at" | "gap" | "scale" | "order",
125
+ ) {
100
126
  delete this.draft.timeline?.[label]?.[key];
101
127
  this.prune();
102
128
  this.recompose("structure");
@@ -1,4 +1,12 @@
1
1
  declare module "virtual:reframe-user-scenes" {
2
2
  import type { SceneIR } from "@reframe/core";
3
- export const userScenes: { name: string; load: () => Promise<{ default: SceneIR }> }[];
3
+ export const userScenes: {
4
+ name: string;
5
+ /** Absolute directory containing the scene file. */
6
+ dir: string;
7
+ load: () => Promise<{ default: SceneIR }>;
8
+ }[];
4
9
  }
10
+
11
+ /** Absolute path of examples/scenes (injected by vite define; "" when packaged). */
12
+ declare const __REFRAME_EXAMPLES_DIR__: string;
@@ -1,5 +1,5 @@
1
1
  import { existsSync, readFileSync, readdirSync } from "node:fs";
2
- import { basename, join, resolve } from "node:path";
2
+ import { basename, dirname, join, resolve } from "node:path";
3
3
  import { defineConfig, type Plugin } from "vite";
4
4
 
5
5
  const PKG_ROOT = resolve(__dirname, "..");
@@ -31,7 +31,7 @@ const userScenesPlugin: Plugin = {
31
31
  if (id !== "\0reframe-user-scenes") return undefined;
32
32
  const entries = userScenes().map(
33
33
  (s) =>
34
- ` { name: ${JSON.stringify(s.name)}, load: () => import(${JSON.stringify(`/@fs${s.path}`)}) },`,
34
+ ` { name: ${JSON.stringify(s.name)}, dir: ${JSON.stringify(dirname(s.path))}, load: () => import(${JSON.stringify(`/@fs${s.path}`)}) },`,
35
35
  );
36
36
  return `export const userScenes = [\n${entries.join("\n")}\n];\n`;
37
37
  },
@@ -39,6 +39,7 @@ const userScenesPlugin: Plugin = {
39
39
 
40
40
  export default defineConfig({
41
41
  plugins: [userScenesPlugin],
42
+ define: { __REFRAME_EXAMPLES_DIR__: '""' }, // packaged preview ships no examples
42
43
  resolve: {
43
44
  alias: {
44
45
  "@reframe/core": resolve(PKG_ROOT, "dist", "index.js"),