@agent-native/core 0.69.0 → 0.70.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/README.md +15 -15
- package/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +109 -0
- package/corpus/core/docs/design/durable-agent-runs.md +217 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/action.ts +89 -0
- package/corpus/core/src/agent/model-config.ts +0 -2
- package/corpus/core/src/agent/run-loop-with-resume.ts +48 -0
- package/corpus/core/src/changelog/parse.ts +245 -0
- package/corpus/core/src/cli/changelog.ts +199 -0
- package/corpus/core/src/cli/index.ts +19 -0
- package/corpus/core/src/cli/recap.ts +24 -1
- package/corpus/core/src/cli/skills.ts +74 -47
- package/corpus/core/src/client/AgentPanel.tsx +5 -3
- package/corpus/core/src/client/AssistantChat.tsx +8 -0
- package/corpus/core/src/client/CommandMenu.tsx +183 -75
- package/corpus/core/src/client/agent-chat-adapter.ts +44 -7
- package/corpus/core/src/client/analytics.ts +181 -0
- package/corpus/core/src/client/changelog/Changelog.tsx +321 -0
- package/corpus/core/src/client/client-surface.ts +41 -0
- package/corpus/core/src/client/composer/TiptapComposer.tsx +123 -12
- package/corpus/core/src/client/feedback-context.ts +4 -0
- package/corpus/core/src/client/index.ts +12 -0
- package/corpus/core/src/client/mcp-apps/McpAppRenderer.tsx +54 -0
- package/corpus/core/src/client/settings/useBuilderStatus.ts +82 -20
- package/corpus/core/src/client/sse-event-processor.ts +7 -1
- package/corpus/core/src/mcp/build-server.ts +57 -1
- package/corpus/core/src/mcp/embed-app.ts +197 -7
- package/corpus/core/src/mcp/oauth-route.ts +109 -13
- package/corpus/core/src/server/attribution.ts +211 -0
- package/corpus/core/src/server/auth.ts +6 -0
- package/corpus/core/src/server/better-auth-instance.ts +49 -5
- package/corpus/core/src/server/builder-browser.ts +27 -1
- package/corpus/core/src/server/core-routes-plugin.ts +136 -0
- package/corpus/core/src/server/google-oauth.ts +19 -6
- package/corpus/core/src/server/onboarding-html.ts +35 -0
- package/corpus/core/src/shared/mcp-embed-headers.ts +1 -1
- package/corpus/core/src/styles/agent-conversation.css +21 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/changelog/SKILL.md +111 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/reliable-mutations/SKILL.md +72 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +46 -14
- package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +22 -0
- package/corpus/templates/analytics/AGENTS.md +8 -1
- package/corpus/templates/analytics/CHANGELOG.md +10 -0
- package/corpus/templates/analytics/actions/install-dashboard-template.ts +99 -4
- package/corpus/templates/analytics/actions/open-traffic-dashboard.ts +1 -1
- package/corpus/templates/analytics/actions/update-dashboard.ts +39 -8
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +30 -3
- package/corpus/templates/analytics/app/components/layout/CommandPalette.tsx +180 -144
- package/corpus/templates/analytics/app/components/layout/Header.tsx +2 -3
- package/corpus/templates/analytics/app/components/layout/Layout.tsx +7 -5
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +221 -215
- package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +6 -6
- package/corpus/templates/analytics/app/lib/demo-dashboard-path.ts +1 -1
- package/corpus/templates/analytics/app/lib/last-opened.ts +2 -1
- package/corpus/templates/analytics/app/pages/Settings.tsx +4 -0
- package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +3 -3
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/DashboardFilterBar.tsx +2 -2
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +32 -2
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +101 -99
- package/corpus/templates/analytics/app/pages/overview/OverviewPage.tsx +1 -1
- package/corpus/templates/analytics/app/routes/adhoc.$id.tsx +18 -5
- package/corpus/templates/analytics/app/routes/catalog.tsx +1 -1
- package/corpus/templates/analytics/app/routes/dashboards.$id.tsx +9 -0
- package/corpus/templates/analytics/app/routes/traffic.tsx +1 -1
- package/corpus/templates/analytics/changelog/2026-06-23-long-dashboard-and-analysis-names-in-the-sidebar-now-truncat.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-23-open-any-dashboard-chart-full-screen-in-a-modal-from-the-pan.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-23-the-dashboards-and-analyses-sidebar-settings-buttons-now-sta.md +6 -0
- package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +145 -0
- package/corpus/templates/analytics/server/db/index.ts +1 -1
- package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +9 -2
- package/corpus/templates/analytics/server/lib/demo-dashboards.ts +1 -1
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +2 -2
- package/corpus/templates/analytics/server/plugins/core-routes.ts +6 -6
- package/corpus/templates/assets/CHANGELOG.md +10 -0
- package/corpus/templates/assets/actions/generate-image.ts +24 -0
- package/corpus/templates/assets/app/components/layout/Sidebar.tsx +2 -2
- package/corpus/templates/assets/app/global.css +17 -1
- package/corpus/templates/assets/app/root.tsx +7 -1
- package/corpus/templates/assets/app/routes/_index.tsx +94 -14
- package/corpus/templates/assets/app/routes/settings.tsx +6 -0
- package/corpus/templates/assets/changelog/2026-06-23-chat-dates-in-the-sidebar-no-longer-wrap-onto-two-lines.md +6 -0
- package/corpus/templates/assets/changelog/2026-06-23-clicking-image-video-or-refine-on-the-start-screen-now-drops.md +6 -0
- package/corpus/templates/assets/changelog/2026-06-23-pick-your-image-generation-model-right-from-the-chat-model-m.md +6 -0
- package/corpus/templates/brain/CHANGELOG.md +10 -0
- package/corpus/templates/brain/app/root.tsx +7 -1
- package/corpus/templates/brain/app/routes/settings.tsx +8 -1
- package/corpus/templates/calendar/CHANGELOG.md +10 -0
- package/corpus/templates/calendar/app/pages/Settings.tsx +4 -0
- package/corpus/templates/calendar/app/root.tsx +7 -1
- package/corpus/templates/chat/CHANGELOG.md +10 -0
- package/corpus/templates/chat/app/root.tsx +7 -1
- package/corpus/templates/clips/CHANGELOG.md +10 -0
- package/corpus/templates/clips/app/components/library/library-grid.tsx +1 -20
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +33 -7
- package/corpus/templates/clips/app/components/player/sign-in-prompt-dialog.tsx +8 -0
- package/corpus/templates/clips/app/components/player/video-player.tsx +150 -20
- package/corpus/templates/clips/app/components/recorder/countdown-overlay.tsx +72 -48
- package/corpus/templates/clips/app/components/recorder/pre-record-panel.tsx +7 -11
- package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +31 -0
- package/corpus/templates/clips/app/components/sharing/slack-share-hint.tsx +71 -0
- package/corpus/templates/clips/app/lib/countdown-audio-cue.ts +35 -17
- package/corpus/templates/clips/app/root.tsx +7 -1
- package/corpus/templates/clips/app/routes/_app.settings._index.tsx +15 -1
- package/corpus/templates/clips/app/routes/record.tsx +78 -5
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +86 -5
- package/corpus/templates/clips/changelog/2026-06-23-clips-shared-in-slack-no-longer-show-a-could-not-start-playb.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-23-made-the-play-button-on-shared-and-embedded-clips-scale-with.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-23-public-clip-share-links-now-show-whether-they-ll-play-inline.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-23-removed-select-button-from-library-hover-a-clip-to-select-it.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-23-screen-camera-recordings-now-keep-the-presenter-s-face-in-th.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-23-shared-video-links-now-play-for-anyone-without-signing-in.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-23-skip-or-cancel-the-recording-countdown-with-buttons-beside-t.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-23-the-desktop-camera-bubble-now-looks-sharp-the-instant-it-ope.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-23-the-desktop-recording-widget-now-grows-downward-only-when-yo.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-23-the-recording-start-sound-is-now-a-softer-more-modern-chime.md +6 -0
- package/corpus/templates/clips/chrome-extension/public/manifest.json +17 -8
- package/corpus/templates/clips/chrome-extension/src/background.ts +769 -180
- package/corpus/templates/clips/chrome-extension/src/content-script.ts +177 -0
- package/corpus/templates/clips/chrome-extension/src/offscreen.ts +466 -329
- package/corpus/templates/clips/chrome-extension/src/overlay.css +377 -0
- package/corpus/templates/clips/chrome-extension/src/overlay.html +12 -0
- package/corpus/templates/clips/chrome-extension/src/overlay.ts +330 -0
- package/corpus/templates/clips/chrome-extension/src/permission.html +227 -0
- package/corpus/templates/clips/chrome-extension/src/permission.ts +105 -0
- package/corpus/templates/clips/chrome-extension/src/popup.html +3 -0
- package/corpus/templates/clips/chrome-extension/src/popup.ts +50 -2
- package/corpus/templates/clips/chrome-extension/src/styles.css +50 -0
- package/corpus/templates/clips/chrome-extension/vite.config.ts +3 -0
- package/corpus/templates/clips/desktop/src/lib/audio-cue.ts +41 -24
- package/corpus/templates/clips/desktop/src/lib/bubble-webrtc.ts +119 -2
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +20 -0
- package/corpus/templates/clips/desktop/src/main.tsx +3 -0
- package/corpus/templates/clips/desktop/src/overlays/countdown.tsx +21 -2
- package/corpus/templates/clips/desktop/src/overlays/region-record-border.tsx +76 -0
- package/corpus/templates/clips/desktop/src/styles.css +119 -2
- package/corpus/templates/clips/desktop/src-tauri/capabilities/default.json +2 -1
- package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +140 -1
- package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +2 -0
- package/corpus/templates/clips/desktop/src-tauri/src/util.rs +4 -1
- package/corpus/templates/clips/server/routes/api/video/[recordingId].get.ts +85 -6
- package/corpus/templates/clips/shared/share-attribution.ts +79 -0
- package/corpus/templates/content/AGENTS.md +20 -3
- package/corpus/templates/content/CHANGELOG.md +10 -0
- package/corpus/templates/content/actions/_database-utils.ts +15 -0
- package/corpus/templates/content/actions/_property-utils.ts +371 -4
- package/corpus/templates/content/actions/configure-document-property.ts +35 -1
- package/corpus/templates/content/actions/create-content-database.ts +5 -1
- package/corpus/templates/content/actions/delete-document-property.ts +52 -2
- package/corpus/templates/content/actions/duplicate-document-property.ts +34 -17
- package/corpus/templates/content/actions/reorder-document-property.ts +79 -0
- package/corpus/templates/content/actions/set-document-property.ts +39 -0
- package/corpus/templates/content/app/components/editor/DocumentBlockFields.tsx +806 -0
- package/corpus/templates/content/app/components/editor/DocumentDatabase.tsx +226 -68
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +59 -30
- package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +25 -1
- package/corpus/templates/content/app/components/editor/blockFieldSaveController.ts +180 -0
- package/corpus/templates/content/app/components/editor/blockFieldSaveRegistry.ts +179 -0
- package/corpus/templates/content/app/components/editor/previewDocumentSaveController.ts +244 -0
- package/corpus/templates/content/app/components/editor/previewDocumentSaveRegistry.ts +132 -0
- package/corpus/templates/content/app/global.css +9 -0
- package/corpus/templates/content/app/hooks/use-document-properties.ts +21 -0
- package/corpus/templates/content/app/root.tsx +7 -1
- package/corpus/templates/content/server/db/schema.ts +30 -0
- package/corpus/templates/content/server/plugins/db.ts +87 -0
- package/corpus/templates/content/shared/api.ts +7 -0
- package/corpus/templates/content/shared/properties.ts +109 -0
- package/corpus/templates/design/CHANGELOG.md +10 -0
- package/corpus/templates/design/actions/generate-design.ts +4 -0
- package/corpus/templates/design/app/root.tsx +7 -1
- package/corpus/templates/design/app/routes/settings.tsx +10 -2
- package/corpus/templates/dispatch/CHANGELOG.md +10 -0
- package/corpus/templates/dispatch/app/root.tsx +7 -1
- package/corpus/templates/forms/.agents/skills/form-responses/SKILL.md +10 -0
- package/corpus/templates/forms/CHANGELOG.md +10 -0
- package/corpus/templates/forms/actions/export-responses.ts +6 -0
- package/corpus/templates/forms/actions/list-responses.ts +2 -0
- package/corpus/templates/forms/actions/response-insights.ts +2 -0
- package/corpus/templates/forms/app/pages/ResponsesPage.tsx +138 -1
- package/corpus/templates/forms/app/root.tsx +7 -1
- package/corpus/templates/forms/changelog/2026-06-23-response-tables-now-show-the-page-each-submission-came-from-.md +6 -0
- package/corpus/templates/forms/changelog/2026-06-23-response-tables-now-show-whether-feedback-came-from-the-web-.md +6 -0
- package/corpus/templates/forms/server/db/schema.ts +7 -0
- package/corpus/templates/forms/server/handlers/submissions.ts +19 -2
- package/corpus/templates/forms/server/lib/integrations.ts +76 -2
- package/corpus/templates/forms/server/plugins/db.ts +20 -0
- package/corpus/templates/forms/shared/types.ts +12 -0
- package/corpus/templates/macros/CHANGELOG.md +10 -0
- package/corpus/templates/macros/app/root.tsx +7 -1
- package/corpus/templates/mail/CHANGELOG.md +10 -0
- package/corpus/templates/mail/app/components/layout/CommandPalette.tsx +3 -0
- package/corpus/templates/mail/app/pages/SettingsPage.tsx +27 -0
- package/corpus/templates/plan/.agents/skills/visual-plan/SKILL.md +31 -17
- package/corpus/templates/plan/.agents/skills/visual-plan/references/wireframe.md +7 -4
- package/corpus/templates/plan/.agents/skills/visual-recap/SKILL.md +34 -24
- package/corpus/templates/plan/.agents/skills/visual-recap/references/wireframe.md +7 -4
- package/corpus/templates/plan/AGENTS.md +4 -0
- package/corpus/templates/plan/CHANGELOG.md +10 -0
- package/corpus/templates/plan/actions/view-screen.ts +5 -0
- package/corpus/templates/plan/app/components/layout/Sidebar.tsx +98 -1
- package/corpus/templates/plan/app/components/plan/wireframe/html-artboard.css +3 -1
- package/corpus/templates/plan/app/pages/PlansPage.tsx +107 -7
- package/corpus/templates/plan/app/root.tsx +7 -1
- package/corpus/templates/plan/changelog/2026-06-23-large-diagrams-in-a-plan-now-scroll-within-their-block-inste.md +6 -0
- package/corpus/templates/plan/shared/share-attribution.ts +72 -0
- package/corpus/templates/slides/CHANGELOG.md +10 -0
- package/corpus/templates/slides/app/root.tsx +7 -1
- package/corpus/templates/videos/CHANGELOG.md +4 -151
- package/corpus/templates/videos/app/root.tsx +7 -1
- package/dist/action.js +87 -0
- package/dist/action.js.map +1 -1
- package/dist/agent/engine/builder-engine.d.ts +1 -1
- package/dist/agent/engine/builder-engine.d.ts.map +1 -1
- package/dist/agent/model-config.d.ts +2 -2
- package/dist/agent/model-config.d.ts.map +1 -1
- package/dist/agent/model-config.js +0 -2
- package/dist/agent/model-config.js.map +1 -1
- package/dist/agent/run-loop-with-resume.d.ts +13 -0
- package/dist/agent/run-loop-with-resume.d.ts.map +1 -1
- package/dist/agent/run-loop-with-resume.js +44 -0
- package/dist/agent/run-loop-with-resume.js.map +1 -1
- package/dist/changelog/parse.d.ts +70 -0
- package/dist/changelog/parse.d.ts.map +1 -0
- package/dist/changelog/parse.js +200 -0
- package/dist/changelog/parse.js.map +1 -0
- package/dist/cli/changelog.d.ts +4 -0
- package/dist/cli/changelog.d.ts.map +1 -0
- package/dist/cli/changelog.js +166 -0
- package/dist/cli/changelog.js.map +1 -0
- package/dist/cli/index.js +18 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/recap.d.ts.map +1 -1
- package/dist/cli/recap.js +22 -1
- package/dist/cli/recap.js.map +1 -1
- package/dist/cli/skills.d.ts +3 -3
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +74 -47
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +5 -3
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +6 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +2 -2
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/CommandMenu.d.ts +17 -1
- package/dist/client/CommandMenu.d.ts.map +1 -1
- package/dist/client/CommandMenu.js +49 -13
- package/dist/client/CommandMenu.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +43 -6
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/analytics.d.ts +18 -0
- package/dist/client/analytics.d.ts.map +1 -1
- package/dist/client/analytics.js +177 -0
- package/dist/client/analytics.js.map +1 -1
- package/dist/client/changelog/Changelog.d.ts +47 -0
- package/dist/client/changelog/Changelog.d.ts.map +1 -0
- package/dist/client/changelog/Changelog.js +138 -0
- package/dist/client/changelog/Changelog.js.map +1 -0
- package/dist/client/client-surface.d.ts +17 -0
- package/dist/client/client-surface.d.ts.map +1 -0
- package/dist/client/client-surface.js +24 -0
- package/dist/client/client-surface.js.map +1 -0
- package/dist/client/composer/TiptapComposer.d.ts +26 -1
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +27 -5
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/feedback-context.d.ts +3 -0
- package/dist/client/feedback-context.d.ts.map +1 -1
- package/dist/client/feedback-context.js +2 -0
- package/dist/client/feedback-context.js.map +1 -1
- package/dist/client/index.d.ts +3 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +3 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/mcp-apps/McpAppRenderer.d.ts +1 -0
- package/dist/client/mcp-apps/McpAppRenderer.d.ts.map +1 -1
- package/dist/client/mcp-apps/McpAppRenderer.js +42 -1
- package/dist/client/mcp-apps/McpAppRenderer.js.map +1 -1
- package/dist/client/settings/useBuilderStatus.d.ts +2 -0
- package/dist/client/settings/useBuilderStatus.d.ts.map +1 -1
- package/dist/client/settings/useBuilderStatus.js +58 -10
- package/dist/client/settings/useBuilderStatus.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +7 -1
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/mcp/build-server.d.ts +21 -0
- package/dist/mcp/build-server.d.ts.map +1 -1
- package/dist/mcp/build-server.js +43 -1
- package/dist/mcp/build-server.js.map +1 -1
- package/dist/mcp/embed-app.d.ts.map +1 -1
- package/dist/mcp/embed-app.js +197 -7
- package/dist/mcp/embed-app.js.map +1 -1
- package/dist/mcp/oauth-route.d.ts.map +1 -1
- package/dist/mcp/oauth-route.js +95 -12
- package/dist/mcp/oauth-route.js.map +1 -1
- package/dist/server/attribution.d.ts +83 -0
- package/dist/server/attribution.d.ts.map +1 -0
- package/dist/server/attribution.js +178 -0
- package/dist/server/attribution.js.map +1 -0
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +7 -0
- package/dist/server/auth.js.map +1 -1
- package/dist/server/better-auth-instance.d.ts +9 -1
- package/dist/server/better-auth-instance.d.ts.map +1 -1
- package/dist/server/better-auth-instance.js +32 -2
- package/dist/server/better-auth-instance.js.map +1 -1
- package/dist/server/builder-browser.d.ts +4 -0
- package/dist/server/builder-browser.d.ts.map +1 -1
- package/dist/server/builder-browser.js +20 -1
- package/dist/server/builder-browser.js.map +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 +101 -0
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/google-oauth.d.ts.map +1 -1
- package/dist/server/google-oauth.js +16 -2
- package/dist/server/google-oauth.js.map +1 -1
- package/dist/server/onboarding-html.d.ts.map +1 -1
- package/dist/server/onboarding-html.js +35 -0
- package/dist/server/onboarding-html.js.map +1 -1
- package/dist/shared/mcp-embed-headers.js +1 -1
- package/dist/shared/mcp-embed-headers.js.map +1 -1
- package/dist/styles/agent-conversation.css +21 -0
- package/dist/templates/workspace-core/.agents/skills/changelog/SKILL.md +111 -0
- package/dist/templates/workspace-core/.agents/skills/reliable-mutations/SKILL.md +72 -0
- package/dist/templates/workspace-core/.agents/skills/tracking/SKILL.md +46 -14
- package/docs/design/durable-agent-runs.md +217 -0
- package/package.json +1 -1
- package/src/templates/workspace-core/.agents/skills/changelog/SKILL.md +111 -0
- package/src/templates/workspace-core/.agents/skills/reliable-mutations/SKILL.md +72 -0
- package/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +46 -14
- package/corpus/templates/analytics/app/pages/About.tsx +0 -108
- package/corpus/templates/analytics/app/routes/about.tsx +0 -9
- /package/corpus/templates/analytics/app/routes/{dashboards.tsx → dashboards._index.tsx} +0 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>Enable camera & microphone — Clips</title>
|
|
7
|
+
<style>
|
|
8
|
+
/* Design tokens mirrored from the popup/options styles.css so this page
|
|
9
|
+
matches the rest of the extension and the Clips app. */
|
|
10
|
+
:root {
|
|
11
|
+
color-scheme: light dark;
|
|
12
|
+
--bg: #ffffff;
|
|
13
|
+
--surface: #f5f6f9;
|
|
14
|
+
--surface-hover: #eef0f5;
|
|
15
|
+
--fg: #0f172a;
|
|
16
|
+
--fg-muted: #64748b;
|
|
17
|
+
--fg-subtle: #94a3b8;
|
|
18
|
+
--border: hsl(220 10% 90%);
|
|
19
|
+
--border-strong: #d4d9e4;
|
|
20
|
+
--brand: #18181b;
|
|
21
|
+
--brand-hover: #27272a;
|
|
22
|
+
--brand-ring: rgba(24, 24, 27, 0.28);
|
|
23
|
+
--status-ok: #22c55e;
|
|
24
|
+
--danger: #b42318;
|
|
25
|
+
--radius: 12px;
|
|
26
|
+
--radius-sm: 8px;
|
|
27
|
+
--shadow-md:
|
|
28
|
+
0 4px 16px rgba(15, 23, 42, 0.08), 0 2px 4px rgba(15, 23, 42, 0.04);
|
|
29
|
+
font-family:
|
|
30
|
+
-apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", Roboto,
|
|
31
|
+
ui-sans-serif, system-ui, sans-serif;
|
|
32
|
+
font-feature-settings: "cv02", "cv03", "cv04", "cv11";
|
|
33
|
+
}
|
|
34
|
+
@media (prefers-color-scheme: dark) {
|
|
35
|
+
:root {
|
|
36
|
+
--bg: #0f1115;
|
|
37
|
+
--surface: #181b23;
|
|
38
|
+
--surface-hover: #1e2230;
|
|
39
|
+
--fg: #f1f5f9;
|
|
40
|
+
--fg-muted: #94a3b8;
|
|
41
|
+
--fg-subtle: #64748b;
|
|
42
|
+
--border: #252938;
|
|
43
|
+
--border-strong: #323747;
|
|
44
|
+
--brand: #f4f4f5;
|
|
45
|
+
--brand-hover: #e4e4e7;
|
|
46
|
+
--brand-ring: rgba(244, 244, 245, 0.28);
|
|
47
|
+
--danger: hsl(6 88% 70%);
|
|
48
|
+
--shadow-md:
|
|
49
|
+
0 8px 24px rgba(0, 0, 0, 0.5), 0 2px 6px rgba(0, 0, 0, 0.3);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
* {
|
|
53
|
+
box-sizing: border-box;
|
|
54
|
+
}
|
|
55
|
+
body {
|
|
56
|
+
margin: 0;
|
|
57
|
+
min-height: 100vh;
|
|
58
|
+
display: flex;
|
|
59
|
+
align-items: center;
|
|
60
|
+
justify-content: center;
|
|
61
|
+
padding: 24px;
|
|
62
|
+
background: var(--bg);
|
|
63
|
+
color: var(--fg);
|
|
64
|
+
-webkit-font-smoothing: antialiased;
|
|
65
|
+
-moz-osx-font-smoothing: grayscale;
|
|
66
|
+
font-size: 14px;
|
|
67
|
+
line-height: 1.45;
|
|
68
|
+
}
|
|
69
|
+
.card {
|
|
70
|
+
width: 100%;
|
|
71
|
+
max-width: 420px;
|
|
72
|
+
background: var(--bg);
|
|
73
|
+
border: 1px solid var(--border);
|
|
74
|
+
border-radius: var(--radius);
|
|
75
|
+
box-shadow: var(--shadow-md);
|
|
76
|
+
padding: 28px;
|
|
77
|
+
}
|
|
78
|
+
.glyph {
|
|
79
|
+
width: 44px;
|
|
80
|
+
height: 44px;
|
|
81
|
+
border-radius: var(--radius-sm);
|
|
82
|
+
display: flex;
|
|
83
|
+
align-items: center;
|
|
84
|
+
justify-content: center;
|
|
85
|
+
background: var(--surface);
|
|
86
|
+
border: 1px solid var(--border);
|
|
87
|
+
color: var(--fg);
|
|
88
|
+
margin-bottom: 18px;
|
|
89
|
+
}
|
|
90
|
+
.glyph svg {
|
|
91
|
+
width: 22px;
|
|
92
|
+
height: 22px;
|
|
93
|
+
display: block;
|
|
94
|
+
}
|
|
95
|
+
h1 {
|
|
96
|
+
font-size: 18px;
|
|
97
|
+
font-weight: 600;
|
|
98
|
+
margin: 0 0 8px;
|
|
99
|
+
letter-spacing: -0.01em;
|
|
100
|
+
}
|
|
101
|
+
p {
|
|
102
|
+
font-size: 13.5px;
|
|
103
|
+
color: var(--fg-muted);
|
|
104
|
+
margin: 0 0 20px;
|
|
105
|
+
}
|
|
106
|
+
.rows {
|
|
107
|
+
display: grid;
|
|
108
|
+
gap: 8px;
|
|
109
|
+
margin: 0 0 22px;
|
|
110
|
+
}
|
|
111
|
+
.row {
|
|
112
|
+
display: flex;
|
|
113
|
+
align-items: center;
|
|
114
|
+
gap: 11px;
|
|
115
|
+
padding: 11px 12px;
|
|
116
|
+
border: 1px solid var(--border);
|
|
117
|
+
border-radius: var(--radius-sm);
|
|
118
|
+
background: var(--surface);
|
|
119
|
+
font-size: 13px;
|
|
120
|
+
color: var(--fg);
|
|
121
|
+
}
|
|
122
|
+
.row .check {
|
|
123
|
+
width: 20px;
|
|
124
|
+
height: 20px;
|
|
125
|
+
border-radius: 50%;
|
|
126
|
+
border: 1.5px solid var(--border-strong);
|
|
127
|
+
display: inline-flex;
|
|
128
|
+
align-items: center;
|
|
129
|
+
justify-content: center;
|
|
130
|
+
font-size: 12px;
|
|
131
|
+
color: var(--fg-subtle);
|
|
132
|
+
flex: none;
|
|
133
|
+
}
|
|
134
|
+
.row.granted {
|
|
135
|
+
border-color: var(--status-ok);
|
|
136
|
+
}
|
|
137
|
+
.row.granted .check {
|
|
138
|
+
border-color: var(--status-ok);
|
|
139
|
+
background: rgba(34, 197, 94, 0.14);
|
|
140
|
+
color: var(--status-ok);
|
|
141
|
+
}
|
|
142
|
+
.row-sub {
|
|
143
|
+
color: var(--fg-subtle);
|
|
144
|
+
font-size: 12px;
|
|
145
|
+
}
|
|
146
|
+
button {
|
|
147
|
+
font: inherit;
|
|
148
|
+
appearance: none;
|
|
149
|
+
border: 0;
|
|
150
|
+
border-radius: var(--radius-sm);
|
|
151
|
+
background: var(--brand);
|
|
152
|
+
color: var(--bg);
|
|
153
|
+
font-size: 14px;
|
|
154
|
+
font-weight: 600;
|
|
155
|
+
padding: 11px 18px;
|
|
156
|
+
width: 100%;
|
|
157
|
+
cursor: pointer;
|
|
158
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
|
|
159
|
+
transition:
|
|
160
|
+
background 120ms,
|
|
161
|
+
transform 80ms;
|
|
162
|
+
}
|
|
163
|
+
button:hover:not(:disabled) {
|
|
164
|
+
background: var(--brand-hover);
|
|
165
|
+
}
|
|
166
|
+
button:active:not(:disabled) {
|
|
167
|
+
transform: translateY(0.5px);
|
|
168
|
+
}
|
|
169
|
+
button:disabled {
|
|
170
|
+
opacity: 0.55;
|
|
171
|
+
cursor: default;
|
|
172
|
+
}
|
|
173
|
+
.status {
|
|
174
|
+
margin-top: 14px;
|
|
175
|
+
font-size: 12.5px;
|
|
176
|
+
min-height: 16px;
|
|
177
|
+
color: var(--fg-muted);
|
|
178
|
+
text-align: center;
|
|
179
|
+
}
|
|
180
|
+
.status.error {
|
|
181
|
+
color: var(--danger);
|
|
182
|
+
}
|
|
183
|
+
</style>
|
|
184
|
+
</head>
|
|
185
|
+
<body>
|
|
186
|
+
<div class="card">
|
|
187
|
+
<div class="glyph">
|
|
188
|
+
<svg
|
|
189
|
+
viewBox="0 0 24 24"
|
|
190
|
+
fill="none"
|
|
191
|
+
stroke="currentColor"
|
|
192
|
+
stroke-width="2"
|
|
193
|
+
stroke-linecap="round"
|
|
194
|
+
stroke-linejoin="round"
|
|
195
|
+
>
|
|
196
|
+
<path d="M23 7l-7 5 7 5V7z" />
|
|
197
|
+
<rect x="1" y="5" width="15" height="14" rx="2" ry="2" />
|
|
198
|
+
</svg>
|
|
199
|
+
</div>
|
|
200
|
+
<h1>Enable camera & microphone</h1>
|
|
201
|
+
<p>
|
|
202
|
+
Clips needs access once so it can show your camera bubble and record
|
|
203
|
+
your voice. Grant it below — Chrome still asks before each recording
|
|
204
|
+
starts.
|
|
205
|
+
</p>
|
|
206
|
+
<div class="rows">
|
|
207
|
+
<div class="row" id="row-mic">
|
|
208
|
+
<span class="check" id="check-mic">●</span>
|
|
209
|
+
<span
|
|
210
|
+
>Microphone
|
|
211
|
+
<span class="row-sub">— record your narration</span></span
|
|
212
|
+
>
|
|
213
|
+
</div>
|
|
214
|
+
<div class="row" id="row-cam">
|
|
215
|
+
<span class="check" id="check-cam">●</span>
|
|
216
|
+
<span
|
|
217
|
+
>Camera
|
|
218
|
+
<span class="row-sub">— show your face in recordings</span></span
|
|
219
|
+
>
|
|
220
|
+
</div>
|
|
221
|
+
</div>
|
|
222
|
+
<button id="enable">Enable camera & microphone</button>
|
|
223
|
+
<div class="status" id="status"></div>
|
|
224
|
+
</div>
|
|
225
|
+
<script type="module" src="./permission.ts"></script>
|
|
226
|
+
</body>
|
|
227
|
+
</html>
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// Camera/microphone permission onboarding. Opened in a tab when the user hits
|
|
2
|
+
// Record without having granted access yet. Requesting getUserMedia from a real
|
|
3
|
+
// extension page (not the headless offscreen document, not a focus-stealing
|
|
4
|
+
// popup) is what makes Chrome show the standard permission dialog and persist
|
|
5
|
+
// the grant for the whole chrome-extension:// origin — so the offscreen recorder
|
|
6
|
+
// (mic) and the camera-bubble iframe both work afterward.
|
|
7
|
+
|
|
8
|
+
const enableBtn = document.getElementById("enable") as HTMLButtonElement;
|
|
9
|
+
const statusEl = document.getElementById("status") as HTMLDivElement;
|
|
10
|
+
const rowMic = document.getElementById("row-mic") as HTMLDivElement;
|
|
11
|
+
const rowCam = document.getElementById("row-cam") as HTMLDivElement;
|
|
12
|
+
const checkMic = document.getElementById("check-mic") as HTMLSpanElement;
|
|
13
|
+
const checkCam = document.getElementById("check-cam") as HTMLSpanElement;
|
|
14
|
+
|
|
15
|
+
const CHECK = "✓";
|
|
16
|
+
const DOT = "●";
|
|
17
|
+
|
|
18
|
+
function setStatus(text: string, isError = false): void {
|
|
19
|
+
statusEl.textContent = text;
|
|
20
|
+
statusEl.classList.toggle("error", isError);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function markRow(kind: "mic" | "cam", granted: boolean): void {
|
|
24
|
+
const row = kind === "mic" ? rowMic : rowCam;
|
|
25
|
+
const check = kind === "mic" ? checkMic : checkCam;
|
|
26
|
+
row.classList.toggle("granted", granted);
|
|
27
|
+
check.textContent = granted ? CHECK : DOT;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function permissionState(
|
|
31
|
+
name: "camera" | "microphone",
|
|
32
|
+
): Promise<PermissionState | "unknown"> {
|
|
33
|
+
try {
|
|
34
|
+
const status = await navigator.permissions.query({
|
|
35
|
+
name: name as PermissionName,
|
|
36
|
+
});
|
|
37
|
+
return status.state;
|
|
38
|
+
} catch {
|
|
39
|
+
return "unknown";
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function requestOne(kind: "mic" | "cam"): Promise<boolean> {
|
|
44
|
+
try {
|
|
45
|
+
const constraints: MediaStreamConstraints =
|
|
46
|
+
kind === "mic" ? { audio: true } : { video: true };
|
|
47
|
+
const stream = await navigator.mediaDevices.getUserMedia(constraints);
|
|
48
|
+
for (const track of stream.getTracks()) track.stop();
|
|
49
|
+
return true;
|
|
50
|
+
} catch {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function finish(camOk: boolean, micOk: boolean): void {
|
|
56
|
+
void chrome.storage.local.set({
|
|
57
|
+
clipsMediaPermission: { camera: camOk, microphone: micOk },
|
|
58
|
+
});
|
|
59
|
+
markRow("mic", micOk);
|
|
60
|
+
markRow("cam", camOk);
|
|
61
|
+
if (camOk || micOk) {
|
|
62
|
+
enableBtn.textContent = "All set — you can close this tab";
|
|
63
|
+
enableBtn.disabled = true;
|
|
64
|
+
setStatus(
|
|
65
|
+
camOk && micOk
|
|
66
|
+
? "Camera and microphone are ready. Click the Clips icon to record."
|
|
67
|
+
: "Saved. Click the Clips icon to record.",
|
|
68
|
+
);
|
|
69
|
+
} else {
|
|
70
|
+
enableBtn.disabled = false;
|
|
71
|
+
enableBtn.textContent = "Try again";
|
|
72
|
+
setStatus(
|
|
73
|
+
"Access was blocked. Click the camera icon in Chrome's address bar to allow it, then try again.",
|
|
74
|
+
true,
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function enable(): Promise<void> {
|
|
80
|
+
enableBtn.disabled = true;
|
|
81
|
+
setStatus("Waiting for Chrome's permission prompt…");
|
|
82
|
+
// Request separately so a camera denial doesn't also block the microphone.
|
|
83
|
+
const micOk = await requestOne("mic");
|
|
84
|
+
const camOk = await requestOne("cam");
|
|
85
|
+
finish(camOk, micOk);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
enableBtn.addEventListener("click", () => void enable());
|
|
89
|
+
|
|
90
|
+
// If both are already granted (returning here later), reflect that immediately.
|
|
91
|
+
void (async () => {
|
|
92
|
+
const [cam, mic] = await Promise.all([
|
|
93
|
+
permissionState("camera"),
|
|
94
|
+
permissionState("microphone"),
|
|
95
|
+
]);
|
|
96
|
+
markRow("mic", mic === "granted");
|
|
97
|
+
markRow("cam", cam === "granted");
|
|
98
|
+
if (cam === "granted" && mic === "granted") {
|
|
99
|
+
enableBtn.textContent = "Already enabled — you can close this tab";
|
|
100
|
+
enableBtn.disabled = true;
|
|
101
|
+
setStatus(
|
|
102
|
+
"Camera and microphone are ready. Click the Clips icon to record.",
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
})();
|
|
@@ -72,6 +72,7 @@
|
|
|
72
72
|
aria-checked="false"
|
|
73
73
|
data-mode="screen"
|
|
74
74
|
aria-label="Screen only"
|
|
75
|
+
data-tooltip="Screen only"
|
|
75
76
|
>
|
|
76
77
|
<svg viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
|
77
78
|
<rect
|
|
@@ -98,6 +99,7 @@
|
|
|
98
99
|
aria-checked="true"
|
|
99
100
|
data-mode="screen-camera"
|
|
100
101
|
aria-label="Screen and camera"
|
|
102
|
+
data-tooltip="Screen and camera"
|
|
101
103
|
>
|
|
102
104
|
<svg viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
|
103
105
|
<rect
|
|
@@ -126,6 +128,7 @@
|
|
|
126
128
|
aria-checked="false"
|
|
127
129
|
data-mode="camera"
|
|
128
130
|
aria-label="Camera only"
|
|
131
|
+
data-tooltip="Camera only"
|
|
129
132
|
>
|
|
130
133
|
<svg viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
|
131
134
|
<rect
|
|
@@ -203,7 +203,7 @@ function readStoredAuth(
|
|
|
203
203
|
settings: ExtensionSettings,
|
|
204
204
|
): Promise<StoredAuth | null> {
|
|
205
205
|
return new Promise((resolve) => {
|
|
206
|
-
chrome.storage.
|
|
206
|
+
chrome.storage.local.get("clipsAuth", (value) => {
|
|
207
207
|
const auth = value.clipsAuth as Partial<StoredAuth> | undefined;
|
|
208
208
|
if (
|
|
209
209
|
auth &&
|
|
@@ -228,7 +228,7 @@ function readStoredAuth(
|
|
|
228
228
|
|
|
229
229
|
function clearStoredAuth(): Promise<void> {
|
|
230
230
|
return new Promise((resolve) => {
|
|
231
|
-
chrome.storage.
|
|
231
|
+
chrome.storage.local.remove("clipsAuth", () => resolve());
|
|
232
232
|
});
|
|
233
233
|
}
|
|
234
234
|
|
|
@@ -290,6 +290,33 @@ function createTab(url: string): Promise<void> {
|
|
|
290
290
|
});
|
|
291
291
|
}
|
|
292
292
|
|
|
293
|
+
async function permissionGranted(
|
|
294
|
+
name: "camera" | "microphone",
|
|
295
|
+
): Promise<boolean> {
|
|
296
|
+
try {
|
|
297
|
+
const status = await navigator.permissions.query({
|
|
298
|
+
name: name as PermissionName,
|
|
299
|
+
});
|
|
300
|
+
return status.state === "granted";
|
|
301
|
+
} catch {
|
|
302
|
+
// Older Chrome can't query these names — don't block; getUserMedia will ask.
|
|
303
|
+
return true;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// True when every device the chosen mode needs is already granted to the
|
|
308
|
+
// extension. If not, the caller routes the user to the permission page.
|
|
309
|
+
async function ensureMediaPermission(
|
|
310
|
+
settings: ExtensionSettings,
|
|
311
|
+
): Promise<boolean> {
|
|
312
|
+
const needsCamera =
|
|
313
|
+
settings.captureSurface === "camera" || settings.includeCamera;
|
|
314
|
+
const needsMic = settings.includeMicrophone;
|
|
315
|
+
if (needsCamera && !(await permissionGranted("camera"))) return false;
|
|
316
|
+
if (needsMic && !(await permissionGranted("microphone"))) return false;
|
|
317
|
+
return true;
|
|
318
|
+
}
|
|
319
|
+
|
|
293
320
|
async function readAuthStatus(
|
|
294
321
|
settings: ExtensionSettings,
|
|
295
322
|
): Promise<AuthStatus> {
|
|
@@ -511,6 +538,16 @@ function renderActiveRecording(recording: NativeRecording | null): void {
|
|
|
511
538
|
|
|
512
539
|
async function init(): Promise<void> {
|
|
513
540
|
const settings = await readSettings();
|
|
541
|
+
// Warm the offscreen recorder so the native screen picker opens promptly when
|
|
542
|
+
// the user presses Record (keeps getDisplayMedia close to the click).
|
|
543
|
+
try {
|
|
544
|
+
chrome.runtime.sendMessage(
|
|
545
|
+
{ type: "CLIPS_PREWARM" },
|
|
546
|
+
() => void chrome.runtime.lastError,
|
|
547
|
+
);
|
|
548
|
+
} catch {
|
|
549
|
+
/* ignore */
|
|
550
|
+
}
|
|
514
551
|
const sourceButton = byId<HTMLButtonElement>("source-button");
|
|
515
552
|
const sourceMenu = byId<HTMLDivElement>("source-menu");
|
|
516
553
|
const includeCamera = byId<HTMLButtonElement>("include-camera");
|
|
@@ -775,6 +812,17 @@ async function init(): Promise<void> {
|
|
|
775
812
|
window.close();
|
|
776
813
|
return;
|
|
777
814
|
}
|
|
815
|
+
// Gate at record time: if the user wants camera/mic but hasn't granted the
|
|
816
|
+
// extension access yet, send them to the onboarding page first. Requesting
|
|
817
|
+
// there (a real extension page) is the only place Chrome reliably shows the
|
|
818
|
+
// permission dialog and persists the grant for the offscreen recorder + bubble.
|
|
819
|
+
if (!(await ensureMediaPermission(settings))) {
|
|
820
|
+
start.disabled = false;
|
|
821
|
+
setStatus("Allow camera & microphone, then start recording.", "error");
|
|
822
|
+
await createTab(chrome.runtime.getURL("src/permission.html"));
|
|
823
|
+
window.close();
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
778
826
|
setStatus("Starting recording...");
|
|
779
827
|
await saveSettings(settings);
|
|
780
828
|
const response = await sendStartMessage(settings);
|
|
@@ -217,6 +217,56 @@ svg {
|
|
|
217
217
|
box-shadow: var(--shadow-sm);
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
+
/* Tooltip (shadcn-style, CSS-only to mirror the desktop app's Radix tooltip) */
|
|
221
|
+
[data-tooltip] {
|
|
222
|
+
position: relative;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
[data-tooltip]::after,
|
|
226
|
+
[data-tooltip]::before {
|
|
227
|
+
position: absolute;
|
|
228
|
+
top: calc(100% + 6px);
|
|
229
|
+
left: 50%;
|
|
230
|
+
z-index: 60;
|
|
231
|
+
opacity: 0;
|
|
232
|
+
pointer-events: none;
|
|
233
|
+
transform: translate(-50%, -2px);
|
|
234
|
+
transition:
|
|
235
|
+
opacity 90ms ease-in,
|
|
236
|
+
transform 90ms ease-in;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
[data-tooltip]::after {
|
|
240
|
+
content: attr(data-tooltip);
|
|
241
|
+
padding: 6px 10px;
|
|
242
|
+
border-radius: var(--radius-sm);
|
|
243
|
+
background: var(--bg);
|
|
244
|
+
color: var(--fg);
|
|
245
|
+
box-shadow: var(--shadow-md);
|
|
246
|
+
font-size: 12px;
|
|
247
|
+
font-weight: 500;
|
|
248
|
+
line-height: 1.45;
|
|
249
|
+
white-space: nowrap;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
[data-tooltip]::before {
|
|
253
|
+
content: "";
|
|
254
|
+
top: calc(100% + 1px);
|
|
255
|
+
border: 5px solid transparent;
|
|
256
|
+
border-bottom-color: var(--bg);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
[data-tooltip]:hover::after,
|
|
260
|
+
[data-tooltip]:hover::before,
|
|
261
|
+
[data-tooltip]:focus-visible::after,
|
|
262
|
+
[data-tooltip]:focus-visible::before {
|
|
263
|
+
opacity: 1;
|
|
264
|
+
transform: translate(-50%, 0);
|
|
265
|
+
transition:
|
|
266
|
+
opacity 120ms ease-out 150ms,
|
|
267
|
+
transform 120ms ease-out 150ms;
|
|
268
|
+
}
|
|
269
|
+
|
|
220
270
|
.panel {
|
|
221
271
|
display: flex;
|
|
222
272
|
flex-direction: column;
|
|
@@ -15,7 +15,10 @@ export default defineConfig({
|
|
|
15
15
|
rollupOptions: {
|
|
16
16
|
input: {
|
|
17
17
|
background: resolve(root, "src/background.ts"),
|
|
18
|
+
"content-script": resolve(root, "src/content-script.ts"),
|
|
18
19
|
offscreen: resolve(root, "src/offscreen.html"),
|
|
20
|
+
overlay: resolve(root, "src/overlay.html"),
|
|
21
|
+
permission: resolve(root, "src/permission.html"),
|
|
19
22
|
popup: resolve(root, "src/popup.html"),
|
|
20
23
|
options: resolve(root, "src/options.html"),
|
|
21
24
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Recording-start audio cue.
|
|
3
3
|
*
|
|
4
|
-
* A short
|
|
4
|
+
* A short, soft "ready" chime played the instant capture begins so the user
|
|
5
5
|
* gets audible confirmation. Two design constraints shape this module:
|
|
6
6
|
*
|
|
7
7
|
* 1. Browser audio needs a user gesture to start. We therefore build the
|
|
@@ -67,37 +67,54 @@ async function playBeforeCapture(
|
|
|
67
67
|
await wait(CUE_SETTLE_MS);
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
/**
|
|
70
|
+
/**
|
|
71
|
+
* Schedule the recording-start chime on an already-running context: a soft
|
|
72
|
+
* rising two-note (D5 → A5) with a faint high shimmer. Fast attack, smooth
|
|
73
|
+
* exponential tail — a gentle confirmation rather than a harsh beep. Kept under
|
|
74
|
+
* ~380ms so it fits inside `CUE_PLAY_TIMEOUT_MS` and lands just before capture.
|
|
75
|
+
*/
|
|
71
76
|
function scheduleTone(ctx: AudioContext): Promise<void> {
|
|
72
77
|
return new Promise<void>((resolve) => {
|
|
73
|
-
const
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
78
|
+
const t0 = ctx.currentTime + 0.005;
|
|
79
|
+
const voices = [
|
|
80
|
+
{ freq: 587.33, at: 0.0, dur: 0.22, peak: 0.06 }, // D5
|
|
81
|
+
{ freq: 880.0, at: 0.06, dur: 0.26, peak: 0.075 }, // A5
|
|
82
|
+
{ freq: 1760.0, at: 0.065, dur: 0.14, peak: 0.02 }, // A6 shimmer
|
|
83
|
+
];
|
|
84
|
+
|
|
85
|
+
let lastStop = t0;
|
|
86
|
+
for (const voice of voices) {
|
|
87
|
+
const startAt = t0 + voice.at;
|
|
88
|
+
const stopAt = startAt + voice.dur;
|
|
89
|
+
lastStop = Math.max(lastStop, stopAt);
|
|
90
|
+
|
|
91
|
+
const oscillator = ctx.createOscillator();
|
|
92
|
+
oscillator.type = "sine";
|
|
93
|
+
oscillator.frequency.setValueAtTime(voice.freq, startAt);
|
|
94
|
+
|
|
95
|
+
const gain = ctx.createGain();
|
|
96
|
+
gain.gain.setValueAtTime(0.0001, startAt);
|
|
97
|
+
gain.gain.exponentialRampToValueAtTime(voice.peak, startAt + 0.012);
|
|
98
|
+
gain.gain.exponentialRampToValueAtTime(0.0001, stopAt);
|
|
99
|
+
|
|
100
|
+
oscillator.connect(gain);
|
|
101
|
+
gain.connect(ctx.destination);
|
|
102
|
+
|
|
103
|
+
oscillator.start(startAt);
|
|
104
|
+
oscillator.stop(stopAt + 0.02);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Resolve a touch after the final voice ends.
|
|
89
108
|
let resolved = false;
|
|
90
109
|
const finish = () => {
|
|
91
110
|
if (resolved) return;
|
|
92
111
|
resolved = true;
|
|
93
|
-
window.clearTimeout(timer);
|
|
94
112
|
resolve();
|
|
95
113
|
};
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
oscillator.stop(startedAt + 0.2);
|
|
114
|
+
window.setTimeout(
|
|
115
|
+
finish,
|
|
116
|
+
Math.ceil((lastStop - ctx.currentTime) * 1000) + 60,
|
|
117
|
+
);
|
|
101
118
|
});
|
|
102
119
|
}
|
|
103
120
|
|