@agent-native/core 0.81.1 → 0.81.2
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/server/core-routes-plugin.ts +4 -5
- package/corpus/core/src/server/create-server.ts +3 -5
- package/corpus/core/src/server/google-oauth.ts +4 -0
- package/corpus/templates/calendar/actions/create-event.ts +9 -2
- package/corpus/templates/calendar/actions/event-action-helpers.ts +8 -2
- package/corpus/templates/calendar/app/components/calendar/GoogleConnectBanner.tsx +11 -0
- package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +17 -0
- package/corpus/templates/calendar/app/hooks/use-google-auth.ts +70 -39
- package/corpus/templates/calendar/changelog/2026-06-30-google-calendar-connection-errors-now-stay-in-the-app-instea.md +6 -0
- package/corpus/templates/calendar/server/handlers/google-auth.ts +77 -21
- package/corpus/templates/calendar/server/lib/google-calendar.ts +19 -8
- package/corpus/templates/design/actions/apply-design-token-edit.ts +13 -6
- package/corpus/templates/design/actions/import-design-tokens.ts +17 -13
- package/corpus/templates/design/actions/preview-design-token-edit.ts +20 -3
- package/corpus/templates/design/actions/run-design-audit.ts +9 -2
- package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +35 -14
- package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +20 -3
- package/corpus/templates/design/app/pages/DesignEditor.tsx +237 -51
- package/corpus/templates/design/app/pages/VisualEdit.tsx +1 -1
- package/corpus/templates/design/changelog/2026-06-30-design-editor-undo-drag-drop-style-editing-and-visual-edit-s.md +6 -0
- package/corpus/templates/design/shared/resolve-tweaks.ts +29 -6
- package/dist/collab/routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +2 -2
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +7 -7
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +1 -2
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/create-server.d.ts.map +1 -1
- package/dist/server/create-server.js +1 -2
- package/dist/server/create-server.js.map +1 -1
- package/dist/server/google-oauth.d.ts +2 -0
- package/dist/server/google-oauth.d.ts.map +1 -1
- package/dist/server/google-oauth.js +3 -0
- package/dist/server/google-oauth.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/package.json +1 -1
|
@@ -870,12 +870,35 @@ interface GeometryHistoryEntry {
|
|
|
870
870
|
after: CanvasFrameGeometryById;
|
|
871
871
|
}
|
|
872
872
|
|
|
873
|
-
interface
|
|
873
|
+
export interface ContentHistoryChange {
|
|
874
874
|
fileId: string;
|
|
875
875
|
before: string;
|
|
876
876
|
after: string;
|
|
877
877
|
}
|
|
878
878
|
|
|
879
|
+
export interface ContentHistoryGroup {
|
|
880
|
+
changes: ContentHistoryChange[];
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
export type ContentHistoryEntry = ContentHistoryChange | ContentHistoryGroup;
|
|
884
|
+
|
|
885
|
+
export function getContentHistoryChanges(
|
|
886
|
+
entry: ContentHistoryEntry,
|
|
887
|
+
): ContentHistoryChange[] {
|
|
888
|
+
return "changes" in entry ? entry.changes : [entry];
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
export function getAvailableContentHistoryChanges(
|
|
892
|
+
entry: ContentHistoryEntry,
|
|
893
|
+
availableFileIds: Iterable<string>,
|
|
894
|
+
activeFileId?: string | null,
|
|
895
|
+
): ContentHistoryChange[] {
|
|
896
|
+
const fileIds = new Set(availableFileIds);
|
|
897
|
+
return getContentHistoryChanges(entry).filter(
|
|
898
|
+
(change) => change.fileId === activeFileId || fileIds.has(change.fileId),
|
|
899
|
+
);
|
|
900
|
+
}
|
|
901
|
+
|
|
879
902
|
type PatchProofStatus =
|
|
880
903
|
| "runtime"
|
|
881
904
|
| "queued"
|
|
@@ -3533,6 +3556,7 @@ export default function DesignEditor() {
|
|
|
3533
3556
|
useState<{ screenId: string; layerId: string } | null>(null);
|
|
3534
3557
|
const pendingOverviewScreenSelectionRef = useRef<string | null>(null);
|
|
3535
3558
|
const pendingOverviewLayerSelectionRef = useRef<string | null>(null);
|
|
3559
|
+
const lastOverviewSelectedScreenIdsRef = useRef<string[]>([]);
|
|
3536
3560
|
// Tracks the nodeId of the most recently created TEXT primitive across one
|
|
3537
3561
|
// handleCreatePrimitive → handlePrimitiveCreated round-trip. Cleared after
|
|
3538
3562
|
// use. Lets handlePrimitiveCreated trigger begin-text-edit without needing
|
|
@@ -3850,10 +3874,13 @@ export default function DesignEditor() {
|
|
|
3850
3874
|
}, []);
|
|
3851
3875
|
const recordContentHistoryEntry = useCallback(
|
|
3852
3876
|
(entry: ContentHistoryEntry) => {
|
|
3853
|
-
|
|
3877
|
+
const changes = getContentHistoryChanges(entry).filter(
|
|
3878
|
+
(change) => change.before !== change.after,
|
|
3879
|
+
);
|
|
3880
|
+
if (changes.length === 0) return;
|
|
3854
3881
|
contentUndoStackRef.current = [
|
|
3855
3882
|
...contentUndoStackRef.current.slice(-(MAX_DESIGN_UNDO_STACK - 1)),
|
|
3856
|
-
|
|
3883
|
+
changes.length === 1 ? changes[0] : { changes },
|
|
3857
3884
|
];
|
|
3858
3885
|
contentRedoStackRef.current = [];
|
|
3859
3886
|
historyOrderRef.current = [
|
|
@@ -3910,6 +3937,13 @@ export default function DesignEditor() {
|
|
|
3910
3937
|
useEffect(() => {
|
|
3911
3938
|
viewModeRef.current = viewMode;
|
|
3912
3939
|
}, [viewMode]);
|
|
3940
|
+
|
|
3941
|
+
useEffect(() => {
|
|
3942
|
+
if (viewMode !== "overview" || overviewSelectedScreenIds.length === 0) {
|
|
3943
|
+
return;
|
|
3944
|
+
}
|
|
3945
|
+
lastOverviewSelectedScreenIdsRef.current = [...overviewSelectedScreenIds];
|
|
3946
|
+
}, [overviewSelectedScreenIds, viewMode]);
|
|
3913
3947
|
const [hasPendingGeneration, setHasPendingGeneration] = useState(() =>
|
|
3914
3948
|
hasFreshPendingGeneration(id),
|
|
3915
3949
|
);
|
|
@@ -6733,20 +6767,24 @@ export default function DesignEditor() {
|
|
|
6733
6767
|
skipPreview?: boolean;
|
|
6734
6768
|
immediateSave?: boolean;
|
|
6735
6769
|
persist?: boolean;
|
|
6770
|
+
recordHistory?: boolean;
|
|
6736
6771
|
updatedAt?: string;
|
|
6737
6772
|
} = {},
|
|
6738
6773
|
) => {
|
|
6739
6774
|
if (!activeFile || !canEditDesignRef.current) return;
|
|
6775
|
+
const shouldRecordHistory =
|
|
6776
|
+
options.recordHistory !== false && !options.updatedAt;
|
|
6740
6777
|
const previousContent =
|
|
6741
6778
|
collabContentFileIdRef.current === activeFile.id &&
|
|
6742
6779
|
typeof collabContentRef.current === "string"
|
|
6743
6780
|
? collabContentRef.current
|
|
6744
6781
|
: (activeFile.content ?? "");
|
|
6745
6782
|
const yjsHistoryAvailable = Boolean(
|
|
6746
|
-
ydoc && isSynced && undoManagerRef.current,
|
|
6783
|
+
shouldRecordHistory && ydoc && isSynced && undoManagerRef.current,
|
|
6747
6784
|
);
|
|
6748
6785
|
if (
|
|
6749
6786
|
!suppressContentHistoryRef.current &&
|
|
6787
|
+
shouldRecordHistory &&
|
|
6750
6788
|
!yjsHistoryAvailable &&
|
|
6751
6789
|
previousContent !== nextContent
|
|
6752
6790
|
) {
|
|
@@ -6810,10 +6848,13 @@ export default function DesignEditor() {
|
|
|
6810
6848
|
if (ydoc && isSynced) {
|
|
6811
6849
|
const ytext = ydoc.getText("content");
|
|
6812
6850
|
if (ytext.toString() !== nextContent) {
|
|
6813
|
-
ydoc.transact(
|
|
6814
|
-
|
|
6815
|
-
|
|
6816
|
-
|
|
6851
|
+
ydoc.transact(
|
|
6852
|
+
() => {
|
|
6853
|
+
ytext.delete(0, ytext.length);
|
|
6854
|
+
ytext.insert(0, nextContent);
|
|
6855
|
+
},
|
|
6856
|
+
shouldRecordHistory ? LOCAL_EDIT_ORIGIN : TAB_ID,
|
|
6857
|
+
);
|
|
6817
6858
|
}
|
|
6818
6859
|
}
|
|
6819
6860
|
if (options.persist === false) {
|
|
@@ -6849,6 +6890,7 @@ export default function DesignEditor() {
|
|
|
6849
6890
|
refreshPreview?: boolean;
|
|
6850
6891
|
skipPreview?: boolean;
|
|
6851
6892
|
persist?: boolean;
|
|
6893
|
+
recordHistory?: boolean;
|
|
6852
6894
|
updatedAt?: string;
|
|
6853
6895
|
} = {},
|
|
6854
6896
|
) => {
|
|
@@ -6858,6 +6900,21 @@ export default function DesignEditor() {
|
|
|
6858
6900
|
return;
|
|
6859
6901
|
}
|
|
6860
6902
|
const previousFile = files.find((file) => file.id === fileId);
|
|
6903
|
+
const previousContent =
|
|
6904
|
+
getScreenContent(fileId) ?? previousFile?.content ?? "";
|
|
6905
|
+
const shouldRecordHistory =
|
|
6906
|
+
options.recordHistory !== false && !options.updatedAt;
|
|
6907
|
+
if (
|
|
6908
|
+
!suppressContentHistoryRef.current &&
|
|
6909
|
+
shouldRecordHistory &&
|
|
6910
|
+
previousContent !== nextContent
|
|
6911
|
+
) {
|
|
6912
|
+
recordContentHistoryEntry({
|
|
6913
|
+
fileId,
|
|
6914
|
+
before: previousContent,
|
|
6915
|
+
after: nextContent,
|
|
6916
|
+
});
|
|
6917
|
+
}
|
|
6861
6918
|
if (options.updatedAt) {
|
|
6862
6919
|
clearPendingLocalFileContent(fileId);
|
|
6863
6920
|
} else {
|
|
@@ -6902,9 +6959,11 @@ export default function DesignEditor() {
|
|
|
6902
6959
|
cancelQueuedFileContentSave,
|
|
6903
6960
|
clearPendingLocalFileContent,
|
|
6904
6961
|
files,
|
|
6962
|
+
getScreenContent,
|
|
6905
6963
|
id,
|
|
6906
6964
|
markPendingLocalFileContent,
|
|
6907
6965
|
queryClient,
|
|
6966
|
+
recordContentHistoryEntry,
|
|
6908
6967
|
saveFileContent,
|
|
6909
6968
|
],
|
|
6910
6969
|
);
|
|
@@ -9461,10 +9520,27 @@ export default function DesignEditor() {
|
|
|
9461
9520
|
destNodeAttrId,
|
|
9462
9521
|
);
|
|
9463
9522
|
|
|
9523
|
+
recordContentHistoryEntry({
|
|
9524
|
+
changes: [
|
|
9525
|
+
{
|
|
9526
|
+
fileId: sourceScreenId,
|
|
9527
|
+
before: sourceContent,
|
|
9528
|
+
after: result.sourceHtml,
|
|
9529
|
+
},
|
|
9530
|
+
{
|
|
9531
|
+
fileId: targetScreenId,
|
|
9532
|
+
before: destContent,
|
|
9533
|
+
after: strippedDest,
|
|
9534
|
+
},
|
|
9535
|
+
],
|
|
9536
|
+
});
|
|
9537
|
+
|
|
9464
9538
|
applyFileContentUpdate(sourceScreenId, result.sourceHtml, {
|
|
9539
|
+
recordHistory: false,
|
|
9465
9540
|
refreshPreview: true,
|
|
9466
9541
|
});
|
|
9467
9542
|
applyFileContentUpdate(targetScreenId, strippedDest, {
|
|
9543
|
+
recordHistory: false,
|
|
9468
9544
|
refreshPreview: true,
|
|
9469
9545
|
});
|
|
9470
9546
|
|
|
@@ -9478,7 +9554,13 @@ export default function DesignEditor() {
|
|
|
9478
9554
|
setSelectedElement(elementInfoFromCodeLayerNode(movedNodeFinal));
|
|
9479
9555
|
}
|
|
9480
9556
|
},
|
|
9481
|
-
[
|
|
9557
|
+
[
|
|
9558
|
+
applyFileContentUpdate,
|
|
9559
|
+
canEditDesign,
|
|
9560
|
+
getScreenContent,
|
|
9561
|
+
recordContentHistoryEntry,
|
|
9562
|
+
t,
|
|
9563
|
+
],
|
|
9482
9564
|
);
|
|
9483
9565
|
|
|
9484
9566
|
/**
|
|
@@ -9573,10 +9655,27 @@ export default function DesignEditor() {
|
|
|
9573
9655
|
destNodeAttrId,
|
|
9574
9656
|
);
|
|
9575
9657
|
|
|
9658
|
+
recordContentHistoryEntry({
|
|
9659
|
+
changes: [
|
|
9660
|
+
{
|
|
9661
|
+
fileId: sourceScreenId,
|
|
9662
|
+
before: sourceContent,
|
|
9663
|
+
after: result.sourceHtml,
|
|
9664
|
+
},
|
|
9665
|
+
{
|
|
9666
|
+
fileId: targetScreenId,
|
|
9667
|
+
before: destContent,
|
|
9668
|
+
after: strippedDest,
|
|
9669
|
+
},
|
|
9670
|
+
],
|
|
9671
|
+
});
|
|
9672
|
+
|
|
9576
9673
|
applyFileContentUpdate(sourceScreenId, result.sourceHtml, {
|
|
9674
|
+
recordHistory: false,
|
|
9577
9675
|
refreshPreview: true,
|
|
9578
9676
|
});
|
|
9579
9677
|
applyFileContentUpdate(targetScreenId, strippedDest, {
|
|
9678
|
+
recordHistory: false,
|
|
9580
9679
|
refreshPreview: true,
|
|
9581
9680
|
});
|
|
9582
9681
|
|
|
@@ -9592,7 +9691,13 @@ export default function DesignEditor() {
|
|
|
9592
9691
|
setSelectedElement(elementInfoFromCodeLayerNode(movedNodeFinal));
|
|
9593
9692
|
}
|
|
9594
9693
|
},
|
|
9595
|
-
[
|
|
9694
|
+
[
|
|
9695
|
+
applyFileContentUpdate,
|
|
9696
|
+
canEditDesign,
|
|
9697
|
+
getScreenContent,
|
|
9698
|
+
recordContentHistoryEntry,
|
|
9699
|
+
t,
|
|
9700
|
+
],
|
|
9596
9701
|
);
|
|
9597
9702
|
|
|
9598
9703
|
const handleCutSelection = useCallback(async () => {
|
|
@@ -9800,12 +9905,12 @@ export default function DesignEditor() {
|
|
|
9800
9905
|
const entry =
|
|
9801
9906
|
contentUndoStackRef.current[contentUndoStackRef.current.length - 1];
|
|
9802
9907
|
if (!entry) return false;
|
|
9803
|
-
|
|
9804
|
-
entry
|
|
9805
|
-
|
|
9806
|
-
|
|
9807
|
-
|
|
9808
|
-
|
|
9908
|
+
const changes = getAvailableContentHistoryChanges(
|
|
9909
|
+
entry,
|
|
9910
|
+
files.map((file) => file.id),
|
|
9911
|
+
activeFile?.id,
|
|
9912
|
+
);
|
|
9913
|
+
if (changes.length === 0) return false;
|
|
9809
9914
|
contentUndoStackRef.current.pop();
|
|
9810
9915
|
contentRedoStackRef.current = [
|
|
9811
9916
|
...contentRedoStackRef.current.slice(-(MAX_DESIGN_UNDO_STACK - 1)),
|
|
@@ -9817,27 +9922,34 @@ export default function DesignEditor() {
|
|
|
9817
9922
|
];
|
|
9818
9923
|
suppressContentHistoryRef.current = true;
|
|
9819
9924
|
try {
|
|
9820
|
-
|
|
9821
|
-
|
|
9822
|
-
|
|
9823
|
-
|
|
9824
|
-
|
|
9825
|
-
|
|
9826
|
-
|
|
9827
|
-
|
|
9828
|
-
|
|
9925
|
+
for (const change of changes) {
|
|
9926
|
+
if (change.fileId === activeFile?.id) {
|
|
9927
|
+
applyLocalContentUpdate(change.before, {
|
|
9928
|
+
refreshPreview: false,
|
|
9929
|
+
immediateSave: true,
|
|
9930
|
+
recordHistory: false,
|
|
9931
|
+
});
|
|
9932
|
+
} else {
|
|
9933
|
+
applyFileContentUpdate(change.fileId, change.before, {
|
|
9934
|
+
recordHistory: false,
|
|
9935
|
+
refreshPreview: false,
|
|
9936
|
+
});
|
|
9937
|
+
}
|
|
9829
9938
|
}
|
|
9830
9939
|
} finally {
|
|
9831
9940
|
suppressContentHistoryRef.current = false;
|
|
9832
9941
|
}
|
|
9833
|
-
|
|
9942
|
+
const activeChange = changes.find(
|
|
9943
|
+
(change) => change.fileId === activeFile?.id,
|
|
9944
|
+
);
|
|
9945
|
+
if (activeChange) {
|
|
9834
9946
|
setSelectedElement((prev) => {
|
|
9835
9947
|
if (!prev) return prev;
|
|
9836
|
-
return refreshElementInfoFromContent(
|
|
9948
|
+
return refreshElementInfoFromContent(activeChange.before, prev);
|
|
9837
9949
|
});
|
|
9838
9950
|
setHoveredElement((prev) => {
|
|
9839
9951
|
if (!prev) return prev;
|
|
9840
|
-
return refreshElementInfoFromContent(
|
|
9952
|
+
return refreshElementInfoFromContent(activeChange.before, prev);
|
|
9841
9953
|
});
|
|
9842
9954
|
}
|
|
9843
9955
|
return true;
|
|
@@ -9927,12 +10039,12 @@ export default function DesignEditor() {
|
|
|
9927
10039
|
const entry =
|
|
9928
10040
|
contentRedoStackRef.current[contentRedoStackRef.current.length - 1];
|
|
9929
10041
|
if (!entry) return false;
|
|
9930
|
-
|
|
9931
|
-
entry
|
|
9932
|
-
|
|
9933
|
-
|
|
9934
|
-
|
|
9935
|
-
|
|
10042
|
+
const changes = getAvailableContentHistoryChanges(
|
|
10043
|
+
entry,
|
|
10044
|
+
files.map((file) => file.id),
|
|
10045
|
+
activeFile?.id,
|
|
10046
|
+
);
|
|
10047
|
+
if (changes.length === 0) return false;
|
|
9936
10048
|
contentRedoStackRef.current.pop();
|
|
9937
10049
|
contentUndoStackRef.current = [
|
|
9938
10050
|
...contentUndoStackRef.current.slice(-(MAX_DESIGN_UNDO_STACK - 1)),
|
|
@@ -9944,27 +10056,34 @@ export default function DesignEditor() {
|
|
|
9944
10056
|
];
|
|
9945
10057
|
suppressContentHistoryRef.current = true;
|
|
9946
10058
|
try {
|
|
9947
|
-
|
|
9948
|
-
|
|
9949
|
-
|
|
9950
|
-
|
|
9951
|
-
|
|
9952
|
-
|
|
9953
|
-
|
|
9954
|
-
|
|
9955
|
-
|
|
10059
|
+
for (const change of changes) {
|
|
10060
|
+
if (change.fileId === activeFile?.id) {
|
|
10061
|
+
applyLocalContentUpdate(change.after, {
|
|
10062
|
+
refreshPreview: false,
|
|
10063
|
+
immediateSave: true,
|
|
10064
|
+
recordHistory: false,
|
|
10065
|
+
});
|
|
10066
|
+
} else {
|
|
10067
|
+
applyFileContentUpdate(change.fileId, change.after, {
|
|
10068
|
+
recordHistory: false,
|
|
10069
|
+
refreshPreview: false,
|
|
10070
|
+
});
|
|
10071
|
+
}
|
|
9956
10072
|
}
|
|
9957
10073
|
} finally {
|
|
9958
10074
|
suppressContentHistoryRef.current = false;
|
|
9959
10075
|
}
|
|
9960
|
-
|
|
10076
|
+
const activeChange = changes.find(
|
|
10077
|
+
(change) => change.fileId === activeFile?.id,
|
|
10078
|
+
);
|
|
10079
|
+
if (activeChange) {
|
|
9961
10080
|
setSelectedElement((prev) => {
|
|
9962
10081
|
if (!prev) return prev;
|
|
9963
|
-
return refreshElementInfoFromContent(
|
|
10082
|
+
return refreshElementInfoFromContent(activeChange.after, prev);
|
|
9964
10083
|
});
|
|
9965
10084
|
setHoveredElement((prev) => {
|
|
9966
10085
|
if (!prev) return prev;
|
|
9967
|
-
return refreshElementInfoFromContent(
|
|
10086
|
+
return refreshElementInfoFromContent(activeChange.after, prev);
|
|
9968
10087
|
});
|
|
9969
10088
|
}
|
|
9970
10089
|
return true;
|
|
@@ -10077,6 +10196,15 @@ export default function DesignEditor() {
|
|
|
10077
10196
|
transition?.updateCallbackDone?.catch(() => {});
|
|
10078
10197
|
}, []);
|
|
10079
10198
|
|
|
10199
|
+
const getRestoredOverviewSelection = useCallback(() => {
|
|
10200
|
+
const fileIds = new Set(files.map((file) => file.id));
|
|
10201
|
+
const restored = lastOverviewSelectedScreenIdsRef.current.filter((id) =>
|
|
10202
|
+
fileIds.has(id),
|
|
10203
|
+
);
|
|
10204
|
+
if (restored.length > 0) return restored;
|
|
10205
|
+
return activeFileId && fileIds.has(activeFileId) ? [activeFileId] : [];
|
|
10206
|
+
}, [activeFileId, files]);
|
|
10207
|
+
|
|
10080
10208
|
const enterOverviewFromZoom = useCallback(() => {
|
|
10081
10209
|
if (viewModeRef.current === "overview") return;
|
|
10082
10210
|
viewModeRef.current = "overview";
|
|
@@ -10084,6 +10212,7 @@ export default function DesignEditor() {
|
|
|
10084
10212
|
pendingOverviewLayerSelectionRef.current = null;
|
|
10085
10213
|
clearPendingOverviewLayerSelectionTimer();
|
|
10086
10214
|
setCreatedOverviewLayerSelection(null);
|
|
10215
|
+
const restoredOverviewSelection = getRestoredOverviewSelection();
|
|
10087
10216
|
runEditorViewTransition(() => {
|
|
10088
10217
|
setDrawMode(false);
|
|
10089
10218
|
setPinMode(false);
|
|
@@ -10091,9 +10220,15 @@ export default function DesignEditor() {
|
|
|
10091
10220
|
setSelectedElement(null);
|
|
10092
10221
|
setHoveredElement(null);
|
|
10093
10222
|
setActiveTool("move");
|
|
10223
|
+
setOverviewSelectedScreenIds(restoredOverviewSelection);
|
|
10224
|
+
setSelectedLayerIdsState(restoredOverviewSelection);
|
|
10094
10225
|
setViewMode("overview");
|
|
10095
10226
|
});
|
|
10096
|
-
}, [
|
|
10227
|
+
}, [
|
|
10228
|
+
clearPendingOverviewLayerSelectionTimer,
|
|
10229
|
+
getRestoredOverviewSelection,
|
|
10230
|
+
runEditorViewTransition,
|
|
10231
|
+
]);
|
|
10097
10232
|
|
|
10098
10233
|
const enterSingleScreen = useCallback(
|
|
10099
10234
|
(fileId?: string | null) => {
|
|
@@ -10198,6 +10333,14 @@ export default function DesignEditor() {
|
|
|
10198
10333
|
|
|
10199
10334
|
const handleSidebarScreenSelect = useCallback(
|
|
10200
10335
|
(screenId: string) => {
|
|
10336
|
+
if (
|
|
10337
|
+
viewModeRef.current === "overview" &&
|
|
10338
|
+
overviewSelectedScreenIds.length > 0
|
|
10339
|
+
) {
|
|
10340
|
+
lastOverviewSelectedScreenIdsRef.current = [
|
|
10341
|
+
...overviewSelectedScreenIds,
|
|
10342
|
+
];
|
|
10343
|
+
}
|
|
10201
10344
|
pendingOverviewScreenSelectionRef.current = null;
|
|
10202
10345
|
pendingOverviewLayerSelectionRef.current = null;
|
|
10203
10346
|
clearPendingOverviewLayerSelectionTimer();
|
|
@@ -10206,16 +10349,21 @@ export default function DesignEditor() {
|
|
|
10206
10349
|
setSelectedLayerIdsState([]);
|
|
10207
10350
|
enterSingleScreen(screenId);
|
|
10208
10351
|
},
|
|
10209
|
-
[
|
|
10352
|
+
[
|
|
10353
|
+
clearPendingOverviewLayerSelectionTimer,
|
|
10354
|
+
enterSingleScreen,
|
|
10355
|
+
overviewSelectedScreenIds,
|
|
10356
|
+
],
|
|
10210
10357
|
);
|
|
10211
10358
|
|
|
10212
10359
|
const handleSidebarScreenOverview = useCallback(() => {
|
|
10360
|
+
const restoredOverviewSelection = getRestoredOverviewSelection();
|
|
10213
10361
|
pendingOverviewScreenSelectionRef.current = null;
|
|
10214
10362
|
pendingOverviewLayerSelectionRef.current = null;
|
|
10215
10363
|
clearPendingOverviewLayerSelectionTimer();
|
|
10216
10364
|
setCreatedOverviewLayerSelection(null);
|
|
10217
|
-
setOverviewSelectedScreenIds(
|
|
10218
|
-
setSelectedLayerIdsState(
|
|
10365
|
+
setOverviewSelectedScreenIds(restoredOverviewSelection);
|
|
10366
|
+
setSelectedLayerIdsState(restoredOverviewSelection);
|
|
10219
10367
|
if (viewModeRef.current === "overview") {
|
|
10220
10368
|
setDrawMode(false);
|
|
10221
10369
|
setPinMode(false);
|
|
@@ -10226,7 +10374,11 @@ export default function DesignEditor() {
|
|
|
10226
10374
|
return;
|
|
10227
10375
|
}
|
|
10228
10376
|
enterOverviewFromZoom();
|
|
10229
|
-
}, [
|
|
10377
|
+
}, [
|
|
10378
|
+
clearPendingOverviewLayerSelectionTimer,
|
|
10379
|
+
enterOverviewFromZoom,
|
|
10380
|
+
getRestoredOverviewSelection,
|
|
10381
|
+
]);
|
|
10230
10382
|
|
|
10231
10383
|
const handlePinToolToggle = useCallback(() => {
|
|
10232
10384
|
if (!activeFile || !canEditDesign) return;
|
|
@@ -12183,6 +12335,7 @@ ${serializedHtml}
|
|
|
12183
12335
|
// Group by source file so multiple nodes from the same source are
|
|
12184
12336
|
// applied sequentially against the running source content.
|
|
12185
12337
|
const sourceContentMap = new Map<string, string>();
|
|
12338
|
+
const sourceOriginalContentMap = new Map<string, string>();
|
|
12186
12339
|
const movedNodeIdByDraggedId = new Map<string, string>();
|
|
12187
12340
|
for (const { draggedId, sourceFileId } of getLayerMoveIterationOrder(
|
|
12188
12341
|
crossFileDrags,
|
|
@@ -12197,6 +12350,9 @@ ${serializedHtml}
|
|
|
12197
12350
|
sourceFileContent: srcFile.content,
|
|
12198
12351
|
sourceContentMap,
|
|
12199
12352
|
});
|
|
12353
|
+
if (!sourceOriginalContentMap.has(sourceFileId)) {
|
|
12354
|
+
sourceOriginalContentMap.set(sourceFileId, currentSourceContent);
|
|
12355
|
+
}
|
|
12200
12356
|
|
|
12201
12357
|
// The dragged node's data-agent-native-node-id is the node id tracked
|
|
12202
12358
|
// by code-layer. Look up the actual attribute value from the owner.
|
|
@@ -12274,9 +12430,37 @@ ${serializedHtml}
|
|
|
12274
12430
|
});
|
|
12275
12431
|
}
|
|
12276
12432
|
|
|
12433
|
+
const hasCrossFileMoves = sourceContentMap.size > 0;
|
|
12434
|
+
if (hasCrossFileMoves) {
|
|
12435
|
+
recordContentHistoryEntry({
|
|
12436
|
+
changes: [
|
|
12437
|
+
...Array.from(sourceContentMap.entries()).map(
|
|
12438
|
+
([sourceFileId, newSourceContent]) => ({
|
|
12439
|
+
fileId: sourceFileId,
|
|
12440
|
+
before:
|
|
12441
|
+
sourceOriginalContentMap.get(sourceFileId) ??
|
|
12442
|
+
files.find((file) => file.id === sourceFileId)?.content ??
|
|
12443
|
+
"",
|
|
12444
|
+
after: newSourceContent,
|
|
12445
|
+
}),
|
|
12446
|
+
),
|
|
12447
|
+
...(nextDestContent !== destContent
|
|
12448
|
+
? [
|
|
12449
|
+
{
|
|
12450
|
+
fileId: targetOwner.fileId,
|
|
12451
|
+
before: destContent,
|
|
12452
|
+
after: nextDestContent,
|
|
12453
|
+
},
|
|
12454
|
+
]
|
|
12455
|
+
: []),
|
|
12456
|
+
],
|
|
12457
|
+
});
|
|
12458
|
+
}
|
|
12459
|
+
|
|
12277
12460
|
// Persist source files that changed.
|
|
12278
12461
|
for (const [sourceFileId, newSourceContent] of sourceContentMap) {
|
|
12279
12462
|
applyFileContentUpdate(sourceFileId, newSourceContent, {
|
|
12463
|
+
recordHistory: !hasCrossFileMoves,
|
|
12280
12464
|
refreshPreview: false,
|
|
12281
12465
|
});
|
|
12282
12466
|
}
|
|
@@ -12284,6 +12468,7 @@ ${serializedHtml}
|
|
|
12284
12468
|
// Persist dest file (which may also be the active file).
|
|
12285
12469
|
if (nextDestContent !== destContent) {
|
|
12286
12470
|
applyFileContentUpdate(targetOwner.fileId, nextDestContent, {
|
|
12471
|
+
recordHistory: !hasCrossFileMoves,
|
|
12287
12472
|
refreshPreview: false,
|
|
12288
12473
|
});
|
|
12289
12474
|
}
|
|
@@ -12297,6 +12482,7 @@ ${serializedHtml}
|
|
|
12297
12482
|
effectiveCodeLayerState,
|
|
12298
12483
|
files,
|
|
12299
12484
|
getFreshActiveContent,
|
|
12485
|
+
recordContentHistoryEntry,
|
|
12300
12486
|
t,
|
|
12301
12487
|
],
|
|
12302
12488
|
);
|
|
@@ -11,7 +11,7 @@ import { Link } from "react-router";
|
|
|
11
11
|
import { Button } from "@/components/ui/button";
|
|
12
12
|
|
|
13
13
|
function buildSignInHref(): string {
|
|
14
|
-
const ret = "
|
|
14
|
+
const ret = "/visual-edit?intent=save";
|
|
15
15
|
return `${agentNativePath("/_agent-native/sign-in")}?return=${encodeURIComponent(ret)}`;
|
|
16
16
|
}
|
|
17
17
|
|
|
@@ -36,27 +36,47 @@ export type TweakSelections = Record<string, string | number | boolean>;
|
|
|
36
36
|
*/
|
|
37
37
|
const CSS_CUSTOM_PROPERTY_NAME = /^--[-_a-zA-Z0-9]+$/;
|
|
38
38
|
|
|
39
|
+
export function isSafeCssVarName(value: string): boolean {
|
|
40
|
+
return CSS_CUSTOM_PROPERTY_NAME.test(value);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function isSafeCssTokenValue(value: string): boolean {
|
|
44
|
+
return (
|
|
45
|
+
value.length > 0 &&
|
|
46
|
+
value.length <= 300 &&
|
|
47
|
+
!/[;{}<>]/.test(value) &&
|
|
48
|
+
!/\/\*/.test(value) &&
|
|
49
|
+
!/[\u0000-\u001f\u007f]/.test(value)
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
39
53
|
export function resolveTweaksToCssVars(
|
|
40
54
|
tweaks: TweakDefinition[],
|
|
41
55
|
selections: TweakSelections,
|
|
42
56
|
): Record<string, string> {
|
|
43
57
|
const out: Record<string, string> = {};
|
|
44
58
|
for (const t of tweaks) {
|
|
45
|
-
if (!t.cssVar) continue;
|
|
59
|
+
if (!t.cssVar || !isSafeCssVarName(t.cssVar)) continue;
|
|
46
60
|
const v = selections[t.id] ?? t.defaultValue;
|
|
47
|
-
|
|
61
|
+
const resolved = stringifyCssVarValue(t.cssVar, v, t.unit);
|
|
62
|
+
if (isSafeCssTokenValue(resolved)) {
|
|
63
|
+
out[t.cssVar] = resolved;
|
|
64
|
+
}
|
|
48
65
|
}
|
|
49
66
|
|
|
50
67
|
for (const [key, value] of Object.entries(selections)) {
|
|
51
68
|
if (!isDirectCssVarSelectionKey(key)) continue;
|
|
52
|
-
|
|
69
|
+
const resolved = stringifyCssVarValue(key, value);
|
|
70
|
+
if (isSafeCssTokenValue(resolved)) {
|
|
71
|
+
out[key] = resolved;
|
|
72
|
+
}
|
|
53
73
|
}
|
|
54
74
|
|
|
55
75
|
return out;
|
|
56
76
|
}
|
|
57
77
|
|
|
58
78
|
export function isDirectCssVarSelectionKey(key: string): boolean {
|
|
59
|
-
return
|
|
79
|
+
return isSafeCssVarName(key);
|
|
60
80
|
}
|
|
61
81
|
|
|
62
82
|
function stringifyCssVarValue(
|
|
@@ -84,8 +104,11 @@ export function renderResolvedRootBlock(
|
|
|
84
104
|
resolvedCssVars: Record<string, string>,
|
|
85
105
|
): string {
|
|
86
106
|
const entries = Object.entries(resolvedCssVars);
|
|
87
|
-
|
|
88
|
-
|
|
107
|
+
const safeEntries = entries.filter(
|
|
108
|
+
([name, value]) => isSafeCssVarName(name) && isSafeCssTokenValue(value),
|
|
109
|
+
);
|
|
110
|
+
if (safeEntries.length === 0) return "";
|
|
111
|
+
const decls = safeEntries
|
|
89
112
|
.map(([name, value]) => ` ${name}: ${value};`)
|
|
90
113
|
.join("\n");
|
|
91
114
|
return `:root {\n${decls}\n}`;
|
package/dist/collab/routes.d.ts
CHANGED
|
@@ -26,8 +26,8 @@ export declare const getCollabState: import("h3").EventHandlerWithFetch<import("
|
|
|
26
26
|
* Body: { update: string (base64), requestSource?: string }
|
|
27
27
|
*/
|
|
28
28
|
export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
29
|
-
ok?: undefined;
|
|
30
29
|
error: string;
|
|
30
|
+
ok?: undefined;
|
|
31
31
|
} | {
|
|
32
32
|
error?: undefined;
|
|
33
33
|
ok: boolean;
|
|
@@ -3,18 +3,18 @@ declare const _default: import("../../action.js").ActionDefinition<{
|
|
|
3
3
|
url?: string;
|
|
4
4
|
filename?: string;
|
|
5
5
|
}, {
|
|
6
|
-
id?: undefined;
|
|
7
6
|
error: string;
|
|
8
7
|
configured?: undefined;
|
|
9
8
|
connectPath?: undefined;
|
|
10
9
|
url?: undefined;
|
|
10
|
+
id?: undefined;
|
|
11
11
|
provider?: undefined;
|
|
12
12
|
} | {
|
|
13
|
-
id?: undefined;
|
|
14
13
|
error: string;
|
|
15
14
|
configured: boolean;
|
|
16
15
|
connectPath: string;
|
|
17
16
|
url?: undefined;
|
|
17
|
+
id?: undefined;
|
|
18
18
|
provider?: undefined;
|
|
19
19
|
} | {
|
|
20
20
|
error?: undefined;
|