@agent-native/core 0.80.10 → 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 +53 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/production-agent.ts +108 -15
- package/corpus/core/src/cli/design-connect.ts +419 -19
- package/corpus/core/src/client/AssistantChat.tsx +123 -26
- 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/sse-event-processor.ts +9 -0
- 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/.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/open-asset-picker.ts +9 -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/global.css +15 -0
- package/corpus/templates/assets/app/hooks/use-navigation-state.ts +4 -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 +233 -52
- 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/changelog/2026-06-30-the-assets-picker-now-supports-a-vertical-embedded-layout-fo.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/finalize-recording.ts +1 -12
- 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/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 +76 -25
- package/corpus/templates/design/actions/apply-design-state.ts +17 -4
- package/corpus/templates/design/actions/apply-motion-edit.ts +76 -115
- 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/delete-design.ts +20 -0
- 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 +21 -26
- 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 +18 -5
- 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-design-states.ts +19 -1
- 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/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/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 +74 -10
- 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 +244 -3197
- package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +30 -20
- package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +795 -289
- package/corpus/templates/design/app/components/design/EditPanel.tsx +635 -234
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +124 -32
- package/corpus/templates/design/app/components/design/LocalhostWriteConsentDialog.tsx +161 -0
- package/corpus/templates/design/app/components/design/MotionDock.tsx +244 -266
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +491 -56
- package/corpus/templates/design/app/components/design/ReviewPanel.tsx +55 -67
- package/corpus/templates/design/app/components/design/StatesPanel.tsx +38 -63
- package/corpus/templates/design/app/components/design/TokensPanel.tsx +264 -8
- 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 +22 -0
- package/corpus/templates/design/app/i18n-data.ts +369 -0
- package/corpus/templates/design/app/lib/pending-generation.ts +1 -0
- package/corpus/templates/design/app/pages/DesignEditor.tsx +3024 -616
- 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-token-edits-now-stay-visible-in-previews-and-token-li.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/global-setup.ts +94 -2
- 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/component-model.ts +35 -0
- package/corpus/templates/design/shared/motion-compiler.ts +137 -15
- package/corpus/templates/design/shared/motion-timeline.ts +2 -2
- package/corpus/templates/design/shared/resolve-tweaks.ts +32 -9
- 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/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/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/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/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/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/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 +3 -3
- 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/agent-engine-api-key-route.d.ts +1 -1
- 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,6 +1,7 @@
|
|
|
1
1
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
|
|
4
|
+
import { createClient } from "@libsql/client";
|
|
4
5
|
import { chromium, type FullConfig } from "@playwright/test";
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -19,6 +20,12 @@ const AUTH_DIR = path.join(import.meta.dirname, ".auth");
|
|
|
19
20
|
const STATE_PATH = path.join(AUTH_DIR, "state.json");
|
|
20
21
|
const SEED_PATH = path.join(AUTH_DIR, "seed.json");
|
|
21
22
|
const BROWSER_CHANNEL = process.env.E2E_BROWSER_CHANNEL;
|
|
23
|
+
const E2E_DATABASE_URL = `file:${path.join(
|
|
24
|
+
import.meta.dirname,
|
|
25
|
+
"..",
|
|
26
|
+
"data",
|
|
27
|
+
"e2e.db",
|
|
28
|
+
)}`;
|
|
22
29
|
|
|
23
30
|
/**
|
|
24
31
|
* Fixture HTML with distinct, text-identifiable elements. Plain inline styles
|
|
@@ -31,6 +38,12 @@ export const FIXTURE_HTML = `<!doctype html>
|
|
|
31
38
|
<meta charset="utf-8" />
|
|
32
39
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
33
40
|
<title>E2E Fixture</title>
|
|
41
|
+
<style>
|
|
42
|
+
:root {
|
|
43
|
+
--e2e-accent-color: #6366f1;
|
|
44
|
+
--e2e-radius: 14px;
|
|
45
|
+
}
|
|
46
|
+
</style>
|
|
34
47
|
</head>
|
|
35
48
|
<body style="margin:0;font-family:system-ui,sans-serif;background:#0f1115;color:#f4f4f5">
|
|
36
49
|
<main style="max-width:720px;margin:0 auto;padding:48px 32px;display:flex;flex-direction:column;gap:24px">
|
|
@@ -38,8 +51,26 @@ export const FIXTURE_HTML = `<!doctype html>
|
|
|
38
51
|
<p style="font-size:18px;line-height:1.6;margin:0;color:#a1a1aa">First fixture paragraph for selection tests.</p>
|
|
39
52
|
<p style="font-size:18px;line-height:1.6;margin:0;color:#a1a1aa">Second fixture paragraph for selection tests.</p>
|
|
40
53
|
<div style="display:flex;flex-direction:row;gap:16px">
|
|
41
|
-
<button style="padding:14px 28px;border-radius:10px;border:0;background:#6366f1;color:#fff;font-size:16px">Alpha Button</button>
|
|
42
|
-
<button style="padding:14px 28px;border-radius:10px;border:0;background:#22c55e;color:#06240f;font-size:16px">Beta Button</button>
|
|
54
|
+
<button data-agent-native-node-id="e2e-alpha-button" data-agent-native-layer-name="Alpha Button" style="padding:14px 28px;border-radius:10px;border:0;background:#6366f1;color:#fff;font-size:16px">Alpha Button</button>
|
|
55
|
+
<button data-agent-native-node-id="e2e-beta-button" data-agent-native-layer-name="Beta Button" style="padding:14px 28px;border-radius:10px;border:0;background:#22c55e;color:#06240f;font-size:16px">Beta Button</button>
|
|
56
|
+
</div>
|
|
57
|
+
<button
|
|
58
|
+
data-agent-native-node-id="e2e-component-button"
|
|
59
|
+
data-agent-native-layer-name="E2E Component Button"
|
|
60
|
+
data-agent-native-component="E2EButton"
|
|
61
|
+
data-agent-native-prop-variant="primary"
|
|
62
|
+
data-agent-native-prop-size="md"
|
|
63
|
+
style="align-self:flex-start;padding:14px 28px;border-radius:var(--e2e-radius);border:0;background:var(--e2e-accent-color);color:#fff;font-size:16px"
|
|
64
|
+
>Variant CTA</button>
|
|
65
|
+
<div
|
|
66
|
+
data-agent-native-node-id="e2e-token-sample"
|
|
67
|
+
data-agent-native-layer-name="E2E Token Sample"
|
|
68
|
+
style="padding:18px 20px;border-radius:var(--e2e-radius);background:var(--e2e-accent-color);color:#fff;font-weight:700"
|
|
69
|
+
>Token swatch sample</div>
|
|
70
|
+
<div style="display:flex;align-items:center;gap:12px">
|
|
71
|
+
<img data-agent-native-node-id="e2e-audit-image" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==" style="width:32px;height:32px;border-radius:8px;background:#27272a" />
|
|
72
|
+
<input data-agent-native-node-id="e2e-audit-input" placeholder="Email" style="height:32px;border-radius:8px;border:1px solid #3f3f46;background:#18181b;color:#fff;padding:0 10px" />
|
|
73
|
+
<button data-agent-native-node-id="e2e-audit-focus-button" class="outline-none" style="height:32px;border-radius:8px;border:1px solid #3f3f46;background:#27272a;color:#fff;padding:0 10px">Focus me</button>
|
|
43
74
|
</div>
|
|
44
75
|
<div style="padding:8px;border:1px solid #27272a;border-radius:12px">
|
|
45
76
|
<div style="padding:8px;border:1px solid #3f3f46;border-radius:10px">
|
|
@@ -76,6 +107,63 @@ async function postAction(
|
|
|
76
107
|
return res.json();
|
|
77
108
|
}
|
|
78
109
|
|
|
110
|
+
function componentIndexId(designId: string, name: string): string {
|
|
111
|
+
return `ci_${designId}_${name.toLowerCase().replace(/[^a-z0-9]/g, "_")}`;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export async function seedComponentVariantMetadata(
|
|
115
|
+
designId: string,
|
|
116
|
+
): Promise<void> {
|
|
117
|
+
const client = createClient({ url: E2E_DATABASE_URL });
|
|
118
|
+
const name = "E2EButton";
|
|
119
|
+
const now = new Date().toISOString();
|
|
120
|
+
const variants = JSON.stringify({
|
|
121
|
+
variant: ["primary", "secondary", "ghost"],
|
|
122
|
+
size: ["sm", "md", "lg"],
|
|
123
|
+
});
|
|
124
|
+
const props = JSON.stringify([
|
|
125
|
+
{ name: "variant", type: "primary | secondary | ghost" },
|
|
126
|
+
{ name: "size", type: "sm | md | lg" },
|
|
127
|
+
]);
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
const result = await client.execute({
|
|
131
|
+
sql: `
|
|
132
|
+
UPDATE component_index
|
|
133
|
+
SET variants = ?, props = ?, file_path = ?, export_name = ?, updated_at = ?
|
|
134
|
+
WHERE design_id = ? AND name = ?
|
|
135
|
+
`,
|
|
136
|
+
args: [variants, props, "index.html", name, now, designId, name],
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
if (result.rowsAffected > 0) return;
|
|
140
|
+
|
|
141
|
+
await client.execute({
|
|
142
|
+
sql: `
|
|
143
|
+
INSERT INTO component_index (
|
|
144
|
+
id, design_id, name, file_path, export_name, props, variants,
|
|
145
|
+
runtime_selectors, owner_email, created_at, updated_at
|
|
146
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
147
|
+
`,
|
|
148
|
+
args: [
|
|
149
|
+
componentIndexId(designId, name),
|
|
150
|
+
designId,
|
|
151
|
+
name,
|
|
152
|
+
"index.html",
|
|
153
|
+
name,
|
|
154
|
+
props,
|
|
155
|
+
variants,
|
|
156
|
+
JSON.stringify(['[data-agent-native-node-id="e2e-component-button"]']),
|
|
157
|
+
E2E_EMAIL,
|
|
158
|
+
now,
|
|
159
|
+
now,
|
|
160
|
+
],
|
|
161
|
+
});
|
|
162
|
+
} finally {
|
|
163
|
+
client.close();
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
79
167
|
export default async function globalSetup(config: FullConfig) {
|
|
80
168
|
const baseURL =
|
|
81
169
|
(config.projects[0]?.use?.baseURL as string | undefined) ??
|
|
@@ -148,6 +236,10 @@ export default async function globalSetup(config: FullConfig) {
|
|
|
148
236
|
content: FIXTURE_HTML,
|
|
149
237
|
fileType: "html",
|
|
150
238
|
});
|
|
239
|
+
await postAction(context.request, baseURL, "index-components", {
|
|
240
|
+
designId,
|
|
241
|
+
});
|
|
242
|
+
await seedComponentVariantMetadata(designId);
|
|
151
243
|
|
|
152
244
|
await writeFile(SEED_PATH, JSON.stringify({ designId }, null, 2));
|
|
153
245
|
// eslint-disable-next-line no-console
|
|
@@ -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;
|