Skip to content

Commit e2ed7a6

Browse files
author
DavidQ
committed
Fix shape delete and refine Object Vector Studio Nerd Font icon coverage - PR_26133_019-object-vector-studio-icon-and-shape-delete-fixes
1 parent 3b3d952 commit e2ed7a6

6 files changed

Lines changed: 175 additions & 72 deletions

File tree

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# PR_26133_018 Playwright V8 Coverage Report
1+
# PR_26133_019 Playwright V8 Coverage Report
22

33
Coverage source: `docs/dev/reports/playwright_v8_coverage_report.txt`, refreshed by the final `npm run test:workspace-v2` run.
44

@@ -7,12 +7,14 @@ Coverage source: `docs/dev/reports/playwright_v8_coverage_report.txt`, refreshed
77
- Coverage is advisory only; no thresholds are enforced.
88
- Workspace Manager V2 entry point: 91%.
99
- Object Vector Studio V2 runtime coverage entries from the generated report:
10-
- `tools/object-vector-studio-v2/js/ToolStarterApp.js`: 93%, executed lines 3827/3827, executed functions 407/438.
11-
- The generated report lists `tests/playwright/tools/WorkspaceManagerV2.spec.mjs` as changed JS not collected by browser runtime coverage.
10+
- `tools/object-vector-studio-v2/js/ToolStarterApp.js`: 93%, executed lines 3849/3849, executed functions 409/440.
11+
- Changed JS files considered by the generated report:
12+
- `tools/object-vector-studio-v2/js/ToolStarterApp.js`: covered by browser V8 coverage.
13+
- `tests/playwright/tools/WorkspaceManagerV2.spec.mjs`: not collected as browser runtime coverage.
1214

1315
## Validation Context
1416

1517
- Main command: `npm run test:workspace-v2`.
1618
- Result: 48 passed.
17-
- Focused Object Vector Studio V2 layout, preview coordinate, geometry-layout, mouse-editing, animation-state, and asset-authoring scenarios passed as part of the workspace-v2 run.
18-
- Coverage includes the runtime paths for the scoped Nerd Font icon mapping, static icon decoration, dynamic tile icon decoration, and existing icon button behavior checks.
19+
- Focused Object Vector Studio V2 layout, icon, palette, geometry-layout, preview coordinate, mouse-editing, animation-state, and asset-authoring scenarios passed as part of the workspace-v2 run.
20+
- Coverage includes the scoped Nerd Font icon mapping, trash/delete glyph mapping, static palette/action icon decoration, dynamic tile icon decoration, shape delete targeting, and geometry layout checks.
Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
1-
# PR_26133_018 Workspace V2 Results
1+
# PR_26133_019 Workspace V2 Results
22

33
## Command Results
44

55
- `node --check tools/object-vector-studio-v2/js/ToolStarterApp.js`: passed.
66
- `node --check tests/playwright/tools/WorkspaceManagerV2.spec.mjs`: passed.
7-
- `npm run test:workspace-v2 -- --grep "shows Object Vector Studio V2 layout shell"`: 48 passed.
87
- `npm run test:workspace-v2`: 48 passed.
98
- `git diff --check`: passed with LF-to-CRLF working-copy warnings for touched files.
109

1110
## Targeted Object Vector Studio V2 Verification
1211

13-
- Confirmed Object Vector Studio V2 registers `@font-face` for `0xProto Nerd Font` from `src/shared/font/0xProtoNerdFont/0xProtoNerdFontMono-Regular.ttf`.
14-
- Confirmed static and dynamic Object Vector Studio V2 icon targets render glyphs with `0xProto Nerd Font` through the scoped icon mapping.
15-
- Confirmed object action, viewport, shape tool, z-order/group, snap/grid, transform, shape tile, and object tile icons preserve their existing click actions.
16-
- Confirmed visible button text, aria labels, and titles/tooltips remain available; disabled controls keep their existing disabled-reason tooltip behavior.
17-
- Confirmed show/hide and lock/unlock tile buttons swap to the matching visible/hidden and locked/unlocked glyph keys without changing behavior.
18-
- Confirmed `src/shared/font/0xProtoNerdFont` had no file changes.
19-
- Confirmed workspace-v2 Object Vector Studio V2 scenarios reported no console/page errors.
12+
- Confirmed shape tile delete carries the source object id and removes the targeted shape only; no console/page errors were reported by the workspace-v2 scenarios.
13+
- Confirmed all Object Vector Studio V2 delete icon paths use `nf-md-trash_can_outline`.
14+
- Confirmed Object Vector Studio V2 icon glyphs render 75% larger through the scoped Nerd Font CSS scale without changing button actions, accessible labels, or tooltips.
15+
- Confirmed Paint, Stroke, Hue, Sat, Bri, and Name controls receive mapped Nerd Font icons.
16+
- Confirmed Palette UI no longer renders visible `Sort` text.
17+
- Confirmed the polygon tool uses `nf-md-vector_polygon`.
18+
- Confirmed Apply Geometry spacing is reduced and Arc Geometry renders `startAngle`/`endAngle` on the same row.
19+
- Confirmed `src/shared/font/0xProtoNerdFont` was preserved.
2020

2121
## Scope Checks
2222

23-
- Existing Object Vector Studio V2 JSON contracts were preserved.
23+
- Existing Object Vector Studio V2 contracts were preserved.
2424
- No sample JSON files were changed.
2525
- No unrelated tool/runtime files were changed.

tests/playwright/tools/WorkspaceManagerV2.spec.mjs

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1251,18 +1251,28 @@ test.describe("Workspace Manager V2 bootstrap", () => {
12511251
const before = getComputedStyle(element, "::before");
12521252
return {
12531253
fontFamily: before.fontFamily,
1254+
fontSize: Number.parseFloat(before.fontSize),
12541255
glyphPresent: Boolean(element.dataset.ovsIcon),
1255-
iconKey: element.dataset.ovsIconKey
1256+
iconKey: element.dataset.ovsIconKey,
1257+
iconName: element.dataset.ovsIconName
12561258
};
12571259
};
12581260
const title = (selector) => document.querySelector(selector).title;
12591261
return {
12601262
actionIcons: {
12611263
add: icon("#objectVectorStudioV2AddObjectButton"),
12621264
delete: icon("#objectVectorStudioV2DeleteObjectButton"),
1263-
rename: icon("#objectVectorStudioV2RenameObjectButton")
1265+
paint: icon("#objectVectorStudioV2PaintModeButton"),
1266+
rename: icon("#objectVectorStudioV2RenameObjectButton"),
1267+
stroke: icon("#objectVectorStudioV2StrokeModeButton")
12641268
},
12651269
fontAssetOk: fontResponse.ok,
1270+
paletteSortIcons: {
1271+
bri: icon("[data-palette-sort='bri']"),
1272+
hue: icon("[data-palette-sort='hue']"),
1273+
name: icon("[data-palette-sort='name']"),
1274+
sat: icon("[data-palette-sort='sat']")
1275+
},
12661276
shapeIcons: {
12671277
arc: icon(".object-vector-studio-v2__shape-icon--arc"),
12681278
circle: icon(".object-vector-studio-v2__shape-icon--circle"),
@@ -1296,12 +1306,23 @@ test.describe("Workspace Manager V2 bootstrap", () => {
12961306
expect(iconStyleState.fontAssetOk).toBe(true);
12971307
[
12981308
...Object.values(iconStyleState.actionIcons),
1309+
...Object.values(iconStyleState.paletteSortIcons),
12991310
...Object.values(iconStyleState.shapeIcons),
13001311
...Object.values(iconStyleState.viewportIcons),
13011312
...Object.values(iconStyleState.zIcons)
13021313
].forEach((icon) => {
13031314
expect(icon.glyphPresent).toBe(true);
13041315
expect(icon.fontFamily).toContain("0xProto Nerd Font");
1316+
expect(icon.fontSize).toBeGreaterThanOrEqual(21);
1317+
});
1318+
expect(iconStyleState.actionIcons.delete.iconName).toBe("nf-md-trash_can_outline");
1319+
expect(iconStyleState.actionIcons.paint.iconName).toBe("nf-fa-paint_brush");
1320+
expect(iconStyleState.actionIcons.stroke.iconName).toBe("nf-fa-pencil");
1321+
expect(iconStyleState.paletteSortIcons).toMatchObject({
1322+
bri: { iconKey: "bri", iconName: "nf-fa-sun_o" },
1323+
hue: { iconKey: "hue", iconName: "nf-fa-eyedropper" },
1324+
name: { iconKey: "name", iconName: "nf-fa-font" },
1325+
sat: { iconKey: "sat", iconName: "nf-fa-tint" }
13051326
});
13061327
expect(Object.fromEntries(Object.entries(iconStyleState.shapeIcons).map(([key, value]) => [key, value.iconKey]))).toEqual({
13071328
arc: "arc",
@@ -1313,6 +1334,9 @@ test.describe("Workspace Manager V2 bootstrap", () => {
13131334
text: "text",
13141335
triangle: "triangle"
13151336
});
1337+
expect(iconStyleState.shapeIcons.polygon.iconName).toBe("nf-md-vector_polygon");
1338+
expect(iconStyleState.shapeIcons.circle.iconName).toBe("nf-fa-circle_o");
1339+
expect(iconStyleState.shapeIcons.ellipse.iconName).toBe("nf-fa-circle_o");
13161340
expect(Object.fromEntries(Object.entries(iconStyleState.viewportIcons).map(([key, value]) => [key, value.iconKey]))).toEqual({
13171341
down: "panDown",
13181342
reset: "reset",
@@ -1535,7 +1559,8 @@ test.describe("Workspace Manager V2 bootstrap", () => {
15351559
await expect(page.locator("#statusLog")).toHaveValue(/INFO Runtime palette is read-only session\/workspace data; Object Vector JSON remains palette-free\./);
15361560
await expect(page.locator("#objectVectorStudioV2PaletteSummary")).not.toContainText(/^Palette/);
15371561
await expect(page.locator("#objectVectorStudioV2StrokeWidth")).toHaveValue("2");
1538-
await expect(page.locator(".object-vector-studio-v2__palette-sort button")).toHaveText(["hue", "sat", "bri", "name"]);
1562+
await expect(page.locator(".object-vector-studio-v2__palette-sort")).not.toContainText("Sort");
1563+
await expect(page.locator(".object-vector-studio-v2__palette-sort button")).toHaveText(["Hue", "Sat", "Bri", "Name"]);
15391564
const swatchState = await page.locator(".object-vector-studio-v2__palette-swatch").evaluateAll((swatches) => swatches.map((swatch) => {
15401565
const rect = swatch.getBoundingClientRect();
15411566
return {
@@ -1763,21 +1788,25 @@ test.describe("Workspace Manager V2 bootstrap", () => {
17631788
deleteTitle: deleteButton.title,
17641789
iconFontsUseNerd: controls.every((control) => getComputedStyle(control.querySelector("[data-ovs-icon]"), "::before").fontFamily.includes("0xProto Nerd Font")),
17651790
iconKeys: controls.map((control) => control.querySelector("[data-ovs-icon]")?.dataset.ovsIconKey),
1791+
iconNames: controls.map((control) => control.querySelector("[data-ovs-icon]")?.dataset.ovsIconName),
1792+
iconGlyphSizes: controls.map((control) => Number.parseFloat(getComputedStyle(control.querySelector("[data-ovs-icon]"), "::before").fontSize)),
17661793
iconSizes: rects.map((rect) => Math.round(Math.max(rect.width, rect.height))),
17671794
stacked: rects.every((rect, index) => index === 0
17681795
|| (Math.abs(rect.left - rects[index - 1].left) <= 1 && rect.top > rects[index - 1].top))
17691796
};
17701797
});
1771-
expect(objectTileActionLayout).toEqual({
1798+
expect(objectTileActionLayout).toMatchObject({
17721799
allTextEmpty: true,
17731800
controlOrder: ["visibility", "lock", "delete"],
17741801
deleteAtFarRight: true,
17751802
deleteTitle: "Delete this object",
17761803
iconFontsUseNerd: true,
17771804
iconKeys: ["eye", "unlock", "delete"],
1805+
iconNames: ["nf-fa-eye", "nf-fa-unlock", "nf-md-trash_can_outline"],
17781806
iconSizes: [26, 26, 26],
17791807
stacked: true
17801808
});
1809+
expect(objectTileActionLayout.iconGlyphSizes.every((size) => size >= 22)).toBe(true);
17811810
await expect(page.locator(".object-vector-studio-v2__object-tile[data-object-id='object.asteroids.object-1'] [data-object-control='visibility']")).toHaveText("");
17821811
await expect(page.locator(".object-vector-studio-v2__object-tile[data-object-id='object.asteroids.object-1'] [data-object-control='lock']")).toHaveText("");
17831812
await expect(page.locator(".object-vector-studio-v2__object-tile[data-object-id='object.asteroids.object-1'] [data-object-control='delete']")).toHaveText("");
@@ -2535,8 +2564,13 @@ test.describe("Workspace Manager V2 bootstrap", () => {
25352564
const geometryLayout = async () => page.locator("#objectVectorStudioV2ObjectDetails").evaluate((details) => {
25362565
const panel = details.querySelector(".object-vector-studio-v2__edit-panel--geometry");
25372566
const grid = panel?.querySelector(".object-vector-studio-v2__edit-grid");
2567+
const applyButton = panel?.querySelector("#objectVectorStudioV2ApplyGeometryButton");
2568+
const shapePanel = details.querySelector(".object-vector-studio-v2__edit-panel--geometry + .object-vector-studio-v2__shape-panel");
25382569
const panelStyle = panel ? getComputedStyle(panel) : null;
25392570
const gridStyle = grid ? getComputedStyle(grid) : null;
2571+
const shapePanelStyle = shapePanel ? getComputedStyle(shapePanel) : null;
2572+
const applyRect = applyButton?.getBoundingClientRect();
2573+
const shapePanelRect = shapePanel?.getBoundingClientRect();
25402574
const fields = Array.from(panel?.querySelectorAll(".object-vector-studio-v2__edit-field") || []).map((field) => {
25412575
const input = field.querySelector("input");
25422576
const label = field.querySelector("span");
@@ -2551,13 +2585,15 @@ test.describe("Workspace Manager V2 bootstrap", () => {
25512585
};
25522586
});
25532587
return {
2588+
applyToSummaryGap: applyRect && shapePanelRect ? Math.round(shapePanelRect.top - applyRect.bottom) : null,
25542589
fieldOrder: fields.map((field) => field.key),
25552590
fillColorRemoved: !details.textContent.includes("Fill Color"),
25562591
heading: panel?.querySelector("h4")?.textContent.trim(),
25572592
inlineFields: fields.every((field) => field.inline),
25582593
inputPaddingTopMax: Math.max(...fields.map((field) => field.paddingTop)),
25592594
panelGap: Number.parseFloat(panelStyle?.gap || "0"),
25602595
rowGap: Number.parseFloat(gridStyle?.rowGap || "0"),
2596+
shapePanelPaddingTop: Number.parseFloat(shapePanelStyle?.paddingTop || "0"),
25612597
tops: Object.fromEntries(fields.map((field) => [field.key, field.top])),
25622598
wideFields: fields.filter((field) => field.wide).map((field) => field.key)
25632599
};
@@ -2574,6 +2610,8 @@ test.describe("Workspace Manager V2 bootstrap", () => {
25742610
expect(layout.inputPaddingTopMax).toBeLessThanOrEqual(4);
25752611
expect(layout.panelGap).toBeLessThanOrEqual(5);
25762612
expect(layout.rowGap).toBeLessThanOrEqual(4);
2613+
expect(layout.applyToSummaryGap).toBeLessThanOrEqual(8);
2614+
expect(layout.shapePanelPaddingTop).toBeLessThanOrEqual(6);
25772615
expect(layout.wideFields).toEqual(wideFields);
25782616
pairs.forEach(([left, right]) => {
25792617
expect(Math.abs(layout.tops[left] - layout.tops[right])).toBeLessThanOrEqual(3);
@@ -2658,8 +2696,9 @@ test.describe("Workspace Manager V2 bootstrap", () => {
26582696
await expectGeometryLayout({
26592697
heading: "Arc Geometry",
26602698
order: ["cx", "cy", "r", "startAngle", "endAngle"],
2661-
pairs: [["cx", "cy"]],
2662-
shapeId: "arc-5"
2699+
pairs: [["cx", "cy"], ["startAngle", "endAngle"]],
2700+
shapeId: "arc-5",
2701+
wideFields: ["r"]
26632702
});
26642703
await expectGeometryLayout({
26652704
heading: "Text Geometry",
@@ -2793,6 +2832,21 @@ test.describe("Workspace Manager V2 bootstrap", () => {
27932832
expect(lineAfterEndpoint.geometry.y2).not.toBe(lineBeforeEndpoint.geometry.y2);
27942833
await expect(page.locator("#statusLog")).toHaveValue(/OK Moved line end for shape line-2\./);
27952834

2835+
const shapeDeleteIconState = await page.locator("[data-shape-delete-id='line-2']").evaluate((button) => {
2836+
const icon = button.querySelector("[data-ovs-icon]");
2837+
return {
2838+
objectId: button.dataset.shapeDeleteObjectId,
2839+
title: button.title,
2840+
iconKey: icon?.dataset.ovsIconKey,
2841+
iconName: icon?.dataset.ovsIconName
2842+
};
2843+
});
2844+
expect(shapeDeleteIconState).toEqual({
2845+
objectId: "object.mouse.editor",
2846+
title: "Delete this shape",
2847+
iconKey: "delete",
2848+
iconName: "nf-md-trash_can_outline"
2849+
});
27962850
await page.locator("[data-shape-delete-id='line-2']").click();
27972851
await expect(page.locator("[data-object-tile-shape-id='line-2']")).toHaveCount(0);
27982852
await expect(page.locator("[data-object-tile-shape-id='rectangle-1']")).toHaveCount(1);

tools/object-vector-studio-v2/index.html

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -264,11 +264,10 @@ <h2 class="tools-platform-frame__eyebrow">First-Class Tools Surface V2</h2>
264264
<hr class="object-vector-studio-v2__separator">
265265
<div id="objectVectorStudioV2PaletteSummary" class="object-vector-studio-v2__palette-swatch-list" aria-label="Read-only palette swatches"></div>
266266
<div class="object-vector-studio-v2__palette-sort" aria-label="Palette sorting">
267-
<span>Sort</span>
268-
<button type="button" data-palette-sort="hue" aria-pressed="false">hue</button>
269-
<button type="button" data-palette-sort="sat" aria-pressed="false">sat</button>
270-
<button type="button" data-palette-sort="bri" aria-pressed="false">bri</button>
271-
<button type="button" data-palette-sort="name" aria-pressed="true">name</button>
267+
<button type="button" data-palette-sort="hue" aria-pressed="false">Hue</button>
268+
<button type="button" data-palette-sort="sat" aria-pressed="false">Sat</button>
269+
<button type="button" data-palette-sort="bri" aria-pressed="false">Bri</button>
270+
<button type="button" data-palette-sort="name" aria-pressed="true">Name</button>
272271
</div>
273272
</div>
274273
</section>

0 commit comments

Comments
 (0)