@agent-native/core 0.80.10 → 0.80.11
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/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +6 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/production-agent.ts +108 -15
- package/corpus/core/src/client/AssistantChat.tsx +123 -26
- package/corpus/core/src/client/sse-event-processor.ts +9 -0
- package/corpus/templates/assets/.agents/skills/image-generation/SKILL.md +9 -2
- package/corpus/templates/assets/AGENTS.md +4 -0
- package/corpus/templates/assets/actions/_tool-activity.ts +46 -0
- package/corpus/templates/assets/actions/generate-image-batch.ts +26 -5
- package/corpus/templates/assets/actions/generate-image.ts +118 -67
- package/corpus/templates/assets/actions/list-draft-assets.ts +50 -0
- package/corpus/templates/assets/actions/rerun-generation-run.ts +52 -31
- package/corpus/templates/assets/app/components/create/RecentDraftsSection.tsx +100 -0
- package/corpus/templates/assets/app/i18n/zh-TW.ts +7 -0
- package/corpus/templates/assets/app/i18n-data.ts +61 -0
- package/corpus/templates/assets/app/routes/_index.tsx +4 -0
- package/corpus/templates/assets/app/routes/library.tsx +145 -38
- package/corpus/templates/assets/changelog/2026-06-29-image-generation-can-now-render-requested-text-and-respect-b.md +6 -0
- package/corpus/templates/assets/changelog/2026-06-30-image-generation-no-longer-looks-stuck-while-the-provider-is-still-working.md +6 -0
- package/corpus/templates/assets/changelog/2026-06-30-tagged-presets-now-control-the-image-aspect-ratio.md +6 -0
- package/corpus/templates/assets/server/lib/generation.ts +135 -11
- package/corpus/templates/assets/shared/api.ts +4 -0
- package/corpus/templates/clips/actions/list-ai-requests.ts +60 -0
- package/corpus/templates/clips/app/hooks/use-auto-title.ts +24 -25
- package/corpus/templates/design/actions/apply-component-prop-edit.ts +13 -5
- package/corpus/templates/design/actions/apply-design-state.ts +17 -4
- package/corpus/templates/design/actions/apply-motion-edit.ts +9 -46
- package/corpus/templates/design/actions/delete-design.ts +20 -0
- package/corpus/templates/design/actions/get-component-details.ts +16 -7
- package/corpus/templates/design/actions/index-design-tokens.ts +8 -4
- package/corpus/templates/design/actions/list-design-states.ts +19 -1
- package/corpus/templates/design/actions/open-component-source.ts +7 -2
- package/corpus/templates/design/actions/preview-component-prop-edit.ts +32 -26
- package/corpus/templates/design/actions/preview-shader-fill.ts +9 -11
- package/corpus/templates/design/actions/run-design-audit.ts +21 -6
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +139 -14
- package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +20 -3
- package/corpus/templates/design/app/components/design/EditPanel.tsx +428 -31
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +110 -30
- package/corpus/templates/design/app/components/design/MotionDock.tsx +9 -0
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +1 -1
- package/corpus/templates/design/app/components/design/ReviewPanel.tsx +12 -5
- package/corpus/templates/design/app/components/design/StatesPanel.tsx +4 -2
- package/corpus/templates/design/app/components/design/TokensPanel.tsx +13 -8
- package/corpus/templates/design/app/i18n/zh-TW.ts +1 -0
- package/corpus/templates/design/app/i18n-data.ts +11 -0
- package/corpus/templates/design/app/pages/DesignEditor.tsx +684 -179
- package/corpus/templates/design/changelog/2026-06-30-design-token-edits-now-stay-visible-in-previews-and-token-li.md +6 -0
- package/corpus/templates/design/e2e/global-setup.ts +94 -2
- package/corpus/templates/design/shared/component-model.ts +35 -0
- package/corpus/templates/design/shared/motion-compiler.ts +79 -8
- package/corpus/templates/design/shared/resolve-tweaks.ts +32 -9
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +94 -10
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +14 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +115 -22
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +10 -0
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/observability/routes.d.ts +3 -3
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/package.json +1 -1
|
@@ -99,6 +99,8 @@ const MOTION_PREVIEW_BRIDGE_SCRIPT = `
|
|
|
99
99
|
var loadedTracks = [];
|
|
100
100
|
// Map of nodeId -> [property, ...] we have touched, for cleanup.
|
|
101
101
|
var touchedProps = {};
|
|
102
|
+
// Map of nodeId -> property -> original inline style value.
|
|
103
|
+
var originalInlineValues = {};
|
|
102
104
|
|
|
103
105
|
function lerp(a, b, ratio) {
|
|
104
106
|
var numA = parseFloat(a);
|
|
@@ -137,6 +139,10 @@ const MOTION_PREVIEW_BRIDGE_SCRIPT = `
|
|
|
137
139
|
if (!el) continue;
|
|
138
140
|
var value = interpolate(track.keyframes, t);
|
|
139
141
|
if (value === '') continue;
|
|
142
|
+
if (!originalInlineValues[track.targetNodeId]) originalInlineValues[track.targetNodeId] = {};
|
|
143
|
+
if (!(track.property in originalInlineValues[track.targetNodeId])) {
|
|
144
|
+
originalInlineValues[track.targetNodeId][track.property] = el.style[track.property] || '';
|
|
145
|
+
}
|
|
140
146
|
el.style[track.property] = value;
|
|
141
147
|
if (!touchedProps[track.targetNodeId]) touchedProps[track.targetNodeId] = [];
|
|
142
148
|
if (touchedProps[track.targetNodeId].indexOf(track.property) === -1) {
|
|
@@ -152,10 +158,14 @@ const MOTION_PREVIEW_BRIDGE_SCRIPT = `
|
|
|
152
158
|
if (!el) continue;
|
|
153
159
|
var props = touchedProps[nodeIds[i]];
|
|
154
160
|
for (var j = 0; j < props.length; j++) {
|
|
155
|
-
|
|
161
|
+
var originals = originalInlineValues[nodeIds[i]] || {};
|
|
162
|
+
el.style[props[j]] = Object.prototype.hasOwnProperty.call(originals, props[j])
|
|
163
|
+
? originals[props[j]]
|
|
164
|
+
: '';
|
|
156
165
|
}
|
|
157
166
|
}
|
|
158
167
|
touchedProps = {};
|
|
168
|
+
originalInlineValues = {};
|
|
159
169
|
loadedTracks = [];
|
|
160
170
|
}
|
|
161
171
|
|
|
@@ -163,8 +173,8 @@ const MOTION_PREVIEW_BRIDGE_SCRIPT = `
|
|
|
163
173
|
if (e.source !== window.parent) return;
|
|
164
174
|
if (!e.data || typeof e.data.type !== 'string') return;
|
|
165
175
|
if (e.data.type === 'motion-load-tracks') {
|
|
176
|
+
clearPreview();
|
|
166
177
|
loadedTracks = Array.isArray(e.data.tracks) ? e.data.tracks : [];
|
|
167
|
-
touchedProps = {};
|
|
168
178
|
return;
|
|
169
179
|
}
|
|
170
180
|
if (e.data.type === 'motion-preview') {
|
|
@@ -1074,6 +1084,10 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
|
|
|
1074
1084
|
}
|
|
1075
1085
|
|
|
1076
1086
|
function isLayerInteractionBlocked(el) {
|
|
1087
|
+
if (!el) return false;
|
|
1088
|
+
if (el.closest && el.closest('[data-agent-native-locked="true"], [data-agent-native-hidden="true"]')) {
|
|
1089
|
+
return true;
|
|
1090
|
+
}
|
|
1077
1091
|
return matchesSelectorList(el, lockedSelectors) || matchesSelectorList(el, hiddenSelectors);
|
|
1078
1092
|
}
|
|
1079
1093
|
|
|
@@ -1948,6 +1962,12 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
|
|
|
1948
1962
|
selectedSpacingHovered = false;
|
|
1949
1963
|
hoveredSpacingHandleKey = '';
|
|
1950
1964
|
selectedEl = selectionTargetForHit(target);
|
|
1965
|
+
if (!selectedEl || isLayerInteractionBlocked(selectedEl)) {
|
|
1966
|
+
selectedEl = null;
|
|
1967
|
+
selectionOverlay.style.display = 'none';
|
|
1968
|
+
clearComponentTag();
|
|
1969
|
+
return;
|
|
1970
|
+
}
|
|
1951
1971
|
var info = getElementInfo(selectedEl);
|
|
1952
1972
|
positionOverlay(selectionOverlay, selectedEl);
|
|
1953
1973
|
window.parent.postMessage({ type: 'element-select', payload: info }, '*');
|
|
@@ -1973,9 +1993,15 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
|
|
|
1973
1993
|
selectedSpacingHovered = false;
|
|
1974
1994
|
hoveredSpacingHandleKey = '';
|
|
1975
1995
|
selectedEl = selectionTargetForHit(target);
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1996
|
+
if (selectedEl && !isLayerInteractionBlocked(selectedEl)) {
|
|
1997
|
+
info = getElementInfo(selectedEl);
|
|
1998
|
+
positionOverlay(selectionOverlay, selectedEl);
|
|
1999
|
+
window.parent.postMessage({ type: 'element-select', payload: info }, '*');
|
|
2000
|
+
} else {
|
|
2001
|
+
selectedEl = null;
|
|
2002
|
+
selectionOverlay.style.display = 'none';
|
|
2003
|
+
clearComponentTag();
|
|
2004
|
+
}
|
|
1979
2005
|
}
|
|
1980
2006
|
window.parent.postMessage({
|
|
1981
2007
|
type: 'element-contextmenu',
|
|
@@ -2198,6 +2224,21 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
|
|
|
2198
2224
|
return !!(ancestorEl && (ancestorEl === el || el.contains(ancestorEl)));
|
|
2199
2225
|
}
|
|
2200
2226
|
|
|
2227
|
+
function normalizeCssPropertyName(property) {
|
|
2228
|
+
var prop = String(property || '').trim();
|
|
2229
|
+
if (!prop) return '';
|
|
2230
|
+
if (prop.indexOf('--') === 0) return prop;
|
|
2231
|
+
return prop.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
2232
|
+
}
|
|
2233
|
+
|
|
2234
|
+
function applyInlineStyleProperty(el, property, value) {
|
|
2235
|
+
if (!el || !property) return false;
|
|
2236
|
+
var cssProperty = normalizeCssPropertyName(property);
|
|
2237
|
+
if (!cssProperty) return false;
|
|
2238
|
+
el.style.setProperty(cssProperty, String(value));
|
|
2239
|
+
return true;
|
|
2240
|
+
}
|
|
2241
|
+
|
|
2201
2242
|
function applyTextRangeStyle(property, value) {
|
|
2202
2243
|
if (!activeTextEditEl || !property) return false;
|
|
2203
2244
|
var selection = window.getSelection && window.getSelection();
|
|
@@ -2205,7 +2246,7 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
|
|
|
2205
2246
|
if (!selectionBelongsToElement(selection, activeTextEditEl)) return false;
|
|
2206
2247
|
var range = selection.getRangeAt(0);
|
|
2207
2248
|
var span = document.createElement('span');
|
|
2208
|
-
span
|
|
2249
|
+
applyInlineStyleProperty(span, property, value);
|
|
2209
2250
|
if (!span.getAttribute('style')) return false;
|
|
2210
2251
|
try {
|
|
2211
2252
|
range.surroundContents(span);
|
|
@@ -2384,7 +2425,7 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
|
|
|
2384
2425
|
insertionGuide.style.height = rect.height + 'px';
|
|
2385
2426
|
insertionGuide.style.background = 'color-mix(in srgb, var(--design-editor-accent-color) 14%, transparent)';
|
|
2386
2427
|
insertionGuide.style.border = '2px solid var(--design-editor-accent-color)';
|
|
2387
|
-
insertionGuide.style.borderRadius = '
|
|
2428
|
+
insertionGuide.style.borderRadius = '2px';
|
|
2388
2429
|
insertionGuide.style.boxShadow = 'none';
|
|
2389
2430
|
return;
|
|
2390
2431
|
}
|
|
@@ -2905,6 +2946,17 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
|
|
|
2905
2946
|
shieldOverlay.addEventListener(type, stopNativeInteraction, true);
|
|
2906
2947
|
});
|
|
2907
2948
|
|
|
2949
|
+
function stopBlockedLayerInteraction(e) {
|
|
2950
|
+
if (isOverlayElement(e.target)) return;
|
|
2951
|
+
var target = e.target && e.target.nodeType === 1 ? e.target : null;
|
|
2952
|
+
if (!target || !isLayerInteractionBlocked(target)) return;
|
|
2953
|
+
stopNativeInteraction(e);
|
|
2954
|
+
}
|
|
2955
|
+
|
|
2956
|
+
['pointerdown','pointerup','mousedown','mouseup','click','auxclick'].forEach(function(type) {
|
|
2957
|
+
document.addEventListener(type, stopBlockedLayerInteraction, true);
|
|
2958
|
+
});
|
|
2959
|
+
|
|
2908
2960
|
shieldOverlay.addEventListener('click', selectElementAtEvent, true);
|
|
2909
2961
|
shieldOverlay.addEventListener('contextmenu', openContextMenuAtEvent, true);
|
|
2910
2962
|
selectionOverlay.addEventListener('contextmenu', openContextMenuAtEvent, true);
|
|
@@ -3123,6 +3175,11 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
|
|
|
3123
3175
|
window.addEventListener('message', function(e) {
|
|
3124
3176
|
if (e.source !== window.parent) return;
|
|
3125
3177
|
if (!e.data) return;
|
|
3178
|
+
if (e.data.payload && typeof e.data.payload === 'object') {
|
|
3179
|
+
Object.keys(e.data.payload).forEach(function(key) {
|
|
3180
|
+
if (e.data[key] === undefined) e.data[key] = e.data.payload[key];
|
|
3181
|
+
});
|
|
3182
|
+
}
|
|
3126
3183
|
if (e.data.type === 'set-editor-chrome-scale') {
|
|
3127
3184
|
// Live-update the constant-size chrome scale WITHOUT rebuilding srcdoc.
|
|
3128
3185
|
// Rebuilding srcdoc reloads the iframe and flashes the content white.
|
|
@@ -3211,6 +3268,7 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
|
|
|
3211
3268
|
if (selectedEl && isLayerInteractionBlocked(selectedEl)) {
|
|
3212
3269
|
selectedEl = null;
|
|
3213
3270
|
selectionOverlay.style.display = 'none';
|
|
3271
|
+
clearComponentTag();
|
|
3214
3272
|
}
|
|
3215
3273
|
if (hoveredEl && isLayerInteractionBlocked(hoveredEl)) {
|
|
3216
3274
|
hoveredEl = null;
|
|
@@ -3242,7 +3300,11 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
|
|
|
3242
3300
|
return;
|
|
3243
3301
|
}
|
|
3244
3302
|
if (e.data.type === 'replace-document-content') {
|
|
3245
|
-
replaceRuntimeDocument(
|
|
3303
|
+
replaceRuntimeDocument(
|
|
3304
|
+
e.data.content,
|
|
3305
|
+
e.data.forceFullDocument ? '' : e.data.selectedSelector,
|
|
3306
|
+
e.data.forceFullDocument ? [] : e.data.selectorCandidates
|
|
3307
|
+
);
|
|
3246
3308
|
return;
|
|
3247
3309
|
}
|
|
3248
3310
|
if (e.data.type === 'delete-element') {
|
|
@@ -3253,13 +3315,63 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
|
|
|
3253
3315
|
var sel = e.data.selector;
|
|
3254
3316
|
var prop = e.data.property;
|
|
3255
3317
|
var val = e.data.value;
|
|
3256
|
-
var
|
|
3257
|
-
if (
|
|
3318
|
+
var candidatesForStyle = Array.isArray(e.data.selectorCandidates) ? e.data.selectorCandidates : [];
|
|
3319
|
+
if (sel && candidatesForStyle.indexOf(String(sel)) === -1) candidatesForStyle.push(String(sel));
|
|
3320
|
+
if (e.data.nodeId) {
|
|
3321
|
+
candidatesForStyle.push('[data-agent-native-node-id="' + String(e.data.nodeId).replace(/"/g, '\\\\"') + '"]');
|
|
3322
|
+
}
|
|
3323
|
+
var el = findRuntimeTarget(String(sel || ''), candidatesForStyle);
|
|
3324
|
+
if (prop && activeTextEditEl && el === activeTextEditEl && applyTextRangeStyle(prop, val)) {
|
|
3258
3325
|
postTextContentChange(activeTextEditEl, activeTextEditEl.textContent || '', activeTextEditEl.innerHTML || '');
|
|
3259
3326
|
refreshOverlays();
|
|
3260
3327
|
return;
|
|
3261
3328
|
}
|
|
3262
|
-
if (el)
|
|
3329
|
+
if (!el) return;
|
|
3330
|
+
var didPatchDom = false;
|
|
3331
|
+
var attributeOverrides = e.data.attributeOverrides;
|
|
3332
|
+
if (attributeOverrides && typeof attributeOverrides === 'object' && !Array.isArray(attributeOverrides)) {
|
|
3333
|
+
Object.keys(attributeOverrides).forEach(function(name) {
|
|
3334
|
+
if (!/^(?!on)[a-zA-Z][a-zA-Z0-9:_.-]*$/i.test(name)) return;
|
|
3335
|
+
var nextValue = attributeOverrides[name];
|
|
3336
|
+
if (nextValue === null || nextValue === undefined || nextValue === false) {
|
|
3337
|
+
el.removeAttribute(name);
|
|
3338
|
+
} else {
|
|
3339
|
+
el.setAttribute(name, String(nextValue));
|
|
3340
|
+
}
|
|
3341
|
+
didPatchDom = true;
|
|
3342
|
+
});
|
|
3343
|
+
}
|
|
3344
|
+
var classEdit = e.data.classEdit;
|
|
3345
|
+
if (classEdit && typeof classEdit === 'object') {
|
|
3346
|
+
var currentClass = (el.getAttribute('class') || '').trim().split(/\\s+/).filter(Boolean);
|
|
3347
|
+
var nextClass = currentClass.slice();
|
|
3348
|
+
if (classEdit.operation === 'replace' && classEdit.from && classEdit.to) {
|
|
3349
|
+
var replaced = false;
|
|
3350
|
+
nextClass = currentClass.map(function(token) {
|
|
3351
|
+
if (token === classEdit.from) {
|
|
3352
|
+
replaced = true;
|
|
3353
|
+
return String(classEdit.to);
|
|
3354
|
+
}
|
|
3355
|
+
return token;
|
|
3356
|
+
});
|
|
3357
|
+
if (!replaced && nextClass.indexOf(String(classEdit.to)) === -1) nextClass.push(String(classEdit.to));
|
|
3358
|
+
didPatchDom = true;
|
|
3359
|
+
} else if (classEdit.operation === 'add' && classEdit.className) {
|
|
3360
|
+
if (nextClass.indexOf(String(classEdit.className)) === -1) nextClass.push(String(classEdit.className));
|
|
3361
|
+
didPatchDom = true;
|
|
3362
|
+
} else if (classEdit.operation === 'remove' && classEdit.className) {
|
|
3363
|
+
nextClass = currentClass.filter(function(token) { return token !== String(classEdit.className); });
|
|
3364
|
+
didPatchDom = true;
|
|
3365
|
+
}
|
|
3366
|
+
if (didPatchDom) el.setAttribute('class', nextClass.join(' '));
|
|
3367
|
+
}
|
|
3368
|
+
if (prop && typeof prop === 'string') {
|
|
3369
|
+
applyInlineStyleProperty(el, prop, val);
|
|
3370
|
+
didPatchDom = true;
|
|
3371
|
+
}
|
|
3372
|
+
if (didPatchDom) {
|
|
3373
|
+
refreshOverlays();
|
|
3374
|
+
}
|
|
3263
3375
|
});
|
|
3264
3376
|
|
|
3265
3377
|
window.addEventListener('scroll', refreshOverlays, true);
|
|
@@ -3590,7 +3702,8 @@ export function DesignCanvas({
|
|
|
3590
3702
|
});
|
|
3591
3703
|
|
|
3592
3704
|
// Build the srcdoc. The tweak bridge ALWAYS goes in so the panel works
|
|
3593
|
-
// outside Edit mode. The editor chrome bridge is omitted
|
|
3705
|
+
// outside Edit mode. The editor chrome bridge is omitted for Interact and
|
|
3706
|
+
// read-only surfaces so preview/app users can interact with the app normally.
|
|
3594
3707
|
const srcdoc = useMemo(() => {
|
|
3595
3708
|
if (externalPreviewUrl) return undefined;
|
|
3596
3709
|
const editorChromeBridge =
|
|
@@ -4074,11 +4187,23 @@ export function DesignCanvas({
|
|
|
4074
4187
|
}, [editorChromeScaleX, editorChromeScaleY]);
|
|
4075
4188
|
|
|
4076
4189
|
const sendStyleChange = useCallback(
|
|
4077
|
-
(
|
|
4190
|
+
(
|
|
4191
|
+
selector: string,
|
|
4192
|
+
property: string,
|
|
4193
|
+
value: string,
|
|
4194
|
+
options?: { selectorCandidates?: string[]; nodeId?: string | null },
|
|
4195
|
+
) => {
|
|
4078
4196
|
const iframe = iframeRef.current;
|
|
4079
4197
|
if (!iframe?.contentWindow) return;
|
|
4080
4198
|
iframe.contentWindow.postMessage(
|
|
4081
|
-
{
|
|
4199
|
+
{
|
|
4200
|
+
type: "style-change",
|
|
4201
|
+
selector,
|
|
4202
|
+
property,
|
|
4203
|
+
value,
|
|
4204
|
+
selectorCandidates: options?.selectorCandidates ?? [],
|
|
4205
|
+
nodeId: options?.nodeId ?? "",
|
|
4206
|
+
},
|
|
4082
4207
|
"*",
|
|
4083
4208
|
);
|
|
4084
4209
|
},
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
IconSparkles,
|
|
27
27
|
} from "@tabler/icons-react";
|
|
28
28
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
29
|
-
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
29
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
30
30
|
import { toast } from "sonner";
|
|
31
31
|
|
|
32
32
|
import { Button } from "@/components/ui/button";
|
|
@@ -86,6 +86,8 @@ export interface DesignExtensionSlotContext extends Record<string, unknown> {
|
|
|
86
86
|
mode: string;
|
|
87
87
|
activeTool: string;
|
|
88
88
|
tweakValues: Record<string, string | number | boolean>;
|
|
89
|
+
onShaderFillPreview?: (descriptor: ShaderDescriptor, css: string) => void;
|
|
90
|
+
onShaderFillPreviewClear?: () => void;
|
|
89
91
|
}
|
|
90
92
|
|
|
91
93
|
interface DesignExtensionsPanelProps {
|
|
@@ -447,6 +449,20 @@ interface ShaderFillsExtPanelProps {
|
|
|
447
449
|
|
|
448
450
|
function ShaderFillsExtPanel({ context }: ShaderFillsExtPanelProps) {
|
|
449
451
|
const [showShaders, setShowShaders] = useState(false);
|
|
452
|
+
const clearPreviewRef = useRef(context.onShaderFillPreviewClear);
|
|
453
|
+
useEffect(() => {
|
|
454
|
+
clearPreviewRef.current = context.onShaderFillPreviewClear;
|
|
455
|
+
}, [context.onShaderFillPreviewClear]);
|
|
456
|
+
useEffect(
|
|
457
|
+
() => () => {
|
|
458
|
+
clearPreviewRef.current?.();
|
|
459
|
+
},
|
|
460
|
+
[],
|
|
461
|
+
);
|
|
462
|
+
const closeShaders = () => {
|
|
463
|
+
context.onShaderFillPreviewClear?.();
|
|
464
|
+
setShowShaders(false);
|
|
465
|
+
};
|
|
450
466
|
// The most recently previewed descriptor — Apply persists exactly this one,
|
|
451
467
|
// so the write is intentional (one atomic call) rather than firing on every
|
|
452
468
|
// slider tweak.
|
|
@@ -535,14 +551,15 @@ function ShaderFillsExtPanel({ context }: ShaderFillsExtPanelProps) {
|
|
|
535
551
|
return (
|
|
536
552
|
<div className="flex flex-col">
|
|
537
553
|
<ShaderFillsPanel
|
|
538
|
-
onApply={(descriptor,
|
|
554
|
+
onApply={(descriptor, css) => {
|
|
539
555
|
// Preview only: ShaderFillsPanel fires apply-shader (planning/codegen)
|
|
540
556
|
// for agent context on every tune and the iframe shows the gradient.
|
|
541
557
|
// We just record the latest descriptor here; the explicit Apply
|
|
542
558
|
// button below performs the single intentional persist write.
|
|
543
559
|
setPreviewed(descriptor);
|
|
560
|
+
context.onShaderFillPreview?.(descriptor, css);
|
|
544
561
|
}}
|
|
545
|
-
onBack={
|
|
562
|
+
onBack={closeShaders}
|
|
546
563
|
applyContext={{
|
|
547
564
|
designId: context.designId || undefined,
|
|
548
565
|
fileId: context.activeFileId || undefined,
|