Skip to content

Commit bf8c710

Browse files
author
DavidQ
committed
Clean up Object panel tag layout and selected geometry summary display - PR_26133_014-object-panel-and-geometry-summary-cleanup
1 parent c7f0108 commit bf8c710

6 files changed

Lines changed: 94 additions & 52 deletions

File tree

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# PR_26133_013 Playwright V8 Coverage Report
1+
# PR_26133_014 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,13 +7,12 @@ 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`: 92%, executed lines 3689/3689, executed functions 393/426.
11-
- `tools/object-vector-studio-v2/js/bootstrap.js`: 80%, executed lines 102/102, executed functions 4/5.
10+
- `tools/object-vector-studio-v2/js/ToolStarterApp.js`: 92%, executed lines 3701/3701, executed functions 395/428.
1211
- The generated report lists `tests/playwright/tools/WorkspaceManagerV2.spec.mjs` as changed JS not collected by browser runtime coverage.
1312

1413
## Validation Context
1514

1615
- Main command: `npm run test:workspace-v2`.
1716
- Result: 47 passed.
18-
- Focused Object Vector Studio V2 layout, preview coordinate, mouse-editing, and asset-authoring scenarios passed.
19-
- Coverage includes the runtime paths for transform summary updates, negative snapped movement, compact preview controls, Up/Down panning, larger center dot rendering, preview mouse edits, polygon X/Y point-list geometry editing, invalid point rejection, and two-row inline ellipse fields. The compact Add/Rename/Dup/Delete object action row is covered by Playwright layout assertions.
17+
- Focused Object Vector Studio V2 layout, preview coordinate, animation-state, and asset-authoring scenarios passed.
18+
- Coverage includes the runtime paths for cleaned Object Details summary rendering and explicit selected-shape color labeling. Tag-row layout, Polygon Geometry spacing, and removal of duplicate/helper text are covered by Playwright layout/text assertions.

docs/dev/reports/playwright_workspace_v2_results.md

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,21 @@
1-
# PR_26133_013 Workspace V2 Results
1+
# PR_26133_014 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-
- `npx playwright test tests/playwright/tools/WorkspaceManagerV2.spec.mjs --grep "Object Vector Studio V2 (layout shell|preview coordinates|preview shapes with mouse actions|asset authoring controls)"`: 4 passed.
7+
- `npx playwright test tests/playwright/tools/WorkspaceManagerV2.spec.mjs --grep "Object Vector Studio V2 (layout shell|preview coordinates|animation states|asset authoring controls)"`: 4 passed.
88
- `npm run test:workspace-v2`: 47 passed.
99
- `git diff --check`: passed with LF-to-CRLF working-copy warnings for touched files.
1010

1111
## Targeted Object Vector Studio V2 Verification
1212

13-
- Confirmed negative snapped Move X/Move Y values move the selected shape back across both axes instead of snapping half-step negatives to zero.
14-
- Confirmed Object Transform summary renders as `Transform x 0, y 0, rot 0, scale 1 x 1` and updates after transform actions.
15-
- Confirmed Object Preview controls use compact final-word labels and include working Up/Down pan controls after In.
16-
- Confirmed Object actions render as a single compact Add/Rename/Dup/Delete row.
17-
- Confirmed the center origin dot renders at radius 9.
18-
- Confirmed preview mouse drag, negative drag, rectangle handle resize, and line endpoint movement still edit the selected shape.
19-
- Confirmed Polygon Geometry renders separate X/Y point inputs, applies valid edits, marks invalid point cells with `aria-invalid`, and preserves invalid text without partial apply.
20-
- Confirmed the polygon point list uses a fixed scrolling area.
21-
- Confirmed Ellipse Geometry renders as two inline label/input rows: Cx/Cy, then Rx/Ry.
22-
- Confirmed Object Preview coordinate/grid/zoom expectations from prior PRs remain covered by the workspace-v2 suite.
13+
- Confirmed Object Tag input and Add button render inline with no visible `Tag` label text.
14+
- Confirmed Polygon Geometry section spacing is reduced to 5px, including section gap, point-list gap, and heading margins.
15+
- Confirmed Object Details no longer renders the helper text or duplicate `Selected Shape` summary text.
16+
- Confirmed Object Details keeps concise shape/group metadata without the duplicate selected-shape heading.
17+
- Confirmed selected shape color is labeled as `Fill Color`, `Stroke Color`, or `Transparent Color` so transparent/background state is not confused with the selected color.
18+
- Confirmed Object Vector Studio V2 geometry editing and animation detail states remain covered by the targeted workspace-v2 slice.
2319
- Confirmed targeted Object Vector Studio tests reported no console/page errors.
2420

2521
## Scope Checks

tests/playwright/tools/WorkspaceManagerV2.spec.mjs

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1218,21 +1218,26 @@ test.describe("Workspace Manager V2 bootstrap", () => {
12181218
const objectPanelLayout = await page.locator("#objectVectorStudioV2ObjectContent").evaluate((element) => {
12191219
const nameLabel = element.querySelector("label[for='objectVectorStudioV2ObjectNameInput'] span");
12201220
const nameInput = element.querySelector("#objectVectorStudioV2ObjectNameInput");
1221-
const tagLabel = element.querySelector("label[for='objectVectorStudioV2ObjectTagInput'] span");
12221221
const tagInput = element.querySelector("#objectVectorStudioV2ObjectTagInput");
1222+
const tagButton = element.querySelector("#objectVectorStudioV2AddTagButton");
12231223
const actions = element.querySelector(".object-vector-studio-v2__object-actions");
12241224
const actionButtons = Array.from(actions.querySelectorAll("button"));
12251225
const actionButtonRects = actionButtons.map((button) => button.getBoundingClientRect());
1226+
const tagInputRect = tagInput.getBoundingClientRect();
1227+
const tagButtonRect = tagButton.getBoundingClientRect();
12261228
return {
12271229
actionButtonLabels: actionButtons.map((button) => button.textContent.trim()),
12281230
actionButtonMaxHeight: Math.max(...actionButtonRects.map((rect) => Math.round(rect.height))),
12291231
actionsSingleLine: actionButtonRects.every((rect) => Math.abs(rect.top - actionButtonRects[0].top) < 2),
12301232
actionsAtBottom: actions.getBoundingClientRect().bottom >= element.getBoundingClientRect().bottom - 12,
12311233
nameInline: Math.abs((nameLabel.getBoundingClientRect().top + nameLabel.getBoundingClientRect().height / 2) - (nameInput.getBoundingClientRect().top + nameInput.getBoundingClientRect().height / 2)) < 4,
1232-
tagInline: Math.abs((tagLabel.getBoundingClientRect().top + tagLabel.getBoundingClientRect().height / 2) - (tagInput.getBoundingClientRect().top + tagInput.getBoundingClientRect().height / 2)) < 4
1234+
noVisibleTagLabel: element.querySelector("label[for='objectVectorStudioV2ObjectTagInput']") === null,
1235+
tagAddText: tagButton.textContent.trim(),
1236+
tagAriaLabel: tagInput.getAttribute("aria-label"),
1237+
tagInline: Math.abs((tagInputRect.top + tagInputRect.height / 2) - (tagButtonRect.top + tagButtonRect.height / 2)) < 4 && tagInputRect.right <= tagButtonRect.left
12331238
};
12341239
});
1235-
expect(objectPanelLayout).toEqual({ actionButtonLabels: ["Add", "Rename", "Dup", "Delete"], actionButtonMaxHeight: 28, actionsAtBottom: true, actionsSingleLine: true, nameInline: true, tagInline: true });
1240+
expect(objectPanelLayout).toEqual({ actionButtonLabels: ["Add", "Rename", "Dup", "Delete"], actionButtonMaxHeight: 28, actionsAtBottom: true, actionsSingleLine: true, nameInline: true, noVisibleTagLabel: true, tagAddText: "Add", tagAriaLabel: "Object tag", tagInline: true });
12361241
await page.locator("#objectVectorStudioV2ObjectNameInput").fill("Blocked Object");
12371242
await page.locator("#objectVectorStudioV2AddObjectButton").click();
12381243
await expect(page.locator("#statusLog")).toHaveValue(/FAIL Add object blocked: load a schema-valid Object Vector Studio V2 payload before adding objects\./);
@@ -1550,34 +1555,37 @@ test.describe("Workspace Manager V2 bootstrap", () => {
15501555
await expect(page.locator(".object-vector-studio-v2__object-tile[data-object-id='object.asteroids.object-1'] [data-object-tile-shape-id='rectangle-1']")).toHaveAttribute("aria-pressed", "true");
15511556
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"selectedShape"');
15521557
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"type": "rectangle"');
1553-
await expect(page.locator("#objectVectorStudioV2ObjectDetails")).toContainText("Editable fields below are limited to schema-valid geometry");
1554-
await expect(page.locator("#objectVectorStudioV2ObjectDetails")).toContainText("Selected Shaperectangle-1 (rectangle)");
1558+
await expect(page.locator("#objectVectorStudioV2ObjectDetails")).not.toContainText("Editable fields below are limited to schema-valid geometry");
1559+
await expect(page.locator("#objectVectorStudioV2ObjectDetails")).not.toContainText("Selected Shape");
1560+
await expect(page.locator("#objectVectorStudioV2ObjectDetails")).toContainText("Shaperectangle-1 (rectangle)");
15551561
await expect(page.locator("#objectVectorStudioV2ObjectDetails")).not.toContainText("Transform");
15561562
await expect(page.locator("#objectVectorStudioV2ObjectDetails #objectVectorStudioV2MoveShapeButton")).toHaveCount(0);
15571563
await expect(page.locator("#objectVectorStudioV2ObjectTransform")).toContainText("Selected Shape: rectangle-1");
15581564
await expect(page.locator("#objectVectorStudioV2ObjectTransform .object-vector-studio-v2__transform-summary")).toHaveText("Transform x 0, y 0, rot 0, scale 1 x 1");
15591565
await expect(page.locator("#objectVectorStudioV2ObjectTransform #objectVectorStudioV2MoveShapeButton")).toHaveCount(1);
15601566
const objectDetailsOrder = await page.locator("#objectVectorStudioV2ObjectDetails").evaluate((details) => {
15611567
const applyButton = details.querySelector("#objectVectorStudioV2ApplyGeometryButton");
1562-
const summaryHeading = Array.from(details.querySelectorAll("h3")).find((heading) => heading.textContent.trim() === "Selected Shape: rectangle-1");
1568+
const summaryGrid = details.querySelector(".object-vector-studio-v2__shape-panel .object-vector-studio-v2__object-detail-grid");
15631569
return {
1564-
applyBeforeSummary: Boolean(applyButton && summaryHeading && (applyButton.compareDocumentPosition(summaryHeading) & Node.DOCUMENT_POSITION_FOLLOWING)),
1565-
summaryItems: Array.from(details.querySelectorAll("h4, #objectVectorStudioV2ApplyGeometryButton, h3, .object-vector-studio-v2__detail-label, .object-vector-studio-v2__detail-value, .tool-starter__hint"))
1566-
.map((element) => element.textContent.trim())
1570+
applyBeforeSummary: Boolean(applyButton && summaryGrid && (applyButton.compareDocumentPosition(summaryGrid) & Node.DOCUMENT_POSITION_FOLLOWING)),
1571+
noHelperText: !details.textContent.includes("Editable fields below"),
1572+
noSelectedShapeText: !details.textContent.includes("Selected Shape"),
1573+
summaryItems: Array.from(details.querySelectorAll("h4, #objectVectorStudioV2ApplyGeometryButton, .object-vector-studio-v2__detail-label, .object-vector-studio-v2__detail-value"))
1574+
.map((element) => element.textContent.trim()),
15671575
};
15681576
});
15691577
expect(objectDetailsOrder.applyBeforeSummary).toBe(true);
1578+
expect(objectDetailsOrder.noHelperText).toBe(true);
1579+
expect(objectDetailsOrder.noSelectedShapeText).toBe(true);
15701580
expect(objectDetailsOrder.summaryItems).toEqual([
15711581
"Rectangle Geometry",
15721582
"Apply Geometry",
1573-
"Selected Shape: rectangle-1",
1574-
"Selected Shape",
1583+
"Shape",
15751584
"rectangle-1 (rectangle)",
15761585
"Group",
15771586
"None",
1578-
"Color",
1579-
"#ffffff",
1580-
"Editable fields below are limited to schema-valid geometry fields for the selected shape."
1587+
"Fill Color",
1588+
"#ffffff"
15811589
]);
15821590
const transformFieldLayout = await page.locator("#objectVectorStudioV2ObjectTransform").evaluate((panel) => (
15831591
Array.from(panel.querySelectorAll(".object-vector-studio-v2__edit-field--inline")).map((field) => {
@@ -2272,10 +2280,14 @@ test.describe("Workspace Manager V2 bootstrap", () => {
22722280
{ label: "Point 4", x: "-14", y: "16" }
22732281
]);
22742282
const polygonPointListLayout = await page.locator("#objectVectorStudioV2ObjectDetails .object-vector-studio-v2__polygon-point-list").evaluate((list) => ({
2283+
headingMarginBottom: Number.parseFloat(getComputedStyle(list.previousElementSibling).marginBottom),
2284+
headingMarginTop: Number.parseFloat(getComputedStyle(list.previousElementSibling).marginTop),
2285+
listGap: Number.parseFloat(getComputedStyle(list).gap),
22752286
maxHeight: Number.parseFloat(getComputedStyle(list).maxHeight),
2276-
overflowY: getComputedStyle(list).overflowY
2287+
overflowY: getComputedStyle(list).overflowY,
2288+
sectionGap: Number.parseFloat(getComputedStyle(list.closest(".object-vector-studio-v2__edit-panel--polygon")).gap)
22772289
}));
2278-
expect(polygonPointListLayout).toEqual({ maxHeight: 138, overflowY: "auto" });
2290+
expect(polygonPointListLayout).toEqual({ headingMarginBottom: 0, headingMarginTop: 0, listGap: 5, maxHeight: 138, overflowY: "auto", sectionGap: 5 });
22792291
await page.locator("#objectVectorStudioV2ObjectDetails [data-polygon-point-index='1'][data-polygon-point-axis='y']").fill("17");
22802292
await page.locator("#objectVectorStudioV2ApplyGeometryButton").click();
22812293
await expect(page.locator("#objectVectorStudioV2RenderSurface [data-shape-id='asteroids-ship-outline']")).toHaveAttribute("points", "0,-180 140,170 0,80 -140,160");
@@ -2791,7 +2803,7 @@ test.describe("Workspace Manager V2 bootstrap", () => {
27912803
await expect(page.locator("#objectVectorStudioV2FrameTimeline [data-state-id='idle']")).toHaveCount(1);
27922804
await expect(page.locator("#objectVectorStudioV2FrameTimeline [data-frame-id='idle-frame-1']")).toHaveAttribute("aria-pressed", "true");
27932805
await expect(page.locator("#objectVectorStudioV2JsonDetails")).toContainText('"id": "idle"');
2794-
await expect(page.locator("#objectVectorStudioV2ObjectDetails")).toContainText("Selected Shape");
2806+
await expect(page.locator("#objectVectorStudioV2ObjectDetails")).not.toContainText("Selected Shape");
27952807
await expect(page.locator("#objectVectorStudioV2ObjectDetails")).toContainText("ship-template-hull");
27962808

27972809
await page.locator("#objectVectorStudioV2DuplicateFrameButton").click();

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

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,8 @@ <h2 class="tools-platform-frame__eyebrow">First-Class Tools Surface V2</h2>
6161
<input id="objectVectorStudioV2ObjectNameInput" type="text" autocomplete="off" placeholder="Object name">
6262
</label>
6363
<div class="object-vector-studio-v2__tag-editor" aria-label="Object tags">
64-
<label class="tool-starter__field tool-starter__field--compact" for="objectVectorStudioV2ObjectTagInput">
65-
<span>Tag</span>
66-
<input id="objectVectorStudioV2ObjectTagInput" type="text" autocomplete="off" placeholder="Add tag">
67-
</label>
68-
<button id="objectVectorStudioV2AddTagButton" type="button">Add Tag</button>
64+
<input id="objectVectorStudioV2ObjectTagInput" type="text" autocomplete="off" placeholder="Add tag" aria-label="Object tag">
65+
<button id="objectVectorStudioV2AddTagButton" type="button" title="Add tag">Add</button>
6966
<div id="objectVectorStudioV2ObjectTagList" class="object-vector-studio-v2__tag-list" aria-label="Selected object tags"></div>
7067
</div>
7168
<div class="object-vector-studio-v2__object-actions" aria-label="Object actions">

tools/object-vector-studio-v2/js/ToolStarterApp.js

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1323,11 +1323,9 @@ export class ToolStarterApp {
13231323
if (!shape) {
13241324
const shapePanel = document.createElement("section");
13251325
shapePanel.className = "object-vector-studio-v2__shape-panel";
1326-
const heading = document.createElement("h3");
1327-
heading.textContent = "Selected Shape";
13281326
const empty = document.createElement("p");
13291327
empty.textContent = "No shape selected. Create a primitive from Shape/Tools.";
1330-
shapePanel.append(heading, empty);
1328+
shapePanel.append(empty);
13311329
wrapper.append(shapePanel);
13321330
return wrapper;
13331331
}
@@ -1339,19 +1337,32 @@ export class ToolStarterApp {
13391337
createSelectedShapeSummary(shape) {
13401338
const shapePanel = document.createElement("section");
13411339
shapePanel.className = "object-vector-studio-v2__shape-panel";
1342-
const heading = document.createElement("h3");
1343-
heading.textContent = `Selected Shape: ${shape.id}`;
1344-
const editableHint = document.createElement("p");
1345-
editableHint.className = "tool-starter__hint";
1346-
editableHint.textContent = "Editable fields below are limited to schema-valid geometry fields for the selected shape.";
1347-
shapePanel.append(heading, this.createDetailGrid([
1348-
["Selected Shape", `${shape.id} (${shape.type})`],
1340+
const [colorLabel, colorValue] = this.selectedShapeColorEntry(shape);
1341+
shapePanel.append(this.createDetailGrid([
1342+
["Shape", `${shape.id} (${shape.type})`],
13491343
["Group", shape.groupId || "None"],
1350-
["Color", shape.style.fill === "none" ? shape.style.stroke : shape.style.fill]
1351-
]), editableHint);
1344+
[colorLabel, colorValue]
1345+
]));
13521346
return shapePanel;
13531347
}
13541348

1349+
selectedShapeColorEntry(shape) {
1350+
const fill = String(shape.style?.fill || "").trim();
1351+
const stroke = String(shape.style?.stroke || "").trim();
1352+
if (fill && !this.isTransparentColor(fill)) {
1353+
return ["Fill Color", fill];
1354+
}
1355+
if (stroke && !this.isTransparentColor(stroke)) {
1356+
return ["Stroke Color", stroke];
1357+
}
1358+
return ["Transparent Color", fill || stroke || "none"];
1359+
}
1360+
1361+
isTransparentColor(color) {
1362+
const normalized = String(color || "").trim().toLowerCase();
1363+
return !normalized || normalized === "none" || normalized === "transparent" || /^rgba\([^)]*,\s*0(?:\.0+)?\)$/u.test(normalized);
1364+
}
1365+
13551366
createObjectTransformDetails(shape) {
13561367
const wrapper = document.createElement("div");
13571368
wrapper.className = "object-vector-studio-v2__object-detail-stack";
@@ -1398,6 +1409,9 @@ export class ToolStarterApp {
13981409
createShapeGeometryControls(shape) {
13991410
const section = document.createElement("section");
14001411
section.className = "object-vector-studio-v2__edit-panel";
1412+
if (shape.type === "polygon") {
1413+
section.classList.add("object-vector-studio-v2__edit-panel--polygon");
1414+
}
14011415
const heading = document.createElement("h4");
14021416
heading.textContent = `${shapeTypeLabel(shape)} Geometry`;
14031417
const grid = document.createElement("div");

tools/object-vector-studio-v2/styles/toolStarter.css

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1438,12 +1438,20 @@ textarea:hover {
14381438

14391439
.object-vector-studio-v2__polygon-point-list {
14401440
display: grid;
1441-
gap: 6px;
1441+
gap: 5px;
14421442
max-height: 138px;
14431443
overflow-y: auto;
14441444
padding-right: 2px;
14451445
}
14461446

1447+
.object-vector-studio-v2__edit-panel--polygon {
1448+
gap: 5px;
1449+
}
1450+
1451+
.object-vector-studio-v2__edit-panel--polygon h4 {
1452+
margin: 0;
1453+
}
1454+
14471455
.object-vector-studio-v2__edit-grid--ellipse {
14481456
grid-template-columns: repeat(2, minmax(0, 1fr));
14491457
}
@@ -1580,7 +1588,23 @@ textarea:hover {
15801588
gap: 8px;
15811589
}
15821590

1591+
.object-vector-studio-v2__tag-editor {
1592+
grid-template-columns: minmax(0, 1fr) auto;
1593+
align-items: center;
1594+
gap: 5px;
1595+
}
1596+
1597+
.object-vector-studio-v2__tag-editor input {
1598+
width: 100%;
1599+
}
1600+
1601+
#objectVectorStudioV2AddTagButton {
1602+
min-height: 28px;
1603+
padding: 3px 7px !important;
1604+
}
1605+
15831606
.object-vector-studio-v2__tag-list {
1607+
grid-column: 1 / -1;
15841608
grid-template-columns: repeat(auto-fit, minmax(72px, 1fr));
15851609
}
15861610

0 commit comments

Comments
 (0)