@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
|
@@ -11,6 +11,7 @@ import type {
|
|
|
11
11
|
GenerationIntent,
|
|
12
12
|
ImageCategory,
|
|
13
13
|
ImageModel,
|
|
14
|
+
ImageQualityTier,
|
|
14
15
|
ImageSize,
|
|
15
16
|
StyleStrength,
|
|
16
17
|
StyleBrief,
|
|
@@ -564,7 +565,8 @@ export async function analyzeStyleWithGemini(input: {
|
|
|
564
565
|
{
|
|
565
566
|
text: [
|
|
566
567
|
"Analyze these brand/style reference images for a reusable image generation style brief.",
|
|
567
|
-
"Return only compact JSON with keys: description, medium, mood, subjectMatter, texture, composition, lighting, typographyPolicy, doNot.",
|
|
568
|
+
"Return only compact JSON with keys: description, medium, mood, subjectMatter, texture, composition, lighting, fontFamilies, fontWeights, letterforms, caseStyle, typographyPolicy, doNot.",
|
|
569
|
+
"For typography, describe specific letterforms and font traits, not just broad sans/serif categories; omit any typography field you cannot determine confidently.",
|
|
568
570
|
"Use specific trait-locking phrases that can be reused across future image prompts.",
|
|
569
571
|
"Do not name brands, artists, studios, franchises, or copyrighted works unless they appear as user-provided brand identity.",
|
|
570
572
|
"Keep doNot as an array of short constraints. Omit uncertain fields instead of guessing.",
|
|
@@ -608,16 +610,24 @@ function extractJsonObject(text: string): string {
|
|
|
608
610
|
return "{}";
|
|
609
611
|
}
|
|
610
612
|
|
|
611
|
-
function sanitizeStyleBrief(value: Record<string, unknown>): StyleBrief {
|
|
613
|
+
export function sanitizeStyleBrief(value: Record<string, unknown>): StyleBrief {
|
|
612
614
|
const stringField = (key: string) => {
|
|
613
615
|
const raw = value[key];
|
|
614
616
|
return typeof raw === "string" && raw.trim() ? raw.trim() : undefined;
|
|
615
617
|
};
|
|
616
|
-
const
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
618
|
+
const stringArrayField = (key: string) => {
|
|
619
|
+
const raw = value[key];
|
|
620
|
+
return Array.isArray(raw)
|
|
621
|
+
? raw
|
|
622
|
+
.filter(
|
|
623
|
+
(item): item is string => typeof item === "string" && !!item.trim(),
|
|
624
|
+
)
|
|
625
|
+
.map((item) => item.trim())
|
|
626
|
+
: undefined;
|
|
627
|
+
};
|
|
628
|
+
const fontFamilies = stringArrayField("fontFamilies");
|
|
629
|
+
const fontWeights = stringArrayField("fontWeights");
|
|
630
|
+
const doNot = stringArrayField("doNot");
|
|
621
631
|
return {
|
|
622
632
|
description: stringField("description"),
|
|
623
633
|
medium: stringField("medium"),
|
|
@@ -626,8 +636,12 @@ function sanitizeStyleBrief(value: Record<string, unknown>): StyleBrief {
|
|
|
626
636
|
texture: stringField("texture"),
|
|
627
637
|
composition: stringField("composition"),
|
|
628
638
|
lighting: stringField("lighting"),
|
|
639
|
+
fontFamilies: fontFamilies?.length ? fontFamilies : undefined,
|
|
640
|
+
fontWeights: fontWeights?.length ? fontWeights : undefined,
|
|
641
|
+
letterforms: stringField("letterforms"),
|
|
642
|
+
caseStyle: stringField("caseStyle"),
|
|
629
643
|
typographyPolicy: stringField("typographyPolicy"),
|
|
630
|
-
doNot: doNot?.length ? doNot
|
|
644
|
+
doNot: doNot?.length ? doNot : undefined,
|
|
631
645
|
};
|
|
632
646
|
}
|
|
633
647
|
|
|
@@ -774,6 +788,66 @@ function toBuilderImageModel(model: ImageModel) {
|
|
|
774
788
|
return model;
|
|
775
789
|
}
|
|
776
790
|
|
|
791
|
+
function resolveModelForTier(
|
|
792
|
+
tier: ImageQualityTier | undefined,
|
|
793
|
+
category: ImageCategory | undefined,
|
|
794
|
+
): ImageModel | undefined {
|
|
795
|
+
if (!tier) return undefined;
|
|
796
|
+
if (tier === "fast") return "gemini-3.1-flash-image";
|
|
797
|
+
if (tier === "best") return "gemini-3-pro-image";
|
|
798
|
+
return ["hero", "landing", "logo", "campaign"].includes(category ?? "")
|
|
799
|
+
? "gemini-3-pro-image"
|
|
800
|
+
: "gemini-3.1-flash-image";
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
export function resolveImageModelForRequest(input: {
|
|
804
|
+
explicitModel?: ImageModel;
|
|
805
|
+
imageModelDefault?: ImageModel;
|
|
806
|
+
explicitTier?: ImageQualityTier;
|
|
807
|
+
resolvedTier?: ImageQualityTier;
|
|
808
|
+
category?: ImageCategory;
|
|
809
|
+
presetModel?: ImageModel | null;
|
|
810
|
+
embeddedText?: string | null;
|
|
811
|
+
}): ImageModel {
|
|
812
|
+
if (input.explicitModel) return input.explicitModel;
|
|
813
|
+
|
|
814
|
+
// Exact embedded text is materially better on Gemini Pro, so upgrade to it by
|
|
815
|
+
// default for text requests — but never override a model or tier the caller
|
|
816
|
+
// or a tagged preset already chose (presets "own" model/tier per the action
|
|
817
|
+
// docs). Only auto-upgrade when there is no tier (explicit or preset-derived)
|
|
818
|
+
// and no preset model in play; this still upgrades a bare text request over
|
|
819
|
+
// the composer default.
|
|
820
|
+
const textAccurateModel: ImageModel | undefined =
|
|
821
|
+
input.embeddedText?.trim() &&
|
|
822
|
+
!input.explicitTier &&
|
|
823
|
+
!input.resolvedTier &&
|
|
824
|
+
!input.presetModel
|
|
825
|
+
? "gemini-3-pro-image"
|
|
826
|
+
: undefined;
|
|
827
|
+
|
|
828
|
+
// Precedence: explicit per-request model (handled above) > embedded-text
|
|
829
|
+
// upgrade > an EXPLICIT per-request tier > the preset's saved model > the
|
|
830
|
+
// preset-DERIVED tier mapping > the sticky composer default > the floor.
|
|
831
|
+
//
|
|
832
|
+
// Two subtleties:
|
|
833
|
+
// - An explicit per-request tier outranks the preset's saved model (the
|
|
834
|
+
// caller is deliberately overriding the preset this turn).
|
|
835
|
+
// - The preset's explicit saved model outranks its OWN derived tier: a
|
|
836
|
+
// preset's `model` column and `settings.tier` can drift out of sync
|
|
837
|
+
// (update-generation-preset can change one without the other), and the
|
|
838
|
+
// explicitly saved model is the authoritative choice.
|
|
839
|
+
// - The composer default ranks LAST of the real signals: it is a global
|
|
840
|
+
// stored preference and must not defeat a tagged preset or a tier request.
|
|
841
|
+
return (
|
|
842
|
+
textAccurateModel ??
|
|
843
|
+
resolveModelForTier(input.explicitTier, input.category) ??
|
|
844
|
+
input.presetModel ??
|
|
845
|
+
resolveModelForTier(input.resolvedTier, input.category) ??
|
|
846
|
+
input.imageModelDefault ??
|
|
847
|
+
"gemini-3.1-flash-image"
|
|
848
|
+
);
|
|
849
|
+
}
|
|
850
|
+
|
|
777
851
|
function toBuilderReferenceRole(role: string) {
|
|
778
852
|
switch (role) {
|
|
779
853
|
case "style_reference":
|
|
@@ -799,6 +873,8 @@ export function compilePrompt(input: {
|
|
|
799
873
|
styleBrief: StyleBrief;
|
|
800
874
|
customInstructions?: string | null;
|
|
801
875
|
prompt: string;
|
|
876
|
+
embeddedText?: string | null;
|
|
877
|
+
textPlacement?: string | null;
|
|
802
878
|
referenceCount: number;
|
|
803
879
|
includeLogo: boolean;
|
|
804
880
|
aspectRatio?: AspectRatio;
|
|
@@ -815,6 +891,11 @@ export function compilePrompt(input: {
|
|
|
815
891
|
const doNot = style.doNot?.length
|
|
816
892
|
? `\nAvoid: ${style.doNot.join("; ")}.`
|
|
817
893
|
: "";
|
|
894
|
+
const typographyBlock = formatBrandTypography(style);
|
|
895
|
+
const requestedEmbeddedText = hasEmbeddedText(input.embeddedText);
|
|
896
|
+
const textInstruction = requestedEmbeddedText
|
|
897
|
+
? formatEmbeddedTextInstruction(input.embeddedText, input.textPlacement)
|
|
898
|
+
: "Do not render headlines, body text, UI labels, or prompt wording inside the image unless the user explicitly asks for exact visible text.";
|
|
818
899
|
const logoInstruction = input.includeLogo
|
|
819
900
|
? "\nLeave a clean uncluttered area in the upper-right for the real brand logo; do not draw or approximate the logo yourself."
|
|
820
901
|
: "";
|
|
@@ -839,14 +920,24 @@ export function compilePrompt(input: {
|
|
|
839
920
|
: "No reference images are attached for this run. Use the style brief and custom instructions as the source of truth.";
|
|
840
921
|
|
|
841
922
|
if (intent === "edit") {
|
|
923
|
+
const editTypographyInstruction =
|
|
924
|
+
requestedEmbeddedText && typographyBlock ? `\n\n${typographyBlock}` : "";
|
|
925
|
+
const editTextInstruction = requestedEmbeddedText
|
|
926
|
+
? `\n\n${formatEmbeddedTextInstruction(input.embeddedText, input.textPlacement)}`
|
|
927
|
+
: "";
|
|
928
|
+
const editConstraint = requestedEmbeddedText
|
|
929
|
+
? "Do not reimagine the image, change its aspect ratio, or alter unrelated subjects. Do not add any other new text. Return the full image."
|
|
930
|
+
: "Do not reimagine the image, change its aspect ratio, add new text, or alter unrelated subjects. Return the full image.";
|
|
842
931
|
return `Edit the attached image for the "${input.libraryTitle}" asset library.
|
|
843
932
|
|
|
844
933
|
${referenceInstruction}
|
|
934
|
+
${editTypographyInstruction}
|
|
845
935
|
|
|
846
936
|
Make only this change:
|
|
847
937
|
${input.prompt}
|
|
938
|
+
${editTextInstruction}
|
|
848
939
|
|
|
849
|
-
|
|
940
|
+
${editConstraint}`;
|
|
850
941
|
}
|
|
851
942
|
|
|
852
943
|
return `Create a brand-consistent image for the "${input.libraryTitle}" asset library.
|
|
@@ -861,15 +952,48 @@ ${style.subjectMatter ? `\nSubject matter: ${style.subjectMatter}.` : ""}
|
|
|
861
952
|
${style.texture ? `\nTexture/material treatment: ${style.texture}.` : ""}
|
|
862
953
|
${style.composition ? `\nComposition: ${style.composition}.` : ""}
|
|
863
954
|
${style.lighting ? `\nLighting: ${style.lighting}.` : ""}
|
|
864
|
-
${
|
|
955
|
+
${typographyBlock ? `\n${typographyBlock}` : ""}${frameInstruction}
|
|
865
956
|
${doNot}${logoInstruction}${diagramInstruction}${customInstructions}
|
|
866
957
|
|
|
867
|
-
|
|
958
|
+
${textInstruction}
|
|
868
959
|
|
|
869
960
|
User request:
|
|
870
961
|
${input.prompt}`;
|
|
871
962
|
}
|
|
872
963
|
|
|
964
|
+
function hasEmbeddedText(value?: string | null): boolean {
|
|
965
|
+
return typeof value === "string" && !!value.trim();
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
function formatEmbeddedTextInstruction(
|
|
969
|
+
embeddedText?: string | null,
|
|
970
|
+
textPlacement?: string | null,
|
|
971
|
+
): string {
|
|
972
|
+
const placement = textPlacement?.trim();
|
|
973
|
+
return [
|
|
974
|
+
`Render this text exactly, spelled correctly, inside the image: ${JSON.stringify(embeddedText ?? "")}.`,
|
|
975
|
+
placement ? `Placement: ${placement}.` : "",
|
|
976
|
+
"Match the brand typography described above where specified; keep it legible and well-kerned.",
|
|
977
|
+
]
|
|
978
|
+
.filter(Boolean)
|
|
979
|
+
.join(" ");
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
function formatBrandTypography(style: StyleBrief): string {
|
|
983
|
+
const parts = [
|
|
984
|
+
style.fontFamilies?.length
|
|
985
|
+
? `families ${style.fontFamilies.join(", ")}`
|
|
986
|
+
: "",
|
|
987
|
+
style.fontWeights?.length ? `weights ${style.fontWeights.join(", ")}` : "",
|
|
988
|
+
style.letterforms ? `letterforms: ${style.letterforms}` : "",
|
|
989
|
+
style.caseStyle ? `case: ${style.caseStyle}` : "",
|
|
990
|
+
style.typographyPolicy,
|
|
991
|
+
].filter((part): part is string => typeof part === "string" && !!part.trim());
|
|
992
|
+
|
|
993
|
+
if (!parts.length) return "";
|
|
994
|
+
return `Brand typography: ${parts.join("; ")}. Match these for any rendered text.`;
|
|
995
|
+
}
|
|
996
|
+
|
|
873
997
|
// A content-only reference is an image the user attached as subject/content for
|
|
874
998
|
// a single request (not a reusable brand/style reference). It should never count
|
|
875
999
|
// as a brand-kit style reference.
|
|
@@ -112,6 +112,10 @@ export interface StyleBrief {
|
|
|
112
112
|
texture?: string;
|
|
113
113
|
composition?: string;
|
|
114
114
|
lighting?: string;
|
|
115
|
+
fontFamilies?: string[];
|
|
116
|
+
fontWeights?: string[];
|
|
117
|
+
letterforms?: string;
|
|
118
|
+
caseStyle?: string;
|
|
115
119
|
typographyPolicy?: string;
|
|
116
120
|
doNot?: string[];
|
|
117
121
|
}
|
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
|
|
11
11
|
import { defineAction } from "@agent-native/core";
|
|
12
12
|
import {
|
|
13
|
-
appStateList,
|
|
14
13
|
readAppState,
|
|
15
14
|
writeAppState,
|
|
16
15
|
deleteAppState,
|
|
@@ -28,6 +27,7 @@ import {
|
|
|
28
27
|
applyFaststart,
|
|
29
28
|
hasPlayableMp4Metadata,
|
|
30
29
|
} from "../server/lib/faststart.js";
|
|
30
|
+
import { listRecordingChunkKeys } from "../server/lib/recording-upload-state.js";
|
|
31
31
|
import {
|
|
32
32
|
getCurrentOwnerEmail,
|
|
33
33
|
ownerEmailMatches,
|
|
@@ -66,17 +66,6 @@ function concatBytes(parts: Uint8Array[]): Uint8Array {
|
|
|
66
66
|
return out;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
async function listRecordingChunkKeys(
|
|
70
|
-
ownerEmail: string,
|
|
71
|
-
recordingId: string,
|
|
72
|
-
): Promise<string[]> {
|
|
73
|
-
const rows = await appStateList(
|
|
74
|
-
ownerEmail,
|
|
75
|
-
`recording-chunks-${recordingId}-`,
|
|
76
|
-
);
|
|
77
|
-
return rows.map((row) => row.key);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
69
|
function chunkIndexFromKey(key: string): number {
|
|
81
70
|
return Number(key.split("-").pop() || 0);
|
|
82
71
|
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* List pending clips AI requests for the current user.
|
|
3
|
+
*
|
|
4
|
+
* Collapses the per-recording `clips-ai-request-<id>` polling done by the
|
|
5
|
+
* auto-title bridge into a single call. Application state is already
|
|
6
|
+
* session-scoped, and we additionally filter the referenced recordings through
|
|
7
|
+
* `accessFilter` so we never return a request for a recording the user can't
|
|
8
|
+
* access. Only requests for `status = "ready"` recordings are returned — the
|
|
9
|
+
* bridge ignores non-ready recordings, so excluding them avoids sending large
|
|
10
|
+
* transcript blobs for recordings the hook will skip this tick.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { defineAction } from "@agent-native/core";
|
|
14
|
+
import { listAppState } from "@agent-native/core/application-state";
|
|
15
|
+
import { accessFilter } from "@agent-native/core/sharing";
|
|
16
|
+
import { and, eq, inArray } from "drizzle-orm";
|
|
17
|
+
import { z } from "zod";
|
|
18
|
+
|
|
19
|
+
import { getDb, schema } from "../server/db/index.js";
|
|
20
|
+
|
|
21
|
+
const REQUEST_PREFIX = "clips-ai-request-";
|
|
22
|
+
|
|
23
|
+
export default defineAction({
|
|
24
|
+
description:
|
|
25
|
+
"List pending clips AI requests (regenerate-title, summary, chapters, etc.) for recordings the current user can access.",
|
|
26
|
+
schema: z.object({}),
|
|
27
|
+
http: { method: "GET" },
|
|
28
|
+
run: async () => {
|
|
29
|
+
const entries = await listAppState(REQUEST_PREFIX);
|
|
30
|
+
|
|
31
|
+
const requests = entries
|
|
32
|
+
.map((e) => e.value as Record<string, unknown>)
|
|
33
|
+
.filter(
|
|
34
|
+
(v): v is Record<string, unknown> & { recordingId: string } =>
|
|
35
|
+
!!v && typeof v.recordingId === "string" && v.recordingId.length > 0,
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
if (requests.length === 0) return { requests: [] };
|
|
39
|
+
|
|
40
|
+
const recordingIds = [...new Set(requests.map((r) => r.recordingId))];
|
|
41
|
+
|
|
42
|
+
const db = getDb();
|
|
43
|
+
const accessible = await db
|
|
44
|
+
.select({ id: schema.recordings.id })
|
|
45
|
+
.from(schema.recordings)
|
|
46
|
+
.where(
|
|
47
|
+
and(
|
|
48
|
+
accessFilter(schema.recordings, schema.recordingShares),
|
|
49
|
+
inArray(schema.recordings.id, recordingIds),
|
|
50
|
+
eq(schema.recordings.status, "ready"),
|
|
51
|
+
),
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const accessibleIds = new Set(accessible.map((r) => r.id));
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
requests: requests.filter((r) => accessibleIds.has(r.recordingId)),
|
|
58
|
+
};
|
|
59
|
+
},
|
|
60
|
+
});
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Clips AI request bridge
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Polls the `list-ai-requests` action — a single access-scoped call that returns
|
|
5
|
+
* every pending `clips-ai-request-*` entry for recordings the user can access.
|
|
6
|
+
* The bridge sends queued recording work to the agent chat exactly once per
|
|
6
7
|
* (recordingId, kind, requestedAt).
|
|
7
8
|
*
|
|
8
9
|
* Once handled we DELETE the request entry so the next page load / tab switch
|
|
@@ -21,6 +22,7 @@ import { useRecordings, type RecordingSummary } from "./use-library";
|
|
|
21
22
|
|
|
22
23
|
const DEFAULT_TITLE = "Untitled recording";
|
|
23
24
|
const POLL_INTERVAL_MS = 3000;
|
|
25
|
+
const TWO_MINUTES_MS = 2 * 60 * 1000;
|
|
24
26
|
|
|
25
27
|
/** True when `title` is blank or equal to the server-seeded default. */
|
|
26
28
|
export function isDefaultTitle(title: string | null | undefined): boolean {
|
|
@@ -62,22 +64,21 @@ const DISPATCHABLE_REQUESTS = new Set([
|
|
|
62
64
|
"generate-workflow",
|
|
63
65
|
]);
|
|
64
66
|
|
|
65
|
-
async function
|
|
66
|
-
const url = agentNativePath(
|
|
67
|
-
`/_agent-native/application-state/${encodeURIComponent(
|
|
68
|
-
`clips-ai-request-${recordingId}`,
|
|
69
|
-
)}`,
|
|
70
|
-
);
|
|
67
|
+
async function listRequests(): Promise<Map<string, AiRequest>> {
|
|
71
68
|
try {
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
69
|
+
const result = (await callAction("list-ai-requests", {} as any, {
|
|
70
|
+
method: "GET",
|
|
71
|
+
})) as { requests?: AiRequest[] } | null | undefined;
|
|
72
|
+
return new Map(
|
|
73
|
+
(result?.requests ?? [])
|
|
74
|
+
.filter(
|
|
75
|
+
(r): r is AiRequest & { recordingId: string } => !!r?.recordingId,
|
|
76
|
+
)
|
|
77
|
+
.map((r) => [r.recordingId, r]),
|
|
78
|
+
);
|
|
79
79
|
} catch {
|
|
80
|
-
|
|
80
|
+
// Swallow — the next tick retries.
|
|
81
|
+
return new Map();
|
|
81
82
|
}
|
|
82
83
|
}
|
|
83
84
|
|
|
@@ -120,16 +121,15 @@ export function useAutoTitleBridge(): void {
|
|
|
120
121
|
if (cancelled || inflight.current) return;
|
|
121
122
|
inflight.current = true;
|
|
122
123
|
try {
|
|
124
|
+
const requestsById = await listRequests();
|
|
125
|
+
if (cancelled) return;
|
|
126
|
+
|
|
123
127
|
for (const rec of readyRecordings) {
|
|
124
128
|
if (cancelled) return;
|
|
125
129
|
|
|
126
|
-
const request =
|
|
130
|
+
const request = requestsById.get(rec.id) ?? null;
|
|
127
131
|
|
|
128
|
-
if (
|
|
129
|
-
request?.kind &&
|
|
130
|
-
DISPATCHABLE_REQUESTS.has(request.kind) &&
|
|
131
|
-
request.recordingId === rec.id
|
|
132
|
-
) {
|
|
132
|
+
if (request?.kind && DISPATCHABLE_REQUESTS.has(request.kind)) {
|
|
133
133
|
// Server queued a delegation — use the full context it provided.
|
|
134
134
|
// Key includes requestedAt so each distinct server request fires
|
|
135
135
|
// exactly once, independent of any prior fallback dispatch.
|
|
@@ -164,9 +164,8 @@ export function useAutoTitleBridge(): void {
|
|
|
164
164
|
continue;
|
|
165
165
|
}
|
|
166
166
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
if (ageMs < TWO_MINUTES_MS) continue;
|
|
167
|
+
if (Date.now() - new Date(rec.createdAt).getTime() < TWO_MINUTES_MS)
|
|
168
|
+
continue;
|
|
170
169
|
|
|
171
170
|
// Use a dedicated key so a later server-queued request (e.g. from
|
|
172
171
|
// a long transcription that finishes after the 2-min window) is
|
|
@@ -86,6 +86,9 @@ import { parsePlaybackSpeed } from "@/lib/playback-speed";
|
|
|
86
86
|
import { isStorageSetupFailureReason } from "@/lib/storage-failures";
|
|
87
87
|
import { cn } from "@/lib/utils";
|
|
88
88
|
|
|
89
|
+
const UPLOAD_STUCK_TIMEOUT_MS = 5 * 60 * 1000;
|
|
90
|
+
const PROCESSING_STUCK_TIMEOUT_MS = 2 * 60 * 1000;
|
|
91
|
+
|
|
89
92
|
export function meta() {
|
|
90
93
|
return [{ title: enMessages.recordingRoute.pageTitle }];
|
|
91
94
|
}
|
|
@@ -562,10 +565,8 @@ export default function RecordingPage() {
|
|
|
562
565
|
.finally(() => playerDataQ.refetch());
|
|
563
566
|
}, [recording?.id, recording?.status, transcriptStatus, role, playerDataQ]);
|
|
564
567
|
|
|
565
|
-
//
|
|
566
|
-
//
|
|
567
|
-
// Even a 10-minute recording's finalize completes in a few seconds with
|
|
568
|
-
// the SQL fallback, so anything past 30s means something is wrong.
|
|
568
|
+
// Long browser-extension clips can still be uploading chunks or assembling
|
|
569
|
+
// for more than 30s. Keep polling before surfacing a stuck-state fallback.
|
|
569
570
|
useEffect(() => {
|
|
570
571
|
if (!recording) {
|
|
571
572
|
setProcessingTimeout(false);
|
|
@@ -579,7 +580,11 @@ export default function RecordingPage() {
|
|
|
579
580
|
setProcessingTimeout(false);
|
|
580
581
|
return;
|
|
581
582
|
}
|
|
582
|
-
const
|
|
583
|
+
const timeoutMs =
|
|
584
|
+
recording.status === "processing"
|
|
585
|
+
? PROCESSING_STUCK_TIMEOUT_MS
|
|
586
|
+
: UPLOAD_STUCK_TIMEOUT_MS;
|
|
587
|
+
const handle = setTimeout(() => setProcessingTimeout(true), timeoutMs);
|
|
583
588
|
return () => clearTimeout(handle);
|
|
584
589
|
}, [recording?.status, recording?.videoUrl, recordingId]);
|
|
585
590
|
|
|
@@ -229,6 +229,8 @@ const CLIPS_SOURCE_URL =
|
|
|
229
229
|
const CLIPS_TEMPLATE_URL = "https://www.agent-native.com/templates/clips";
|
|
230
230
|
const CLIPS_AGENT_DOCS_URL =
|
|
231
231
|
"https://www.agent-native.com/docs/template-clips#agent-readable-clips";
|
|
232
|
+
const UPLOAD_STUCK_TIMEOUT_MS = 5 * 60 * 1000;
|
|
233
|
+
const PROCESSING_STUCK_TIMEOUT_MS = 2 * 60 * 1000;
|
|
232
234
|
|
|
233
235
|
type ViewerPlatform = "mac" | "windows";
|
|
234
236
|
|
|
@@ -453,17 +455,13 @@ export default function ShareRoute() {
|
|
|
453
455
|
return;
|
|
454
456
|
}
|
|
455
457
|
|
|
456
|
-
const progress = Number(recording.uploadProgress ?? 0);
|
|
457
458
|
const timeoutMs =
|
|
458
|
-
recording.status === "processing"
|
|
459
|
+
recording.status === "processing"
|
|
460
|
+
? PROCESSING_STUCK_TIMEOUT_MS
|
|
461
|
+
: UPLOAD_STUCK_TIMEOUT_MS;
|
|
459
462
|
const handle = setTimeout(() => setProcessingTimeout(true), timeoutMs);
|
|
460
463
|
return () => clearTimeout(handle);
|
|
461
|
-
}, [
|
|
462
|
-
recording?.id,
|
|
463
|
-
recording?.status,
|
|
464
|
-
recording?.videoUrl,
|
|
465
|
-
recording?.uploadProgress,
|
|
466
|
-
]);
|
|
464
|
+
}, [recording?.id, recording?.status, recording?.videoUrl]);
|
|
467
465
|
|
|
468
466
|
usePlayerShortcuts({ playerRef });
|
|
469
467
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"manifest_version": 3,
|
|
3
3
|
"name": "Agent-Native Clips",
|
|
4
4
|
"short_name": "Clips",
|
|
5
|
-
"version": "0.1.
|
|
5
|
+
"version": "0.1.7",
|
|
6
6
|
"description": "Start Clips recordings from Chrome, capture optional diagnostics, and preview Clips links on GitHub.",
|
|
7
7
|
"minimum_chrome_version": "116",
|
|
8
8
|
"action": {
|
|
@@ -261,6 +261,7 @@ const CROSS_TAB_FOLLOW: boolean = false;
|
|
|
261
261
|
// The tab the recording was launched from — the only tab activeTab lets us touch.
|
|
262
262
|
let overlayTabId: number | null = null;
|
|
263
263
|
let countdownEndsAtMs = 0;
|
|
264
|
+
let armingNativeRecordingSessionId: string | null = null;
|
|
264
265
|
|
|
265
266
|
function desiredParts(): OverlayPart[] {
|
|
266
267
|
// The on-page controls match the desktop app: a left-edge vertical pill plus
|
|
@@ -950,15 +951,7 @@ function createSession(
|
|
|
950
951
|
}
|
|
951
952
|
|
|
952
953
|
async function handlePopupStart(message: PopupStartMessage) {
|
|
953
|
-
|
|
954
|
-
if (!tab || typeof tab.id !== "number") {
|
|
955
|
-
return { ok: false, error: "No active tab is available to record." };
|
|
956
|
-
}
|
|
957
|
-
|
|
958
|
-
const settings = await readSettings(message.settings);
|
|
959
|
-
await storageSet(settings);
|
|
960
|
-
|
|
961
|
-
if (activeNativeRecording) {
|
|
954
|
+
if (activeNativeRecording || armingNativeRecordingSessionId) {
|
|
962
955
|
return {
|
|
963
956
|
ok: false,
|
|
964
957
|
error: "Clips is already recording. Stop the active clip first.",
|
|
@@ -966,7 +959,22 @@ async function handlePopupStart(message: PopupStartMessage) {
|
|
|
966
959
|
}
|
|
967
960
|
|
|
968
961
|
const sessionId = crypto.randomUUID();
|
|
969
|
-
|
|
962
|
+
armingNativeRecordingSessionId = sessionId;
|
|
963
|
+
try {
|
|
964
|
+
const tab = await queryActiveTab();
|
|
965
|
+
if (!tab || typeof tab.id !== "number") {
|
|
966
|
+
return { ok: false, error: "No active tab is available to record." };
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
const settings = await readSettings(message.settings);
|
|
970
|
+
await storageSet(settings);
|
|
971
|
+
|
|
972
|
+
return await armRecording({ sessionId, tab, settings });
|
|
973
|
+
} finally {
|
|
974
|
+
if (armingNativeRecordingSessionId === sessionId) {
|
|
975
|
+
armingNativeRecordingSessionId = null;
|
|
976
|
+
}
|
|
977
|
+
}
|
|
970
978
|
}
|
|
971
979
|
|
|
972
980
|
// Arm a Loom-style in-page recording: show the native picker, create the row,
|
|
@@ -1474,6 +1482,7 @@ async function handlePopupStatus() {
|
|
|
1474
1482
|
return {
|
|
1475
1483
|
ok: true,
|
|
1476
1484
|
activeRecording: activeNativeRecording,
|
|
1485
|
+
arming: Boolean(armingNativeRecordingSessionId),
|
|
1477
1486
|
};
|
|
1478
1487
|
}
|
|
1479
1488
|
|