@agent-native/core 0.80.11 → 0.81.0
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 +47 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/cli/design-connect.ts +419 -19
- package/corpus/core/src/client/ConnectBuilderCard.tsx +1 -0
- package/corpus/core/src/client/chat/index.ts +1 -0
- package/corpus/core/src/client/chat/run-recovery.tsx +9 -7
- package/corpus/core/src/client/chat-view-transition.ts +33 -2
- package/corpus/core/src/client/composer/PromptComposer.tsx +21 -2
- package/corpus/core/src/client/index.ts +1 -0
- package/corpus/core/src/client/org/OrgSwitcher.tsx +26 -4
- package/corpus/core/src/client/sharing/ShareButton.tsx +3 -3
- package/corpus/core/src/client/use-agent-chat-home-handoff.ts +31 -3
- package/corpus/core/src/db/client.ts +29 -2
- package/corpus/core/src/provider-api/index.ts +29 -0
- package/corpus/core/src/server/core-routes-plugin.ts +58 -20
- package/corpus/core/src/styles/agent-native.css +54 -0
- package/corpus/core/src/usage/store.ts +2 -2
- package/corpus/templates/analytics/app/components/layout/Layout.tsx +118 -10
- package/corpus/templates/analytics/app/components/layout/layout-route-policy.ts +7 -0
- package/corpus/templates/analytics/app/global.css +8 -0
- package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +7 -1
- package/corpus/templates/analytics/app/lib/chat-handoff.ts +34 -0
- package/corpus/templates/analytics/app/pages/Ask.tsx +9 -18
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +2 -2
- package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +109 -21
- package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +4 -6
- package/corpus/templates/analytics/changelog/2026-06-30-ask-opens-a-fresh-chat-unless-you-were-just-chatting-and-das.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-30-dashboard-email-reports-are-more-reliable-and-use-cleaner.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-30-dashboard-toolbar-controls-use-a-quieter-share-button-and-ve.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-30-session-replay-rows-show-whole-minute-durations-in-the-playl.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-30-session-replays-now-render-captured-styling-and-keep-the-eve.md +6 -0
- package/corpus/templates/analytics/server/jobs/dashboard-report.ts +16 -3
- package/corpus/templates/analytics/server/lib/dashboard-report.ts +31 -14
- package/corpus/templates/assets/actions/open-asset-picker.ts +9 -0
- package/corpus/templates/assets/app/global.css +15 -0
- package/corpus/templates/assets/app/hooks/use-navigation-state.ts +4 -0
- package/corpus/templates/assets/app/routes/library.tsx +90 -16
- package/corpus/templates/assets/changelog/2026-06-30-the-assets-picker-now-supports-a-vertical-embedded-layout-fo.md +6 -0
- package/corpus/templates/clips/actions/finalize-recording.ts +1 -12
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +10 -5
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +6 -8
- package/corpus/templates/clips/changelog/2026-06-30-extension-save-recovery-avoids-false-upload-failures.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-30-longer-chrome-extension-recordings-now-keep-saving-reliably-.md +6 -0
- package/corpus/templates/clips/chrome-extension/public/manifest.json +1 -1
- package/corpus/templates/clips/chrome-extension/src/background.ts +19 -10
- package/corpus/templates/clips/chrome-extension/src/content-script.ts +38 -12
- package/corpus/templates/clips/chrome-extension/src/finalize-recovery.ts +224 -0
- package/corpus/templates/clips/chrome-extension/src/offscreen.ts +119 -16
- package/corpus/templates/clips/chrome-extension/src/overlay.ts +16 -0
- package/corpus/templates/clips/server/lib/recording-upload-state.ts +48 -0
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/abort.post.ts +9 -1
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +63 -13
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/status.get.ts +74 -0
- package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +32 -0
- package/corpus/templates/design/AGENTS.md +27 -0
- package/corpus/templates/design/CODE_NATIVE_DESIGN_STUDIO_IMPLEMENTATION_PLAN.md +1 -1
- package/corpus/templates/design/DESIGN-STUDIO-PLAN.md +5 -5
- package/corpus/templates/design/actions/apply-component-prop-edit.ts +63 -20
- package/corpus/templates/design/actions/apply-motion-edit.ts +67 -69
- package/corpus/templates/design/actions/apply-shader-fill.ts +148 -56
- package/corpus/templates/design/actions/capture-design-state.ts +27 -4
- package/corpus/templates/design/actions/connect-localhost.ts +59 -22
- package/corpus/templates/design/actions/export-zip.ts +23 -8
- package/corpus/templates/design/actions/generate-design.ts +53 -1
- package/corpus/templates/design/actions/get-component-details.ts +5 -19
- package/corpus/templates/design/actions/get-motion-timeline.ts +135 -10
- package/corpus/templates/design/actions/grant-localhost-write-consent.ts +116 -0
- package/corpus/templates/design/actions/import-design-tokens.ts +521 -0
- package/corpus/templates/design/actions/index-design-tokens.ts +10 -1
- package/corpus/templates/design/actions/insert-design-native-asset.ts +268 -0
- package/corpus/templates/design/actions/insert-figma-library-asset.ts +233 -0
- package/corpus/templates/design/actions/list-design-extensions.ts +32 -10
- package/corpus/templates/design/actions/list-design-native-assets.ts +105 -0
- package/corpus/templates/design/actions/list-figma-library-assets.ts +292 -0
- package/corpus/templates/design/actions/migrate-board-objects-to-file.ts +238 -0
- package/corpus/templates/design/actions/navigate.ts +18 -3
- package/corpus/templates/design/actions/provider-api-catalog.ts +2 -2
- package/corpus/templates/design/actions/provider-api-docs.ts +1 -1
- package/corpus/templates/design/actions/provider-api-request.ts +3 -3
- package/corpus/templates/design/actions/revoke-localhost-write-consent.ts +48 -0
- package/corpus/templates/design/actions/run-design-audit.ts +53 -4
- package/corpus/templates/design/actions/run-design-extension-action.ts +1 -1
- package/corpus/templates/design/actions/view-screen.ts +1 -1
- package/corpus/templates/design/actions/write-local-file.ts +213 -0
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +230 -3308
- package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +30 -20
- package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +776 -287
- package/corpus/templates/design/app/components/design/EditPanel.tsx +212 -208
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +14 -2
- package/corpus/templates/design/app/components/design/LocalhostWriteConsentDialog.tsx +161 -0
- package/corpus/templates/design/app/components/design/MotionDock.tsx +235 -266
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +490 -55
- package/corpus/templates/design/app/components/design/ReviewPanel.tsx +43 -62
- package/corpus/templates/design/app/components/design/StatesPanel.tsx +34 -61
- package/corpus/templates/design/app/components/design/TokensPanel.tsx +251 -0
- package/corpus/templates/design/app/components/design/bridge/codegen.ts +125 -0
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +4037 -0
- package/corpus/templates/design/app/components/design/bridge/embedded-wheel.bridge.ts +62 -0
- package/corpus/templates/design/app/components/design/bridge/hit-test.bridge.ts +272 -0
- package/corpus/templates/design/app/components/design/bridge/nav.bridge.ts +116 -0
- package/corpus/templates/design/app/components/design/bridge/sample.bridge.ts +24 -0
- package/corpus/templates/design/app/components/design/bridge/shader-fill-preview.bridge.ts +78 -0
- package/corpus/templates/design/app/components/design/bridge/tsconfig.json +17 -0
- package/corpus/templates/design/app/components/design/bridge/tweak.bridge.ts +28 -0
- package/corpus/templates/design/app/components/design/bridge/zoom.bridge.ts +43 -0
- package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +45 -16
- package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +88 -9
- package/corpus/templates/design/app/components/editor/PromptDialog.tsx +94 -9
- package/corpus/templates/design/app/components/layout/Layout.tsx +19 -3
- package/corpus/templates/design/app/components/layout/Sidebar.tsx +1 -1
- package/corpus/templates/design/app/global.css +7 -0
- package/corpus/templates/design/app/hooks/use-navigation-state.ts +23 -0
- package/corpus/templates/design/app/i18n/zh-TW.ts +21 -0
- package/corpus/templates/design/app/i18n-data.ts +358 -0
- package/corpus/templates/design/app/lib/pending-generation.ts +1 -0
- package/corpus/templates/design/app/pages/DesignEditor.tsx +2402 -499
- package/corpus/templates/design/app/pages/Index.tsx +5 -1
- package/corpus/templates/design/changelog/2026-06-30-applying-the-one-click-fix-for-a-small-tap-target-accessibil.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-canvas-creation-screen-dragging-undo-redo-and-image-fill-edi.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-component-prop-edits-and-shader-fills-now-preserve-rapid-sam.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-can-browse-figma-library-components-and-insert-them-i.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-now-has-a-figma-style-left-rail-for-file-agent-assets.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-now-has-native-components-embedded-media-and-figma-im.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-preview-now-includes-a-publish-app-waitlist-option-fr.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-s-left-rail-now-matches-the-sidebar-chrome-and-uses-t.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-s-tools-create-menu-now-stays-compact-instead-of-rend.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-starter-prompts-now-begin-generating-immediately-and-.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-tools-now-use-a-quieter-figma-style-list-layout.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-drawing-rectangles-on-the-all-screens-canvas-no-longer-flash.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-fixed-code-native-design-studio-edits-so-component-props-mot.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-fixed-visual-editing-hover-overlays-and-spacing-handles-so-s.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-motion-edits-now-save-automatically-without-a-separate-css-a.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-motion-timelines-now-reopen-as-editable-tracks-after-writing.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-new-design-prompts-with-uploaded-screenshots-work-reliably-o.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-screen-previews-in-all-screens-view-now-keep-rounded-outline.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-selection-outlines-now-sit-flush-on-rectangles-drawn-directl.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-share-options-use-a-tighter-design-editor-popover-with-compa.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-the-collapsed-motion-panel-now-stays-tucked-under-the-left-s.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-the-design-inspector-now-shows-states-and-review-in-a-tighte.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-the-sidebar-keeps-the-organization-picker-space-stable-while.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-tokens-can-now-be-imported-from-pasted-notes-code-files-fold.md +6 -0
- package/corpus/templates/design/e2e/helpers.ts +168 -65
- package/corpus/templates/design/package.json +3 -1
- package/corpus/templates/design/server/db/schema.ts +23 -0
- package/corpus/templates/design/server/handlers/uploads.ts +22 -1
- package/corpus/templates/design/server/lib/provider-api.ts +7 -4
- package/corpus/templates/design/server/lib/verify-write-grant.ts +108 -0
- package/corpus/templates/design/server/plugins/db.ts +39 -0
- package/corpus/templates/design/server/register-secrets.ts +38 -0
- package/corpus/templates/design/shared/board-file.ts +228 -0
- package/corpus/templates/design/shared/board-objects.ts +288 -0
- package/corpus/templates/design/shared/code-layer.ts +52 -0
- package/corpus/templates/design/shared/motion-compiler.ts +58 -7
- package/corpus/templates/design/shared/motion-timeline.ts +2 -2
- package/corpus/templates/design/tsconfig.json +2 -1
- package/corpus/templates/slides/AGENTS.md +9 -0
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +4 -0
- package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +4 -24
- package/corpus/templates/slides/app/lib/export-pdf-client.ts +29 -20
- package/corpus/templates/slides/app/lib/export-pptx-client.ts +365 -0
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +12 -9
- package/corpus/templates/slides/app/vite-env.d.ts +18 -0
- package/corpus/templates/slides/changelog/2026-06-30-powerpoint-downloads-now-preserve-the-rendered-slide-layout-.md +6 -0
- package/corpus/templates/slides/changelog/2026-06-30-unavailable-decks-open-in-a-cleaner-full-height-centered-sta.md +6 -0
- package/corpus/templates/slides/package.json +1 -0
- package/corpus/templates/slides/vite.config.ts +1 -0
- package/dist/cli/design-connect.d.ts +25 -0
- package/dist/cli/design-connect.d.ts.map +1 -1
- package/dist/cli/design-connect.js +357 -18
- package/dist/cli/design-connect.js.map +1 -1
- package/dist/client/ConnectBuilderCard.d.ts.map +1 -1
- package/dist/client/ConnectBuilderCard.js +1 -0
- package/dist/client/ConnectBuilderCard.js.map +1 -1
- package/dist/client/chat/index.d.ts +1 -1
- package/dist/client/chat/index.d.ts.map +1 -1
- package/dist/client/chat/index.js +1 -1
- package/dist/client/chat/index.js.map +1 -1
- package/dist/client/chat/run-recovery.d.ts.map +1 -1
- package/dist/client/chat/run-recovery.js +5 -5
- package/dist/client/chat/run-recovery.js.map +1 -1
- package/dist/client/chat-view-transition.d.ts +6 -0
- package/dist/client/chat-view-transition.d.ts.map +1 -1
- package/dist/client/chat-view-transition.js +23 -2
- package/dist/client/chat-view-transition.js.map +1 -1
- package/dist/client/composer/PromptComposer.d.ts.map +1 -1
- package/dist/client/composer/PromptComposer.js +3 -2
- package/dist/client/composer/PromptComposer.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
- package/dist/client/org/OrgSwitcher.js +11 -4
- package/dist/client/org/OrgSwitcher.js.map +1 -1
- package/dist/client/sharing/ShareButton.js +3 -3
- package/dist/client/sharing/ShareButton.js.map +1 -1
- package/dist/client/use-agent-chat-home-handoff.d.ts +8 -2
- package/dist/client/use-agent-chat-home-handoff.d.ts.map +1 -1
- package/dist/client/use-agent-chat-home-handoff.js +20 -6
- package/dist/client/use-agent-chat-home-handoff.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/routes.d.ts +2 -2
- package/dist/db/client.d.ts +1 -0
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +28 -2
- package/dist/db/client.js.map +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +2 -2
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/index.d.ts +5 -4
- package/dist/provider-api/index.d.ts.map +1 -1
- package/dist/provider-api/index.js +28 -0
- package/dist/provider-api/index.js.map +1 -1
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/server/core-routes-plugin.d.ts +25 -0
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +43 -18
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/styles/agent-native.css +54 -0
- package/dist/usage/store.js +2 -2
- package/dist/usage/store.js.map +1 -1
- package/package.json +1 -1
|
@@ -249,6 +249,7 @@ export default function Index() {
|
|
|
249
249
|
prompt: string,
|
|
250
250
|
files: UploadedFile[],
|
|
251
251
|
options: PromptComposerSubmitOptions,
|
|
252
|
+
pendingOptions?: { skipQuestions?: boolean },
|
|
252
253
|
) => {
|
|
253
254
|
// Derive a short title from the prompt — first line, ~40 chars max,
|
|
254
255
|
// word-boundary truncated. The full prompt still drives generation;
|
|
@@ -266,6 +267,7 @@ export default function Index() {
|
|
|
266
267
|
files,
|
|
267
268
|
title,
|
|
268
269
|
designSystemId,
|
|
270
|
+
skipQuestions: pendingOptions?.skipQuestions,
|
|
269
271
|
...options,
|
|
270
272
|
});
|
|
271
273
|
|
|
@@ -425,7 +427,9 @@ export default function Index() {
|
|
|
425
427
|
) : designs.length === 0 ? (
|
|
426
428
|
<EmptyState
|
|
427
429
|
onCreateDesign={openNewDesign}
|
|
428
|
-
onStarterPrompt={(prompt) =>
|
|
430
|
+
onStarterPrompt={(prompt) =>
|
|
431
|
+
handleSubmitPrompt(prompt, [], {}, { skipQuestions: true })
|
|
432
|
+
}
|
|
429
433
|
/>
|
|
430
434
|
) : (
|
|
431
435
|
<>
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
expect,
|
|
6
|
+
type Page,
|
|
7
|
+
type FrameLocator,
|
|
8
|
+
type Locator,
|
|
9
|
+
} from "@playwright/test";
|
|
10
|
+
|
|
11
|
+
import { FIXTURE_HTML, SEED_TITLE } from "./global-setup";
|
|
5
12
|
|
|
6
13
|
/**
|
|
7
14
|
* Helpers for driving the Design visual editor in real Chrome.
|
|
@@ -25,15 +32,122 @@ export async function readSeedDesignId(): Promise<string> {
|
|
|
25
32
|
return designId;
|
|
26
33
|
}
|
|
27
34
|
|
|
35
|
+
function e2eBaseUrl(page: Page): string {
|
|
36
|
+
const currentUrl = page.url();
|
|
37
|
+
if (currentUrl && currentUrl !== "about:blank") {
|
|
38
|
+
return new URL(currentUrl).origin;
|
|
39
|
+
}
|
|
40
|
+
return (
|
|
41
|
+
process.env.E2E_BASE_URL ??
|
|
42
|
+
`http://127.0.0.1:${process.env.E2E_PORT ?? "9333"}`
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function postAction(
|
|
47
|
+
page: Page,
|
|
48
|
+
name: string,
|
|
49
|
+
input: Record<string, unknown>,
|
|
50
|
+
): Promise<any> {
|
|
51
|
+
const res = await page.request.post(
|
|
52
|
+
`${e2eBaseUrl(page)}/_agent-native/actions/${name}`,
|
|
53
|
+
{
|
|
54
|
+
data: input,
|
|
55
|
+
headers: { "Content-Type": "application/json" },
|
|
56
|
+
},
|
|
57
|
+
);
|
|
58
|
+
if (!res.ok()) {
|
|
59
|
+
throw new Error(
|
|
60
|
+
`action ${name} failed: ${res.status()} ${await res.text()}`,
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
return res.json();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export async function createFixtureDesign(
|
|
67
|
+
page: Page,
|
|
68
|
+
title = SEED_TITLE,
|
|
69
|
+
): Promise<string> {
|
|
70
|
+
const created = await postAction(page, "create-design", {
|
|
71
|
+
title,
|
|
72
|
+
projectType: "prototype",
|
|
73
|
+
});
|
|
74
|
+
const designId: string | undefined =
|
|
75
|
+
created?.id ?? created?.data?.id ?? created?.design?.id;
|
|
76
|
+
if (!designId) {
|
|
77
|
+
throw new Error(
|
|
78
|
+
`create-design did not return an id: ${JSON.stringify(created)}`,
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
await postAction(page, "create-file", {
|
|
82
|
+
designId,
|
|
83
|
+
filename: "index.html",
|
|
84
|
+
content: FIXTURE_HTML,
|
|
85
|
+
fileType: "html",
|
|
86
|
+
});
|
|
87
|
+
return designId;
|
|
88
|
+
}
|
|
89
|
+
|
|
28
90
|
const DESIGN_PREVIEW_IFRAME_SELECTOR = "iframe[data-design-preview-iframe]";
|
|
91
|
+
const E2E_BASE_URL = process.env.E2E_BASE_URL;
|
|
92
|
+
const E2E_BASE_PATH = (() => {
|
|
93
|
+
if (!E2E_BASE_URL) return "";
|
|
94
|
+
try {
|
|
95
|
+
return new URL(E2E_BASE_URL).pathname.replace(/\/$/, "");
|
|
96
|
+
} catch {
|
|
97
|
+
return "";
|
|
98
|
+
}
|
|
99
|
+
})();
|
|
100
|
+
|
|
101
|
+
export function appPath(path: string): string {
|
|
102
|
+
const route = new URL(path, "http://agent-native.local");
|
|
103
|
+
if (E2E_BASE_URL && E2E_BASE_PATH) {
|
|
104
|
+
const url = new URL(E2E_BASE_URL);
|
|
105
|
+
// dev-lazy strips the app mount (/design) before handing the request to the
|
|
106
|
+
// app, so the editor's own /design/:id route intentionally becomes
|
|
107
|
+
// /design/design/:id when E2E_BASE_URL points at the gateway mount.
|
|
108
|
+
url.pathname = `${E2E_BASE_PATH}${route.pathname}`;
|
|
109
|
+
url.search = route.search;
|
|
110
|
+
url.hash = route.hash;
|
|
111
|
+
return url.toString();
|
|
112
|
+
}
|
|
113
|
+
return `${route.pathname}${route.search}${route.hash}`;
|
|
114
|
+
}
|
|
29
115
|
|
|
30
116
|
export function designFrame(page: Page): FrameLocator {
|
|
31
|
-
return page.locator(DESIGN_PREVIEW_IFRAME_SELECTOR).
|
|
117
|
+
return page.locator(DESIGN_PREVIEW_IFRAME_SELECTOR).last().contentFrame();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function selectableNodeByText(
|
|
121
|
+
page: Page,
|
|
122
|
+
text: string,
|
|
123
|
+
): Promise<Locator> {
|
|
124
|
+
const candidates = designFrame(page).locator("[data-agent-native-node-id]", {
|
|
125
|
+
hasText: text,
|
|
126
|
+
});
|
|
127
|
+
const fallback = designFrame(page).getByText(text, { exact: false }).first();
|
|
128
|
+
const count = await candidates.count();
|
|
129
|
+
if (count === 0) return fallback;
|
|
130
|
+
|
|
131
|
+
let bestIndex = 0;
|
|
132
|
+
let bestArea = Number.POSITIVE_INFINITY;
|
|
133
|
+
for (let index = 0; index < count; index += 1) {
|
|
134
|
+
const candidate = candidates.nth(index);
|
|
135
|
+
const box = await candidate.boundingBox().catch(() => null);
|
|
136
|
+
if (!box || box.width <= 0 || box.height <= 0) continue;
|
|
137
|
+
const area = box.width * box.height;
|
|
138
|
+
if (area < bestArea) {
|
|
139
|
+
bestIndex = index;
|
|
140
|
+
bestArea = area;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return candidates.nth(bestIndex);
|
|
32
144
|
}
|
|
33
145
|
|
|
34
146
|
/** Open the editor for a design and wait for the toolbar + iframe to be ready. */
|
|
35
147
|
export async function gotoEditor(page: Page, designId: string): Promise<void> {
|
|
36
|
-
await page.goto(`/design/${designId}
|
|
148
|
+
await page.goto(appPath(`/design/${designId}`), {
|
|
149
|
+
waitUntil: "domcontentloaded",
|
|
150
|
+
});
|
|
37
151
|
await expect(
|
|
38
152
|
page.getByRole("button", { name: "Move", exact: true }),
|
|
39
153
|
).toBeVisible({ timeout: 30_000 });
|
|
@@ -44,9 +158,16 @@ async function waitForDesignBridgeReady(page: Page): Promise<void> {
|
|
|
44
158
|
await expect(
|
|
45
159
|
page.locator(DESIGN_PREVIEW_IFRAME_SELECTOR).first(),
|
|
46
160
|
).toBeVisible();
|
|
47
|
-
await
|
|
48
|
-
|
|
49
|
-
|
|
161
|
+
const overviewChromeVisible = await page
|
|
162
|
+
.getByRole("button", { name: "Full view", exact: true })
|
|
163
|
+
.first()
|
|
164
|
+
.isVisible()
|
|
165
|
+
.catch(() => false);
|
|
166
|
+
if (!overviewChromeVisible) {
|
|
167
|
+
await expect(
|
|
168
|
+
designFrame(page).locator('[data-agent-native-edit-overlay="shield"]'),
|
|
169
|
+
).toBeVisible({ timeout: 10_000 });
|
|
170
|
+
}
|
|
50
171
|
// Wait for the iframe bridge to stamp at least one selectable node.
|
|
51
172
|
await expect
|
|
52
173
|
.poll(
|
|
@@ -57,6 +178,25 @@ async function waitForDesignBridgeReady(page: Page): Promise<void> {
|
|
|
57
178
|
{ timeout: 20_000 },
|
|
58
179
|
)
|
|
59
180
|
.toBeGreaterThan(0);
|
|
181
|
+
if (overviewChromeVisible) {
|
|
182
|
+
await expect(page.locator("[data-screen-shell]").first()).toBeVisible({
|
|
183
|
+
timeout: 10_000,
|
|
184
|
+
});
|
|
185
|
+
await expect
|
|
186
|
+
.poll(
|
|
187
|
+
async () => {
|
|
188
|
+
const box = await page
|
|
189
|
+
.locator("[data-screen-shell]")
|
|
190
|
+
.first()
|
|
191
|
+
.locator("[data-screen-card]")
|
|
192
|
+
.boundingBox();
|
|
193
|
+
return box && box.width > 0 && box.height > 0;
|
|
194
|
+
},
|
|
195
|
+
{ timeout: 10_000 },
|
|
196
|
+
)
|
|
197
|
+
.toBeTruthy();
|
|
198
|
+
await page.waitForTimeout(750);
|
|
199
|
+
}
|
|
60
200
|
}
|
|
61
201
|
|
|
62
202
|
export async function enterDirectMode(page: Page): Promise<void> {
|
|
@@ -76,7 +216,7 @@ export async function enterDirectMode(page: Page): Promise<void> {
|
|
|
76
216
|
(
|
|
77
217
|
await page
|
|
78
218
|
.locator(DESIGN_PREVIEW_IFRAME_SELECTOR)
|
|
79
|
-
.
|
|
219
|
+
.last()
|
|
80
220
|
.boundingBox()
|
|
81
221
|
)?.width ?? 0,
|
|
82
222
|
{ timeout: 10_000 },
|
|
@@ -135,7 +275,7 @@ export async function selectByText(page: Page, text: string): Promise<any> {
|
|
|
135
275
|
await enterDirectMode(page);
|
|
136
276
|
await installBridge(page);
|
|
137
277
|
await page.evaluate(() => ((window as any).__bridge = []));
|
|
138
|
-
const target =
|
|
278
|
+
const target = await selectableNodeByText(page, text);
|
|
139
279
|
await target.waitFor({ state: "visible", timeout: 8_000 });
|
|
140
280
|
const box = await target.boundingBox();
|
|
141
281
|
if (!box) throw new Error(`no bounding box for "${text}"`);
|
|
@@ -157,60 +297,25 @@ async function dispatchShieldClickByText(
|
|
|
157
297
|
page: Page,
|
|
158
298
|
text: string,
|
|
159
299
|
): Promise<void> {
|
|
300
|
+
const target = await selectableNodeByText(page, text);
|
|
301
|
+
await target.waitFor({ state: "visible", timeout: 8_000 });
|
|
302
|
+
const rect = await target.boundingBox();
|
|
303
|
+
if (!rect) throw new Error(`unable to dispatch selection for "${text}"`);
|
|
304
|
+
const frameRect = await page
|
|
305
|
+
.locator(DESIGN_PREVIEW_IFRAME_SELECTOR)
|
|
306
|
+
.last()
|
|
307
|
+
.boundingBox();
|
|
308
|
+
if (!frameRect) throw new Error("unable to locate design iframe");
|
|
160
309
|
await designFrame(page)
|
|
161
|
-
.locator("
|
|
162
|
-
.
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
rect.height > 0 &&
|
|
171
|
-
styles.display !== "none" &&
|
|
172
|
-
styles.visibility !== "hidden"
|
|
173
|
-
);
|
|
174
|
-
};
|
|
175
|
-
const shield = document.querySelector<HTMLElement>(
|
|
176
|
-
'[data-agent-native-edit-overlay="shield"]',
|
|
177
|
-
);
|
|
178
|
-
const leafCandidates = Array.from(
|
|
179
|
-
document.querySelectorAll<HTMLElement>(
|
|
180
|
-
"h1, h2, h3, h4, h5, h6, p, button, a, label, span, input, textarea",
|
|
181
|
-
),
|
|
182
|
-
).filter(isVisible);
|
|
183
|
-
const nodeCandidates = Array.from(
|
|
184
|
-
document.querySelectorAll<HTMLElement>("[data-agent-native-node-id]"),
|
|
185
|
-
).filter(isVisible);
|
|
186
|
-
const target =
|
|
187
|
-
nodeCandidates.find(
|
|
188
|
-
(element) => normalizeText(element) === targetText,
|
|
189
|
-
) ??
|
|
190
|
-
leafCandidates.find(
|
|
191
|
-
(element) => normalizeText(element) === targetText,
|
|
192
|
-
) ??
|
|
193
|
-
nodeCandidates.find((element) =>
|
|
194
|
-
normalizeText(element).includes(targetText),
|
|
195
|
-
) ??
|
|
196
|
-
leafCandidates.find((element) =>
|
|
197
|
-
normalizeText(element).includes(targetText),
|
|
198
|
-
);
|
|
199
|
-
if (!shield || !target) {
|
|
200
|
-
throw new Error(`unable to dispatch selection for "${targetText}"`);
|
|
201
|
-
}
|
|
202
|
-
const rect = target.getBoundingClientRect();
|
|
203
|
-
shield.dispatchEvent(
|
|
204
|
-
new MouseEvent("click", {
|
|
205
|
-
bubbles: true,
|
|
206
|
-
cancelable: true,
|
|
207
|
-
view: window,
|
|
208
|
-
clientX: rect.left + rect.width / 2,
|
|
209
|
-
clientY: rect.top + rect.height / 2,
|
|
210
|
-
detail: 1,
|
|
211
|
-
}),
|
|
212
|
-
);
|
|
213
|
-
}, text);
|
|
310
|
+
.locator('[data-agent-native-edit-overlay="shield"]')
|
|
311
|
+
.first()
|
|
312
|
+
.dispatchEvent("click", {
|
|
313
|
+
bubbles: true,
|
|
314
|
+
cancelable: true,
|
|
315
|
+
clientX: rect.x - frameRect.x + rect.width / 2,
|
|
316
|
+
clientY: rect.y - frameRect.y + rect.height / 2,
|
|
317
|
+
detail: 1,
|
|
318
|
+
});
|
|
214
319
|
}
|
|
215
320
|
|
|
216
321
|
/** Number of inputs in the right-hand inspector (proxy for "inspector populated"). */
|
|
@@ -231,10 +336,8 @@ export async function dragCanvasByText(
|
|
|
231
336
|
): Promise<string[]> {
|
|
232
337
|
await selectByText(page, text);
|
|
233
338
|
await page.evaluate(() => ((window as any).__bridge = []));
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
.first()
|
|
237
|
-
.boundingBox();
|
|
339
|
+
const target = await selectableNodeByText(page, text);
|
|
340
|
+
const box = await target.boundingBox();
|
|
238
341
|
if (!box) throw new Error(`no bounding box for "${text}"`);
|
|
239
342
|
const cx = box.x + box.width / 2;
|
|
240
343
|
const cy = box.y + box.height / 2;
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
"build": "agent-native build",
|
|
9
9
|
"start": "agent-native start",
|
|
10
10
|
"test": "vitest --run",
|
|
11
|
-
"typecheck": "agent-native typecheck",
|
|
11
|
+
"typecheck": "agent-native typecheck && pnpm exec tsc --noEmit -p app/components/design/bridge/tsconfig.json",
|
|
12
|
+
"codegen:bridge": "tsx app/components/design/bridge/codegen.ts",
|
|
12
13
|
"action": "agent-native action",
|
|
13
14
|
"script": "agent-native script",
|
|
14
15
|
"e2e": "playwright test",
|
|
@@ -78,6 +79,7 @@
|
|
|
78
79
|
"clsx": "^2.1.1",
|
|
79
80
|
"cmdk": "^1.1.1",
|
|
80
81
|
"embla-carousel-react": "^8.6.0",
|
|
82
|
+
"esbuild": "^0.27.7",
|
|
81
83
|
"input-otp": "^1.4.2",
|
|
82
84
|
"next-themes": "^0.4.6",
|
|
83
85
|
"oxfmt": "catalog:",
|
|
@@ -75,6 +75,7 @@ export const designLocalhostConnections = table(
|
|
|
75
75
|
.notNull()
|
|
76
76
|
.default("connected"),
|
|
77
77
|
lastSeenAt: text("last_seen_at"),
|
|
78
|
+
bridgeToken: text("bridge_token"),
|
|
78
79
|
ownerEmail: text("owner_email").notNull(),
|
|
79
80
|
orgId: text("org_id"),
|
|
80
81
|
createdAt: text("created_at").default(now()),
|
|
@@ -178,6 +179,28 @@ export const designState = table("design_state", {
|
|
|
178
179
|
...ownableColumns(),
|
|
179
180
|
});
|
|
180
181
|
|
|
182
|
+
/**
|
|
183
|
+
* Localhost write-consent grants minted when the user explicitly allows file
|
|
184
|
+
* writes for a specific design + connection pair. Expire after 8 hours.
|
|
185
|
+
* Only HTML/CSS files may be written (enforced at the action layer).
|
|
186
|
+
*/
|
|
187
|
+
export const designLocalhostWriteGrants = table(
|
|
188
|
+
"design_localhost_write_grants",
|
|
189
|
+
{
|
|
190
|
+
id: text("id").primaryKey(),
|
|
191
|
+
designId: text("design_id").notNull(),
|
|
192
|
+
connectionId: text("connection_id").notNull(),
|
|
193
|
+
/** Filesystem root that was visible when the grant was created. */
|
|
194
|
+
rootPath: text("root_path").notNull(),
|
|
195
|
+
/** Short-lived token passed in the X-Bridge-Token header. */
|
|
196
|
+
bridgeToken: text("bridge_token").notNull(),
|
|
197
|
+
/** ISO timestamp after which the grant is considered expired (8 hours). */
|
|
198
|
+
grantedUntil: text("granted_until").notNull(),
|
|
199
|
+
createdAt: text("created_at").default(now()),
|
|
200
|
+
...ownableColumns(),
|
|
201
|
+
},
|
|
202
|
+
);
|
|
203
|
+
|
|
181
204
|
/**
|
|
182
205
|
* Cached accessibility audit + visual diff results for a design.
|
|
183
206
|
* Keyed by design + optional base/compare design_versions pair + source ref.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import crypto from "crypto";
|
|
2
2
|
import fs from "fs";
|
|
3
|
+
import os from "os";
|
|
3
4
|
import path from "path";
|
|
4
5
|
|
|
5
6
|
import { getSession } from "@agent-native/core/server";
|
|
@@ -11,7 +12,27 @@ import {
|
|
|
11
12
|
} from "h3";
|
|
12
13
|
import { nanoid } from "nanoid";
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
function isServerlessRuntime(env: NodeJS.ProcessEnv = process.env): boolean {
|
|
16
|
+
return Boolean(
|
|
17
|
+
env.NETLIFY ||
|
|
18
|
+
env.VERCEL ||
|
|
19
|
+
env.AWS_LAMBDA_FUNCTION_NAME ||
|
|
20
|
+
env.LAMBDA_TASK_ROOT ||
|
|
21
|
+
env.CF_PAGES,
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function uploadsRootForRuntime(
|
|
26
|
+
cwd = process.cwd(),
|
|
27
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
28
|
+
): string {
|
|
29
|
+
const baseDir = isServerlessRuntime(env)
|
|
30
|
+
? path.join(os.tmpdir(), "agent-native-design")
|
|
31
|
+
: cwd;
|
|
32
|
+
return path.join(baseDir, "data", "uploads");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const UPLOADS_ROOT = uploadsRootForRuntime();
|
|
15
36
|
const MAX_EXTRACTED_TEXT_CHARS = 8_000;
|
|
16
37
|
const TEXT_EXTENSIONS = new Set([
|
|
17
38
|
".html",
|
|
@@ -19,18 +19,21 @@ export type { ProviderApiMethod, ProviderApiRequestArgs };
|
|
|
19
19
|
const resolveDesignCredential: ProviderApiCredentialResolver = async (
|
|
20
20
|
options,
|
|
21
21
|
) => {
|
|
22
|
-
|
|
22
|
+
const supported =
|
|
23
|
+
(options.provider === "github" && options.key === "GITHUB_TOKEN") ||
|
|
24
|
+
(options.provider === "figma" && options.key === "FIGMA_ACCESS_TOKEN");
|
|
25
|
+
if (!supported) {
|
|
23
26
|
return null;
|
|
24
27
|
}
|
|
25
28
|
|
|
26
|
-
const value = await resolveSecret(
|
|
29
|
+
const value = await resolveSecret(options.key);
|
|
27
30
|
if (!value) return null;
|
|
28
31
|
|
|
29
32
|
return {
|
|
30
|
-
key:
|
|
33
|
+
key: options.key,
|
|
31
34
|
value,
|
|
32
35
|
source: `${DESIGN_APP_ID}_secret`,
|
|
33
|
-
provider:
|
|
36
|
+
provider: options.provider,
|
|
34
37
|
scope: "request",
|
|
35
38
|
};
|
|
36
39
|
};
|