Skip to content

Commit

Permalink
feat: navigate to scene and node source (motion-canvas#144)
Browse files Browse the repository at this point in the history
  • Loading branch information
aarthificial authored Jan 20, 2023
1 parent 09ab7f9 commit 86d495d
Show file tree
Hide file tree
Showing 11 changed files with 86 additions and 41 deletions.
2 changes: 2 additions & 0 deletions packages/2d/src/components/Node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,9 +232,11 @@ export class Node implements Promisable<Node> {
public readonly parent = createSignal<Node | null>(null);
public readonly properties = getPropertiesOf(this);
public readonly key: string;
public readonly creationStack?: string;

public constructor({children, key, ...rest}: NodeProps) {
this.key = useScene2D()?.registerNode(this, key) ?? key ?? '';
this.creationStack = new Error().stack;
initialize(this, {defaults: rest});
for (const {signal} of this) {
signal.reset();
Expand Down
10 changes: 4 additions & 6 deletions packages/2d/src/scenes/Scene2D.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,26 @@
import {
FullSceneDescription,
GeneratorScene,
Inspectable,
InspectedAttributes,
InspectedElement,
Scene,
SceneMetadata,
SceneRenderEvent,
ThreadGeneratorFactory,
} from '@motion-canvas/core/lib/scenes';
import {endScene, startScene} from '@motion-canvas/core/lib/utils';
import {Vector2} from '@motion-canvas/core/lib/types';
import {Node, View2D} from '../components';
import {Meta} from '@motion-canvas/core';

export class Scene2D extends GeneratorScene<View2D> implements Inspectable {
private readonly view: View2D;
private registeredNodes: Record<string, Node> = {};
private nodeCounters: Record<string, number> = {};

public constructor(
name: string,
meta: Meta<SceneMetadata>,
runnerFactory: ThreadGeneratorFactory<View2D>,
description: FullSceneDescription<ThreadGeneratorFactory<View2D>>,
) {
super(name, meta, runnerFactory);
super(description);
startScene(this);
this.view = new View2D();
endScene(this);
Expand Down Expand Up @@ -72,6 +69,7 @@ export class Scene2D extends GeneratorScene<View2D> implements Inspectable {
if (!node) return null;

const attributes: Record<string, any> = {
stack: node.creationStack,
key: node.key,
};
for (const {key, meta, signal} of node) {
Expand Down
1 change: 1 addition & 0 deletions packages/2d/src/scenes/makeScene2D.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ export function makeScene2D(
return {
klass: Scene2D,
config: runner,
stack: new Error().stack,
};
}
15 changes: 8 additions & 7 deletions packages/core/src/Project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,13 +218,14 @@ export class Project {

const instances: Scene[] = [];
for (const description of scenes) {
const scene = new description.klass(
description.name,
description.meta,
description.config,
);
scene.project = this;
description.onReplaced?.subscribe(config => scene.reload(config), false);
const scene = new description.klass({
...description,
project: this,
});
description.onReplaced?.subscribe(description => {
scene.creationStack = description.stack;
scene.reload(description.config);
}, false);
scene.onReloaded.subscribe(() => this.reloaded.dispatch());
instances.push(scene);
}
Expand Down
36 changes: 20 additions & 16 deletions packages/core/src/scenes/GeneratorScene.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,13 @@ import {EventDispatcher, ValueDispatcher} from '../events';
import {Project} from '../Project';
import {decorate, threadable} from '../decorators';
import {endScene, setProject, startScene} from '../utils';
import {CachedSceneData, Scene, SceneMetadata, SceneRenderEvent} from './Scene';
import {
CachedSceneData,
FullSceneDescription,
Scene,
SceneMetadata,
SceneRenderEvent,
} from './Scene';
import {LifecycleEvents} from './LifecycleEvents';
import {Threadable} from './Threadable';
import {Rect, Vector2} from '../types';
Expand All @@ -31,18 +37,12 @@ export interface ThreadGeneratorFactory<T> {
export abstract class GeneratorScene<T>
implements Scene<ThreadGeneratorFactory<T>>, Threadable
{
public readonly name: string;
public readonly project: Project;
public readonly meta: Meta<SceneMetadata>;
public readonly timeEvents: TimeEvents;
public random: Random;

public get project(): Project {
if (!this.currentProject) {
throw new Error(`Project for Scene ${this.name} has not been set.`);
}
return this.currentProject;
}
public set project(value: Project) {
this.currentProject = value;
}
public creationStack?: string;

public get firstFrame() {
return this.cache.current.firstFrame;
Expand Down Expand Up @@ -95,19 +95,23 @@ export abstract class GeneratorScene<T>
return this.previousScene;
}

private runnerFactory: ThreadGeneratorFactory<T>;
private previousScene: Scene | null = null;
private currentProject: Project | null = null;
private runner: ThreadGenerator | null = null;
private state: SceneState = SceneState.Initial;
private cached = false;
private counters: Record<string, number> = {};

public constructor(
public readonly name: string,
public readonly meta: Meta<SceneMetadata>,
private runnerFactory: ThreadGeneratorFactory<T>,
description: FullSceneDescription<ThreadGeneratorFactory<T>>,
) {
decorate(this.runnerFactory, threadable(name));
this.name = description.name;
this.project = description.project;
this.meta = description.meta;
this.runnerFactory = description.config;
this.creationStack = description.stack;

decorate(this.runnerFactory, threadable(this.name));
this.timeEvents = new TimeEvents(this);

let seed = this.meta.getData().seed;
Expand Down
5 changes: 4 additions & 1 deletion packages/core/src/scenes/Inspectable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ export type InspectedElement = unknown;
/**
* Represents attributes of an inspected element.
*/
export type InspectedAttributes = Record<string, any>;
export type InspectedAttributes = {
stack?: string;
[K: string]: any;
};

/**
* Scenes can implement this interface to make their components
Expand Down
12 changes: 9 additions & 3 deletions packages/core/src/scenes/Scene.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export interface SceneMetadata extends Metadata {
* {@link SceneDescription.config}.
*/
export interface SceneConstructor<T> {
new (name: string, meta: Meta<SceneMetadata>, config: T): Scene;
new (description: FullSceneDescription<T>): Scene;
}

/**
Expand All @@ -44,6 +44,10 @@ export interface SceneDescription<T = unknown> {
* Configuration object.
*/
config: T;
/**
* The stack trace at the moment of creation.
*/
stack?: string;
}

/**
Expand All @@ -54,7 +58,8 @@ export interface SceneDescription<T = unknown> {
export interface FullSceneDescription<T = unknown> extends SceneDescription<T> {
name: string;
meta: Meta<SceneMetadata>;
onReplaced: ValueDispatcher<T>;
project: Project;
onReplaced: ValueDispatcher<FullSceneDescription<T>>;
}

export type DescriptionOf<TScene> = TScene extends Scene<infer TConfig>
Expand Down Expand Up @@ -115,10 +120,11 @@ export interface Scene<T = unknown> {
/**
* Reference to the project.
*/
project: Project;
readonly project: Project;
readonly timeEvents: TimeEvents;
readonly random: Random;
readonly meta: Meta<SceneMetadata>;
creationStack?: string;

/**
* The frame at which this scene starts.
Expand Down
22 changes: 18 additions & 4 deletions packages/ui/src/components/sidebar/Properties.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {isInspectable} from '@motion-canvas/core/lib/scenes/Inspectable';
import {Pane} from '../tabs';
import {useInspection} from '../../contexts';
import {AutoField} from '../fields';
import {Button, Group, Label} from '../controls';
import {findAndOpenFirstUserFile} from '../../utils';

export function Properties() {
const {inspectedElement} = useInspection();
Expand All @@ -16,13 +18,25 @@ export function Properties() {
[scene],
);

const attributes = useMemo(
() => inspectable?.inspectAttributes(inspectedElement),
[inspectedElement, inspectable, frame],
);
const [stack, attributes] = useMemo(() => {
const attributes = inspectable?.inspectAttributes(inspectedElement);
if (attributes) {
const {stack, ...rest} = attributes;
return [stack, rest];
}
return [undefined, undefined];
}, [inspectedElement, inspectable, frame]);

return (
<Pane title="Properties" id="properties-pane">
{stack && (
<Group>
<Label />
<Button onClick={() => findAndOpenFirstUserFile(stack)} main>
GO TO SOURCE
</Button>
</Group>
)}
{attributes
? Object.entries(attributes).map(([key, value]) => (
<AutoField label={key} value={value} />
Expand Down
15 changes: 13 additions & 2 deletions packages/ui/src/components/timeline/SceneTrack.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type {Scene} from '@motion-canvas/core/lib/scenes';
import {useScenes, useSubscribableValue} from '../../hooks';
import {usePlayer, useTimelineContext} from '../../contexts';
import {useMemo} from 'preact/hooks';
import {findAndOpenFirstUserFile} from '../../utils';

export function SceneTrack() {
const scenes = useScenes();
Expand Down Expand Up @@ -63,9 +64,19 @@ function SceneClip({scene}: SceneClipProps) {
className={styles.transition}
/>
)}
<div className={styles.sceneName} style={nameStyle}>
<button
className={styles.sceneName}
style={nameStyle}
title="Go to source"
onMouseUp={async event => {
event.stopPropagation();
if (scene.creationStack) {
await findAndOpenFirstUserFile(scene.creationStack);
}
}}
>
{scene.name}
</div>
</button>
</div>
</div>
);
Expand Down
7 changes: 6 additions & 1 deletion packages/ui/src/components/timeline/Timeline.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -110,12 +110,17 @@
}

.sceneName {
color: inherit;
cursor: pointer;
position: relative;
line-height: 24px;
pointer-events: none;
overflow: hidden;
text-overflow: ellipsis;
margin: 8px 12px;

&:hover {
text-decoration: underline;
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/vite-plugin/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ export default ({
`if (import.meta.hot) {`,
` import.meta.hot.accept();`,
` if (import.meta.hot.data.onReplaced) {`,
` description.onReplaced.current = description.config;`,
` description.onReplaced.current = description;`,
` } else {`,
` import.meta.hot.data.onReplaced = description.onReplaced;`,
` }`,
Expand Down

0 comments on commit 86d495d

Please sign in to comment.