Give the three asset tabs one base, and move the viewports into the editor
src/viewport/ held three classes only src/editor/ ever used, and framing.ts sat under graph/ although the scene view wants the same bounds sum. The graph, the texture and the container tab repeated the same header, subscription and disposal code with two silent divergences: the graph tab said "nothing shown" with null instead of '', and the texture tab refell back to the first asset on a selection change but not on a project event. These arrive together because GraphCanvas ties them: it reads framing from its new home, hands its fold marks to folding.ts, and its '' change is what GraphEditor must agree with. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a2f2f8465c
commit
11981b40d0
15 files changed
+408
-425
No files matched your search
@@ -5,7 +5,15 @@ import { GraphInterpreter } from '../../src/core/graph';
|
||||
import type { BehaviorAsset } from '../../src/core/model/Asset';
|
||||
import { findAsset } from '../../src/core/model/Project';
|
||||
import { deserializeProject, serializeProject } from '../../src/core/store/serialize';
|
||||
import { foldedEdgeShown, foldMarkKey, foldMarkLabel } from '../../src/editor/graph/folding';
|
||||
import {
|
||||
buildFoldMarks,
|
||||
foldedEdgeShown,
|
||||
type FoldMarkEnd,
|
||||
foldMarkAt,
|
||||
foldMarkKey,
|
||||
foldMarkLabel,
|
||||
foldMarkRadius,
|
||||
} from '../../src/editor/graph/folding';
|
||||
import { fakeContext, graphEditor, makeBehavior, makeProject, roundTrip } from '../helpers';
|
||||
|
||||
/** "On start → Move(self, dx = 7)": two edges, so a fold can hide one and leave the other. */
|
||||
@@ -145,3 +153,67 @@ describe('several hidden links at one port', () => {
|
||||
expect(foldMarkLabel([])).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
/** The bundling that keeps two hidden links out of the same port from stacking on one pixel. */
|
||||
describe('fold mark bundling', () => {
|
||||
const end = (over: Partial<FoldMarkEnd> & { edgeId: string; key: string }): FoldMarkEnd => ({
|
||||
other: 'far',
|
||||
otherLabel: 'Far',
|
||||
isOutput: true,
|
||||
portX: 100,
|
||||
portY: 50,
|
||||
color: 1,
|
||||
hot: false,
|
||||
...over,
|
||||
});
|
||||
|
||||
it('gives one port one mark, however many links it hides', () => {
|
||||
const marks = buildFoldMarks(
|
||||
[
|
||||
end({ edgeId: 'e1', key: 'a', other: 'n1', otherLabel: 'Add' }),
|
||||
end({ edgeId: 'e2', key: 'a', other: 'n2', otherLabel: 'Compare' }),
|
||||
end({ edgeId: 'e3', key: 'b', other: 'n3', otherLabel: 'Move' }),
|
||||
],
|
||||
16,
|
||||
);
|
||||
expect(marks).toHaveLength(2);
|
||||
expect(marks[0]!.edgeIds).toEqual(['e1', 'e2']);
|
||||
expect(foldMarkLabel(marks[0]!.names)).toBe('Add +1');
|
||||
expect(marks[1]!.edgeIds).toEqual(['e3']);
|
||||
});
|
||||
|
||||
it('dedupes the far nodes, so two links to one node select it once', () => {
|
||||
const marks = buildFoldMarks(
|
||||
[end({ edgeId: 'e1', key: 'a', other: 'n1' }), end({ edgeId: 'e2', key: 'a', other: 'n1' })],
|
||||
16,
|
||||
);
|
||||
expect(marks[0]!.others).toEqual(['n1']);
|
||||
});
|
||||
|
||||
it('puts the mark a stub out of its port, on the side the port faces', () => {
|
||||
const [out] = buildFoldMarks([end({ edgeId: 'e1', key: 'a', isOutput: true })], 16);
|
||||
const [into] = buildFoldMarks([end({ edgeId: 'e2', key: 'b', isOutput: false })], 16);
|
||||
expect(out!.x).toBe(116);
|
||||
expect(into!.x).toBe(84);
|
||||
});
|
||||
|
||||
it('lets a pulse anywhere in the bundle colour the whole mark', () => {
|
||||
const marks = buildFoldMarks(
|
||||
[
|
||||
end({ edgeId: 'e1', key: 'a', color: 1, hot: false }),
|
||||
end({ edgeId: 'e2', key: 'a', color: 9, hot: true }),
|
||||
],
|
||||
16,
|
||||
);
|
||||
expect(marks[0]!.hot).toBe(true);
|
||||
expect(marks[0]!.color).toBe(9);
|
||||
});
|
||||
|
||||
it('grows the dot for a bundle and finds it under a click', () => {
|
||||
expect(foldMarkRadius(1, 5)).toBe(5);
|
||||
expect(foldMarkRadius(2, 5)).toBe(7);
|
||||
const marks = [{ edgeIds: ['e1', 'e2'], x: 100, y: 50 }];
|
||||
expect(foldMarkAt(marks, { x: 108, y: 50 }, 5)).toBe(marks[0]);
|
||||
expect(foldMarkAt(marks, { x: 120, y: 50 }, 5)).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { type Box, boxesInBand, frameBoxes } from '../../src/editor/graph/framing';
|
||||
import { boundsOf, type Box, boxesInBand, frameBoxes } from '../../src/editor/framing';
|
||||
|
||||
const LIMITS = { min: 0.25, max: 2.5 };
|
||||
|
||||
@@ -83,3 +83,29 @@ describe('fit', () => {
|
||||
expect(f).toEqual({ zoom: 1, panX: 100, panY: 50 });
|
||||
});
|
||||
});
|
||||
|
||||
describe('boundsOf', () => {
|
||||
it('encloses every box', () => {
|
||||
const boxes: Box[] = [
|
||||
{ id: 'a', x: 10, y: 20, w: 100, h: 60 },
|
||||
{ id: 'b', x: -40, y: 5, w: 20, h: 10 },
|
||||
];
|
||||
expect(boundsOf(boxes)).toEqual({ minX: -40, minY: 5, maxX: 110, maxY: 80 });
|
||||
});
|
||||
|
||||
it('reports nothing for no boxes at all', () => {
|
||||
expect(boundsOf([])).toBeNull();
|
||||
});
|
||||
|
||||
/** The scene view seeds the origin so a template drawn far to one side cannot hide it. */
|
||||
it('keeps a seed inside the result even when every box is far away', () => {
|
||||
const far: Box[] = [{ id: 'a', x: 500, y: 500, w: 10, h: 10 }];
|
||||
const origin = { minX: 0, minY: 0, maxX: 0, maxY: 0 };
|
||||
expect(boundsOf(far, origin)).toEqual({ minX: 0, minY: 0, maxX: 510, maxY: 510 });
|
||||
});
|
||||
|
||||
it('still answers for no boxes when a seed is given', () => {
|
||||
const origin = { minX: 0, minY: 0, maxX: 0, maxY: 0 };
|
||||
expect(boundsOf([], origin)).toEqual(origin);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user