mock: 選択枠を太くし、最前面の描画層に移す(T-30 単位1)

選択枠と長押し候補の枠を NodeSvg の再帰から外し、ノードの描画より後に描く
PhySelectionLayer に移した。枠はワールド変換とローカル矩形から純粋関数
selectionFrames で求め、画面上3.5pxで描く。部品の選択表示は形状上に残す。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TDkA8qMQ3Uf1eY3Ar19vjy
This commit is contained in:
2026-10-21 08:42:14 +09:00
co-authored by Claude Opus 5.5
parent d110320cb8
commit e92708dc12
6 changed files with 202 additions and 53 deletions
+32
View File
@@ -12,6 +12,7 @@ import {
pasteParent,
resolveKey,
snap,
selectionFrames,
tabOrder,
treeIndex,
visibleNodeIds,
@@ -34,6 +35,37 @@ const phy: PhyFile = {
],
};
describe("PHY pure interaction rules", () => {
it("computes selected frame world transforms through nested rotations", () => {
const roots: PhyNode[] = [
{
type: "cluster",
id: "outer",
transform: { x: 10, y: 20, rotation: 90 },
children: [
{
type: "cluster",
id: "inner",
transform: { x: 2, y: 3, rotation: -90 },
children: [
{
type: "key",
id: "target",
profile: "p",
transform: { x: 4, y: 5, rotation: 30 },
size: { w: 2, h: 1 },
},
],
},
],
},
];
const frame = selectionFrames(roots, ["target"], {}, null)[0]!;
expect(frame.id).toBe("target");
expect(frame.transform.e).toBeCloseTo(11);
expect(frame.transform.f).toBeCloseTo(27);
expect(frame.transform.a).toBeCloseTo(Math.cos(Math.PI / 6));
expect(frame.transform.b).toBeCloseTo(Math.sin(Math.PI / 6));
});
it("removes ancestors and descendants from mixed selections", () => {
expect(normalizeSelection(phy, ["c", "k"])).toEqual(["k"]);
expect(normalizeSelection(phy, ["k", "c"])).toEqual(["c"]);