@agent-native/core 0.192.0 → 0.192.1-nightly-20260926144920
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/templates/calendar/actions/cancel-booking.ts +12 -4
- package/corpus/templates/calendar/actions/list-bookings.ts +23 -2
- package/corpus/templates/calendar/actions/list-events.ts +42 -29
- package/corpus/templates/calendar/actions/update-booking-link.ts +27 -1
- package/corpus/templates/calendar/app/hooks/use-bookings.ts +9 -2
- package/corpus/templates/calendar/app/i18n/en-US.ts +8 -0
- package/corpus/templates/calendar/app/lib/booking-sorting.ts +7 -0
- package/corpus/templates/calendar/app/lib/event-colors.ts +29 -4
- package/corpus/templates/calendar/app/pages/BookingsList.tsx +99 -19
- package/corpus/templates/calendar/app/pages/CalendarView.tsx +5 -11
- package/corpus/templates/calendar/app/pages/ManageBookingPage.tsx +5 -0
- package/corpus/templates/calendar/changelog/2026-09-25-ambiguous-zoom-bookings-need-review.md +6 -0
- package/corpus/templates/calendar/changelog/2026-09-25-bookings-list-shows-newest-bookings-first.md +6 -0
- package/corpus/templates/calendar/changelog/2026-09-25-canceling-a-booking-now-resolves-its-zoom-meeting-first.md +6 -0
- package/corpus/templates/calendar/changelog/2026-09-25-colleague-events-use-sidebar-colors.md +6 -0
- package/corpus/templates/calendar/changelog/2026-09-25-public-booking-pages-reject-malformed-meeting-settings-befor.md +6 -0
- package/corpus/templates/calendar/changelog/2026-09-25-shared-booking-cancellations-require-editor-access-and-unres.md +6 -0
- package/corpus/templates/calendar/server/db/schema.ts +5 -0
- package/corpus/templates/calendar/server/handlers/availability.ts +32 -29
- package/corpus/templates/calendar/server/handlers/bookings.ts +189 -62
- package/corpus/templates/calendar/server/lib/booking-link-utils.ts +46 -1
- package/corpus/templates/calendar/server/lib/zoom.ts +66 -1
- package/corpus/templates/calendar/server/plugins/db.ts +13 -0
- package/corpus/templates/calendar/shared/api.ts +4 -0
- package/corpus/templates/clips/app/components/capture-install-options.tsx +9 -6
- package/corpus/templates/clips/app/components/clips-command-menu.tsx +4 -0
- package/corpus/templates/clips/app/i18n/en-US.ts +5 -4
- package/corpus/templates/clips/app/routes/_app.settings._index.tsx +4 -0
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +7 -33
- package/corpus/templates/clips/changelog/2026-09-26-desktop-platform-icon.md +6 -0
- package/corpus/templates/clips/changelog/2026-09-26-the-empty-comments-state-now-explains-how-screen-recordings-.md +6 -0
- package/corpus/templates/clips/changelog/2026-09-26-the-empty-comments-view-has-a-clearer-headline-and-a-focused.md +6 -0
- package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +26 -2
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +74 -40
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +64 -42
- package/corpus/templates/design/app/components/design/design-canvas/creation.ts +1 -0
- package/corpus/templates/design/app/components/design/multi-screen/types.ts +5 -2
- package/corpus/templates/design/app/pages/design-editor/commands/duplicate-screen.ts +56 -25
- package/corpus/templates/design/app/routes/design-systems.tsx +2 -4
- package/corpus/templates/design/app/routes/design.$id.tsx +2 -1
- package/corpus/templates/design/app/routes/public-design-meta.ts +0 -39
- package/corpus/templates/design/app/routes/templates.tsx +2 -4
- package/corpus/templates/design/app/routes/visual-edit_.$id.tsx +2 -1
- package/corpus/templates/design/changelog/2026-09-25-escape-finishes-open-pen-paths.md +7 -0
- package/corpus/templates/design/changelog/2026-09-25-pen-paths-remain-available-after-save-failures-and-retries-k.md +6 -0
- package/corpus/templates/design/changelog/2026-09-25-pen-retries-keep-extending-the-selected-vector-after-a-save-.md +6 -0
- package/corpus/templates/design/changelog/2026-09-25-pressing-enter-finishes-a-new-pen-path-and-selects-its-vector-on-move.md +6 -0
- package/corpus/templates/design/changelog/2026-09-26-cmd-d-places-screen-duplicates-in-the-next-open-slot.md +6 -0
- package/corpus/templates/design/changelog/2026-09-26-dropping-a-layer-beside-a-sibling-keeps-that-insertion-point.md +6 -0
- package/corpus/templates/design/changelog/2026-09-26-layers-dragged-out-of-frames-now-land-at-the-board-level-and.md +6 -0
- package/corpus/templates/design/changelog/2026-09-26-opening-shared-designs-no-longer-fails-during-client-startup.md +6 -0
- package/corpus/templates/design/package.json +1 -1
- package/corpus/templates/design/scripts/check-client-server-boundary.ts +55 -0
- package/corpus/templates/design/server/lib/public-design-meta.server.ts +40 -0
- package/corpus/templates/mail/app/components/email/EmailListItem.tsx +11 -9
- package/corpus/templates/mail/app/components/email/EmailThread.tsx +9 -1
- package/corpus/templates/mail/app/components/layout/AppLayout.tsx +2 -2
- package/corpus/templates/mail/app/components/settings/AiFilterSection.tsx +25 -2
- package/corpus/templates/mail/app/hooks/use-emails.ts +159 -27
- package/corpus/templates/mail/app/i18n/en-US.ts +2 -1
- package/corpus/templates/mail/app/lib/label-display.ts +6 -0
- package/corpus/templates/mail/app/pages/InboxPage.tsx +5 -0
- package/corpus/templates/mail/changelog/2026-09-26-fixed-importance-actions-label-display-and-triage-loading-fe.md +6 -0
- package/corpus/templates/mail/changelog/2026-09-26-mail-cancels-stale-thread-read-cooldown-retries-after-a-newe.md +6 -0
- package/corpus/templates/mail/server/lib/google-api.ts +12 -7
- package/corpus/templates/slides/app/i18n/en-US.ts +0 -1
- package/corpus/templates/slides/app/pages/Index.tsx +1 -29
- package/corpus/templates/slides/changelog/2026-09-26-create-decks-from-the-prompt-without-a-separate-new-deck-button.md +6 -0
- package/corpus/templates/tasks/.agents/skills/agent-watchdog/SKILL.md +7 -2
- package/dist/client/settings/UsageSection.js +18 -5
- package/dist/client/settings/recent-prompt-groups.d.ts +12 -0
- package/dist/client/settings/recent-prompt-groups.js +20 -0
- package/dist/client/use-action.js +7 -0
- package/dist/observability/routes.d.ts +5 -5
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +14 -14
- package/dist/provider-api/actions/provider-api.d.ts +13 -13
- package/dist/secrets/routes.d.ts +3 -3
- package/dist/server/action-routes.js +25 -2
- package/dist/server/agent-engine-ollama-models-route.d.ts +2 -2
- package/package.json +3 -3
|
@@ -882,11 +882,6 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
882
882
|
setPenPointer(null);
|
|
883
883
|
setPenCloseHover(false);
|
|
884
884
|
}, []);
|
|
885
|
-
const cancelActivePenPath = useCallback(() => {
|
|
886
|
-
clearActivePenPath();
|
|
887
|
-
penContinuesVectorEditRef.current = false;
|
|
888
|
-
penContinuationBaseCountRef.current = 0;
|
|
889
|
-
}, [clearActivePenPath]);
|
|
890
885
|
// Last raw client point the pen ghost/close-hover preview was computed
|
|
891
886
|
// from (P18). A wheel pan/zoom gesture mutates pan/zoom every animation
|
|
892
887
|
// frame via applyViewToDom without the mouse itself moving, so the
|
|
@@ -5636,19 +5631,16 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
5636
5631
|
useEffect(() => {
|
|
5637
5632
|
const handleKeyDown = (event: KeyboardEvent) => {
|
|
5638
5633
|
if (event.key !== "Escape") return;
|
|
5639
|
-
const hasActivePenPath =
|
|
5640
|
-
Boolean(activePenPathRef.current) &&
|
|
5641
|
-
!isEditableHotkeyTarget(event.target);
|
|
5642
5634
|
const cancelled = cancelActiveDrag(
|
|
5643
5635
|
eventEpochMilliseconds(event.timeStamp),
|
|
5644
5636
|
);
|
|
5645
|
-
if (
|
|
5646
|
-
if (cancelled || hasActivePenPath) {
|
|
5637
|
+
if (cancelled) {
|
|
5647
5638
|
event.preventDefault();
|
|
5648
5639
|
event.stopPropagation();
|
|
5649
5640
|
event.stopImmediatePropagation();
|
|
5650
5641
|
return;
|
|
5651
5642
|
}
|
|
5643
|
+
if (activePenPathRef.current) return;
|
|
5652
5644
|
// No in-flight drag to cancel: Escape while in vector edit mode exits
|
|
5653
5645
|
// the mode entirely (matches Figma), rather than being a no-op.
|
|
5654
5646
|
if (vectorEdit) {
|
|
@@ -5663,7 +5655,7 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
5663
5655
|
return () => {
|
|
5664
5656
|
window.removeEventListener("keydown", handleKeyDown, true);
|
|
5665
5657
|
};
|
|
5666
|
-
}, [cancelActiveDrag,
|
|
5658
|
+
}, [cancelActiveDrag, vectorEdit]);
|
|
5667
5659
|
|
|
5668
5660
|
const beginPan = useCallback(
|
|
5669
5661
|
(e: React.MouseEvent) => {
|
|
@@ -6359,7 +6351,9 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
6359
6351
|
const lastPersisted = persistedEntries[persistedEntries.length - 1];
|
|
6360
6352
|
// Do not call onPrimitiveCreated for board objects (sentinel frameId).
|
|
6361
6353
|
if (lastPersisted && lastPersisted.frameId !== "__board__") {
|
|
6362
|
-
onPrimitiveCreated?.(lastPersisted.frameId, lastPersisted.nodeId
|
|
6354
|
+
onPrimitiveCreated?.(lastPersisted.frameId, lastPersisted.nodeId, {
|
|
6355
|
+
preserveActiveTool: true,
|
|
6356
|
+
});
|
|
6363
6357
|
}
|
|
6364
6358
|
}, [
|
|
6365
6359
|
onCreatePrimitive,
|
|
@@ -6382,7 +6376,7 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
6382
6376
|
nextTool?: "move" | "pen";
|
|
6383
6377
|
},
|
|
6384
6378
|
) => {
|
|
6385
|
-
|
|
6379
|
+
let nextTool: "move" | "pen" = options?.nextTool ?? "pen";
|
|
6386
6380
|
// Clear before committing: the commit flushes React synchronously, and
|
|
6387
6381
|
// an effect it wakes can re-enter here and commit the same path twice.
|
|
6388
6382
|
clearActivePenPath();
|
|
@@ -6392,31 +6386,47 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
6392
6386
|
}
|
|
6393
6387
|
|
|
6394
6388
|
if (penContinuesVectorEditRef.current) {
|
|
6395
|
-
penContinuesVectorEditRef.current = false;
|
|
6396
6389
|
const active = vectorEditRef.current;
|
|
6397
6390
|
const baseCount = penContinuationBaseCountRef.current;
|
|
6398
|
-
|
|
6399
|
-
|
|
6400
|
-
|
|
6401
|
-
|
|
6402
|
-
|
|
6403
|
-
|
|
6404
|
-
|
|
6405
|
-
|
|
6406
|
-
|
|
6407
|
-
|
|
6408
|
-
|
|
6409
|
-
|
|
6410
|
-
|
|
6411
|
-
|
|
6412
|
-
|
|
6413
|
-
|
|
6414
|
-
|
|
6415
|
-
|
|
6416
|
-
|
|
6417
|
-
|
|
6391
|
+
const changed =
|
|
6392
|
+
path.nodes.length > baseCount || path.closed !== active?.path.closed;
|
|
6393
|
+
const accepted =
|
|
6394
|
+
!changed ||
|
|
6395
|
+
Boolean(
|
|
6396
|
+
active &&
|
|
6397
|
+
active.onChange(
|
|
6398
|
+
{
|
|
6399
|
+
closed: path.closed,
|
|
6400
|
+
nodes: path.nodes.map((node) => ({
|
|
6401
|
+
...node,
|
|
6402
|
+
point: {
|
|
6403
|
+
x: node.point.x - active.originCanvas.x,
|
|
6404
|
+
y: node.point.y - active.originCanvas.y,
|
|
6405
|
+
},
|
|
6406
|
+
handleIn: node.handleIn
|
|
6407
|
+
? {
|
|
6408
|
+
x: node.handleIn.x - active.originCanvas.x,
|
|
6409
|
+
y: node.handleIn.y - active.originCanvas.y,
|
|
6410
|
+
}
|
|
6411
|
+
: undefined,
|
|
6412
|
+
handleOut: node.handleOut
|
|
6413
|
+
? {
|
|
6414
|
+
x: node.handleOut.x - active.originCanvas.x,
|
|
6415
|
+
y: node.handleOut.y - active.originCanvas.y,
|
|
6416
|
+
}
|
|
6417
|
+
: undefined,
|
|
6418
|
+
})),
|
|
6419
|
+
},
|
|
6420
|
+
"commit",
|
|
6421
|
+
),
|
|
6418
6422
|
);
|
|
6423
|
+
if (!accepted) {
|
|
6424
|
+
activePenPathRef.current = path;
|
|
6425
|
+
setActivePenPath(path);
|
|
6426
|
+
return;
|
|
6419
6427
|
}
|
|
6428
|
+
penContinuesVectorEditRef.current = false;
|
|
6429
|
+
penContinuationBaseCountRef.current = 0;
|
|
6420
6430
|
if (nextTool === "move") active?.onExit();
|
|
6421
6431
|
onActiveToolChange?.(nextTool);
|
|
6422
6432
|
return;
|
|
@@ -6432,7 +6442,6 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
6432
6442
|
updateNodeId: continuation.nodeId,
|
|
6433
6443
|
});
|
|
6434
6444
|
if (!persisted) {
|
|
6435
|
-
continuationPenPathRef.current = null;
|
|
6436
6445
|
activePenPathRef.current = path;
|
|
6437
6446
|
setActivePenPath(path);
|
|
6438
6447
|
return;
|
|
@@ -6445,6 +6454,7 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
6445
6454
|
const persisted = commitDraftPrimitive(draft, undefined, {
|
|
6446
6455
|
nextTool,
|
|
6447
6456
|
});
|
|
6457
|
+
if (!persisted) nextTool = "pen";
|
|
6448
6458
|
continuationPenPathRef.current =
|
|
6449
6459
|
persisted && !path.closed && options?.continueAfterCommit
|
|
6450
6460
|
? {
|
|
@@ -6455,9 +6465,7 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
6455
6465
|
: null;
|
|
6456
6466
|
}
|
|
6457
6467
|
// The parent selection callback receives nextTool, but board primitives
|
|
6458
|
-
// intentionally bypass
|
|
6459
|
-
// asynchronous selection reconciliation can otherwise paint Move for a
|
|
6460
|
-
// frame. Drive the controlled tool explicitly at the commit boundary.
|
|
6468
|
+
// intentionally bypass it, so update the controlled tool here too.
|
|
6461
6469
|
onActiveToolChange?.(nextTool);
|
|
6462
6470
|
},
|
|
6463
6471
|
[
|
|
@@ -10513,13 +10521,27 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
10513
10521
|
event.preventDefault();
|
|
10514
10522
|
event.stopPropagation();
|
|
10515
10523
|
event.stopImmediatePropagation();
|
|
10516
|
-
|
|
10517
|
-
|
|
10518
|
-
|
|
10524
|
+
if (dragState.current?.type === "pen-node") cancelActiveDrag();
|
|
10525
|
+
const pathToFinish = activePenPathRef.current;
|
|
10526
|
+
if (!pathToFinish) return;
|
|
10527
|
+
const continuesExistingPath =
|
|
10528
|
+
continuationPenPathRef.current !== null ||
|
|
10529
|
+
penContinuesVectorEditRef.current;
|
|
10530
|
+
finishPenPath(pathToFinish, {
|
|
10531
|
+
continueAfterCommit: continuationPenPathRef.current !== null,
|
|
10532
|
+
nextTool: continuesExistingPath ? "pen" : "move",
|
|
10519
10533
|
});
|
|
10520
10534
|
return;
|
|
10521
10535
|
}
|
|
10522
10536
|
|
|
10537
|
+
if (event.key === "Escape") {
|
|
10538
|
+
event.preventDefault();
|
|
10539
|
+
event.stopPropagation();
|
|
10540
|
+
event.stopImmediatePropagation();
|
|
10541
|
+
finishPenPath(path);
|
|
10542
|
+
return;
|
|
10543
|
+
}
|
|
10544
|
+
|
|
10523
10545
|
if (event.key === "Delete" || event.key === "Backspace") {
|
|
10524
10546
|
event.preventDefault();
|
|
10525
10547
|
event.stopPropagation();
|
|
@@ -10532,7 +10554,7 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
|
|
|
10532
10554
|
return () => {
|
|
10533
10555
|
window.removeEventListener("keydown", handleKeyDown, true);
|
|
10534
10556
|
};
|
|
10535
|
-
}, [finishPenPath, undoActivePenPathSegment]);
|
|
10557
|
+
}, [cancelActiveDrag, finishPenPath, undoActivePenPathSegment]);
|
|
10536
10558
|
|
|
10537
10559
|
useEffect(() => {
|
|
10538
10560
|
const tool = normalizeCanvasTool(activeTool ?? localActiveTool);
|
|
@@ -293,7 +293,10 @@ export interface MultiScreenCanvasProps {
|
|
|
293
293
|
onPrimitiveCreated?: (
|
|
294
294
|
screenId: string,
|
|
295
295
|
nodeId: string,
|
|
296
|
-
options?: {
|
|
296
|
+
options?: {
|
|
297
|
+
nextTool?: "move" | "pen";
|
|
298
|
+
preserveActiveTool?: boolean;
|
|
299
|
+
},
|
|
297
300
|
) => void;
|
|
298
301
|
onUpdatePenPath?: (
|
|
299
302
|
screenId: string,
|
|
@@ -855,7 +858,7 @@ export interface VectorEditOverlayState {
|
|
|
855
858
|
originCanvas: Point;
|
|
856
859
|
selectedAnchorIndex: number | null;
|
|
857
860
|
onSelectedAnchorChange: (nodeIndex: number | null) => void;
|
|
858
|
-
onChange: (nextPath: PenPath, phase: "preview" | "commit") =>
|
|
861
|
+
onChange: (nextPath: PenPath, phase: "preview" | "commit") => boolean;
|
|
859
862
|
onExit: () => void;
|
|
860
863
|
}
|
|
861
864
|
|
|
@@ -58,50 +58,76 @@ export function getDuplicateScreenGeometry(
|
|
|
58
58
|
sourceGeometry: FrameGeometry,
|
|
59
59
|
occupiedGeometries: readonly FrameGeometry[],
|
|
60
60
|
): FrameGeometry {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
(geometry) => geometry.x + geometry.width + DUPLICATE_SCREEN_GAP,
|
|
61
|
+
return {
|
|
62
|
+
...getFirstFreeDuplicateGeometry(
|
|
63
|
+
{
|
|
64
|
+
...sourceGeometry,
|
|
65
|
+
x: sourceGeometry.x + sourceGeometry.width + DUPLICATE_SCREEN_GAP,
|
|
66
|
+
y: sourceGeometry.y,
|
|
67
|
+
},
|
|
68
|
+
occupiedGeometries,
|
|
70
69
|
),
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
70
|
+
z: (sourceGeometry.z ?? 0) + 1,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function getFirstFreeDuplicateGeometry(
|
|
75
|
+
candidate: FrameGeometry,
|
|
76
|
+
occupiedGeometries: readonly FrameGeometry[],
|
|
77
|
+
): FrameGeometry {
|
|
78
|
+
let free = { ...candidate };
|
|
79
|
+
while (true) {
|
|
80
|
+
const overlap = occupiedGeometries
|
|
81
|
+
.filter((geometry) => duplicateGeometriesOverlap(free, geometry))
|
|
82
|
+
.sort((left, right) => left.x - right.x)[0];
|
|
83
|
+
if (!overlap) return free;
|
|
84
|
+
free = {
|
|
85
|
+
...free,
|
|
86
|
+
x: overlap.x + overlap.width + DUPLICATE_SCREEN_GAP,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
77
89
|
}
|
|
78
90
|
|
|
79
91
|
function duplicateGeometriesOverlap(
|
|
80
92
|
left: FrameGeometry,
|
|
81
93
|
right: FrameGeometry,
|
|
94
|
+
gap = DUPLICATE_SCREEN_GAP,
|
|
82
95
|
): boolean {
|
|
83
96
|
const sameRow =
|
|
84
97
|
left.y < right.y + right.height && right.y < left.y + left.height;
|
|
85
98
|
return (
|
|
86
99
|
sameRow &&
|
|
87
|
-
left.x < right.x + right.width +
|
|
88
|
-
right.x < left.x + left.width +
|
|
100
|
+
left.x < right.x + right.width + gap &&
|
|
101
|
+
right.x < left.x + left.width + gap
|
|
89
102
|
);
|
|
90
103
|
}
|
|
91
104
|
|
|
92
105
|
function reserveDuplicateGeometry(
|
|
93
106
|
filename: string,
|
|
94
107
|
candidate: FrameGeometry,
|
|
108
|
+
occupiedGeometries: readonly FrameGeometry[],
|
|
95
109
|
pendingGeometries: ReadonlyMap<string, FrameGeometry>,
|
|
110
|
+
preserveRequestedPosition: boolean,
|
|
96
111
|
): FrameGeometry {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
)
|
|
103
|
-
|
|
104
|
-
|
|
112
|
+
const otherPending = [...pendingGeometries.entries()]
|
|
113
|
+
.filter(([pendingFilename]) => pendingFilename !== filename)
|
|
114
|
+
.map(([, geometry]) => geometry);
|
|
115
|
+
const reserved =
|
|
116
|
+
preserveRequestedPosition &&
|
|
117
|
+
!otherPending.some((geometry) =>
|
|
118
|
+
duplicateGeometriesOverlap(candidate, geometry),
|
|
119
|
+
)
|
|
120
|
+
? { ...candidate }
|
|
121
|
+
: getFirstFreeDuplicateGeometry(candidate, [
|
|
122
|
+
...occupiedGeometries,
|
|
123
|
+
...otherPending,
|
|
124
|
+
]);
|
|
125
|
+
if (reserved.x === candidate.x && reserved.y === candidate.y) {
|
|
126
|
+
const overlappingZ = occupiedGeometries
|
|
127
|
+
.filter((geometry) => duplicateGeometriesOverlap(candidate, geometry, 0))
|
|
128
|
+
.map((geometry) => geometry.z ?? 0);
|
|
129
|
+
if (overlappingZ.length > 0) {
|
|
130
|
+
reserved.z = Math.max(reserved.z ?? 0, ...overlappingZ) + 1;
|
|
105
131
|
}
|
|
106
132
|
}
|
|
107
133
|
const pendingZ = [...pendingGeometries.values()].map(
|
|
@@ -185,6 +211,7 @@ export function runDuplicateScreen(
|
|
|
185
211
|
}: DuplicateScreenArgs,
|
|
186
212
|
screenId: string,
|
|
187
213
|
request?: {
|
|
214
|
+
mode?: "alt-click" | "alt-drag";
|
|
188
215
|
canvasPosition?: { x: number; y: number };
|
|
189
216
|
preserveCamera?: boolean;
|
|
190
217
|
historyBatchId?: string;
|
|
@@ -269,12 +296,16 @@ export function runDuplicateScreen(
|
|
|
269
296
|
z: (adjacentGeometry.z ?? 0) + (request.duplicateStackIndex ?? 0),
|
|
270
297
|
}
|
|
271
298
|
: adjacentGeometry);
|
|
299
|
+
const preserveRequestedPosition =
|
|
300
|
+
request?.canvasPosition !== undefined && request.mode !== "alt-click";
|
|
272
301
|
const createdGeometry = recoveryState?.geometry
|
|
273
302
|
? recoveryState.geometry
|
|
274
303
|
: reserveDuplicateGeometry(
|
|
275
304
|
filename,
|
|
276
305
|
requestedGeometry,
|
|
306
|
+
occupiedGeometries,
|
|
277
307
|
pendingDuplicateGeometriesRef.current,
|
|
308
|
+
preserveRequestedPosition,
|
|
278
309
|
);
|
|
279
310
|
pendingDuplicateGeometriesRef.current.set(filename, createdGeometry);
|
|
280
311
|
// Carry screen dimensions/height mode for every duplicate so the new frame
|
|
@@ -5,10 +5,8 @@ import type { LoaderFunctionArgs, MetaFunction } from "react-router";
|
|
|
5
5
|
import enUSMessages from "@/i18n/en-US";
|
|
6
6
|
|
|
7
7
|
import { getDb, schema } from "../../server/db";
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
publicDesignMetaLoaderData,
|
|
11
|
-
} from "./public-design-meta";
|
|
8
|
+
import { publicDesignMetaLoaderData } from "../../server/lib/public-design-meta.server";
|
|
9
|
+
import { designResourceMeta } from "./public-design-meta";
|
|
12
10
|
|
|
13
11
|
export { default } from "../pages/DesignSystems";
|
|
14
12
|
|
|
@@ -2,8 +2,9 @@ import type { LoaderFunctionArgs, MetaFunction } from "react-router";
|
|
|
2
2
|
|
|
3
3
|
import enUSMessages from "@/i18n/en-US";
|
|
4
4
|
|
|
5
|
+
import { loadPublicDesignMeta } from "../../server/lib/public-design-meta.server";
|
|
5
6
|
import DesignEditorRoute from "../pages/DesignEditor";
|
|
6
|
-
import { designResourceMeta
|
|
7
|
+
import { designResourceMeta } from "./public-design-meta";
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Keep the route module itself as a React Fast Refresh boundary. A bare
|
|
@@ -1,14 +1,8 @@
|
|
|
1
|
-
import { getConfiguredAppBasePath } from "@agent-native/core/server";
|
|
2
1
|
import {
|
|
3
|
-
SSR_QUERY_CACHE_KEY_HEADER,
|
|
4
2
|
buildResourceSocialMeta,
|
|
5
3
|
normalizeDocumentTitle,
|
|
6
4
|
type SocialMetaDescriptor,
|
|
7
5
|
} from "@agent-native/core/shared";
|
|
8
|
-
import { and, eq } from "drizzle-orm";
|
|
9
|
-
import { data } from "react-router";
|
|
10
|
-
|
|
11
|
-
import { getDb, schema } from "../../server/db";
|
|
12
6
|
|
|
13
7
|
export interface PublicDesignMetaData {
|
|
14
8
|
resource: { title: string; description: string | null } | null;
|
|
@@ -16,39 +10,6 @@ export interface PublicDesignMetaData {
|
|
|
16
10
|
basePath: string;
|
|
17
11
|
}
|
|
18
12
|
|
|
19
|
-
export function publicDesignMetaLoaderData(payload: PublicDesignMetaData) {
|
|
20
|
-
return data(payload, {
|
|
21
|
-
headers: { [SSR_QUERY_CACHE_KEY_HEADER]: "query" },
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export async function loadPublicDesignMeta(
|
|
26
|
-
id: string | undefined,
|
|
27
|
-
requestUrl: string,
|
|
28
|
-
): Promise<PublicDesignMetaData> {
|
|
29
|
-
const [resource] = id
|
|
30
|
-
? await getDb()
|
|
31
|
-
.select({
|
|
32
|
-
title: schema.designs.title,
|
|
33
|
-
description: schema.designs.description,
|
|
34
|
-
})
|
|
35
|
-
.from(schema.designs)
|
|
36
|
-
.where(
|
|
37
|
-
and(
|
|
38
|
-
eq(schema.designs.id, id),
|
|
39
|
-
eq(schema.designs.visibility, "public"),
|
|
40
|
-
),
|
|
41
|
-
)
|
|
42
|
-
.limit(1)
|
|
43
|
-
: [];
|
|
44
|
-
|
|
45
|
-
return {
|
|
46
|
-
resource: resource ?? null,
|
|
47
|
-
origin: new URL(requestUrl).origin,
|
|
48
|
-
basePath: getConfiguredAppBasePath(),
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
|
|
52
13
|
export function designResourceMeta(
|
|
53
14
|
loaderData: PublicDesignMetaData | undefined,
|
|
54
15
|
fallbackTitle: string,
|
|
@@ -5,10 +5,8 @@ import type { LoaderFunctionArgs, MetaFunction } from "react-router";
|
|
|
5
5
|
import enUSMessages from "@/i18n/en-US";
|
|
6
6
|
|
|
7
7
|
import { getDb, schema } from "../../server/db";
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
publicDesignMetaLoaderData,
|
|
11
|
-
} from "./public-design-meta";
|
|
8
|
+
import { publicDesignMetaLoaderData } from "../../server/lib/public-design-meta.server";
|
|
9
|
+
import { designResourceMeta } from "./public-design-meta";
|
|
12
10
|
|
|
13
11
|
export { default } from "../pages/Templates";
|
|
14
12
|
|
|
@@ -2,8 +2,9 @@ import type { LoaderFunctionArgs, MetaFunction } from "react-router";
|
|
|
2
2
|
|
|
3
3
|
import enUSMessages from "@/i18n/en-US";
|
|
4
4
|
|
|
5
|
+
import { loadPublicDesignMeta } from "../../server/lib/public-design-meta.server";
|
|
5
6
|
import DesignEditorRoute from "../pages/DesignEditor";
|
|
6
|
-
import { designResourceMeta
|
|
7
|
+
import { designResourceMeta } from "./public-design-meta";
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* The skill's local editor has its own route so a capability can authorize this
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"dev": "agent-native dev --inspect",
|
|
8
|
-
"build": "agent-native build",
|
|
8
|
+
"build": "agent-native build && tsx scripts/check-client-server-boundary.ts",
|
|
9
9
|
"start": "agent-native start",
|
|
10
10
|
"test": "vitest --run",
|
|
11
11
|
"typecheck": "agent-native typecheck && pnpm exec tsc --noEmit -p app/components/design/bridge/tsconfig.json",
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
const clientRoot = path.resolve(
|
|
6
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
7
|
+
"../build/client",
|
|
8
|
+
);
|
|
9
|
+
const textAsset = /\.(?:css|html|js|json|map|mjs)$/i;
|
|
10
|
+
const forbidden = [
|
|
11
|
+
["PGlite database client", "__agentNativePgliteClients"],
|
|
12
|
+
["server database module", /server[\\/]db(?:[\\/.'"?]|$)/i],
|
|
13
|
+
] as const;
|
|
14
|
+
|
|
15
|
+
async function textAssets(directory: string): Promise<string[]> {
|
|
16
|
+
const entries = await readdir(directory, { withFileTypes: true });
|
|
17
|
+
const paths = await Promise.all(
|
|
18
|
+
entries.map((entry) => {
|
|
19
|
+
const filePath = path.join(directory, entry.name);
|
|
20
|
+
return entry.isDirectory()
|
|
21
|
+
? textAssets(filePath)
|
|
22
|
+
: textAsset.test(entry.name)
|
|
23
|
+
? [filePath]
|
|
24
|
+
: [];
|
|
25
|
+
}),
|
|
26
|
+
);
|
|
27
|
+
return paths.flat();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const assets = await textAssets(clientRoot);
|
|
31
|
+
if (assets.length === 0) {
|
|
32
|
+
throw new Error(`No text assets found in client build: ${clientRoot}`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const leaks: string[] = [];
|
|
36
|
+
for (const asset of assets) {
|
|
37
|
+
const source = await readFile(asset, "utf8");
|
|
38
|
+
for (const [label, pattern] of forbidden) {
|
|
39
|
+
if (
|
|
40
|
+
typeof pattern === "string"
|
|
41
|
+
? source.includes(pattern)
|
|
42
|
+
: pattern.test(source)
|
|
43
|
+
) {
|
|
44
|
+
leaks.push(`${path.relative(clientRoot, asset)}: ${label}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (leaks.length > 0) {
|
|
50
|
+
throw new Error(
|
|
51
|
+
`Server database code found in client assets:\n${leaks.join("\n")}`,
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
console.log(`Checked ${assets.length} client assets for server database code.`);
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { getConfiguredAppBasePath } from "@agent-native/core/server";
|
|
2
|
+
import { SSR_QUERY_CACHE_KEY_HEADER } from "@agent-native/core/shared";
|
|
3
|
+
import { and, eq } from "drizzle-orm";
|
|
4
|
+
import { data } from "react-router";
|
|
5
|
+
|
|
6
|
+
import type { PublicDesignMetaData } from "../../app/routes/public-design-meta";
|
|
7
|
+
import { getDb, schema } from "../db";
|
|
8
|
+
|
|
9
|
+
export function publicDesignMetaLoaderData(payload: PublicDesignMetaData) {
|
|
10
|
+
return data(payload, {
|
|
11
|
+
headers: { [SSR_QUERY_CACHE_KEY_HEADER]: "query" },
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function loadPublicDesignMeta(
|
|
16
|
+
id: string | undefined,
|
|
17
|
+
requestUrl: string,
|
|
18
|
+
): Promise<PublicDesignMetaData> {
|
|
19
|
+
const [resource] = id
|
|
20
|
+
? await getDb()
|
|
21
|
+
.select({
|
|
22
|
+
title: schema.designs.title,
|
|
23
|
+
description: schema.designs.description,
|
|
24
|
+
})
|
|
25
|
+
.from(schema.designs)
|
|
26
|
+
.where(
|
|
27
|
+
and(
|
|
28
|
+
eq(schema.designs.id, id),
|
|
29
|
+
eq(schema.designs.visibility, "public"),
|
|
30
|
+
),
|
|
31
|
+
)
|
|
32
|
+
.limit(1)
|
|
33
|
+
: [];
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
resource: resource ?? null,
|
|
37
|
+
origin: new URL(requestUrl).origin,
|
|
38
|
+
basePath: getConfiguredAppBasePath(),
|
|
39
|
+
};
|
|
40
|
+
}
|