Files
rootandClaude Opus 5 854ae2e756 Add Get/Set properties nodes and a breadcrumb for opened sub-behaviors
Get properties / Set properties work like their variable counterparts:
one port per chosen property, an unconnected input writes its inline
value. A port chosen from the built-in list keeps that property's field
and default (height starts at 100, color is a colour field). "object" is
reserved for the fixed input, checked by the command and on load.

The property picker names the owning type ("Sprite width"), and text
fields can now carry a suggestion list; Get/Set property use it.

Double-clicking a call node shows the path of opened behaviors under the
graph header. Clicking an entry jumps back and trims the path; choosing
a behavior any other way hides it. Reopening a behavior already on the
path steps back to it instead of repeating it. The bar shows before the
graph switches, otherwise it clips the graph that was just fitted.

The "Add node..." header button is gone; nodes come from the context menu.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-11 15:07:07 +02:00

45 lines
1.7 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import { backTo, descend, follow } from '../../src/editor/graph/trail';
const alive = () => true;
describe('breadcrumb trail', () => {
it('starts at the graph the call was opened from', () => {
expect(descend([], 'game', 'move')).toEqual(['game', 'move']);
});
it('grows by one per call opened further down', () => {
expect(descend(['game', 'move'], 'move', 'other')).toEqual(['game', 'move', 'other']);
});
it('starts over when the call sits in a graph the trail does not end at', () => {
expect(descend(['game', 'move'], 'jump', 'other')).toEqual(['jump', 'other']);
});
it('steps back instead of repeating a behavior already on the trail', () => {
expect(descend(['game', 'move', 'other'], 'other', 'move')).toEqual(['game', 'move']);
expect(descend(['game', 'move'], 'move', 'game')).toEqual([]);
});
it('leaves the trail alone when a behavior calls itself', () => {
expect(descend(['game', 'move'], 'move', 'move')).toEqual(['game', 'move']);
});
it('cuts back to the crumb clicked, and disappears at the root', () => {
const trail = ['game', 'move', 'other'];
expect(backTo(trail, 1)).toEqual(['game', 'move']);
expect(backTo(trail, 0)).toEqual([]);
});
it('survives only while it still leads to the graph shown', () => {
const trail = ['game', 'move'];
expect(follow(trail, 'move', alive)).toEqual(trail);
expect(follow(trail, 'game', alive)).toEqual([]);
expect(follow(trail, '', alive)).toEqual([]);
});
it('is dropped when a behavior along it was deleted', () => {
expect(follow(['game', 'move', 'other'], 'other', (id) => id !== 'move')).toEqual([]);
});
});