@agent-native/core 0.80.11 → 0.81.1
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/cli/design-connect.ts +419 -19
- package/corpus/core/src/client/AgentPanel.tsx +33 -1
- 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/AgentPanel.d.ts +1 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +18 -3
- package/dist/client/AgentPanel.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/routes.d.ts +1 -1
- 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/file-upload/actions/upload-image.d.ts +2 -2
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/observability/routes.d.ts +8 -8
- 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
|
@@ -25,6 +25,8 @@ import {
|
|
|
25
25
|
IconChevronRight,
|
|
26
26
|
IconCircle,
|
|
27
27
|
IconCode,
|
|
28
|
+
IconFileText,
|
|
29
|
+
IconFolder,
|
|
28
30
|
IconLetterCase,
|
|
29
31
|
IconPalette,
|
|
30
32
|
IconPlus,
|
|
@@ -32,6 +34,7 @@ import {
|
|
|
32
34
|
IconRuler,
|
|
33
35
|
IconSpacingVertical,
|
|
34
36
|
IconShadow,
|
|
37
|
+
IconUpload,
|
|
35
38
|
} from "@tabler/icons-react";
|
|
36
39
|
import { useEffect, useRef, useState } from "react";
|
|
37
40
|
|
|
@@ -43,6 +46,7 @@ import {
|
|
|
43
46
|
PopoverContent,
|
|
44
47
|
PopoverTrigger,
|
|
45
48
|
} from "@/components/ui/popover";
|
|
49
|
+
import { Textarea } from "@/components/ui/textarea";
|
|
46
50
|
import {
|
|
47
51
|
Tooltip,
|
|
48
52
|
TooltipContent,
|
|
@@ -75,6 +79,18 @@ interface IndexDesignTokensResult {
|
|
|
75
79
|
tokens: DesignToken[];
|
|
76
80
|
}
|
|
77
81
|
|
|
82
|
+
interface ImportDesignTokensResult {
|
|
83
|
+
designId: string;
|
|
84
|
+
importedCount: number;
|
|
85
|
+
filesAnalyzed: string[];
|
|
86
|
+
resolvedCssVars?: Record<string, string>;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
interface TokenImportFile {
|
|
90
|
+
filename: string;
|
|
91
|
+
content: string;
|
|
92
|
+
}
|
|
93
|
+
|
|
78
94
|
// ---------------------------------------------------------------------------
|
|
79
95
|
// Props
|
|
80
96
|
// ---------------------------------------------------------------------------
|
|
@@ -388,6 +404,200 @@ function NewTokenPopover({ onAdd }: NewTokenPopoverProps) {
|
|
|
388
404
|
);
|
|
389
405
|
}
|
|
390
406
|
|
|
407
|
+
// ---------------------------------------------------------------------------
|
|
408
|
+
// Import popover
|
|
409
|
+
// ---------------------------------------------------------------------------
|
|
410
|
+
|
|
411
|
+
const TOKEN_IMPORT_ACCEPT = [
|
|
412
|
+
".css",
|
|
413
|
+
".scss",
|
|
414
|
+
".sass",
|
|
415
|
+
".less",
|
|
416
|
+
".json",
|
|
417
|
+
".ts",
|
|
418
|
+
".tsx",
|
|
419
|
+
".js",
|
|
420
|
+
".jsx",
|
|
421
|
+
".md",
|
|
422
|
+
".mdx",
|
|
423
|
+
".txt",
|
|
424
|
+
].join(",");
|
|
425
|
+
|
|
426
|
+
async function readImportFiles(fileList: FileList): Promise<TokenImportFile[]> {
|
|
427
|
+
const selected = [...fileList].slice(0, 20);
|
|
428
|
+
const files = await Promise.all(
|
|
429
|
+
selected.map(async (file) => ({
|
|
430
|
+
filename:
|
|
431
|
+
(file as File & { webkitRelativePath?: string }).webkitRelativePath ||
|
|
432
|
+
file.name,
|
|
433
|
+
content: await file.text(),
|
|
434
|
+
})),
|
|
435
|
+
);
|
|
436
|
+
|
|
437
|
+
return files.filter((file) => file.content.trim().length > 0);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
interface ImportTokensPopoverProps {
|
|
441
|
+
onImportFiles: (
|
|
442
|
+
files: TokenImportFile[],
|
|
443
|
+
) => Promise<ImportDesignTokensResult>;
|
|
444
|
+
onImportText: (text: string) => Promise<ImportDesignTokensResult>;
|
|
445
|
+
onImportCurrentDesign: () => Promise<ImportDesignTokensResult>;
|
|
446
|
+
isPending: boolean;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function ImportTokensPopover({
|
|
450
|
+
onImportFiles,
|
|
451
|
+
onImportText,
|
|
452
|
+
onImportCurrentDesign,
|
|
453
|
+
isPending,
|
|
454
|
+
}: ImportTokensPopoverProps) {
|
|
455
|
+
const t = useT();
|
|
456
|
+
const fileInputRef = useRef<HTMLInputElement>(null);
|
|
457
|
+
const folderInputRef = useRef<HTMLInputElement>(null);
|
|
458
|
+
const [open, setOpen] = useState(false);
|
|
459
|
+
const [text, setText] = useState("");
|
|
460
|
+
const [status, setStatus] = useState<string | null>(null);
|
|
461
|
+
|
|
462
|
+
const runImport = async (
|
|
463
|
+
importer: () => Promise<ImportDesignTokensResult>,
|
|
464
|
+
) => {
|
|
465
|
+
setStatus(null);
|
|
466
|
+
try {
|
|
467
|
+
const result = await importer();
|
|
468
|
+
setStatus(
|
|
469
|
+
t("designEditor.tokens.importedCount", {
|
|
470
|
+
count: result.importedCount,
|
|
471
|
+
}),
|
|
472
|
+
);
|
|
473
|
+
if (result.importedCount > 0) {
|
|
474
|
+
setText("");
|
|
475
|
+
}
|
|
476
|
+
} catch (error) {
|
|
477
|
+
setStatus(error instanceof Error ? error.message : String(error));
|
|
478
|
+
}
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
482
|
+
const files = event.currentTarget.files;
|
|
483
|
+
event.currentTarget.value = "";
|
|
484
|
+
if (!files?.length) return;
|
|
485
|
+
void runImport(async () => onImportFiles(await readImportFiles(files)));
|
|
486
|
+
};
|
|
487
|
+
|
|
488
|
+
return (
|
|
489
|
+
<Popover open={open} onOpenChange={setOpen}>
|
|
490
|
+
<Tooltip>
|
|
491
|
+
<TooltipTrigger asChild>
|
|
492
|
+
<PopoverTrigger asChild>
|
|
493
|
+
<Button
|
|
494
|
+
type="button"
|
|
495
|
+
variant="ghost"
|
|
496
|
+
size="icon"
|
|
497
|
+
className="size-6 cursor-pointer text-muted-foreground/60 hover:text-foreground"
|
|
498
|
+
aria-label={t("designEditor.tokens.import")}
|
|
499
|
+
>
|
|
500
|
+
<IconUpload className="size-3" />
|
|
501
|
+
</Button>
|
|
502
|
+
</PopoverTrigger>
|
|
503
|
+
</TooltipTrigger>
|
|
504
|
+
<TooltipContent>{t("designEditor.tokens.import")}</TooltipContent>
|
|
505
|
+
</Tooltip>
|
|
506
|
+
<PopoverContent align="end" className="w-72 p-3 text-[12px]">
|
|
507
|
+
<div className="space-y-3">
|
|
508
|
+
<div className="space-y-1">
|
|
509
|
+
<p className="text-[11px] font-medium text-foreground">
|
|
510
|
+
{t("designEditor.tokens.importTitle")}
|
|
511
|
+
</p>
|
|
512
|
+
<p className="text-[10px] leading-snug text-muted-foreground">
|
|
513
|
+
{t("designEditor.tokens.importHint")}
|
|
514
|
+
</p>
|
|
515
|
+
</div>
|
|
516
|
+
|
|
517
|
+
<div className="grid grid-cols-3 gap-1.5">
|
|
518
|
+
<Button
|
|
519
|
+
type="button"
|
|
520
|
+
variant="outline"
|
|
521
|
+
size="sm"
|
|
522
|
+
className="h-7 cursor-pointer gap-1 px-2 text-[10px]"
|
|
523
|
+
disabled={isPending}
|
|
524
|
+
onClick={() => fileInputRef.current?.click()}
|
|
525
|
+
>
|
|
526
|
+
<IconFileText className="size-3" />
|
|
527
|
+
{t("designEditor.tokens.files")}
|
|
528
|
+
</Button>
|
|
529
|
+
<Button
|
|
530
|
+
type="button"
|
|
531
|
+
variant="outline"
|
|
532
|
+
size="sm"
|
|
533
|
+
className="h-7 cursor-pointer gap-1 px-2 text-[10px]"
|
|
534
|
+
disabled={isPending}
|
|
535
|
+
onClick={() => folderInputRef.current?.click()}
|
|
536
|
+
>
|
|
537
|
+
<IconFolder className="size-3" />
|
|
538
|
+
{t("designEditor.tokens.folder")}
|
|
539
|
+
</Button>
|
|
540
|
+
<Button
|
|
541
|
+
type="button"
|
|
542
|
+
variant="outline"
|
|
543
|
+
size="sm"
|
|
544
|
+
className="h-7 cursor-pointer px-2 text-[10px]"
|
|
545
|
+
disabled={isPending}
|
|
546
|
+
onClick={() => void runImport(onImportCurrentDesign)}
|
|
547
|
+
>
|
|
548
|
+
{t("designEditor.tokens.current")}
|
|
549
|
+
</Button>
|
|
550
|
+
</div>
|
|
551
|
+
|
|
552
|
+
<input
|
|
553
|
+
ref={fileInputRef}
|
|
554
|
+
type="file"
|
|
555
|
+
multiple
|
|
556
|
+
accept={TOKEN_IMPORT_ACCEPT}
|
|
557
|
+
className="hidden"
|
|
558
|
+
onChange={handleFileChange}
|
|
559
|
+
/>
|
|
560
|
+
<input
|
|
561
|
+
ref={folderInputRef}
|
|
562
|
+
type="file"
|
|
563
|
+
multiple
|
|
564
|
+
accept={TOKEN_IMPORT_ACCEPT}
|
|
565
|
+
className="hidden"
|
|
566
|
+
onChange={handleFileChange}
|
|
567
|
+
{...({ directory: "", webkitdirectory: "" } as Record<
|
|
568
|
+
string,
|
|
569
|
+
string
|
|
570
|
+
>)}
|
|
571
|
+
/>
|
|
572
|
+
|
|
573
|
+
<div className="space-y-1.5">
|
|
574
|
+
<Textarea
|
|
575
|
+
value={text}
|
|
576
|
+
onChange={(event) => setText(event.target.value)}
|
|
577
|
+
placeholder={t("designEditor.tokens.pastePlaceholder")}
|
|
578
|
+
className="min-h-24 resize-none font-mono text-[11px]"
|
|
579
|
+
/>
|
|
580
|
+
<Button
|
|
581
|
+
type="button"
|
|
582
|
+
className="h-7 w-full cursor-pointer text-[11px]"
|
|
583
|
+
disabled={isPending || !text.trim()}
|
|
584
|
+
onClick={() => void runImport(() => onImportText(text))}
|
|
585
|
+
>
|
|
586
|
+
{t("designEditor.tokens.importPasted")}
|
|
587
|
+
</Button>
|
|
588
|
+
</div>
|
|
589
|
+
|
|
590
|
+
{status && (
|
|
591
|
+
<p className="text-[10px] leading-snug text-muted-foreground">
|
|
592
|
+
{status}
|
|
593
|
+
</p>
|
|
594
|
+
)}
|
|
595
|
+
</div>
|
|
596
|
+
</PopoverContent>
|
|
597
|
+
</Popover>
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
|
|
391
601
|
// ---------------------------------------------------------------------------
|
|
392
602
|
// Main panel
|
|
393
603
|
// ---------------------------------------------------------------------------
|
|
@@ -409,6 +619,7 @@ export function TokensPanel({ designId, onTokensApplied }: TokensPanelProps) {
|
|
|
409
619
|
);
|
|
410
620
|
|
|
411
621
|
const applyMutation = useActionMutation("apply-design-token-edit");
|
|
622
|
+
const importMutation = useActionMutation("import-design-tokens");
|
|
412
623
|
|
|
413
624
|
// ------------------------------------------------------------------
|
|
414
625
|
// Local edit state
|
|
@@ -463,6 +674,40 @@ export function TokensPanel({ designId, onTokensApplied }: TokensPanelProps) {
|
|
|
463
674
|
);
|
|
464
675
|
};
|
|
465
676
|
|
|
677
|
+
const handleImportSuccess = (result: ImportDesignTokensResult) => {
|
|
678
|
+
void refetch();
|
|
679
|
+
if (result.resolvedCssVars && onTokensApplied) {
|
|
680
|
+
onTokensApplied(result.resolvedCssVars);
|
|
681
|
+
}
|
|
682
|
+
return result;
|
|
683
|
+
};
|
|
684
|
+
|
|
685
|
+
const importFiles = async (files: TokenImportFile[]) => {
|
|
686
|
+
const result = (await importMutation.mutateAsync({
|
|
687
|
+
designId,
|
|
688
|
+
source: "files",
|
|
689
|
+
files,
|
|
690
|
+
})) as ImportDesignTokensResult;
|
|
691
|
+
return handleImportSuccess(result);
|
|
692
|
+
};
|
|
693
|
+
|
|
694
|
+
const importText = async (text: string) => {
|
|
695
|
+
const result = (await importMutation.mutateAsync({
|
|
696
|
+
designId,
|
|
697
|
+
source: "paste",
|
|
698
|
+
text,
|
|
699
|
+
})) as ImportDesignTokensResult;
|
|
700
|
+
return handleImportSuccess(result);
|
|
701
|
+
};
|
|
702
|
+
|
|
703
|
+
const importCurrentDesign = async () => {
|
|
704
|
+
const result = (await importMutation.mutateAsync({
|
|
705
|
+
designId,
|
|
706
|
+
source: "current-design",
|
|
707
|
+
})) as ImportDesignTokensResult;
|
|
708
|
+
return handleImportSuccess(result);
|
|
709
|
+
};
|
|
710
|
+
|
|
466
711
|
// ------------------------------------------------------------------
|
|
467
712
|
// Render
|
|
468
713
|
// ------------------------------------------------------------------
|
|
@@ -500,6 +745,12 @@ export function TokensPanel({ designId, onTokensApplied }: TokensPanelProps) {
|
|
|
500
745
|
</TooltipTrigger>
|
|
501
746
|
<TooltipContent>{t("designEditor.tokens.refresh")}</TooltipContent>
|
|
502
747
|
</Tooltip>
|
|
748
|
+
<ImportTokensPopover
|
|
749
|
+
onImportFiles={importFiles}
|
|
750
|
+
onImportText={importText}
|
|
751
|
+
onImportCurrentDesign={importCurrentDesign}
|
|
752
|
+
isPending={importMutation.isPending}
|
|
753
|
+
/>
|
|
503
754
|
<NewTokenPopover onAdd={handleNewToken} />
|
|
504
755
|
</div>
|
|
505
756
|
</div>
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bridge codegen — compiles every *.bridge.ts in this directory to a self-contained
|
|
3
|
+
* injectable string and writes the result as a generated TypeScript module under
|
|
4
|
+
* .generated/bridge/<name>.generated.ts.
|
|
5
|
+
*
|
|
6
|
+
* Usage (from the design template root):
|
|
7
|
+
* pnpm exec tsx app/components/design/bridge/codegen.ts
|
|
8
|
+
*
|
|
9
|
+
* Called automatically by:
|
|
10
|
+
* - The freshness guard test (bridge.guard.spec.ts)
|
|
11
|
+
* - The project build script (see scripts.build in package.json)
|
|
12
|
+
*
|
|
13
|
+
* Mechanism
|
|
14
|
+
* ---------
|
|
15
|
+
* esbuild is invoked with bundle:true, format:'iife', platform:'browser',
|
|
16
|
+
* write:false for each .bridge.ts file. This means:
|
|
17
|
+
* • Any ES import/require that CAN'T be resolved by esbuild causes a build error
|
|
18
|
+
* (self-containment guarantee for non-local deps, e.g. npm packages).
|
|
19
|
+
* • Any ES import that CAN be resolved (e.g. a relative .ts file) gets bundled
|
|
20
|
+
* inline — the guard test's regex check on source prevents this.
|
|
21
|
+
* • The output is an IIFE-wrapped, browser-runnable JS string.
|
|
22
|
+
*
|
|
23
|
+
* The generated module is a plain committed .ts file that any bundler (Vite, Nitro,
|
|
24
|
+
* Rolldown) handles as a normal module — no Vite plugin, no SSR complications.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { execSync } from "node:child_process";
|
|
28
|
+
import { readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
29
|
+
import { join, resolve } from "node:path";
|
|
30
|
+
import { fileURLToPath } from "node:url";
|
|
31
|
+
|
|
32
|
+
import * as esbuild from "esbuild";
|
|
33
|
+
|
|
34
|
+
const __dirname = fileURLToPath(new URL(".", import.meta.url));
|
|
35
|
+
const designRoot = resolve(__dirname, "../../../..");
|
|
36
|
+
const bridgeDir = __dirname;
|
|
37
|
+
const generatedDir = join(designRoot, ".generated", "bridge");
|
|
38
|
+
|
|
39
|
+
const GENERATED_BANNER = `// AUTO-GENERATED by bridge/codegen.ts — do not edit manually.
|
|
40
|
+
// Run: pnpm exec tsx app/components/design/bridge/codegen.ts
|
|
41
|
+
`;
|
|
42
|
+
|
|
43
|
+
async function compileBridge(srcFile: string): Promise<string> {
|
|
44
|
+
const result = await esbuild.build({
|
|
45
|
+
entryPoints: [srcFile],
|
|
46
|
+
bundle: true,
|
|
47
|
+
format: "iife",
|
|
48
|
+
platform: "browser",
|
|
49
|
+
target: "es2020",
|
|
50
|
+
write: false,
|
|
51
|
+
// No external packages — unresolved imports are a hard error.
|
|
52
|
+
// (Relative imports that CAN be resolved get bundled inline, which is also
|
|
53
|
+
// wrong; the guard test prevents those at the source level.)
|
|
54
|
+
external: [],
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
if (result.errors.length > 0) {
|
|
58
|
+
const msgs = await esbuild.formatMessages(result.errors, { kind: "error" });
|
|
59
|
+
throw new Error(`esbuild error for ${srcFile}:\n${msgs.join("\n")}`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const output = result.outputFiles[0];
|
|
63
|
+
if (!output) throw new Error(`No output from esbuild for ${srcFile}`);
|
|
64
|
+
return output.text;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function bridgeName(filename: string): string {
|
|
68
|
+
return filename.replace(/\.bridge\.ts$/, "");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function generatedModuleSrc(name: string, compiled: string): string {
|
|
72
|
+
// Escape backticks and ${} in the compiled string so it embeds safely.
|
|
73
|
+
const escaped = compiled
|
|
74
|
+
.replace(/\\/g, "\\\\")
|
|
75
|
+
.replace(/`/g, "\\`")
|
|
76
|
+
.replace(/\$\{/g, "\\${");
|
|
77
|
+
return `${GENERATED_BANNER}
|
|
78
|
+
/** Compiled IIFE string for ${name}.bridge.ts — inject into an iframe via srcdoc or a <script> tag. */
|
|
79
|
+
export const ${camelCase(name)}BridgeScript: string = \`${escaped}\`;
|
|
80
|
+
`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function camelCase(name: string): string {
|
|
84
|
+
return name.replace(/[-_]([a-z])/g, (_, c: string) => c.toUpperCase());
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function runCodegen(): Promise<void> {
|
|
88
|
+
const bridgeFiles = readdirSync(bridgeDir).filter((f) =>
|
|
89
|
+
f.endsWith(".bridge.ts"),
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
if (bridgeFiles.length === 0) {
|
|
93
|
+
console.log("[bridge/codegen] No *.bridge.ts files found.");
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
for (const filename of bridgeFiles) {
|
|
98
|
+
const srcPath = join(bridgeDir, filename);
|
|
99
|
+
const name = bridgeName(filename);
|
|
100
|
+
const outPath = join(generatedDir, `${name}.generated.ts`);
|
|
101
|
+
|
|
102
|
+
console.log(`[bridge/codegen] Compiling ${filename} ...`);
|
|
103
|
+
const compiled = await compileBridge(srcPath);
|
|
104
|
+
const src = generatedModuleSrc(name, compiled);
|
|
105
|
+
writeFileSync(outPath, src, "utf-8");
|
|
106
|
+
console.log(`[bridge/codegen] → .generated/bridge/${name}.generated.ts`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Run oxfmt on generated files so they pass the formatter check.
|
|
110
|
+
try {
|
|
111
|
+
execSync(`pnpm exec oxfmt .generated/bridge/`, {
|
|
112
|
+
cwd: designRoot,
|
|
113
|
+
stdio: "pipe",
|
|
114
|
+
});
|
|
115
|
+
} catch {
|
|
116
|
+
// oxfmt not critical — formatting mismatches caught by CI linter, not here.
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
console.log("[bridge/codegen] Done.");
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
runCodegen().catch((err) => {
|
|
123
|
+
console.error("[bridge/codegen] FAILED:", err);
|
|
124
|
+
process.exit(1);
|
|
125
|
+
});
|