tux-review 0.1.2 → 0.1.3

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.
@@ -131,14 +131,6 @@
131
131
  }
132
132
  function locate(target) {
133
133
  if (!target) return null;
134
- if (target.tux_id) {
135
- const el = document.querySelector(`[data-tux-id="${cssEscape(target.tux_id)}"]`);
136
- if (el) return el;
137
- }
138
- if (target.test_id) {
139
- const el = document.querySelector(`[data-testid="${cssEscape(target.test_id)}"], [data-test-id="${cssEscape(target.test_id)}"]`);
140
- if (el) return el;
141
- }
142
134
  // Fingerprint validation: when a resolution strategy yields several
143
135
  // candidates (nth-child paths drift after DOM changes), pick the one
144
136
  // that best matches the stored fingerprint — component/instance scope,
@@ -163,23 +155,45 @@
163
155
  }
164
156
  return best;
165
157
  }
166
- if (target.css_selector) {
167
- try {
168
- const nodes = document.querySelectorAll(target.css_selector);
169
- if (nodes.length) return pickBest(nodes);
170
- } catch (e) { /* invalid selector */ }
158
+ function resolveByPath() {
159
+ if (target.css_selector) {
160
+ try {
161
+ const nodes = document.querySelectorAll(target.css_selector);
162
+ if (nodes.length) return pickBest(nodes);
163
+ } catch (e) { /* invalid selector */ }
164
+ }
165
+ if (target.dom_path) {
166
+ try {
167
+ const snapshot = document.evaluate(target.dom_path, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
168
+ if (snapshot.snapshotLength) {
169
+ const nodes = [];
170
+ for (let i = 0; i < snapshot.snapshotLength; i++) nodes.push(snapshot.snapshotItem(i));
171
+ return pickBest(nodes);
172
+ }
173
+ } catch (e) { /* invalid path */ }
174
+ }
175
+ return null;
171
176
  }
172
- if (target.dom_path) {
173
- try {
174
- const snapshot = document.evaluate(target.dom_path, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
175
- if (snapshot.snapshotLength) {
176
- const nodes = [];
177
- for (let i = 0; i < snapshot.snapshotLength; i++) nodes.push(snapshot.snapshotItem(i));
178
- return pickBest(nodes);
179
- }
180
- } catch (e) { /* invalid path */ }
177
+
178
+ const byTest = target.test_id
179
+ ? document.querySelector(`[data-testid="${cssEscape(target.test_id)}"], [data-test-id="${cssEscape(target.test_id)}"]`)
180
+ : null;
181
+ if (byTest) return byTest; // explicit test hook: exact element (SPC 16)
182
+
183
+ const byPath = resolveByPath();
184
+ const byId = target.tux_id
185
+ ? document.querySelector(`[data-tux-id="${cssEscape(target.tux_id)}"]`)
186
+ : null;
187
+
188
+ // A tux_id on a CONTAINER must not coarsen the target: when the path
189
+ // resolves to an element inside the id element, the path is the finer,
190
+ // correct target. Only trust the id over a path that drifted outside
191
+ // of it (SPC 16: multiple independent signals, finest wins).
192
+ if (byId) {
193
+ if (byPath && (byId === byPath || byId.contains(byPath))) return byPath;
194
+ return byId;
181
195
  }
182
- return null;
196
+ return byPath;
183
197
  }
184
198
 
185
199
  // ─── route + state tracking (SPC 13–15, 59) ───
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tux-review",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "TUX - TUX UIX review system: attach structured, machine-readable feedback directly to running web UIs (pages, routes, components, elements, states) with a deterministic CLI, canonical JSON and LLM-friendly skills.",
5
5
  "license": "MIT",
6
6
  "author": {