@tekyzinc/gsd-t 2.73.22 → 2.73.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,22 @@
2
2
 
3
3
  All notable changes to GSD-T are documented here. Updated with each release.
4
4
 
5
+ ## [2.73.24] - 2026-04-09
6
+
7
+ ### Added
8
+ - **Remove element button** — hover over any component in the list to reveal a red × button. Clicking it excludes the element from review and auto-comments "EXCLUDED — not in Figma design". Excluded count shown in submit stats.
9
+
10
+ ### Fixed
11
+ - **Comment validation removed** — all comments are now accepted (questions, exclusions, feedback). The "don't suggest specific changes" popup no longer blocks submission.
12
+
13
+ ### Changed
14
+ - **Submit stats** show removed count alongside changed/commented.
15
+
16
+ ## [2.73.23] - 2026-04-09
17
+
18
+ ### Fixed
19
+ - **Container props auto-redirect to parent** — setting `gap`, `borderRadius`, or `overflow` on a bar segment (child) now auto-targets the parent flex/grid container. Previously only worked when the container itself was selected.
20
+
5
21
  ## [2.73.22] - 2026-04-09
6
22
 
7
23
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tekyzinc/gsd-t",
3
- "version": "2.73.22",
3
+ "version": "2.73.24",
4
4
  "description": "GSD-T: Contract-Driven Development for Claude Code — 56 slash commands with headless CI/CD mode, graph-powered code analysis, real-time agent dashboard, execution intelligence, task telemetry, doc-ripple enforcement, backlog management, impact analysis, test sync, milestone archival, and PRD generation",
5
5
  "author": "Tekyz, Inc.",
6
6
  "license": "MIT",
@@ -712,8 +712,22 @@
712
712
  const propagate = msg.propagate !== false; // default true
713
713
  let propagatedCount = 0;
714
714
 
715
- // Apply to primary element
716
- lockedEl.style.setProperty(cssName, msg.value, "important");
715
+ // Container-only props: if applied to a non-container child, redirect to parent
716
+ const containerOnlyProps = new Set(["gap", "row-gap", "column-gap", "border-radius"]);
717
+ let targetEl = lockedEl;
718
+ if (containerOnlyProps.has(cssName)) {
719
+ const elStyle = getComputedStyle(lockedEl);
720
+ const isContainer = elStyle.display === "flex" || elStyle.display === "inline-flex" || elStyle.display === "grid";
721
+ if (!isContainer && lockedEl.parentElement) {
722
+ const parentStyle = getComputedStyle(lockedEl.parentElement);
723
+ if (parentStyle.display === "flex" || parentStyle.display === "inline-flex" || parentStyle.display === "grid") {
724
+ targetEl = lockedEl.parentElement;
725
+ }
726
+ }
727
+ }
728
+
729
+ // Apply to target element (may be parent for container-only props)
730
+ targetEl.style.setProperty(cssName, msg.value, "important");
717
731
 
718
732
  // Typography props cascade to descendants
719
733
  const inheritProps = new Set(["font-size", "font-weight", "font-family", "line-height",
@@ -792,22 +806,24 @@
792
806
  } else if (tag === "div" && parent) {
793
807
  const elStyle = getComputedStyle(lockedEl);
794
808
  const parentStyle = getComputedStyle(parent);
795
- const layoutProps = new Set(["gap", "row-gap", "column-gap"]);
796
809
 
797
810
  // Props that propagate across sibling containers (all bar columns)
798
811
  const containerProps = new Set(["gap", "row-gap", "column-gap", "border-radius", "overflow"]);
799
812
 
800
813
  if (containerProps.has(cssName)) {
801
- const elDisplay = elStyle.display;
802
- if (elDisplay === "flex" || elDisplay === "inline-flex" || elDisplay === "grid") {
814
+ // Use targetEl (may be parent if redirected) for container propagation
815
+ const containerEl = targetEl;
816
+ const containerParent = containerEl.parentElement;
817
+ const containerDisplay = getComputedStyle(containerEl).display;
818
+ if (containerParent && (containerDisplay === "flex" || containerDisplay === "inline-flex" || containerDisplay === "grid")) {
803
819
  // Auto-set overflow:hidden when border-radius is applied to a container
804
820
  if (cssName === "border-radius" && msg.value && msg.value !== "0px" && msg.value !== "0") {
805
- lockedEl.style.setProperty("overflow", "hidden", "important");
821
+ containerEl.style.setProperty("overflow", "hidden", "important");
806
822
  }
807
- for (const sib of parent.children) {
808
- if (sib === lockedEl || sib.tagName !== "DIV") continue;
823
+ for (const sib of containerParent.children) {
824
+ if (sib === containerEl || sib.tagName !== "DIV") continue;
809
825
  const sibStyle = getComputedStyle(sib);
810
- if (sibStyle.display === elDisplay) {
826
+ if (sibStyle.display === containerDisplay) {
811
827
  sib.style.setProperty(cssName, msg.value, "important");
812
828
  if (cssName === "border-radius" && msg.value && msg.value !== "0px" && msg.value !== "0") {
813
829
  sib.style.setProperty("overflow", "hidden", "important");
@@ -195,6 +195,12 @@
195
195
 
196
196
  .component-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
197
197
  .component-type { font-size: 10px; color: #94a3b8; }
198
+ .remove-btn {
199
+ display: none; background: none; border: none; color: #ef4444; font-size: 16px;
200
+ cursor: pointer; padding: 0 4px; line-height: 1; opacity: 0.6;
201
+ }
202
+ .remove-btn:hover { opacity: 1; }
203
+ .component-item:hover .remove-btn { display: block; }
198
204
 
199
205
  /* ── Submit bar ─────────────────────────────────── */
200
206
  .submit-bar {
@@ -801,6 +807,7 @@
801
807
  let inspectActive = false;
802
808
  const changes = new Map(); // componentId → [{path, property, oldValue, newValue}]
803
809
  const comments = new Map(); // componentId → string
810
+ const excluded = new Set(); // componentIds removed from review
804
811
  let currentElementPath = null;
805
812
  let currentStyles = null;
806
813
 
@@ -952,14 +959,28 @@
952
959
  statusClass = "rejected";
953
960
  }
954
961
 
962
+ if (excluded.has(item.id)) return; // skip excluded elements
963
+
955
964
  const div = document.createElement("div");
956
965
  div.className = `component-item${idx === selectedIdx ? " selected" : ""}`;
957
966
  div.innerHTML = `
958
967
  <div class="component-status ${statusClass}"></div>
959
968
  <div class="component-name">${item.name || item.id}</div>
960
969
  <div class="component-type">${item.type || ""}</div>
970
+ <button class="remove-btn" title="Remove from review">×</button>
961
971
  `;
962
- div.addEventListener("click", () => selectComponent(idx));
972
+ div.querySelector(".component-name").addEventListener("click", () => selectComponent(idx));
973
+ div.querySelector(".component-status").addEventListener("click", () => selectComponent(idx));
974
+ div.querySelector(".remove-btn").addEventListener("click", (e) => {
975
+ e.stopPropagation();
976
+ excluded.add(item.id);
977
+ comments.set(item.id, "EXCLUDED — not in Figma design");
978
+ if (selectedIdx === idx) {
979
+ selectedIdx = Math.min(idx, filteredQueue.filter(q => !excluded.has(q.id)).length - 1);
980
+ }
981
+ renderComponentList();
982
+ updateSubmitStats();
983
+ });
963
984
  componentList.appendChild(div);
964
985
  });
965
986
  }
@@ -2110,21 +2131,20 @@
2110
2131
  // ── Submit ────────────────────────────────────────
2111
2132
  function updateSubmitStats() {
2112
2133
  const total = queue.length;
2134
+ const excludedCount = excluded.size;
2113
2135
  const changed = Array.from(changes.keys()).filter(id => (changes.get(id) || []).length > 0).length;
2114
- const commented = comments.size;
2115
-
2116
- if (changed > 0 || commented > 0) {
2117
- submitStats.innerHTML = `
2118
- <span><span class="component-status changed" style="display:inline-block"></span> ${changed} changed</span>
2119
- <span><span class="component-status rejected" style="display:inline-block"></span> ${commented} commented</span>
2120
- <span style="color:var(--text-dim)">${total} total</span>
2121
- `;
2122
- } else {
2123
- submitStats.innerHTML = `<span style="color:var(--text-dim)">${total} elements no changes</span>`;
2124
- }
2125
-
2126
- submitAll.textContent = changed > 0 || commented > 0
2127
- ? `Submit (${changed} changes, ${commented} comments)`
2136
+ const commented = Array.from(comments.keys()).filter(id => !excluded.has(id)).length;
2137
+
2138
+ const parts = [];
2139
+ if (changed > 0) parts.push(`<span><span class="component-status changed" style="display:inline-block"></span> ${changed} changed</span>`);
2140
+ if (commented > 0) parts.push(`<span><span class="component-status rejected" style="display:inline-block"></span> ${commented} commented</span>`);
2141
+ if (excludedCount > 0) parts.push(`<span style="color:#ef4444">${excludedCount} removed</span>`);
2142
+ parts.push(`<span style="color:var(--text-dim)">${total - excludedCount} of ${total}</span>`);
2143
+ submitStats.innerHTML = parts.join(" ");
2144
+
2145
+ const actionCount = changed + commented + excludedCount;
2146
+ submitAll.textContent = actionCount > 0
2147
+ ? `Submit (${changed} changes, ${commented} comments, ${excludedCount} removed)`
2128
2148
  : "Submit — Approve All";
2129
2149
  }
2130
2150
 
@@ -2136,28 +2156,7 @@
2136
2156
  else comments.delete(filteredQueue[selectedIdx].id);
2137
2157
  }
2138
2158
 
2139
- // Check for non-actionable comments (documentation, not change requests)
2140
- const actionWords = /change|make|set|move|add|remove|reduce|increase|fix|use|switch|replace|adjust|align|center|should be|needs to|too |bigger|smaller|wider|narrower|thicker|thinner|lighter|darker|bolder|px|rem|%|#[0-9a-f]/i;
2141
- const docComments = [];
2142
- for (const [compId, comment] of comments) {
2143
- if (!actionWords.test(comment)) {
2144
- const item = queue.find(q => q.id === compId);
2145
- docComments.push(item ? item.name : compId);
2146
- }
2147
- }
2148
- if (docComments.length > 0) {
2149
- const proceed = confirm(
2150
- "These comments don't suggest specific changes:\n\n" +
2151
- docComments.map(n => " \u2022 " + n).join("\n") +
2152
- "\n\nComments should describe what to change, e.g.:\n" +
2153
- ' "make padding 8px"\n "use darker blue"\n "reduce gap between title and chart"\n\n' +
2154
- "Non-actionable comments will be discarded.\n\nSubmit anyway?"
2155
- );
2156
- if (!proceed) return;
2157
- for (const [compId, comment] of comments) {
2158
- if (!actionWords.test(comment)) comments.delete(compId);
2159
- }
2160
- }
2159
+ // All comments are accepted — questions, exclusions, and change requests alike
2161
2160
 
2162
2161
  // Build feedback: each element gets its changes and comments
2163
2162
  const feedback = queue.map(item => ({