made-refine 0.2.11 → 0.2.15

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/dist/index.js CHANGED
@@ -197,6 +197,225 @@ function getZoomScale() {
197
197
  return snap.active ? snap.zoom : 1;
198
198
  }
199
199
 
200
+ // src/utils/react-fiber.ts
201
+ function getFiberForElement(element) {
202
+ if (typeof window !== "undefined") {
203
+ const devtools = window.__DIRECT_EDIT_DEVTOOLS__;
204
+ if (devtools?.getFiberForElement) {
205
+ const fiber = devtools.getFiberForElement(element);
206
+ if (fiber) return fiber;
207
+ }
208
+ }
209
+ const fiberKey = Object.keys(element).find(
210
+ (key) => key.startsWith("__reactFiber$") || key.startsWith("__reactInternalInstance$")
211
+ );
212
+ if (!fiberKey) return null;
213
+ return element[fiberKey] || null;
214
+ }
215
+ function getSourceFromFiber(fiber) {
216
+ const debugSource = fiber?._debugSource;
217
+ if (debugSource?.fileName) return debugSource;
218
+ const owner = fiber?._debugOwner;
219
+ const ownerPending = owner?.pendingProps?.__source;
220
+ if (ownerPending?.fileName) return ownerPending;
221
+ const ownerMemo = owner?.memoizedProps?.__source;
222
+ if (ownerMemo?.fileName) return ownerMemo;
223
+ const pending = fiber?.pendingProps?.__source;
224
+ if (pending?.fileName) return pending;
225
+ const memo = fiber?.memoizedProps?.__source;
226
+ if (memo?.fileName) return memo;
227
+ return null;
228
+ }
229
+ function buildFrame(fiber) {
230
+ const type = fiber?.type;
231
+ if (typeof type !== "function" && typeof type !== "object") return null;
232
+ const name = type?.displayName || type?.name || null;
233
+ if (!name || name === "Fragment") return null;
234
+ const frame = { name };
235
+ const source = getSourceFromFiber(fiber);
236
+ if (source?.fileName) {
237
+ frame.file = source.fileName;
238
+ if (typeof source.lineNumber === "number") {
239
+ frame.line = source.lineNumber;
240
+ }
241
+ if (typeof source.columnNumber === "number") {
242
+ frame.column = source.columnNumber;
243
+ }
244
+ }
245
+ return frame;
246
+ }
247
+ function shouldIncludeFrame(frame, lastFrame) {
248
+ if (!lastFrame) return true;
249
+ if (frame.name !== lastFrame.name) return true;
250
+ if (!lastFrame.file && frame.file) return true;
251
+ if (lastFrame.file && frame.file && lastFrame.line == null && frame.line != null) return true;
252
+ if (lastFrame.file && frame.file && lastFrame.line != null && frame.line != null && lastFrame.column == null && frame.column != null) {
253
+ return true;
254
+ }
255
+ return false;
256
+ }
257
+ function getOwnerStack(fiber) {
258
+ const frames = [];
259
+ let current = fiber;
260
+ let lastFrame = null;
261
+ let nearestComponentFiber = null;
262
+ while (current) {
263
+ const frame = buildFrame(current);
264
+ if (frame && shouldIncludeFrame(frame, lastFrame)) {
265
+ frames.push(frame);
266
+ lastFrame = frame;
267
+ if (!nearestComponentFiber) {
268
+ nearestComponentFiber = current;
269
+ }
270
+ }
271
+ current = current._debugOwner;
272
+ }
273
+ return { frames, nearestComponentFiber };
274
+ }
275
+ function getRenderStack(fiber) {
276
+ const frames = [];
277
+ let current = fiber;
278
+ let lastFrame = null;
279
+ let nearestComponentFiber = null;
280
+ while (current) {
281
+ const frame = buildFrame(current);
282
+ if (frame && shouldIncludeFrame(frame, lastFrame)) {
283
+ frames.push(frame);
284
+ lastFrame = frame;
285
+ if (!nearestComponentFiber) {
286
+ nearestComponentFiber = current;
287
+ }
288
+ }
289
+ current = current.return;
290
+ }
291
+ return { frames, nearestComponentFiber };
292
+ }
293
+ function getReactComponentInfo(element) {
294
+ const fiber = getFiberForElement(element);
295
+ if (!fiber) return { frames: [], nearestComponentFiber: null };
296
+ const elementSource = getSourceFromFiber(fiber);
297
+ const elementSourceFile = elementSource?.fileName || void 0;
298
+ const ownerResult = getOwnerStack(fiber);
299
+ if (ownerResult.frames.length > 0) {
300
+ return { ...ownerResult, elementSourceFile };
301
+ }
302
+ return { ...getRenderStack(fiber), elementSourceFile };
303
+ }
304
+ var EXCLUDED_PROP_KEYS = /* @__PURE__ */ new Set([
305
+ "className",
306
+ "style",
307
+ "children",
308
+ "ref",
309
+ "key",
310
+ "render"
311
+ ]);
312
+ function serializePropValue(value) {
313
+ if (typeof value === "function") return "[function]";
314
+ if (typeof value === "symbol") return void 0;
315
+ if (value === void 0) return void 0;
316
+ if (value !== null && typeof value === "object") {
317
+ if ("$$typeof" in value) return "[element]";
318
+ try {
319
+ JSON.stringify(value);
320
+ return value;
321
+ } catch {
322
+ return "[object]";
323
+ }
324
+ }
325
+ return value;
326
+ }
327
+ function getComponentProps(fiber) {
328
+ const props = fiber?.memoizedProps ?? fiber?.pendingProps;
329
+ if (!props || typeof props !== "object") return {};
330
+ const result = {};
331
+ for (const [key, value] of Object.entries(props)) {
332
+ if (EXCLUDED_PROP_KEYS.has(key)) continue;
333
+ if (key.startsWith("data-")) continue;
334
+ const serialized = serializePropValue(value);
335
+ if (serialized !== void 0) {
336
+ result[key] = serialized;
337
+ }
338
+ }
339
+ return result;
340
+ }
341
+ function getCallSiteSource(fiber) {
342
+ const source = fiber?._debugSource;
343
+ if (source?.fileName) {
344
+ return {
345
+ file: source.fileName,
346
+ line: typeof source.lineNumber === "number" ? source.lineNumber : void 0,
347
+ column: typeof source.columnNumber === "number" ? source.columnNumber : void 0
348
+ };
349
+ }
350
+ const pending = fiber?.pendingProps?.__source;
351
+ if (pending?.fileName) {
352
+ return {
353
+ file: pending.fileName,
354
+ line: typeof pending.lineNumber === "number" ? pending.lineNumber : void 0,
355
+ column: typeof pending.columnNumber === "number" ? pending.columnNumber : void 0
356
+ };
357
+ }
358
+ return null;
359
+ }
360
+ function deriveDefinitionSource(frames) {
361
+ for (const frame of frames) {
362
+ if (frame.file && isComponentPrimitivePath(frame.file)) {
363
+ return { file: frame.file, line: frame.line, column: frame.column };
364
+ }
365
+ }
366
+ return null;
367
+ }
368
+ var PRIMITIVE_PATH_PATTERNS = [
369
+ /\/components\/ui\//,
370
+ /\/ui\/primitives\//,
371
+ /\/design-system\//
372
+ ];
373
+ var PRIMITIVE_NPM_PATTERNS = [
374
+ /@base-ui\//,
375
+ /@radix-ui\//,
376
+ /@headlessui\//,
377
+ /@chakra-ui\//,
378
+ /@mantine\//,
379
+ /@mui\//,
380
+ /@ark-ui\//
381
+ ];
382
+ var FRAMEWORK_EXCLUSION_PATTERNS = [
383
+ /node_modules\/react\//,
384
+ /node_modules\/react-dom\//,
385
+ /node_modules\/next\/dist\//,
386
+ /node_modules\/scheduler\//,
387
+ /node_modules\/react-server\//
388
+ ];
389
+ function isComponentPrimitivePath(filePath) {
390
+ const normalized = filePath.replace(/\\/g, "/");
391
+ for (const pattern of FRAMEWORK_EXCLUSION_PATTERNS) {
392
+ if (pattern.test(normalized)) return false;
393
+ }
394
+ for (const pattern of PRIMITIVE_NPM_PATTERNS) {
395
+ if (pattern.test(normalized)) return true;
396
+ }
397
+ for (const pattern of PRIMITIVE_PATH_PATTERNS) {
398
+ if (pattern.test(normalized)) return true;
399
+ }
400
+ return false;
401
+ }
402
+ function classifyComponentFiber(fiber, frames, elementSourceFile) {
403
+ if (elementSourceFile && isComponentPrimitivePath(elementSourceFile)) {
404
+ return { isComponentPrimitive: true };
405
+ }
406
+ if (!fiber) return { isComponentPrimitive: false };
407
+ const callSite = getCallSiteSource(fiber);
408
+ if (callSite?.file && isComponentPrimitivePath(callSite.file)) {
409
+ return { isComponentPrimitive: true };
410
+ }
411
+ for (const frame of frames) {
412
+ if (frame.file && isComponentPrimitivePath(frame.file)) {
413
+ return { isComponentPrimitive: true };
414
+ }
415
+ }
416
+ return { isComponentPrimitive: false };
417
+ }
418
+
200
419
  // src/utils.ts
201
420
  function clamp(value, min, max) {
202
421
  if (!Number.isFinite(value)) return min;
@@ -1584,7 +1803,7 @@ function calculateDropPosition(container, pointerX, pointerY, draggedElement) {
1584
1803
  };
1585
1804
  return { insertBefore, indicator };
1586
1805
  }
1587
- function getFiberForElement(element) {
1806
+ function getFiberForElement2(element) {
1588
1807
  if (typeof window !== "undefined") {
1589
1808
  const devtools = window.__DIRECT_EDIT_DEVTOOLS__;
1590
1809
  if (devtools?.getFiberForElement) {
@@ -1845,7 +2064,7 @@ function getSourceFromDebugStack(fiber) {
1845
2064
  }
1846
2065
  return null;
1847
2066
  }
1848
- function getSourceFromFiber(fiber) {
2067
+ function getSourceFromFiber2(fiber) {
1849
2068
  const debugSource = fiber?._debugSource;
1850
2069
  if (debugSource?.fileName) return debugSource;
1851
2070
  const owner = fiber?._debugOwner;
@@ -1861,13 +2080,13 @@ function getSourceFromFiber(fiber) {
1861
2080
  if (fromDebugStack?.fileName) return fromDebugStack;
1862
2081
  return null;
1863
2082
  }
1864
- function buildFrame(fiber) {
2083
+ function buildFrame2(fiber) {
1865
2084
  const type = fiber?.type;
1866
2085
  if (typeof type !== "function" && typeof type !== "object") return null;
1867
2086
  const name = type?.displayName || type?.name || null;
1868
2087
  if (!name || name === "Fragment") return null;
1869
2088
  const frame = { name };
1870
- const source = getSourceFromFiber(fiber);
2089
+ const source = getSourceFromFiber2(fiber);
1871
2090
  if (source?.fileName) {
1872
2091
  frame.file = source.fileName;
1873
2092
  if (typeof source.lineNumber === "number") {
@@ -1879,7 +2098,7 @@ function buildFrame(fiber) {
1879
2098
  }
1880
2099
  return frame;
1881
2100
  }
1882
- function shouldIncludeFrame(frame, lastFrame) {
2101
+ function shouldIncludeFrame2(frame, lastFrame) {
1883
2102
  if (!lastFrame) return true;
1884
2103
  if (frame.name !== lastFrame.name) return true;
1885
2104
  if (!lastFrame.file && frame.file) return true;
@@ -1889,42 +2108,52 @@ function shouldIncludeFrame(frame, lastFrame) {
1889
2108
  }
1890
2109
  return false;
1891
2110
  }
1892
- function getOwnerStack(fiber) {
2111
+ function getOwnerStack2(fiber) {
1893
2112
  const frames = [];
1894
2113
  let current = fiber;
1895
2114
  let lastFrame = null;
2115
+ let nearestComponentFiber = null;
1896
2116
  while (current) {
1897
- const frame = buildFrame(current);
1898
- if (frame && shouldIncludeFrame(frame, lastFrame)) {
2117
+ const frame = buildFrame2(current);
2118
+ if (frame && shouldIncludeFrame2(frame, lastFrame)) {
1899
2119
  frames.push(frame);
1900
2120
  lastFrame = frame;
2121
+ if (!nearestComponentFiber) {
2122
+ nearestComponentFiber = current;
2123
+ }
1901
2124
  }
1902
2125
  current = current._debugOwner;
1903
2126
  }
1904
- return frames;
2127
+ return { frames, nearestComponentFiber };
1905
2128
  }
1906
- function getRenderStack(fiber) {
2129
+ function getRenderStack2(fiber) {
1907
2130
  const frames = [];
1908
2131
  let current = fiber;
1909
2132
  let lastFrame = null;
2133
+ let nearestComponentFiber = null;
1910
2134
  while (current) {
1911
- const frame = buildFrame(current);
1912
- if (frame && shouldIncludeFrame(frame, lastFrame)) {
2135
+ const frame = buildFrame2(current);
2136
+ if (frame && shouldIncludeFrame2(frame, lastFrame)) {
1913
2137
  frames.push(frame);
1914
2138
  lastFrame = frame;
2139
+ if (!nearestComponentFiber) {
2140
+ nearestComponentFiber = current;
2141
+ }
1915
2142
  }
1916
2143
  current = current.return;
1917
2144
  }
1918
- return frames;
2145
+ return { frames, nearestComponentFiber };
1919
2146
  }
1920
- function getReactComponentStack(element) {
1921
- const fiber = getFiberForElement(element);
1922
- if (!fiber) return [];
1923
- const ownerStack = getOwnerStack(fiber);
1924
- if (ownerStack.length > 0) {
1925
- return ownerStack;
2147
+ function getReactComponentInfo2(element) {
2148
+ const fiber = getFiberForElement2(element);
2149
+ if (!fiber) return { frames: [], nearestComponentFiber: null };
2150
+ const elementSource = getSourceFromFiber2(fiber);
2151
+ const elementSourceFile = elementSource?.fileName || void 0;
2152
+ const ownerResult = getOwnerStack2(fiber);
2153
+ if (ownerResult.frames.length > 0) {
2154
+ return { ...ownerResult, elementSourceFile };
1926
2155
  }
1927
- return getRenderStack(fiber);
2156
+ return { ...getRenderStack2(fiber), elementSourceFile };
1928
2157
  }
1929
2158
  function getElementDisplayName(element) {
1930
2159
  const tag = element.tagName.toLowerCase();
@@ -2219,14 +2448,35 @@ function parseDomSource(element) {
2219
2448
  }
2220
2449
  return { file, line, column };
2221
2450
  }
2451
+ var MAX_SUB_ELEMENT_SOURCES = 20;
2452
+ function collectSubElementSources(element) {
2453
+ const sources = {};
2454
+ const children = element.querySelectorAll("[data-direct-edit-source]");
2455
+ const labelCounts = /* @__PURE__ */ new Map();
2456
+ let count = 0;
2457
+ for (const child of children) {
2458
+ if (count >= MAX_SUB_ELEMENT_SOURCES) break;
2459
+ if (!(child instanceof HTMLElement)) continue;
2460
+ const source = parseDomSource(child);
2461
+ if (!source) continue;
2462
+ const text = ((child.innerText || child.textContent) ?? "").trim();
2463
+ let baseLabel = text.length > 0 && text.length <= 30 ? text.slice(0, 30).toLowerCase().replace(/\s+/g, "_") : child.tagName.toLowerCase();
2464
+ const existing = labelCounts.get(baseLabel) ?? 0;
2465
+ labelCounts.set(baseLabel, existing + 1);
2466
+ const label = existing > 0 ? `${baseLabel}_${existing + 1}` : baseLabel;
2467
+ sources[label] = source;
2468
+ count++;
2469
+ }
2470
+ return sources;
2471
+ }
2222
2472
  function getElementSource(element) {
2223
2473
  const domSource = parseDomSource(element);
2224
2474
  if (domSource) return domSource;
2225
2475
  const seenFibers = /* @__PURE__ */ new Set();
2226
- let fiber = getFiberForElement(element);
2476
+ let fiber = getFiberForElement2(element);
2227
2477
  while (fiber && !seenFibers.has(fiber)) {
2228
2478
  seenFibers.add(fiber);
2229
- const fiberSource = getSourceFromFiber(fiber);
2479
+ const fiberSource = getSourceFromFiber2(fiber);
2230
2480
  if (fiberSource?.fileName) {
2231
2481
  return {
2232
2482
  file: fiberSource.fileName,
@@ -2241,8 +2491,15 @@ function getElementSource(element) {
2241
2491
  function getElementLocator(element) {
2242
2492
  const elementInfo = getElementInfo(element);
2243
2493
  const domSource = getElementSource(element);
2494
+ const { frames, nearestComponentFiber, elementSourceFile } = getReactComponentInfo2(element);
2495
+ const componentName = nearestComponentFiber?.type?.displayName || nearestComponentFiber?.type?.name || void 0;
2496
+ const authoredProps = nearestComponentFiber ? getComponentProps(nearestComponentFiber) : void 0;
2497
+ const classification = classifyComponentFiber(nearestComponentFiber, frames, elementSourceFile);
2498
+ const callSite = nearestComponentFiber ? getCallSiteSource(nearestComponentFiber) : null;
2499
+ const definitionSrc = classification.isComponentPrimitive ? deriveDefinitionSource(frames) : null;
2500
+ const subSources = collectSubElementSources(element);
2244
2501
  return {
2245
- reactStack: getReactComponentStack(element),
2502
+ reactStack: frames,
2246
2503
  domSelector: buildDomSelector(element),
2247
2504
  domContextHtml: buildDomContextHtml(element),
2248
2505
  targetHtml: buildTargetHtml(element),
@@ -2250,36 +2507,80 @@ function getElementLocator(element) {
2250
2507
  tagName: elementInfo.tagName,
2251
2508
  id: elementInfo.id,
2252
2509
  classList: elementInfo.classList,
2253
- domSource: domSource ?? void 0
2510
+ domSource: domSource ?? void 0,
2511
+ reactComponentName: componentName,
2512
+ authoredProps: authoredProps && Object.keys(authoredProps).length > 0 ? authoredProps : void 0,
2513
+ subElementSources: Object.keys(subSources).length > 0 ? subSources : void 0,
2514
+ callSiteSource: callSite ?? void 0,
2515
+ definitionSource: definitionSrc ?? void 0,
2516
+ isComponentPrimitive: nearestComponentFiber || elementSourceFile ? classification.isComponentPrimitive : void 0
2254
2517
  };
2255
2518
  }
2256
2519
  function getLocatorHeader(locator) {
2257
2520
  const primaryFrame = getPrimaryFrame(locator);
2258
- const componentLabel = primaryFrame?.name ? primaryFrame.name : locator.tagName;
2259
- const formattedSource = locator.domSource?.file ? formatSourceLocation(locator.domSource.file, locator.domSource.line, locator.domSource.column) : primaryFrame?.file ? formatSourceLocation(primaryFrame.file, primaryFrame.line, primaryFrame.column) : null;
2260
- return { componentLabel, formattedSource };
2521
+ const componentLabel = locator.reactComponentName ?? primaryFrame?.name ?? locator.tagName;
2522
+ let formattedSource;
2523
+ if (locator.isComponentPrimitive && locator.definitionSource?.file) {
2524
+ formattedSource = formatSourceLocation(
2525
+ locator.definitionSource.file,
2526
+ locator.definitionSource.line,
2527
+ locator.definitionSource.column
2528
+ );
2529
+ } else {
2530
+ formattedSource = locator.domSource?.file ? formatSourceLocation(locator.domSource.file, locator.domSource.line, locator.domSource.column) : primaryFrame?.file ? formatSourceLocation(primaryFrame.file, primaryFrame.line, primaryFrame.column) : null;
2531
+ }
2532
+ const formattedCallSite = locator.callSiteSource?.file ? formatSourceLocation(locator.callSiteSource.file, locator.callSiteSource.line, locator.callSiteSource.column) : null;
2533
+ return { componentLabel, formattedSource, formattedCallSite };
2534
+ }
2535
+ function formatComponentTree(reactStack) {
2536
+ const names = reactStack.map((f) => f.name).filter(Boolean);
2537
+ if (names.length === 0) return null;
2538
+ if (names.length === 1) return names[0];
2539
+ const [component, ...ancestors] = names;
2540
+ return `${component} (in ${ancestors.join(" > ")})`;
2261
2541
  }
2262
2542
  function buildLocatorContextLines(locator, options) {
2263
2543
  const lines = [];
2264
- const { componentLabel, formattedSource } = getLocatorHeader(locator);
2544
+ const { componentLabel, formattedSource, formattedCallSite } = getLocatorHeader(locator);
2265
2545
  const target = (locator.targetHtml || locator.domContextHtml || "").trim();
2266
2546
  const context = locator.domContextHtml?.trim() || "";
2267
- const selector = locator.domSelector?.trim();
2547
+ const path = locator.domSelector?.trim();
2268
2548
  const text = locator.textPreview?.trim();
2269
2549
  lines.push(`@<${componentLabel}>`);
2270
2550
  lines.push("");
2551
+ const tree = formatComponentTree(locator.reactStack);
2552
+ if (tree) {
2553
+ lines.push(`react: ${tree}`);
2554
+ }
2555
+ if (locator.authoredProps && Object.keys(locator.authoredProps).length > 0) {
2556
+ lines.push(`props: ${JSON.stringify(locator.authoredProps)}`);
2557
+ }
2558
+ if (locator.isComponentPrimitive != null) {
2559
+ lines.push(`type: ${locator.isComponentPrimitive ? "component" : "instance"}`);
2560
+ }
2561
+ lines.push(`source: ${formattedSource ?? "(file not available)"}`);
2562
+ if (formattedCallSite && formattedCallSite !== formattedSource) {
2563
+ lines.push(`call-site: ${formattedCallSite}`);
2564
+ }
2565
+ if (locator.subElementSources && Object.keys(locator.subElementSources).length > 0) {
2566
+ lines.push("source-map:");
2567
+ for (const [label, source] of Object.entries(locator.subElementSources)) {
2568
+ lines.push(` - ${label}: ${formatSourceLocation(source.file, source.line, source.column)}`);
2569
+ }
2570
+ }
2571
+ if (path) {
2572
+ lines.push(`path: ${path}`);
2573
+ }
2271
2574
  if (target) {
2575
+ lines.push("");
2272
2576
  lines.push("target:");
2273
2577
  lines.push(target);
2274
2578
  }
2275
2579
  if (!options?.skipContext && context && context !== target) {
2580
+ lines.push("");
2276
2581
  lines.push("context:");
2277
2582
  lines.push(context);
2278
2583
  }
2279
- lines.push(`in ${formattedSource ?? "(file not available)"}`);
2280
- if (selector) {
2281
- lines.push(`selector: ${selector}`);
2282
- }
2283
2584
  if (text) {
2284
2585
  lines.push(`text: ${text}`);
2285
2586
  }
@@ -2980,7 +3281,7 @@ function buildExportInstruction(profile) {
2980
3281
  return hasComments ? "Address this feedback on the UI. Use the provided source location and selector to find each element in the codebase." : "";
2981
3282
  }
2982
3283
  const parts = [];
2983
- if (hasCssEdits) parts.push("Apply the CSS changes to the targeted elements using the project's existing styling approach (Tailwind, CSS modules, etc.).");
3284
+ if (hasCssEdits) parts.push("Apply the CSS changes to the targeted elements using the project's existing styling approach (Tailwind, CSS modules, etc.). Map values to existing CSS variables, design tokens, or utility classes already used in the project whenever possible.");
2984
3285
  if (hasTextEdits) parts.push("Update the text content as specified.");
2985
3286
  if (hasMoves) parts.push("Implement the move plan below directly in source code. For `structural_move`, reorder/reparent elements using the target anchors. For `layout_refactor`, apply the listed flex/grid refactor steps. Do NOT simulate movement with absolute positioning, left/top offsets, transform, or margin hacks.");
2986
3287
  if (hasComments) parts.push("Address the comments on the relevant elements.");
@@ -3692,11 +3993,6 @@ function useStyleUpdaters({
3692
3993
  var React3 = __toESM(require("react"));
3693
3994
 
3694
3995
  // src/clipboard.ts
3695
- function buildAgentClipboardText(markdown) {
3696
- return `implement the visual edits
3697
-
3698
- ${markdown}`;
3699
- }
3700
3996
  function tryRestoreFocus(element) {
3701
3997
  if (!(element instanceof HTMLElement)) return;
3702
3998
  try {
@@ -4029,6 +4325,8 @@ function useSessionManager({
4029
4325
  const originalStyles = options?.originalStyles ?? existingEdit?.originalStyles ?? getOriginalInlineStyles(nextSingleElement);
4030
4326
  const pendingStyles = options?.pendingStyles ?? existingEdit?.pendingStyles ?? {};
4031
4327
  const elementInfo = getElementInfo(nextSingleElement);
4328
+ const { frames, nearestComponentFiber, elementSourceFile } = getReactComponentInfo(nextSingleElement);
4329
+ const isPrimitive = classifyComponentFiber(nearestComponentFiber, frames, elementSourceFile).isComponentPrimitive;
4032
4330
  setState((prev) => ({
4033
4331
  comments: prev.activeCommentId ? prev.comments.filter((comment) => {
4034
4332
  if (comment.id !== prev.activeCommentId) return true;
@@ -4047,6 +4345,7 @@ function useSessionManager({
4047
4345
  computedColor: computed.color,
4048
4346
  computedBoxShadow: computed.boxShadow,
4049
4347
  computedTypography: computed.typography,
4348
+ isComponentPrimitive: isPrimitive,
4050
4349
  originalStyles,
4051
4350
  pendingStyles,
4052
4351
  editModeActive: prev.editModeActive,
@@ -4077,6 +4376,7 @@ function useSessionManager({
4077
4376
  computedColor: null,
4078
4377
  computedBoxShadow: null,
4079
4378
  computedTypography: null,
4379
+ isComponentPrimitive: false,
4080
4380
  originalStyles: {},
4081
4381
  pendingStyles: {},
4082
4382
  activeCommentId: null,
@@ -4617,6 +4917,7 @@ function useSessionManager({
4617
4917
  computedColor: computed.color,
4618
4918
  computedBoxShadow: computed.boxShadow,
4619
4919
  computedTypography: computed.typography,
4920
+ isComponentPrimitive: prev.isComponentPrimitive,
4620
4921
  originalStyles: styleState.originalStyles,
4621
4922
  pendingStyles: styleState.pendingStyles,
4622
4923
  editModeActive: prev.editModeActive,
@@ -5394,7 +5695,15 @@ async function checkAgentConnection() {
5394
5695
  }
5395
5696
 
5396
5697
  // src/use-agent-comms.ts
5698
+ function withInstruction(profile, markdown) {
5699
+ const instruction = buildExportInstruction(profile);
5700
+ return instruction ? `${instruction}
5701
+
5702
+ ${markdown}` : markdown;
5703
+ }
5397
5704
  function buildLocatorPayload(locator) {
5705
+ const { componentLabel, formattedSource, formattedCallSite } = getLocatorHeader(locator);
5706
+ const reactTree = formatComponentTree(locator.reactStack);
5398
5707
  return {
5399
5708
  element: {
5400
5709
  tagName: locator.tagName,
@@ -5402,10 +5711,22 @@ function buildLocatorPayload(locator) {
5402
5711
  classList: locator.classList,
5403
5712
  domSelector: locator.domSelector,
5404
5713
  targetHtml: locator.targetHtml,
5714
+ contextHtml: locator.domContextHtml || null,
5405
5715
  textPreview: locator.textPreview
5406
5716
  },
5407
- source: locator.domSource || null,
5408
- reactStack: locator.reactStack
5717
+ componentLabel,
5718
+ reactTree,
5719
+ reactStack: locator.reactStack,
5720
+ reactComponentName: locator.reactComponentName ?? null,
5721
+ authoredProps: locator.authoredProps ?? null,
5722
+ type: locator.isComponentPrimitive != null ? locator.isComponentPrimitive ? "component" : "instance" : null,
5723
+ isComponentPrimitive: locator.isComponentPrimitive ?? false,
5724
+ source: formattedSource,
5725
+ callSite: formattedCallSite,
5726
+ rawSource: locator.domSource || null,
5727
+ callSiteSource: locator.callSiteSource ?? null,
5728
+ definitionSource: locator.definitionSource ?? null,
5729
+ subElementSources: locator.subElementSources ?? null
5409
5730
  };
5410
5731
  }
5411
5732
  function useAgentComms({ stateRef, sessionEditsRef, getSessionItems, saveCurrentToSession, removeSessionEdit, deleteComment }) {
@@ -5486,6 +5807,11 @@ function useAgentComms({ stateRef, sessionEditsRef, getSessionItems, saveCurrent
5486
5807
  const movePlan = includeBatchMoveEnvelope ? resolvedPlanContext.movePlan : null;
5487
5808
  const hasMeaningfulPayload = changes.length > 0 || sessionEdit.textEdit != null || moveIntent != null;
5488
5809
  if (!hasMeaningfulPayload) return true;
5810
+ const profile = getExportContentProfile(
5811
+ [sessionEdit],
5812
+ [],
5813
+ resolvedPlanContext
5814
+ );
5489
5815
  try {
5490
5816
  const result = await sendEditToAgent({
5491
5817
  ...buildLocatorPayload(locator),
@@ -5493,7 +5819,7 @@ function useAgentComms({ stateRef, sessionEditsRef, getSessionItems, saveCurrent
5493
5819
  textChange: sessionEdit.textEdit ?? null,
5494
5820
  moveIntent,
5495
5821
  ...movePlan ? { movePlan } : {},
5496
- exportMarkdown
5822
+ exportMarkdown: withInstruction(profile, exportMarkdown)
5497
5823
  });
5498
5824
  if (result.ok) {
5499
5825
  removeSessionEdit(sessionEdit.element);
@@ -5505,12 +5831,13 @@ function useAgentComms({ stateRef, sessionEditsRef, getSessionItems, saveCurrent
5505
5831
  }, [updateAgentAvailability, removeSessionEdit]);
5506
5832
  const sendSessionCommentToAgent = React5.useCallback(async (comment) => {
5507
5833
  const exportMarkdown = buildCommentExport(comment.locator, comment.text, comment.replies);
5834
+ const commentProfile = { hasCssEdits: false, hasTextEdits: false, hasMoves: false, hasComments: true };
5508
5835
  try {
5509
5836
  const result = await sendCommentToAgent({
5510
5837
  ...buildLocatorPayload(comment.locator),
5511
5838
  commentText: comment.text,
5512
5839
  replies: comment.replies,
5513
- exportMarkdown
5840
+ exportMarkdown: withInstruction(commentProfile, exportMarkdown)
5514
5841
  });
5515
5842
  if (result.ok) {
5516
5843
  deleteComment(comment.id);
@@ -5530,12 +5857,14 @@ function useAgentComms({ stateRef, sessionEditsRef, getSessionItems, saveCurrent
5530
5857
  );
5531
5858
  if (editsWithChanges.length === 0 && contextBlocks.length === 0) return false;
5532
5859
  const markdownParts = [];
5860
+ let movePlanCtx = null;
5533
5861
  if (editsWithChanges.length > 0) {
5534
- const movePlanContext = buildMovePlanContext(editsWithChanges);
5535
- markdownParts.push(buildSessionExport(editsWithChanges, [], { movePlanContext }));
5862
+ movePlanCtx = buildMovePlanContext(editsWithChanges);
5863
+ markdownParts.push(buildSessionExport(editsWithChanges, [], { movePlanContext: movePlanCtx }));
5536
5864
  }
5537
5865
  markdownParts.push(...contextBlocks);
5538
5866
  const exportMarkdown = markdownParts.join("\n\n");
5867
+ const multiProfile = getExportContentProfile(editsWithChanges, [], movePlanCtx);
5539
5868
  const primaryEl = current.selectedElements.find((el) => el.isConnected);
5540
5869
  if (!primaryEl) return false;
5541
5870
  const primary = getElementLocator(primaryEl);
@@ -5545,7 +5874,7 @@ function useAgentComms({ stateRef, sessionEditsRef, getSessionItems, saveCurrent
5545
5874
  changes: [],
5546
5875
  textChange: null,
5547
5876
  moveIntent: null,
5548
- exportMarkdown
5877
+ exportMarkdown: withInstruction(multiProfile, exportMarkdown)
5549
5878
  });
5550
5879
  if (result.ok) {
5551
5880
  for (const el of current.selectedElements) {
@@ -6373,6 +6702,7 @@ function DirectEditProvider({ children }) {
6373
6702
  computedColor: null,
6374
6703
  computedBoxShadow: null,
6375
6704
  computedTypography: null,
6705
+ isComponentPrimitive: false,
6376
6706
  originalStyles: {},
6377
6707
  pendingStyles: {},
6378
6708
  editModeActive: false,
@@ -6494,9 +6824,10 @@ function DirectEditProvider({ children }) {
6494
6824
  enterCanvas();
6495
6825
  }
6496
6826
  if (wasActive) {
6827
+ clearSelection();
6497
6828
  closePanel();
6498
6829
  }
6499
- }, [toggleEditModeBase, stateRef, exitCanvas, enterCanvas, closePanel]);
6830
+ }, [toggleEditModeBase, stateRef, exitCanvas, enterCanvas, clearSelection, closePanel]);
6500
6831
  const toggleCanvasWithPreference = React8.useCallback(() => {
6501
6832
  const willBeActive = !stateRef.current.canvas.active;
6502
6833
  toggleCanvas();
@@ -8664,6 +8995,7 @@ function useSelectionResize({
8664
8995
  var import_jsx_runtime9 = require("react/jsx-runtime");
8665
8996
  var BLUE3 = "#0D99FF";
8666
8997
  var MAGENTA = "#E11BB6";
8998
+ var COMPONENT_PURPLE = "#8B5CF6";
8667
8999
  var DRAG_THRESHOLD = 4;
8668
9000
  var DBLCLICK_DELAY = 300;
8669
9001
  var HANDLE_SIZE = 12;
@@ -8707,9 +9039,11 @@ function SelectionOverlay({
8707
9039
  onHoverElement,
8708
9040
  onClickThrough,
8709
9041
  onSelectPageFrame,
9042
+ isComponentPrimitive = false,
8710
9043
  enableResizeHandles = false,
8711
9044
  onResizeSizingChange
8712
9045
  }) {
9046
+ const selectionColor = isComponentPrimitive ? COMPONENT_PURPLE : BLUE3;
8713
9047
  const rectElement = isDragging && draggedElement ? draggedElement : selectedElement;
8714
9048
  const [rect, setRect] = React19.useState(() => rectElement.getBoundingClientRect());
8715
9049
  const [pageFrameRect, setPageFrameRect] = React19.useState(() => pageFrameElement ? pageFrameElement.getBoundingClientRect() : null);
@@ -9037,7 +9371,7 @@ function SelectionOverlay({
9037
9371
  zIndex: 99997,
9038
9372
  background: "transparent",
9039
9373
  border: "none",
9040
- color: BLUE3,
9374
+ color: selectionColor,
9041
9375
  fontSize: `${pageLabelFontSize}px`,
9042
9376
  lineHeight: `${pageLabelLineHeight}px`,
9043
9377
  padding: 0,
@@ -9065,7 +9399,7 @@ function SelectionOverlay({
9065
9399
  height: rect.height,
9066
9400
  pointerEvents: "none",
9067
9401
  zIndex: 99996,
9068
- border: `1px solid ${BLUE3}`,
9402
+ border: `1px solid ${selectionColor}`,
9069
9403
  borderRadius: "0px",
9070
9404
  boxSizing: "border-box"
9071
9405
  }
@@ -9082,18 +9416,28 @@ function SelectionOverlay({
9082
9416
  transform: "translateX(-50%)",
9083
9417
  pointerEvents: "none",
9084
9418
  zIndex: 99992,
9085
- background: BLUE3,
9086
- color: "white",
9087
- fontSize: "11px",
9088
- lineHeight: "20px",
9089
- padding: "0 6px",
9090
- borderRadius: "4px",
9091
- whiteSpace: "nowrap",
9092
- fontFamily: "system-ui, sans-serif",
9093
- fontWeight: 500,
9094
- letterSpacing: "-0.01em"
9419
+ display: "flex",
9420
+ alignItems: "center",
9421
+ gap: "4px"
9095
9422
  },
9096
- children: dimensionText
9423
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
9424
+ "span",
9425
+ {
9426
+ style: {
9427
+ background: selectionColor,
9428
+ color: "white",
9429
+ fontSize: "11px",
9430
+ lineHeight: "20px",
9431
+ padding: "0 6px",
9432
+ borderRadius: "4px",
9433
+ whiteSpace: "nowrap",
9434
+ fontFamily: "system-ui, sans-serif",
9435
+ fontWeight: 500,
9436
+ letterSpacing: "-0.01em"
9437
+ },
9438
+ children: dimensionText
9439
+ }
9440
+ )
9097
9441
  }
9098
9442
  ),
9099
9443
  !isDragging && !isTextEditing && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
@@ -9149,7 +9493,7 @@ function SelectionOverlay({
9149
9493
  position: "absolute",
9150
9494
  width: RESIZE_CORNER_SIZE,
9151
9495
  height: RESIZE_CORNER_SIZE,
9152
- border: `1px solid ${BLUE3}`,
9496
+ border: `1px solid ${selectionColor}`,
9153
9497
  background: "#fff",
9154
9498
  borderRadius: 1,
9155
9499
  boxSizing: "border-box",
@@ -12523,6 +12867,7 @@ function DirectEditPanelContent() {
12523
12867
  computedColor,
12524
12868
  computedBoxShadow,
12525
12869
  computedTypography,
12870
+ isComponentPrimitive,
12526
12871
  borderStyleControlPreference,
12527
12872
  pendingStyles,
12528
12873
  editModeActive,
@@ -12843,6 +13188,7 @@ function DirectEditPanelContent() {
12843
13188
  selectElement(child);
12844
13189
  }
12845
13190
  },
13191
+ isComponentPrimitive,
12846
13192
  enableResizeHandles: true,
12847
13193
  onResizeSizingChange: updateSizingProperties
12848
13194
  }
@@ -14821,7 +15167,10 @@ function DirectEditDemo() {
14821
15167
  const handleExportEdits = async () => {
14822
15168
  if (Object.keys(pendingStyles).length === 0) return false;
14823
15169
  const exportMarkdown = buildEditExport(DEMO_LOCATOR, pendingStyles);
14824
- return copyText(buildAgentClipboardText(exportMarkdown));
15170
+ const instruction = buildExportInstruction({ hasCssEdits: true, hasTextEdits: false, hasMoves: false, hasComments: false });
15171
+ return copyText(`${instruction}
15172
+
15173
+ ${exportMarkdown}`);
14825
15174
  };
14826
15175
  return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "min-h-screen p-8", children: /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "mx-auto max-w-4xl", children: [
14827
15176
  /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("h1", { className: "mb-2 text-2xl font-bold", children: "Direct Edit Panel" }),