@agent-native/core 0.151.2 → 0.152.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/templates/analytics/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/analytics/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/analytics/actions/bigquery-query.ts +21 -0
- package/corpus/templates/analytics/app/components/ConnectionTestStatus.tsx +65 -0
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +3 -3
- package/corpus/templates/analytics/app/pages/DataSources.tsx +15 -37
- package/corpus/templates/analytics/app/root.tsx +0 -1
- package/corpus/templates/analytics/changelog/2026-08-11-analytics-custom-blocks-keep-loading-with-legacy-bigquery-action.md +6 -0
- package/corpus/templates/analytics/changelog/2026-08-11-analytics-routes-company-knowledge-and-slack-context-questio.md +6 -0
- package/corpus/templates/analytics/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/analytics/public/manifest.json +0 -1
- package/corpus/templates/analytics/server/lib/agent-chat-plan-mode.ts +5 -0
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +5 -0
- package/corpus/templates/assets/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/assets/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/assets/app/root.tsx +0 -1
- package/corpus/templates/assets/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/assets/public/manifest.json +0 -1
- package/corpus/templates/brain/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/brain/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/brain/app/root.tsx +0 -1
- package/corpus/templates/brain/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/brain/public/manifest.json +0 -1
- package/corpus/templates/calendar/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/calendar/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/calendar/app/components/calendar/GoogleConnectBanner.tsx +0 -53
- package/corpus/templates/calendar/app/i18n-data.ts +0 -80
- package/corpus/templates/calendar/app/root.tsx +0 -1
- package/corpus/templates/calendar/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/calendar/changelog/2026-08-11-google-calendar-connection-buttons-now-open-the-sign-in-flow.md +6 -0
- package/corpus/templates/calendar/public/manifest.json +0 -1
- package/corpus/templates/calendar/server/plugins/auth.ts +0 -10
- package/corpus/templates/chat/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/chat/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/chat/app/root.tsx +0 -1
- package/corpus/templates/chat/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/chat/public/manifest.json +0 -1
- package/corpus/templates/clips/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/clips/app/components/player/playback-comment-overlay.tsx +1 -1
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +6 -0
- package/corpus/templates/clips/app/components/sharing/share-ui.tsx +20 -3
- package/corpus/templates/clips/app/i18n/en-US.ts +4 -4
- package/corpus/templates/clips/app/root.tsx +0 -1
- package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +1 -3
- package/corpus/templates/clips/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-11-clips-desktop-settings-are-now-easier-to-scan-with-a-wider-t.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-11-google-calendar-connections-from-meetings-now-open-directly.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-11-recording-viewers-can-comment-and-react.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-11-system-audio-recording-now-lives-inside-the-microphone-menu-.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-11-timestamped-comments-stay-on-the-video-for-three-seconds-wh.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +773 -1001
- package/corpus/templates/clips/desktop/src/components/MediaDeviceRow.tsx +15 -27
- package/corpus/templates/clips/desktop/src/styles.css +364 -5
- package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +1 -1
- package/corpus/templates/clips/public/manifest.json +0 -1
- package/corpus/templates/content/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/content/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/content/AGENTS.md +3 -0
- package/corpus/templates/content/actions/resolve-content-landing.ts +120 -0
- package/corpus/templates/content/app/components/editor/BubbleToolbar.tsx +196 -30
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +9 -0
- package/corpus/templates/content/app/i18n-data.ts +58 -0
- package/corpus/templates/content/app/lib/content-landing.ts +22 -0
- package/corpus/templates/content/app/root.tsx +0 -1
- package/corpus/templates/content/app/routes/_app._index.tsx +36 -26
- package/corpus/templates/content/changelog/2026-08-03-choose-text-or-a-heading-from-one-compact-formatting-control.md +6 -0
- package/corpus/templates/content/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/content/changelog/2026-08-11-content-opens-your-last-page-on-return-and-gives-new-users-a.md +6 -0
- package/corpus/templates/content/docs/product/capabilities/content.workspace.session-restore.md +7 -1
- package/corpus/templates/content/docs/solutions/evidence/content-text-selector/README.md +26 -0
- package/corpus/templates/content/parity/matrix.md +1 -0
- package/corpus/templates/content/parity/matrix.ts +22 -0
- package/corpus/templates/content/public/manifest.json +0 -1
- package/corpus/templates/content/shared/content-landing.ts +17 -0
- package/corpus/templates/crm/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/crm/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/crm/app/root.tsx +0 -1
- package/corpus/templates/crm/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/crm/public/manifest.json +0 -1
- package/corpus/templates/design/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/design/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/design/app/i18n/en-US.ts +5 -0
- package/corpus/templates/design/app/root.tsx +0 -1
- package/corpus/templates/design/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/design/changelog/2026-08-11-design-sharing-now-identifies-viewers-who-can-add-review-com.md +6 -0
- package/corpus/templates/design/public/manifest.json +0 -1
- package/corpus/templates/dispatch/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/dispatch/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/dispatch/AGENTS.md +9 -0
- package/corpus/templates/dispatch/actions/list-dispatch-usage-metrics.ts +5 -19
- package/corpus/templates/dispatch/actions/view-screen.ts +10 -3
- package/corpus/templates/dispatch/app/root.tsx +0 -1
- package/corpus/templates/dispatch/app/routes/settings.tsx +13 -1
- package/corpus/templates/dispatch/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-08-11-clicking-the-dispatch-logo-returns-to-the-overview-page.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-08-11-dispatch-no-longer-links-to-itself-from-the-app-switcher.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-08-11-dispatch-settings-now-link-directly-to-admin.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-08-11-dispatch-shows-usage-trends-recent-prompts-and-an-agent-revi.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-08-11-human-friendly-app-names.md +6 -0
- package/corpus/templates/dispatch/public/manifest.json +0 -1
- package/corpus/templates/dispatch/server/lib/identity-sso.ts +432 -158
- package/corpus/templates/dispatch/server/plugins/identity-sso.ts +204 -123
- package/corpus/templates/dispatch/server/plugins/setup-dispatch.ts +1 -0
- package/corpus/templates/factory/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/factory/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/factory/app/root.tsx +0 -1
- package/corpus/templates/factory/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/factory/public/manifest.json +0 -1
- package/corpus/templates/forms/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/forms/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/forms/app/root.tsx +0 -1
- package/corpus/templates/forms/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/forms/public/manifest.json +0 -1
- package/corpus/templates/macros/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/macros/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/mail/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/mail/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/mail/app/root.tsx +0 -1
- package/corpus/templates/mail/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/mail/changelog/2026-08-11-google-sign-in-now-opens-the-gmail-connection-flow-directly.md +6 -0
- package/corpus/templates/mail/public/manifest.json +0 -1
- package/corpus/templates/mail/server/plugins/auth.ts +0 -10
- package/corpus/templates/plan/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/plan/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/plan/app/i18n/en-US.ts +2 -0
- package/corpus/templates/plan/app/pages/PlansPage.tsx +6 -0
- package/corpus/templates/plan/app/root.tsx +0 -1
- package/corpus/templates/plan/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/plan/changelog/2026-08-11-plan-sharing-now-identifies-viewers-who-can-add-comments.md +6 -0
- package/corpus/templates/plan/public/manifest.json +0 -1
- package/corpus/templates/slides/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/slides/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +6 -0
- package/corpus/templates/slides/app/i18n/en-US.ts +2 -0
- package/corpus/templates/slides/app/root.tsx +0 -1
- package/corpus/templates/slides/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-11-share-dialog-uses-commenter-terminology.md +6 -0
- package/corpus/templates/slides/public/manifest.json +0 -1
- package/corpus/templates/tasks/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/tasks/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/tasks/app/root.tsx +0 -1
- package/corpus/templates/tasks/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/tasks/public/manifest.json +0 -1
- package/dist/agent/production-agent.d.ts +8 -0
- package/dist/agent/production-agent.js +67 -22
- package/dist/agent/run-manager.d.ts +7 -0
- package/dist/agent/run-manager.js +46 -3
- package/dist/agent/tool-approval-migrations.d.ts +40 -0
- package/dist/agent/tool-approval-migrations.js +51 -0
- package/dist/agent/tool-approval-store.js +5 -20
- package/dist/cli/templates-meta.js +2 -2
- package/dist/client/AgentPanel.js +10 -1
- package/dist/client/AssistantChat.d.ts +3 -0
- package/dist/client/AssistantChat.js +151 -141
- package/dist/client/agent-page/AgentTabsPage.js +2 -2
- package/dist/client/chat/attachment-adapters.js +8 -2
- package/dist/client/chat/tool-call-display.d.ts +1 -0
- package/dist/client/chat/tool-call-display.js +3 -1
- package/dist/client/chat-first.js +11 -5
- package/dist/client/guided-questions.d.ts +15 -1
- package/dist/client/guided-questions.js +33 -15
- package/dist/client/hooks/index.d.ts +1 -1
- package/dist/client/hooks/index.js +1 -1
- package/dist/client/org/OrgSwitcher.d.ts +3 -1
- package/dist/client/org/OrgSwitcher.js +22 -16
- package/dist/client/org/WorkspaceNotice.js +3 -1
- package/dist/client/resources/index.d.ts +1 -1
- package/dist/client/resources/index.js +1 -1
- package/dist/client/resources/use-mcp-servers.d.ts +1 -0
- package/dist/client/resources/use-mcp-servers.js +14 -0
- package/dist/client/settings/AccountSettingsCard.js +8 -4
- package/dist/client/settings/SettingsPanel.js +46 -13
- package/dist/client/settings/SettingsTabsPage.js +8 -3
- package/dist/client/sharing/ShareButton.d.ts +7 -1
- package/dist/client/sharing/ShareButton.js +14 -7
- package/dist/client/sharing/ShareDialog.js +5 -1
- package/dist/client/sharing/useShareButtonController.d.ts +4 -0
- package/dist/client/sharing/useShareButtonController.js +15 -0
- package/dist/client/sharing/useShareDialogController.d.ts +7 -0
- package/dist/client/sharing/useShareDialogController.js +14 -0
- package/dist/client/sse-event-processor.js +37 -0
- package/dist/client/use-session.d.ts +2 -0
- package/dist/client/use-session.js +69 -5
- package/dist/deploy/build.js +1 -1
- package/dist/file-upload/pre-upload-attachments.d.ts +3 -4
- package/dist/file-upload/pre-upload-attachments.js +65 -17
- package/dist/ingestion/index.d.ts +1 -0
- package/dist/ingestion/index.js +1 -0
- package/dist/ingestion/office.d.ts +3 -3
- package/dist/ingestion/office.js +24 -2
- package/dist/ingestion/spreadsheet.d.ts +41 -0
- package/dist/ingestion/spreadsheet.js +172 -0
- package/dist/integrations/adapters/slack.js +7 -3
- package/dist/integrations/webhook-handler.js +11 -3
- package/dist/localization/default-messages.d.ts +3 -0
- package/dist/localization/default-messages.js +3 -0
- package/dist/mcp/embed-app.js +20 -9
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/oauth-tokens/migrations.d.ts +8 -0
- package/dist/oauth-tokens/migrations.js +36 -0
- package/dist/observability/routes.d.ts +5 -5
- package/dist/org/workspace-url.d.ts +8 -0
- package/dist/org/workspace-url.js +26 -2
- package/dist/progress/routes.d.ts +1 -1
- package/dist/secrets/routes.d.ts +6 -6
- package/dist/server/agent-chat/context-tools.js +27 -6
- package/dist/server/agent-chat-plugin.js +16 -8
- package/dist/server/auth.d.ts +0 -11
- package/dist/server/auth.js +37 -20
- package/dist/server/better-auth-migrations.js +23 -0
- package/dist/server/email-template.d.ts +5 -0
- package/dist/server/email-template.js +9 -0
- package/dist/server/embed-route.js +17 -5
- package/dist/server/identity-sso-store.d.ts +40 -66
- package/dist/server/identity-sso-store.js +187 -150
- package/dist/server/identity-sso.d.ts +17 -73
- package/dist/server/identity-sso.js +294 -297
- package/dist/server/onboarding-html.d.ts +0 -12
- package/dist/server/onboarding-html.js +46 -269
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/release-migrations.js +8 -0
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/shared/index.d.ts +1 -1
- package/dist/shared/index.js +1 -1
- package/dist/shared/workspace-app-id.d.ts +2 -0
- package/dist/shared/workspace-app-id.js +10 -0
- package/dist/sharing/actions/share-resource.d.ts +1 -0
- package/dist/sharing/actions/share-resource.js +20 -5
- package/dist/templates/chat/.agents/skills/sharing/SKILL.md +1 -1
- package/dist/templates/chat/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/dist/templates/chat/app/root.tsx +0 -1
- package/dist/templates/chat/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/dist/templates/chat/public/manifest.json +0 -1
- package/dist/templates/default/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/dist/templates/default/app/root.tsx +0 -1
- package/dist/templates/default/public/manifest.json +0 -1
- package/dist/templates/headless/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +22 -11
- package/dist/templates/workspace-core/.agents/skills/sharing/SKILL.md +1 -1
- package/dist/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/docs/content/cross-app-sso.mdx +85 -27
- package/docs/content/template-mail.mdx +0 -6
- package/package.json +4 -3
- package/src/templates/chat/.agents/skills/sharing/SKILL.md +1 -1
- package/src/templates/chat/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/src/templates/chat/app/root.tsx +0 -1
- package/src/templates/chat/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/src/templates/chat/public/manifest.json +0 -1
- package/src/templates/default/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/src/templates/default/app/root.tsx +0 -1
- package/src/templates/default/public/manifest.json +0 -1
- package/src/templates/headless/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +22 -11
- package/src/templates/workspace-core/.agents/skills/sharing/SKILL.md +1 -1
- package/src/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/calendar/changelog/2026-06-23-added-a-heads-up-explaining-google-s-app-not-verified-screen.md +0 -6
- package/corpus/templates/clips/app/components/meetings/google-oauth-identity-notice.tsx +0 -51
- package/corpus/templates/clips/changelog/2026-07-30-google-calendar-warning.md +0 -6
|
@@ -164,6 +164,8 @@ type PopoverView =
|
|
|
164
164
|
| "meetings"
|
|
165
165
|
| "dictation";
|
|
166
166
|
|
|
167
|
+
type SettingsTabId = "general" | "recording" | "meetings" | "dictation";
|
|
168
|
+
|
|
167
169
|
interface PopoverMeeting {
|
|
168
170
|
id: string;
|
|
169
171
|
title: string;
|
|
@@ -2622,8 +2624,12 @@ export function App() {
|
|
|
2622
2624
|
// and we call `resize_popover` to match.
|
|
2623
2625
|
const appRef = useRef<HTMLDivElement | null>(null);
|
|
2624
2626
|
usePopoverAutoSize(appRef, {
|
|
2625
|
-
disabled:
|
|
2626
|
-
|
|
2627
|
+
disabled:
|
|
2628
|
+
(popoverView !== "settings" && !popoverVisible) ||
|
|
2629
|
+
isRecording ||
|
|
2630
|
+
recordingFlowActive,
|
|
2631
|
+
width:
|
|
2632
|
+
popoverView === "settings" ? 920 : popoverView === "memory" ? 440 : 360,
|
|
2627
2633
|
});
|
|
2628
2634
|
|
|
2629
2635
|
const loadPendingUploads = useCallback(async () => {
|
|
@@ -5315,6 +5321,7 @@ function Setup({
|
|
|
5315
5321
|
}) {
|
|
5316
5322
|
const [url, setUrl] = useState(initial ?? DEFAULT_URL);
|
|
5317
5323
|
const [readinessOpen, setReadinessOpen] = useState(false);
|
|
5324
|
+
const [settingsTab, setSettingsTab] = useState<SettingsTabId>("general");
|
|
5318
5325
|
const featureConfig = useFeatureConfig();
|
|
5319
5326
|
const updateStatus = useUpdateStatus();
|
|
5320
5327
|
const voiceEnabled = featureConfig?.voiceEnabled !== false;
|
|
@@ -6069,13 +6076,6 @@ function Setup({
|
|
|
6069
6076
|
updateStatus.state === "available" ||
|
|
6070
6077
|
updateStatus.state === "downloading";
|
|
6071
6078
|
const updateReady = updateStatus.state === "downloaded";
|
|
6072
|
-
const updateStatusClass =
|
|
6073
|
-
updateStatus.state === "error"
|
|
6074
|
-
? "setup-warning"
|
|
6075
|
-
: updateStatus.state === "downloaded" ||
|
|
6076
|
-
updateStatus.state === "not-available"
|
|
6077
|
-
? "setup-success"
|
|
6078
|
-
: "setup-hint";
|
|
6079
6079
|
const updateCheckLabel = !updateChecksSupported
|
|
6080
6080
|
? "Release builds only"
|
|
6081
6081
|
: updateStatus.state === "checking"
|
|
@@ -6808,1071 +6808,843 @@ function Setup({
|
|
|
6808
6808
|
);
|
|
6809
6809
|
}
|
|
6810
6810
|
|
|
6811
|
-
|
|
6812
|
-
|
|
6813
|
-
<div className="
|
|
6814
|
-
|
|
6815
|
-
|
|
6816
|
-
|
|
6817
|
-
className="setup-back"
|
|
6818
|
-
onClick={onCancel}
|
|
6819
|
-
aria-label="Back"
|
|
6820
|
-
>
|
|
6821
|
-
<IconArrowLeft size={18} stroke={1.75} />
|
|
6822
|
-
</button>
|
|
6823
|
-
) : null}
|
|
6824
|
-
<h2>Settings</h2>
|
|
6825
|
-
</div>
|
|
6826
|
-
|
|
6827
|
-
<div className="setup-section-heading">General</div>
|
|
6828
|
-
|
|
6829
|
-
<div className="setup-section">
|
|
6830
|
-
<SettingLabel
|
|
6831
|
-
label="Clips server URL"
|
|
6832
|
-
hint="The URL of the Clips backend this tray app connects to."
|
|
6833
|
-
htmlFor="clips-url"
|
|
6834
|
-
/>
|
|
6835
|
-
<input
|
|
6836
|
-
id="clips-url"
|
|
6837
|
-
type="url"
|
|
6838
|
-
value={url}
|
|
6839
|
-
onChange={(e) => setUrl(e.target.value)}
|
|
6840
|
-
placeholder="http://localhost:8080"
|
|
6841
|
-
/>
|
|
6842
|
-
<button
|
|
6843
|
-
className="secondary setup-connect-button"
|
|
6844
|
-
type="button"
|
|
6845
|
-
onClick={handleConnect}
|
|
6811
|
+
function renderGeneralSettings() {
|
|
6812
|
+
return (
|
|
6813
|
+
<div className="settings-tab-content">
|
|
6814
|
+
<DesktopSettingsGroup
|
|
6815
|
+
title="Connection"
|
|
6816
|
+
description="Choose the Clips backend used by this desktop recorder."
|
|
6846
6817
|
>
|
|
6847
|
-
|
|
6848
|
-
|
|
6849
|
-
|
|
6818
|
+
<DesktopSettingsRow
|
|
6819
|
+
label="Clips server URL"
|
|
6820
|
+
description="The backend this tray app connects to."
|
|
6821
|
+
control={
|
|
6822
|
+
<div className="settings-control-group settings-control-group--wide">
|
|
6823
|
+
<input
|
|
6824
|
+
id="clips-url"
|
|
6825
|
+
className="settings-url-input"
|
|
6826
|
+
type="url"
|
|
6827
|
+
value={url}
|
|
6828
|
+
onChange={(event) => setUrl(event.target.value)}
|
|
6829
|
+
placeholder="http://localhost:8080"
|
|
6830
|
+
/>
|
|
6831
|
+
<button
|
|
6832
|
+
className="secondary"
|
|
6833
|
+
type="button"
|
|
6834
|
+
onClick={handleConnect}
|
|
6835
|
+
>
|
|
6836
|
+
Connect
|
|
6837
|
+
</button>
|
|
6838
|
+
</div>
|
|
6839
|
+
}
|
|
6840
|
+
/>
|
|
6841
|
+
</DesktopSettingsGroup>
|
|
6850
6842
|
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
|
|
6843
|
+
<DesktopSettingsGroup
|
|
6844
|
+
title="App behavior"
|
|
6845
|
+
description="Keep Clips ready when you need it and out of the way when you do not."
|
|
6846
|
+
>
|
|
6847
|
+
<DesktopSettingsRow
|
|
6854
6848
|
label="Open at login"
|
|
6855
|
-
|
|
6856
|
-
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
6849
|
+
description="Start Clips when you sign in."
|
|
6850
|
+
control={
|
|
6851
|
+
<Switch
|
|
6852
|
+
on={launchAtLoginEnabled}
|
|
6853
|
+
onChange={setLaunchAtLoginEnabled}
|
|
6854
|
+
label="Open Clips at login"
|
|
6855
|
+
/>
|
|
6856
|
+
}
|
|
6861
6857
|
/>
|
|
6862
|
-
|
|
6863
|
-
</div>
|
|
6864
|
-
|
|
6865
|
-
<div className="setup-section">
|
|
6866
|
-
<div className="setup-toggle-row">
|
|
6867
|
-
<SettingLabel
|
|
6858
|
+
<DesktopSettingsRow
|
|
6868
6859
|
label="Hide when inactive"
|
|
6869
|
-
|
|
6870
|
-
|
|
6871
|
-
|
|
6872
|
-
|
|
6873
|
-
|
|
6874
|
-
|
|
6875
|
-
|
|
6876
|
-
|
|
6877
|
-
</div>
|
|
6878
|
-
|
|
6879
|
-
<div className="setup-section">
|
|
6880
|
-
<div className="setup-toggle-row">
|
|
6881
|
-
<SettingLabel
|
|
6882
|
-
label="Show Clips in screen captures"
|
|
6883
|
-
hint="When off, Clips windows and recording overlays stay out of screenshots, normal screen recordings, and Rewind. Turn on only for debugging or demos."
|
|
6884
|
-
/>
|
|
6885
|
-
<Switch
|
|
6886
|
-
on={showInScreenCapture}
|
|
6887
|
-
onChange={setShowInScreenCapture}
|
|
6888
|
-
label="Show Clips in screen captures"
|
|
6860
|
+
description="Hide the tray window when it loses focus."
|
|
6861
|
+
control={
|
|
6862
|
+
<Switch
|
|
6863
|
+
on={autoHidePopoverEnabled}
|
|
6864
|
+
onChange={setAutoHidePopoverEnabled}
|
|
6865
|
+
label="Hide Clips when focus leaves"
|
|
6866
|
+
/>
|
|
6867
|
+
}
|
|
6889
6868
|
/>
|
|
6890
|
-
|
|
6891
|
-
|
|
6892
|
-
|
|
6893
|
-
|
|
6894
|
-
|
|
6895
|
-
|
|
6896
|
-
|
|
6897
|
-
|
|
6898
|
-
|
|
6899
|
-
|
|
6900
|
-
type="button"
|
|
6901
|
-
className="secondary"
|
|
6902
|
-
onClick={checkForDesktopUpdate}
|
|
6903
|
-
disabled={!updateChecksSupported || updateBusy || updateReady}
|
|
6904
|
-
>
|
|
6905
|
-
<IconRefresh
|
|
6906
|
-
size={15}
|
|
6907
|
-
stroke={1.9}
|
|
6908
|
-
className={updateBusy ? "update-spinner" : undefined}
|
|
6909
|
-
/>
|
|
6910
|
-
{updateCheckLabel}
|
|
6911
|
-
</button>
|
|
6912
|
-
{updateReady ? (
|
|
6913
|
-
<button
|
|
6914
|
-
type="button"
|
|
6915
|
-
className="secondary"
|
|
6916
|
-
onClick={() => {
|
|
6917
|
-
installAndRestart().catch((err) => {
|
|
6918
|
-
console.error("[clips-updater] relaunch failed:", err);
|
|
6919
|
-
});
|
|
6920
|
-
}}
|
|
6921
|
-
>
|
|
6922
|
-
Restart to install
|
|
6923
|
-
</button>
|
|
6924
|
-
) : null}
|
|
6925
|
-
</div>
|
|
6926
|
-
<p className={updateStatusClass}>
|
|
6927
|
-
{updateChecksSupported
|
|
6928
|
-
? desktopUpdateStatusText(updateStatus)
|
|
6929
|
-
: "Update checks are available in signed release builds."}
|
|
6930
|
-
</p>
|
|
6931
|
-
</div>
|
|
6932
|
-
|
|
6933
|
-
<div className="setup-section-heading">Permissions</div>
|
|
6934
|
-
|
|
6935
|
-
<div className="setup-section">
|
|
6936
|
-
<ReadinessPanel
|
|
6937
|
-
mode="screen-camera"
|
|
6938
|
-
cameraOn={true}
|
|
6939
|
-
micOn={true}
|
|
6940
|
-
includeVoicePaste={voiceEnabled}
|
|
6941
|
-
includeFnMonitoring={fnShortcutSelected}
|
|
6942
|
-
open={readinessOpen}
|
|
6943
|
-
onOpenChange={setReadinessOpen}
|
|
6944
|
-
onOpenPermission={openPrivacySettings}
|
|
6945
|
-
/>
|
|
6946
|
-
</div>
|
|
6947
|
-
|
|
6948
|
-
<div className="setup-section-heading">Recording</div>
|
|
6949
|
-
|
|
6950
|
-
<div className="setup-section rewind-settings-entry">
|
|
6951
|
-
<div className="rewind-settings-entry-main">
|
|
6952
|
-
<span
|
|
6953
|
-
className={`rewind-home-dot ${rewindStatusPresentation.isLive ? "is-live" : ""}`}
|
|
6869
|
+
<DesktopSettingsRow
|
|
6870
|
+
label="Show in screen captures"
|
|
6871
|
+
description="Include Clips windows in screenshots and recordings."
|
|
6872
|
+
control={
|
|
6873
|
+
<Switch
|
|
6874
|
+
on={showInScreenCapture}
|
|
6875
|
+
onChange={setShowInScreenCapture}
|
|
6876
|
+
label="Show Clips in screen captures"
|
|
6877
|
+
/>
|
|
6878
|
+
}
|
|
6954
6879
|
/>
|
|
6955
|
-
|
|
6956
|
-
<strong>Rewind</strong>
|
|
6957
|
-
<p className="setup-hint">{rewindStatusPresentation.title}</p>
|
|
6958
|
-
</div>
|
|
6959
|
-
</div>
|
|
6960
|
-
<button type="button" className="secondary" onClick={onOpenRewind}>
|
|
6961
|
-
Rewind settings
|
|
6962
|
-
</button>
|
|
6963
|
-
</div>
|
|
6880
|
+
</DesktopSettingsGroup>
|
|
6964
6881
|
|
|
6965
|
-
|
|
6966
|
-
|
|
6967
|
-
|
|
6968
|
-
hint="Only clips you dismiss from the saved-upload card appear in Movies/Clips/Drafts. To retry a failed upload, return to the Clips popover and use Retry."
|
|
6969
|
-
/>
|
|
6970
|
-
<button
|
|
6971
|
-
type="button"
|
|
6972
|
-
className="secondary"
|
|
6973
|
-
onClick={openClipDraftsFolder}
|
|
6882
|
+
<DesktopSettingsGroup
|
|
6883
|
+
title="Updates"
|
|
6884
|
+
description="Keep the signed Clips desktop release current."
|
|
6974
6885
|
>
|
|
6975
|
-
<
|
|
6976
|
-
|
|
6977
|
-
|
|
6978
|
-
|
|
6979
|
-
|
|
6980
|
-
|
|
6981
|
-
</div>
|
|
6982
|
-
|
|
6983
|
-
<div className="setup-section setup-rewind-legacy">
|
|
6984
|
-
<div className="setup-toggle-row">
|
|
6985
|
-
<SettingLabel
|
|
6986
|
-
label="Rewind"
|
|
6987
|
-
hint="A disabled-by-default rolling local archive for recent screen and app context. It never becomes a shared Clip on its own."
|
|
6988
|
-
/>
|
|
6989
|
-
<Switch
|
|
6990
|
-
on={screenMemory.enabled}
|
|
6991
|
-
onChange={(enabled) =>
|
|
6992
|
-
void setScreenMemoryConfig({ enabled, paused: false })
|
|
6886
|
+
<DesktopSettingsRow
|
|
6887
|
+
label="Desktop updates"
|
|
6888
|
+
description={
|
|
6889
|
+
updateChecksSupported
|
|
6890
|
+
? desktopUpdateStatusText(updateStatus)
|
|
6891
|
+
: "Update checks are available in signed release builds."
|
|
6993
6892
|
}
|
|
6994
|
-
|
|
6995
|
-
|
|
6996
|
-
/>
|
|
6997
|
-
</div>
|
|
6998
|
-
<p className="setup-hint">
|
|
6999
|
-
Local-only by default. Media uploads and sharing happen only when you
|
|
7000
|
-
make a normal Clip.
|
|
7001
|
-
</p>
|
|
7002
|
-
{screenMemory.enabled ? (
|
|
7003
|
-
<>
|
|
7004
|
-
<div className="setup-toggle-row">
|
|
7005
|
-
<SettingLabel
|
|
7006
|
-
label="Pause capture"
|
|
7007
|
-
hint="Stop retaining new Rewind segments without clearing the local archive."
|
|
7008
|
-
/>
|
|
7009
|
-
<Switch
|
|
7010
|
-
on={screenMemory.paused}
|
|
7011
|
-
onChange={(paused) => void setScreenMemoryConfig({ paused })}
|
|
7012
|
-
label="Pause Rewind"
|
|
7013
|
-
disabled={screenMemoryConfigBusy || captureControlsLocked}
|
|
7014
|
-
/>
|
|
7015
|
-
</div>
|
|
7016
|
-
<div className="setup-grid">
|
|
7017
|
-
<label className="setup-mini-field">
|
|
7018
|
-
<span>Retention</span>
|
|
7019
|
-
<select
|
|
7020
|
-
className="setup-select"
|
|
7021
|
-
disabled={screenMemoryConfigBusy || captureControlsLocked}
|
|
7022
|
-
value={screenMemory.retentionHours}
|
|
7023
|
-
onChange={(event) =>
|
|
7024
|
-
setScreenMemoryConfig({
|
|
7025
|
-
retentionHours: Number(event.target.value),
|
|
7026
|
-
})
|
|
7027
|
-
}
|
|
7028
|
-
>
|
|
7029
|
-
<option value={8}>8 hours</option>
|
|
7030
|
-
<option value={24}>24 hours</option>
|
|
7031
|
-
</select>
|
|
7032
|
-
</label>
|
|
7033
|
-
<label className="setup-mini-field">
|
|
7034
|
-
<span>Disk cap</span>
|
|
7035
|
-
<select
|
|
7036
|
-
className="setup-select"
|
|
7037
|
-
disabled={screenMemoryConfigBusy}
|
|
7038
|
-
value={screenMemory.maxBytes}
|
|
7039
|
-
onChange={(event) =>
|
|
7040
|
-
setScreenMemoryConfig({
|
|
7041
|
-
maxBytes: Number(event.target.value),
|
|
7042
|
-
})
|
|
7043
|
-
}
|
|
7044
|
-
>
|
|
7045
|
-
<option value={5 * 1024 * 1024 * 1024}>5 GB</option>
|
|
7046
|
-
<option value={20 * 1024 * 1024 * 1024}>20 GB</option>
|
|
7047
|
-
<option value={50 * 1024 * 1024 * 1024}>50 GB</option>
|
|
7048
|
-
</select>
|
|
7049
|
-
</label>
|
|
7050
|
-
</div>
|
|
7051
|
-
<div className="setup-grid">
|
|
7052
|
-
<label className="setup-mini-field">
|
|
7053
|
-
<span>Capture mode</span>
|
|
7054
|
-
<select
|
|
7055
|
-
className="setup-select"
|
|
7056
|
-
disabled={screenMemoryConfigBusy}
|
|
7057
|
-
value={screenMemory.captureMode}
|
|
7058
|
-
onChange={(event) =>
|
|
7059
|
-
setScreenMemoryConfig({
|
|
7060
|
-
captureMode: event.target.value as
|
|
7061
|
-
| "visuals"
|
|
7062
|
-
| "visuals-audio",
|
|
7063
|
-
})
|
|
7064
|
-
}
|
|
7065
|
-
>
|
|
7066
|
-
<option value="visuals">Visuals</option>
|
|
7067
|
-
<option value="visuals-audio">Visuals + audio</option>
|
|
7068
|
-
</select>
|
|
7069
|
-
</label>
|
|
7070
|
-
<label className="setup-mini-field">
|
|
7071
|
-
<span>Agent handoff review</span>
|
|
7072
|
-
<select
|
|
7073
|
-
className="setup-select"
|
|
7074
|
-
disabled={screenMemoryConfigBusy}
|
|
7075
|
-
value={screenMemory.reviewBeforeSending ? "review" : "direct"}
|
|
7076
|
-
onChange={(event) =>
|
|
7077
|
-
setScreenMemoryConfig({
|
|
7078
|
-
reviewBeforeSending: event.target.value === "review",
|
|
7079
|
-
})
|
|
7080
|
-
}
|
|
7081
|
-
>
|
|
7082
|
-
<option value="review">Review before sending</option>
|
|
7083
|
-
<option value="direct">Send requested range directly</option>
|
|
7084
|
-
</select>
|
|
7085
|
-
</label>
|
|
7086
|
-
</div>
|
|
7087
|
-
<p className="setup-hint">
|
|
7088
|
-
{screenMemory.captureMode === "visuals-audio"
|
|
7089
|
-
? "Visuals + audio is configured to retain microphone and system audio as separate local tracks."
|
|
7090
|
-
: "Visuals retains screen and app context without selecting audio capture."}
|
|
7091
|
-
</p>
|
|
7092
|
-
<p className="setup-hint">
|
|
7093
|
-
Agents receive bounded matching text when you ask. Raw Rewind
|
|
7094
|
-
media remains local unless a bounded range becomes a private Clip.
|
|
7095
|
-
</p>
|
|
7096
|
-
<label className="setup-mini-field">
|
|
7097
|
-
<span>Excluded apps</span>
|
|
7098
|
-
<input
|
|
7099
|
-
value={excludedBundleIdsInput}
|
|
7100
|
-
onChange={(event) =>
|
|
7101
|
-
setExcludedBundleIdsInput(event.target.value)
|
|
7102
|
-
}
|
|
7103
|
-
onBlur={() =>
|
|
7104
|
-
setScreenMemoryConfig({
|
|
7105
|
-
excludedBundleIds: parseExcludedBundleIds(
|
|
7106
|
-
excludedBundleIdsInput,
|
|
7107
|
-
),
|
|
7108
|
-
})
|
|
7109
|
-
}
|
|
7110
|
-
placeholder="com.example.private-app, com.example.vault"
|
|
7111
|
-
aria-describedby="rewind-excluded-apps-hint"
|
|
7112
|
-
/>
|
|
7113
|
-
</label>
|
|
7114
|
-
<button
|
|
7115
|
-
type="button"
|
|
7116
|
-
className="secondary"
|
|
7117
|
-
disabled={screenMemoryConfigBusy}
|
|
7118
|
-
onClick={() =>
|
|
7119
|
-
void setScreenMemoryConfig({
|
|
7120
|
-
excludedBundleIds: parseExcludedBundleIds(
|
|
7121
|
-
excludedBundleIdsInput,
|
|
7122
|
-
),
|
|
7123
|
-
})
|
|
7124
|
-
}
|
|
7125
|
-
>
|
|
7126
|
-
Apply exclusions
|
|
7127
|
-
</button>
|
|
7128
|
-
<p id="rewind-excluded-apps-hint" className="setup-hint">
|
|
7129
|
-
Bundle IDs, comma-separated. Password managers are excluded by
|
|
7130
|
-
default. Recognized bundle IDs stop media capture and discard the
|
|
7131
|
-
entire in-flight segment; apps that do not expose a recognized
|
|
7132
|
-
bundle ID cannot be detected.
|
|
7133
|
-
</p>
|
|
7134
|
-
<div className="whisper-status">
|
|
7135
|
-
{rewindStatusPresentation.isLive ? (
|
|
7136
|
-
<IconCircleCheck size={13} className="whisper-status-icon" />
|
|
7137
|
-
) : (
|
|
7138
|
-
<IconAlertTriangle size={13} className="whisper-status-icon" />
|
|
7139
|
-
)}
|
|
7140
|
-
<span>
|
|
7141
|
-
{rewindStatusPresentation.kind === "recording" &&
|
|
7142
|
-
!rewindStatusPresentation.hasError
|
|
7143
|
-
? `Rewind is retaining local coverage: ${screenMemorySegments.length} segment${screenMemorySegments.length === 1 ? "" : "s"}, ${formatStorageBytes(screenMemoryTotalBytes)}.`
|
|
7144
|
-
: rewindStatusPresentation.detail}
|
|
7145
|
-
</span>
|
|
7146
|
-
</div>
|
|
7147
|
-
{screenMemorySegments[0] ? (
|
|
7148
|
-
<p className="setup-hint">
|
|
7149
|
-
Latest local coverage:{" "}
|
|
7150
|
-
{new Date(screenMemorySegments[0].endedAt).toLocaleTimeString()}
|
|
7151
|
-
.
|
|
7152
|
-
</p>
|
|
7153
|
-
) : null}
|
|
7154
|
-
<div className="setup-advanced-body">
|
|
7155
|
-
<SettingLabel
|
|
7156
|
-
label="Ask Rewind"
|
|
7157
|
-
hint="Searches app context, local transcripts, and local visual text on this Mac. Private mode allows this because no model or network service is called."
|
|
7158
|
-
/>
|
|
7159
|
-
<div className="setup-button-row">
|
|
7160
|
-
<input
|
|
7161
|
-
value={rewindLocalQuery}
|
|
7162
|
-
onChange={(event) => setRewindLocalQuery(event.target.value)}
|
|
7163
|
-
placeholder="What was Lilian saying about the audience?"
|
|
7164
|
-
aria-label="Ask Rewind locally"
|
|
7165
|
-
maxLength={500}
|
|
7166
|
-
onKeyDown={(event) => {
|
|
7167
|
-
if (event.key !== "Enter") return;
|
|
7168
|
-
event.preventDefault();
|
|
7169
|
-
event.stopPropagation();
|
|
7170
|
-
void askRewindLocally();
|
|
7171
|
-
}}
|
|
7172
|
-
/>
|
|
6893
|
+
control={
|
|
6894
|
+
<div className="settings-control-group">
|
|
7173
6895
|
<button
|
|
7174
6896
|
type="button"
|
|
7175
6897
|
className="secondary"
|
|
7176
|
-
|
|
7177
|
-
|
|
6898
|
+
onClick={checkForDesktopUpdate}
|
|
6899
|
+
disabled={!updateChecksSupported || updateBusy || updateReady}
|
|
7178
6900
|
>
|
|
7179
|
-
|
|
6901
|
+
<IconRefresh
|
|
6902
|
+
size={15}
|
|
6903
|
+
stroke={1.9}
|
|
6904
|
+
className={updateBusy ? "update-spinner" : undefined}
|
|
6905
|
+
/>
|
|
6906
|
+
{updateCheckLabel}
|
|
7180
6907
|
</button>
|
|
6908
|
+
{updateReady ? (
|
|
6909
|
+
<button
|
|
6910
|
+
type="button"
|
|
6911
|
+
className="secondary"
|
|
6912
|
+
onClick={() => {
|
|
6913
|
+
installAndRestart().catch((err) => {
|
|
6914
|
+
console.error("[clips-updater] relaunch failed:", err);
|
|
6915
|
+
});
|
|
6916
|
+
}}
|
|
6917
|
+
>
|
|
6918
|
+
Restart to install
|
|
6919
|
+
</button>
|
|
6920
|
+
) : null}
|
|
7181
6921
|
</div>
|
|
7182
|
-
|
|
7183
|
-
|
|
7184
|
-
|
|
7185
|
-
|
|
7186
|
-
|
|
7187
|
-
|
|
7188
|
-
|
|
7189
|
-
|
|
7190
|
-
|
|
7191
|
-
|
|
7192
|
-
|
|
7193
|
-
|
|
7194
|
-
|
|
7195
|
-
|
|
7196
|
-
|
|
7197
|
-
|
|
7198
|
-
|
|
7199
|
-
|
|
7200
|
-
|
|
7201
|
-
|
|
7202
|
-
|
|
7203
|
-
|
|
7204
|
-
|
|
7205
|
-
|
|
7206
|
-
|
|
7207
|
-
|
|
7208
|
-
|
|
7209
|
-
<details className="setup-advanced">
|
|
7210
|
-
<summary className="setup-advanced-summary">
|
|
7211
|
-
Coverage gaps
|
|
7212
|
-
</summary>
|
|
7213
|
-
<div className="setup-advanced-body">
|
|
7214
|
-
{rewindLocalResult.coverage.gaps
|
|
7215
|
-
.slice(0, 10)
|
|
7216
|
-
.map((gap, index) => (
|
|
7217
|
-
<p
|
|
7218
|
-
className="setup-hint"
|
|
7219
|
-
key={`${gap.kind}-${gap.source}-${gap.startedAt ?? index}`}
|
|
7220
|
-
>
|
|
7221
|
-
<strong>{gap.source}</strong> · {gap.detail}
|
|
7222
|
-
</p>
|
|
7223
|
-
))}
|
|
7224
|
-
</div>
|
|
7225
|
-
</details>
|
|
7226
|
-
) : null}
|
|
7227
|
-
{rewindLocalResult.evidence.length === 0 ? (
|
|
7228
|
-
<p className="setup-hint">No matching evidence to show.</p>
|
|
7229
|
-
) : (
|
|
7230
|
-
rewindLocalResult.evidence.map((evidence) => (
|
|
7231
|
-
<div className="setup-section" key={evidence.id}>
|
|
7232
|
-
<p className="setup-hint">
|
|
7233
|
-
<strong>{evidence.sourceType}</strong> ·{" "}
|
|
7234
|
-
{new Date(evidence.capturedAt).toLocaleString()}
|
|
7235
|
-
{typeof evidence.confidence === "number"
|
|
7236
|
-
? ` · ${Math.round(evidence.confidence * 100)}% OCR confidence`
|
|
7237
|
-
: ""}
|
|
7238
|
-
<br />
|
|
7239
|
-
{evidence.excerpt}
|
|
7240
|
-
</p>
|
|
7241
|
-
<button
|
|
7242
|
-
type="button"
|
|
7243
|
-
className="secondary"
|
|
7244
|
-
onClick={() => void replayRewindMoment(evidence)}
|
|
7245
|
-
disabled={rewindReplayId === evidence.id}
|
|
7246
|
-
>
|
|
7247
|
-
<IconExternalLink size={14} stroke={1.9} />
|
|
7248
|
-
{rewindReplayId === evidence.id
|
|
7249
|
-
? "Preparing replay…"
|
|
7250
|
-
: "Replay moment"}
|
|
7251
|
-
</button>
|
|
7252
|
-
</div>
|
|
7253
|
-
))
|
|
7254
|
-
)}
|
|
7255
|
-
{rewindLocalResult.truncated ? (
|
|
7256
|
-
<p className="setup-hint">
|
|
7257
|
-
More local matches exist; results are bounded to the
|
|
7258
|
-
strongest 12.
|
|
7259
|
-
</p>
|
|
7260
|
-
) : null}
|
|
7261
|
-
</div>
|
|
7262
|
-
) : null}
|
|
7263
|
-
</div>
|
|
7264
|
-
<div className="setup-button-row">
|
|
6922
|
+
}
|
|
6923
|
+
/>
|
|
6924
|
+
</DesktopSettingsGroup>
|
|
6925
|
+
|
|
6926
|
+
<DesktopSettingsGroup
|
|
6927
|
+
title="Account & diagnostics"
|
|
6928
|
+
description="Manage the local desktop session and find troubleshooting information."
|
|
6929
|
+
>
|
|
6930
|
+
{signedInAs && onSignOut ? (
|
|
6931
|
+
<DesktopSettingsRow
|
|
6932
|
+
label="Signed in"
|
|
6933
|
+
description={signedInAs}
|
|
6934
|
+
control={
|
|
6935
|
+
<button
|
|
6936
|
+
type="button"
|
|
6937
|
+
className="link-button"
|
|
6938
|
+
onClick={onSignOut}
|
|
6939
|
+
>
|
|
6940
|
+
Sign out
|
|
6941
|
+
</button>
|
|
6942
|
+
}
|
|
6943
|
+
/>
|
|
6944
|
+
) : null}
|
|
6945
|
+
<DesktopSettingsRow
|
|
6946
|
+
label="Diagnostic logs"
|
|
6947
|
+
description="Open the local Clips logs folder."
|
|
6948
|
+
control={
|
|
7265
6949
|
<button
|
|
7266
6950
|
type="button"
|
|
7267
6951
|
className="secondary"
|
|
7268
|
-
onClick={
|
|
7269
|
-
|
|
6952
|
+
onClick={() => {
|
|
6953
|
+
invoke("open_logs").catch((err) => {
|
|
6954
|
+
console.error("[clips-tray] open logs failed:", err);
|
|
6955
|
+
});
|
|
6956
|
+
}}
|
|
7270
6957
|
>
|
|
7271
|
-
<
|
|
7272
|
-
|
|
6958
|
+
<IconFolderOpen size={15} stroke={1.9} />
|
|
6959
|
+
Open logs
|
|
7273
6960
|
</button>
|
|
6961
|
+
}
|
|
6962
|
+
/>
|
|
6963
|
+
</DesktopSettingsGroup>
|
|
6964
|
+
</div>
|
|
6965
|
+
);
|
|
6966
|
+
}
|
|
6967
|
+
|
|
6968
|
+
function renderRecordingSettings() {
|
|
6969
|
+
return (
|
|
6970
|
+
<div className="settings-tab-content">
|
|
6971
|
+
<DesktopSettingsGroup
|
|
6972
|
+
title="Permissions"
|
|
6973
|
+
description="Clips needs access to capture your screen, camera, and microphone."
|
|
6974
|
+
>
|
|
6975
|
+
<div className="settings-group-inset">
|
|
6976
|
+
<ReadinessPanel
|
|
6977
|
+
mode="screen-camera"
|
|
6978
|
+
cameraOn={true}
|
|
6979
|
+
micOn={true}
|
|
6980
|
+
includeVoicePaste={voiceEnabled}
|
|
6981
|
+
includeFnMonitoring={fnShortcutSelected}
|
|
6982
|
+
open={readinessOpen}
|
|
6983
|
+
onOpenChange={setReadinessOpen}
|
|
6984
|
+
onOpenPermission={openPrivacySettings}
|
|
6985
|
+
/>
|
|
6986
|
+
</div>
|
|
6987
|
+
</DesktopSettingsGroup>
|
|
6988
|
+
|
|
6989
|
+
<DesktopSettingsGroup
|
|
6990
|
+
title="Capture"
|
|
6991
|
+
description="Choose how recordings are saved and how the tray opens them."
|
|
6992
|
+
>
|
|
6993
|
+
<DesktopSettingsRow
|
|
6994
|
+
label="Rewind"
|
|
6995
|
+
description={rewindStatusPresentation.title}
|
|
6996
|
+
control={
|
|
7274
6997
|
<button
|
|
7275
6998
|
type="button"
|
|
7276
6999
|
className="secondary"
|
|
7277
|
-
onClick={
|
|
7000
|
+
onClick={() => onOpenRewind?.()}
|
|
7278
7001
|
>
|
|
7279
|
-
|
|
7280
|
-
Open folder
|
|
7002
|
+
Open Rewind settings
|
|
7281
7003
|
</button>
|
|
7004
|
+
}
|
|
7005
|
+
/>
|
|
7006
|
+
<DesktopSettingsRow
|
|
7007
|
+
label="Clip Drafts"
|
|
7008
|
+
description="Open local recordings that were kept after an upload issue."
|
|
7009
|
+
control={
|
|
7282
7010
|
<button
|
|
7283
7011
|
type="button"
|
|
7284
7012
|
className="secondary"
|
|
7285
|
-
onClick={
|
|
7286
|
-
disabled={screenMemoryBusy || screenMemorySegments.length === 0}
|
|
7013
|
+
onClick={openClipDraftsFolder}
|
|
7287
7014
|
>
|
|
7288
|
-
<
|
|
7289
|
-
|
|
7015
|
+
<IconFolderOpen size={15} stroke={1.9} />
|
|
7016
|
+
Open Clip Drafts
|
|
7290
7017
|
</button>
|
|
7291
|
-
|
|
7292
|
-
|
|
7293
|
-
|
|
7294
|
-
|
|
7295
|
-
|
|
7296
|
-
|
|
7297
|
-
|
|
7298
|
-
|
|
7299
|
-
|
|
7300
|
-
|
|
7301
|
-
<
|
|
7302
|
-
|
|
7303
|
-
|
|
7304
|
-
|
|
7305
|
-
|
|
7306
|
-
|
|
7307
|
-
|
|
7308
|
-
|
|
7309
|
-
</p>
|
|
7310
|
-
{rewindEgressEvents.length === 0 ? (
|
|
7311
|
-
<p className="setup-hint">No evidence requests yet.</p>
|
|
7312
|
-
) : (
|
|
7313
|
-
rewindEgressEvents.slice(0, 10).map((event) => (
|
|
7314
|
-
<details
|
|
7315
|
-
className="setup-advanced"
|
|
7316
|
-
key={`${event.requestId}-${event.state}`}
|
|
7317
|
-
>
|
|
7318
|
-
<summary className="popover-kv">
|
|
7319
|
-
<span>
|
|
7320
|
-
{new Date(event.occurredAt).toLocaleString()} ·{" "}
|
|
7321
|
-
{event.state}
|
|
7322
|
-
</span>
|
|
7323
|
-
<strong>
|
|
7324
|
-
{event.evidenceCount} item
|
|
7325
|
-
{event.evidenceCount === 1 ? "" : "s"}
|
|
7326
|
-
</strong>
|
|
7327
|
-
</summary>
|
|
7328
|
-
<div className="setup-advanced-body">
|
|
7329
|
-
<p className="setup-hint">
|
|
7330
|
-
<strong>{event.operation ?? "Agent access"}</strong>
|
|
7331
|
-
{" · "}Request {event.requestId}
|
|
7332
|
-
</p>
|
|
7333
|
-
{event.receipt?.evidence?.map((evidence) => (
|
|
7334
|
-
<p className="setup-hint" key={evidence.id}>
|
|
7335
|
-
<strong>{evidence.sourceType}</strong>
|
|
7336
|
-
{evidence.capturedAt
|
|
7337
|
-
? ` · ${new Date(evidence.capturedAt).toLocaleTimeString()}`
|
|
7338
|
-
: ""}
|
|
7339
|
-
<br />
|
|
7340
|
-
Evidence {evidence.id} · moment {evidence.momentId}
|
|
7341
|
-
</p>
|
|
7342
|
-
))}
|
|
7343
|
-
{event.receipt?.frames?.map((frame) => (
|
|
7344
|
-
<p className="setup-hint" key={frame.timestamp}>
|
|
7345
|
-
<strong>Local frame</strong>
|
|
7346
|
-
{` · ${new Date(frame.timestamp).toLocaleTimeString()}`}
|
|
7347
|
-
<br />
|
|
7348
|
-
Segment {frame.segmentId}
|
|
7349
|
-
</p>
|
|
7350
|
-
))}
|
|
7351
|
-
{event.receipt?.mediaInterval ? (
|
|
7352
|
-
<p className="setup-hint">
|
|
7353
|
-
<strong>Private Clip range</strong>
|
|
7354
|
-
{` · ${new Date(event.receipt.mediaInterval.startAt).toLocaleTimeString()}–${new Date(event.receipt.mediaInterval.endAt).toLocaleTimeString()}`}
|
|
7355
|
-
</p>
|
|
7356
|
-
) : null}
|
|
7357
|
-
{!event.receipt ? (
|
|
7358
|
-
<p className="setup-hint">
|
|
7359
|
-
This completion record refers to the prepared
|
|
7360
|
-
receipt with request ID {event.requestId}.
|
|
7361
|
-
</p>
|
|
7362
|
-
) : null}
|
|
7363
|
-
{event.error ? (
|
|
7364
|
-
<p className="setup-warning">{event.error}</p>
|
|
7365
|
-
) : null}
|
|
7366
|
-
</div>
|
|
7367
|
-
</details>
|
|
7368
|
-
))
|
|
7369
|
-
)}
|
|
7370
|
-
</div>
|
|
7371
|
-
</details>
|
|
7372
|
-
{screenMemoryMessage ? (
|
|
7373
|
-
<p
|
|
7374
|
-
className={
|
|
7375
|
-
screenMemoryMessage.kind === "ok"
|
|
7376
|
-
? "setup-success"
|
|
7377
|
-
: "setup-warning"
|
|
7018
|
+
}
|
|
7019
|
+
>
|
|
7020
|
+
{clipDraftsError ? (
|
|
7021
|
+
<p className="setup-warning">{clipDraftsError}</p>
|
|
7022
|
+
) : null}
|
|
7023
|
+
</DesktopSettingsRow>
|
|
7024
|
+
<DesktopSettingsRow
|
|
7025
|
+
label="Save recordings locally"
|
|
7026
|
+
description="Keep recordings on this Mac instead of uploading them."
|
|
7027
|
+
control={
|
|
7028
|
+
<select
|
|
7029
|
+
id="local-recording-mode"
|
|
7030
|
+
className="setup-select"
|
|
7031
|
+
value={localRecordingMode}
|
|
7032
|
+
onChange={(event) =>
|
|
7033
|
+
setLocalRecordingMode(
|
|
7034
|
+
event.target.value as LocalRecordingMode,
|
|
7035
|
+
)
|
|
7378
7036
|
}
|
|
7379
7037
|
>
|
|
7380
|
-
|
|
7381
|
-
|
|
7382
|
-
|
|
7383
|
-
|
|
7384
|
-
|
|
7385
|
-
|
|
7386
|
-
|
|
7387
|
-
|
|
7388
|
-
|
|
7389
|
-
|
|
7390
|
-
|
|
7391
|
-
|
|
7392
|
-
|
|
7393
|
-
|
|
7394
|
-
|
|
7395
|
-
|
|
7396
|
-
|
|
7397
|
-
|
|
7398
|
-
|
|
7399
|
-
|
|
7400
|
-
|
|
7401
|
-
|
|
7402
|
-
}
|
|
7403
|
-
>
|
|
7404
|
-
<option value="off">Cloud Clips (default)</option>
|
|
7405
|
-
<option value="composed">One local composed video</option>
|
|
7406
|
-
<option value="separate">
|
|
7407
|
-
Two local files: desktop + camera
|
|
7408
|
-
</option>
|
|
7409
|
-
</select>
|
|
7410
|
-
<p className="setup-hint">
|
|
7411
|
-
Two-file mode records desktop with audio and a raw rectangular
|
|
7412
|
-
camera video with no audio.
|
|
7413
|
-
</p>
|
|
7414
|
-
</div>
|
|
7415
|
-
|
|
7416
|
-
<div className="setup-section">
|
|
7417
|
-
<div className="setup-toggle-row">
|
|
7418
|
-
<SettingLabel
|
|
7419
|
-
label="Screen region guides"
|
|
7420
|
-
hint="Show private rectangle guides over your screen while recording. They stay out of the saved Clip."
|
|
7421
|
-
/>
|
|
7422
|
-
<Switch
|
|
7423
|
-
on={regionGuides.enabled}
|
|
7424
|
-
onChange={setRegionGuidesEnabled}
|
|
7425
|
-
label="Show screen region guides while recording"
|
|
7426
|
-
/>
|
|
7427
|
-
</div>
|
|
7428
|
-
{regionGuides.enabled && (
|
|
7429
|
-
<div className="setup-toggle-row">
|
|
7430
|
-
<SettingLabel
|
|
7431
|
-
label="Keep guides on screen even when not recording"
|
|
7432
|
-
hint="Stays visible at all times so you can frame recordings made with other tools like OBS or QuickTime. Still excluded from every screen recording."
|
|
7038
|
+
<option value="off">Cloud Clips (default)</option>
|
|
7039
|
+
<option value="composed">One local composed video</option>
|
|
7040
|
+
<option value="separate">
|
|
7041
|
+
Two local files: desktop + camera
|
|
7042
|
+
</option>
|
|
7043
|
+
</select>
|
|
7044
|
+
}
|
|
7045
|
+
/>
|
|
7046
|
+
<DesktopSettingsRow
|
|
7047
|
+
label="Screen region guides"
|
|
7048
|
+
description={
|
|
7049
|
+
regionGuideCount === 0
|
|
7050
|
+
? "Frame recordings with private guides that never enter the saved Clip."
|
|
7051
|
+
: `${regionGuideCount} ${regionGuideCount === 1 ? "rectangle" : "rectangles"} saved.`
|
|
7052
|
+
}
|
|
7053
|
+
extraClassName="settings-row-extra--inline"
|
|
7054
|
+
control={
|
|
7055
|
+
<div className="settings-control-group">
|
|
7056
|
+
<Switch
|
|
7057
|
+
on={regionGuides.enabled}
|
|
7058
|
+
onChange={setRegionGuidesEnabled}
|
|
7059
|
+
label="Show screen region guides while recording"
|
|
7433
7060
|
/>
|
|
7061
|
+
<button
|
|
7062
|
+
type="button"
|
|
7063
|
+
className="secondary"
|
|
7064
|
+
onClick={openRegionGuideEditor}
|
|
7065
|
+
>
|
|
7066
|
+
<IconPencil size={15} stroke={1.9} />
|
|
7067
|
+
Edit preset
|
|
7068
|
+
</button>
|
|
7069
|
+
</div>
|
|
7070
|
+
}
|
|
7071
|
+
>
|
|
7072
|
+
{regionGuides.enabled ? (
|
|
7073
|
+
<>
|
|
7074
|
+
<span>Keep guides visible when you are not recording.</span>
|
|
7434
7075
|
<Switch
|
|
7435
7076
|
on={regionGuidesAlwaysVisible}
|
|
7436
7077
|
onChange={setRegionGuidesAlwaysVisible}
|
|
7437
7078
|
label="Keep region guides on screen even when not recording"
|
|
7438
7079
|
/>
|
|
7439
|
-
|
|
7440
|
-
|
|
7441
|
-
|
|
7442
|
-
|
|
7443
|
-
|
|
7444
|
-
|
|
7445
|
-
|
|
7446
|
-
|
|
7447
|
-
|
|
7448
|
-
|
|
7449
|
-
|
|
7450
|
-
|
|
7451
|
-
|
|
7452
|
-
className="secondary"
|
|
7453
|
-
onClick={clearRegionGuidePreset}
|
|
7454
|
-
disabled={regionGuideCount === 0}
|
|
7455
|
-
>
|
|
7456
|
-
<IconTrash size={15} stroke={1.9} />
|
|
7457
|
-
Clear
|
|
7458
|
-
</button>
|
|
7459
|
-
</div>
|
|
7460
|
-
<p className="setup-hint">
|
|
7461
|
-
{regionGuideCount === 0
|
|
7462
|
-
? "No guide preset saved yet."
|
|
7463
|
-
: `${regionGuideCount} ${regionGuideCount === 1 ? "rectangle" : "rectangles"} saved.`}
|
|
7464
|
-
</p>
|
|
7465
|
-
</div>
|
|
7466
|
-
</div>
|
|
7467
|
-
</details>
|
|
7468
|
-
|
|
7469
|
-
<div className="setup-section">
|
|
7470
|
-
<SettingLabel
|
|
7471
|
-
label="Start/stop recording shortcut"
|
|
7472
|
-
hint="Optional global shortcut for starting full-screen, region, or camera recordings and stopping the active recording."
|
|
7473
|
-
/>
|
|
7474
|
-
<ShortcutRecorder
|
|
7475
|
-
value={recordCustomShortcut}
|
|
7476
|
-
placeholder="Record shortcut"
|
|
7477
|
-
onChange={onRecordCustomShortcutChange}
|
|
7478
|
-
/>
|
|
7479
|
-
<p className="setup-hint">
|
|
7480
|
-
Window and browser-tab sources still open Clips first so the picker
|
|
7481
|
-
can use a click.
|
|
7482
|
-
</p>
|
|
7483
|
-
</div>
|
|
7080
|
+
{regionGuideCount > 0 ? (
|
|
7081
|
+
<button
|
|
7082
|
+
type="button"
|
|
7083
|
+
className="settings-text-button"
|
|
7084
|
+
onClick={clearRegionGuidePreset}
|
|
7085
|
+
>
|
|
7086
|
+
Clear saved preset
|
|
7087
|
+
</button>
|
|
7088
|
+
) : null}
|
|
7089
|
+
</>
|
|
7090
|
+
) : null}
|
|
7091
|
+
</DesktopSettingsRow>
|
|
7092
|
+
</DesktopSettingsGroup>
|
|
7484
7093
|
|
|
7485
|
-
|
|
7486
|
-
|
|
7487
|
-
|
|
7488
|
-
|
|
7489
|
-
|
|
7490
|
-
|
|
7491
|
-
|
|
7492
|
-
|
|
7493
|
-
|
|
7494
|
-
|
|
7495
|
-
|
|
7496
|
-
|
|
7497
|
-
|
|
7498
|
-
|
|
7499
|
-
|
|
7500
|
-
<
|
|
7501
|
-
|
|
7094
|
+
<DesktopSettingsGroup
|
|
7095
|
+
title="Keyboard shortcuts"
|
|
7096
|
+
description="Optional global shortcuts for recording and opening the tray."
|
|
7097
|
+
>
|
|
7098
|
+
<DesktopSettingsRow
|
|
7099
|
+
label="Start/stop recording"
|
|
7100
|
+
description="Start full-screen, region, or camera recordings and stop the active recording."
|
|
7101
|
+
control={
|
|
7102
|
+
<ShortcutRecorder
|
|
7103
|
+
value={recordCustomShortcut}
|
|
7104
|
+
placeholder="Record shortcut"
|
|
7105
|
+
onChange={onRecordCustomShortcutChange}
|
|
7106
|
+
/>
|
|
7107
|
+
}
|
|
7108
|
+
/>
|
|
7109
|
+
<DesktopSettingsRow
|
|
7110
|
+
label="Open Clips"
|
|
7111
|
+
description="Open the tray popover; Cmd+Shift+L remains available."
|
|
7112
|
+
control={
|
|
7113
|
+
<ShortcutRecorder
|
|
7114
|
+
value={popoverCustomShortcut}
|
|
7115
|
+
placeholder="Record shortcut"
|
|
7116
|
+
onChange={onPopoverCustomShortcutChange}
|
|
7117
|
+
/>
|
|
7118
|
+
}
|
|
7119
|
+
>
|
|
7120
|
+
<>
|
|
7121
|
+
<span>Leave this empty to use only Cmd+Shift+L.</span>
|
|
7122
|
+
{shortcutRegistrationError ? (
|
|
7123
|
+
<span className="setup-warning">
|
|
7124
|
+
{shortcutRegistrationError}
|
|
7125
|
+
</span>
|
|
7126
|
+
) : null}
|
|
7127
|
+
</>
|
|
7128
|
+
</DesktopSettingsRow>
|
|
7129
|
+
</DesktopSettingsGroup>
|
|
7502
7130
|
</div>
|
|
7131
|
+
);
|
|
7132
|
+
}
|
|
7503
7133
|
|
|
7504
|
-
|
|
7505
|
-
|
|
7506
|
-
<div className="
|
|
7507
|
-
<
|
|
7508
|
-
|
|
7134
|
+
function renderMeetingSettings() {
|
|
7135
|
+
return (
|
|
7136
|
+
<div className="settings-tab-content">
|
|
7137
|
+
<DesktopSettingsGroup
|
|
7138
|
+
title="Meeting notes"
|
|
7139
|
+
description="Use calendar meetings to show reminders and start live transcription."
|
|
7140
|
+
>
|
|
7141
|
+
<DesktopSettingsRow
|
|
7509
7142
|
label="Meeting notes"
|
|
7510
|
-
|
|
7511
|
-
|
|
7512
|
-
|
|
7513
|
-
|
|
7514
|
-
|
|
7515
|
-
|
|
7143
|
+
description="Show the meeting widget and enable live notes."
|
|
7144
|
+
control={
|
|
7145
|
+
<Switch
|
|
7146
|
+
on={meetingsEnabled}
|
|
7147
|
+
onChange={setMeetingsEnabled}
|
|
7148
|
+
label="Enable meeting notes"
|
|
7149
|
+
/>
|
|
7150
|
+
}
|
|
7516
7151
|
/>
|
|
7517
|
-
|
|
7518
|
-
|
|
7519
|
-
|
|
7520
|
-
|
|
7521
|
-
|
|
7522
|
-
|
|
7523
|
-
|
|
7524
|
-
|
|
7525
|
-
|
|
7526
|
-
|
|
7527
|
-
|
|
7528
|
-
|
|
7529
|
-
|
|
7530
|
-
|
|
7531
|
-
|
|
7532
|
-
|
|
7533
|
-
|
|
7534
|
-
|
|
7535
|
-
|
|
7536
|
-
|
|
7537
|
-
|
|
7538
|
-
|
|
7539
|
-
|
|
7540
|
-
<option value="manual">Manual only</option>
|
|
7541
|
-
</select>
|
|
7542
|
-
<p className="setup-hint">
|
|
7543
|
-
Auto-start still shows the notes pill while transcription is
|
|
7544
|
-
active.
|
|
7545
|
-
</p>
|
|
7546
|
-
</div>
|
|
7547
|
-
|
|
7548
|
-
<div className="setup-section">
|
|
7549
|
-
<div className="setup-toggle-row">
|
|
7550
|
-
<SettingLabel
|
|
7551
|
-
label="Meeting widget"
|
|
7552
|
-
hint="Show the on-screen meeting widget near calendar start times, even when macOS notifications are hidden."
|
|
7152
|
+
{meetingsEnabled ? (
|
|
7153
|
+
<>
|
|
7154
|
+
<DesktopSettingsRow
|
|
7155
|
+
label="Meeting transcription"
|
|
7156
|
+
description="Ask first, start automatically, or wait for a manual start."
|
|
7157
|
+
control={
|
|
7158
|
+
<select
|
|
7159
|
+
id="meeting-transcription-mode"
|
|
7160
|
+
className="setup-select"
|
|
7161
|
+
value={meetingTranscriptionMode}
|
|
7162
|
+
onChange={(event) =>
|
|
7163
|
+
setMeetingTranscriptionMode(
|
|
7164
|
+
event.target.value as MeetingTranscriptionMode,
|
|
7165
|
+
)
|
|
7166
|
+
}
|
|
7167
|
+
>
|
|
7168
|
+
<option value="ask">Ask at meeting time</option>
|
|
7169
|
+
<option value="auto">
|
|
7170
|
+
Auto-start during meeting times
|
|
7171
|
+
</option>
|
|
7172
|
+
<option value="manual">Manual only</option>
|
|
7173
|
+
</select>
|
|
7174
|
+
}
|
|
7553
7175
|
/>
|
|
7554
|
-
<
|
|
7555
|
-
|
|
7556
|
-
|
|
7557
|
-
|
|
7176
|
+
<DesktopSettingsRow
|
|
7177
|
+
label="Meeting widget"
|
|
7178
|
+
description="Show the on-screen meeting widget near calendar start times."
|
|
7179
|
+
control={
|
|
7180
|
+
<Switch
|
|
7181
|
+
on={showMeetingWidgetEnabled}
|
|
7182
|
+
onChange={setShowMeetingWidgetEnabled}
|
|
7183
|
+
label="Show meeting widget"
|
|
7184
|
+
/>
|
|
7185
|
+
}
|
|
7558
7186
|
/>
|
|
7559
|
-
|
|
7560
|
-
|
|
7561
|
-
|
|
7562
|
-
|
|
7563
|
-
|
|
7564
|
-
|
|
7187
|
+
</>
|
|
7188
|
+
) : null}
|
|
7189
|
+
</DesktopSettingsGroup>
|
|
7190
|
+
</div>
|
|
7191
|
+
);
|
|
7192
|
+
}
|
|
7565
7193
|
|
|
7566
|
-
|
|
7567
|
-
|
|
7568
|
-
|
|
7194
|
+
function renderDictationSettings() {
|
|
7195
|
+
return (
|
|
7196
|
+
<div className="settings-tab-content">
|
|
7197
|
+
<DesktopSettingsGroup
|
|
7198
|
+
title="Local transcription"
|
|
7199
|
+
description="Use Whisper for offline transcription without an API key."
|
|
7200
|
+
>
|
|
7201
|
+
<DesktopSettingsRow
|
|
7569
7202
|
label="Whisper model"
|
|
7570
|
-
|
|
7571
|
-
|
|
7572
|
-
|
|
7573
|
-
|
|
7574
|
-
|
|
7575
|
-
|
|
7203
|
+
description="Enable the local model for dictation and meetings."
|
|
7204
|
+
control={
|
|
7205
|
+
<Switch
|
|
7206
|
+
on={whisperModelEnabled}
|
|
7207
|
+
onChange={whisper.setEnabled}
|
|
7208
|
+
label="Enable Whisper model"
|
|
7209
|
+
/>
|
|
7210
|
+
}
|
|
7576
7211
|
/>
|
|
7577
|
-
|
|
7578
|
-
|
|
7579
|
-
|
|
7580
|
-
|
|
7581
|
-
|
|
7582
|
-
|
|
7583
|
-
|
|
7584
|
-
|
|
7585
|
-
|
|
7586
|
-
|
|
7587
|
-
|
|
7588
|
-
|
|
7589
|
-
|
|
7590
|
-
|
|
7591
|
-
|
|
7592
|
-
|
|
7593
|
-
|
|
7594
|
-
|
|
7595
|
-
|
|
7596
|
-
|
|
7597
|
-
|
|
7598
|
-
|
|
7599
|
-
|
|
7600
|
-
|
|
7601
|
-
|
|
7602
|
-
|
|
7603
|
-
|
|
7604
|
-
|
|
7605
|
-
|
|
7606
|
-
|
|
7607
|
-
|
|
7608
|
-
|
|
7609
|
-
|
|
7610
|
-
|
|
7611
|
-
|
|
7612
|
-
|
|
7613
|
-
|
|
7614
|
-
|
|
7615
|
-
|
|
7616
|
-
|
|
7617
|
-
|
|
7618
|
-
|
|
7619
|
-
|
|
7620
|
-
|
|
7621
|
-
|
|
7622
|
-
|
|
7623
|
-
|
|
7624
|
-
|
|
7625
|
-
|
|
7626
|
-
|
|
7627
|
-
|
|
7628
|
-
|
|
7212
|
+
<DesktopSettingsRow
|
|
7213
|
+
label="Model"
|
|
7214
|
+
description={
|
|
7215
|
+
selectedWhisperModel?.description ??
|
|
7216
|
+
"Choose the downloaded model used for offline transcription."
|
|
7217
|
+
}
|
|
7218
|
+
control={
|
|
7219
|
+
<select
|
|
7220
|
+
id="whisper-model"
|
|
7221
|
+
className="setup-select"
|
|
7222
|
+
value={whisperModelId}
|
|
7223
|
+
onChange={(event) => whisper.setModelId(event.target.value)}
|
|
7224
|
+
disabled={
|
|
7225
|
+
whisperModels.length === 0 ||
|
|
7226
|
+
whisperStatus?.state === "downloading"
|
|
7227
|
+
}
|
|
7228
|
+
>
|
|
7229
|
+
{whisperModels.map((model) => (
|
|
7230
|
+
<option key={model.id} value={model.id}>
|
|
7231
|
+
{model.title} · {model.sizeMb} MB — {model.description}
|
|
7232
|
+
</option>
|
|
7233
|
+
))}
|
|
7234
|
+
</select>
|
|
7235
|
+
}
|
|
7236
|
+
>
|
|
7237
|
+
<>
|
|
7238
|
+
<WhisperModelStatusRow
|
|
7239
|
+
status={whisperStatus}
|
|
7240
|
+
enabled={whisperModelEnabled}
|
|
7241
|
+
onDownload={whisper.triggerDownload}
|
|
7242
|
+
/>
|
|
7243
|
+
{deletableModels.length > 0 ? (
|
|
7244
|
+
<div className="whisper-other-models">
|
|
7245
|
+
<p className="setup-hint">Other downloaded models</p>
|
|
7246
|
+
{deletableModels.map((model) => (
|
|
7247
|
+
<div key={model.id} className="whisper-other-model-row">
|
|
7248
|
+
<span className="whisper-other-model-name">
|
|
7249
|
+
{model.title} · {model.sizeMb} MB
|
|
7250
|
+
</span>
|
|
7251
|
+
<button
|
|
7252
|
+
type="button"
|
|
7253
|
+
className="whisper-delete-btn"
|
|
7254
|
+
onClick={() => whisper.deleteModel(model.id)}
|
|
7255
|
+
>
|
|
7256
|
+
<IconTrash size={13} />
|
|
7257
|
+
Delete
|
|
7258
|
+
</button>
|
|
7259
|
+
</div>
|
|
7260
|
+
))}
|
|
7261
|
+
</div>
|
|
7262
|
+
) : null}
|
|
7263
|
+
</>
|
|
7264
|
+
</DesktopSettingsRow>
|
|
7265
|
+
</DesktopSettingsGroup>
|
|
7629
7266
|
|
|
7630
|
-
|
|
7631
|
-
|
|
7632
|
-
|
|
7267
|
+
<DesktopSettingsGroup
|
|
7268
|
+
title="Voice dictation"
|
|
7269
|
+
description="Speak to type anywhere on your Mac."
|
|
7270
|
+
>
|
|
7271
|
+
<DesktopSettingsRow
|
|
7633
7272
|
label="Voice dictation"
|
|
7634
|
-
|
|
7635
|
-
|
|
7636
|
-
|
|
7637
|
-
|
|
7638
|
-
|
|
7639
|
-
|
|
7273
|
+
description="Enable the global dictation shortcut."
|
|
7274
|
+
control={
|
|
7275
|
+
<Switch
|
|
7276
|
+
on={voiceEnabled}
|
|
7277
|
+
onChange={setVoiceEnabled}
|
|
7278
|
+
label="Enable voice dictation"
|
|
7279
|
+
/>
|
|
7280
|
+
}
|
|
7640
7281
|
/>
|
|
7641
|
-
|
|
7642
|
-
|
|
7643
|
-
|
|
7644
|
-
|
|
7645
|
-
|
|
7646
|
-
|
|
7647
|
-
|
|
7648
|
-
|
|
7649
|
-
|
|
7650
|
-
|
|
7651
|
-
|
|
7652
|
-
|
|
7653
|
-
|
|
7654
|
-
|
|
7655
|
-
|
|
7656
|
-
|
|
7657
|
-
|
|
7658
|
-
|
|
7659
|
-
|
|
7660
|
-
|
|
7661
|
-
|
|
7662
|
-
|
|
7663
|
-
|
|
7664
|
-
|
|
7665
|
-
|
|
7666
|
-
|
|
7667
|
-
|
|
7668
|
-
|
|
7669
|
-
|
|
7670
|
-
|
|
7671
|
-
|
|
7672
|
-
|
|
7673
|
-
|
|
7674
|
-
|
|
7675
|
-
|
|
7676
|
-
|
|
7677
|
-
|
|
7678
|
-
) : null}
|
|
7679
|
-
{selectedMode === "builder" && !providerStatus?.builder ? (
|
|
7680
|
-
<button
|
|
7681
|
-
type="button"
|
|
7682
|
-
className="secondary"
|
|
7683
|
-
onClick={connectBuilder}
|
|
7282
|
+
{voiceEnabled ? (
|
|
7283
|
+
<>
|
|
7284
|
+
<DesktopSettingsRow
|
|
7285
|
+
label="Provider"
|
|
7286
|
+
description={providerHint[selectedMode]}
|
|
7287
|
+
control={
|
|
7288
|
+
<div className="settings-control-group">
|
|
7289
|
+
<select
|
|
7290
|
+
id="voice-provider"
|
|
7291
|
+
className="setup-select"
|
|
7292
|
+
value={selectedMode}
|
|
7293
|
+
onChange={(event) =>
|
|
7294
|
+
selectProviderMode(
|
|
7295
|
+
event.target.value as VoiceProviderMode,
|
|
7296
|
+
)
|
|
7297
|
+
}
|
|
7298
|
+
>
|
|
7299
|
+
<option value="native">On-device (free, fast)</option>
|
|
7300
|
+
<option value="whisper" disabled={!whisperModelEnabled}>
|
|
7301
|
+
{whisperModelEnabled
|
|
7302
|
+
? "Local Whisper (offline AI)"
|
|
7303
|
+
: "Local Whisper — enable Whisper model first"}
|
|
7304
|
+
</option>
|
|
7305
|
+
<option value="builder">Builder.io</option>
|
|
7306
|
+
<option value="byok">Add your own key</option>
|
|
7307
|
+
</select>
|
|
7308
|
+
{selectedMode === "builder" && !providerStatus?.builder ? (
|
|
7309
|
+
<button
|
|
7310
|
+
type="button"
|
|
7311
|
+
className="secondary"
|
|
7312
|
+
onClick={connectBuilder}
|
|
7313
|
+
>
|
|
7314
|
+
Use Builder.io
|
|
7315
|
+
</button>
|
|
7316
|
+
) : null}
|
|
7317
|
+
</div>
|
|
7318
|
+
}
|
|
7684
7319
|
>
|
|
7685
|
-
|
|
7686
|
-
|
|
7687
|
-
|
|
7688
|
-
|
|
7320
|
+
{providerWarning ||
|
|
7321
|
+
(selectedMode === "whisper" && !whisperModelEnabled) ? (
|
|
7322
|
+
<p className="setup-warning">
|
|
7323
|
+
{providerWarning ??
|
|
7324
|
+
"Whisper model is disabled. Enable it in Local transcription."}
|
|
7325
|
+
</p>
|
|
7326
|
+
) : null}
|
|
7327
|
+
</DesktopSettingsRow>
|
|
7328
|
+
|
|
7329
|
+
{selectedMode === "byok" ? (
|
|
7330
|
+
<>
|
|
7331
|
+
<DesktopSettingsRow
|
|
7332
|
+
label="Key provider"
|
|
7333
|
+
description="Choose the provider key used for cleanup."
|
|
7334
|
+
control={
|
|
7335
|
+
<select
|
|
7336
|
+
id="voice-byok-provider"
|
|
7337
|
+
className="setup-select"
|
|
7338
|
+
value={byokProvider}
|
|
7339
|
+
onChange={(event) => {
|
|
7340
|
+
setApiKeyMessage(null);
|
|
7341
|
+
onVoiceProviderChange(
|
|
7342
|
+
event.target.value as ByokVoiceProvider,
|
|
7343
|
+
);
|
|
7344
|
+
}}
|
|
7345
|
+
>
|
|
7346
|
+
<option value="gemini">
|
|
7347
|
+
Google Gemini (recommended)
|
|
7348
|
+
</option>
|
|
7349
|
+
<option value="groq">Groq</option>
|
|
7350
|
+
</select>
|
|
7351
|
+
}
|
|
7352
|
+
/>
|
|
7353
|
+
<DesktopSettingsRow
|
|
7354
|
+
label="API key"
|
|
7355
|
+
description={
|
|
7356
|
+
providerStatus?.[byokProvider]
|
|
7357
|
+
? `${labelForByokProvider(byokProvider)} key is set. Paste a new key to rotate it.`
|
|
7358
|
+
: `Save a ${keyForByokProvider(byokProvider)} key for cleanup.`
|
|
7359
|
+
}
|
|
7360
|
+
control={
|
|
7361
|
+
<div className="settings-control-group settings-control-group--wide">
|
|
7362
|
+
<input
|
|
7363
|
+
type="password"
|
|
7364
|
+
value={apiKeyValue}
|
|
7365
|
+
onChange={(event) =>
|
|
7366
|
+
setApiKeyValue(event.target.value)
|
|
7367
|
+
}
|
|
7368
|
+
onKeyDown={(event) => {
|
|
7369
|
+
if (event.key === "Enter") {
|
|
7370
|
+
event.preventDefault();
|
|
7371
|
+
saveApiKey();
|
|
7372
|
+
}
|
|
7373
|
+
}}
|
|
7374
|
+
placeholder={
|
|
7375
|
+
providerStatus?.[byokProvider]
|
|
7376
|
+
? "Paste to rotate"
|
|
7377
|
+
: `Paste ${keyForByokProvider(byokProvider)}`
|
|
7378
|
+
}
|
|
7379
|
+
className="settings-url-input"
|
|
7380
|
+
/>
|
|
7381
|
+
<button
|
|
7382
|
+
type="button"
|
|
7383
|
+
className="secondary"
|
|
7384
|
+
onClick={saveApiKey}
|
|
7385
|
+
disabled={!apiKeyValue.trim() || apiKeySaving}
|
|
7386
|
+
>
|
|
7387
|
+
{apiKeySaving
|
|
7388
|
+
? "Saving..."
|
|
7389
|
+
: providerStatus?.[byokProvider]
|
|
7390
|
+
? "Rotate"
|
|
7391
|
+
: "Save"}
|
|
7392
|
+
</button>
|
|
7393
|
+
</div>
|
|
7394
|
+
}
|
|
7395
|
+
>
|
|
7396
|
+
{apiKeyMessage ? (
|
|
7397
|
+
<p
|
|
7398
|
+
className={
|
|
7399
|
+
apiKeyMessage.kind === "ok"
|
|
7400
|
+
? "setup-success"
|
|
7401
|
+
: "setup-warning"
|
|
7402
|
+
}
|
|
7403
|
+
>
|
|
7404
|
+
{apiKeyMessage.text}
|
|
7405
|
+
</p>
|
|
7406
|
+
) : null}
|
|
7407
|
+
</DesktopSettingsRow>
|
|
7408
|
+
</>
|
|
7409
|
+
) : null}
|
|
7689
7410
|
|
|
7690
|
-
|
|
7691
|
-
|
|
7692
|
-
|
|
7693
|
-
|
|
7694
|
-
|
|
7695
|
-
|
|
7696
|
-
|
|
7697
|
-
|
|
7698
|
-
|
|
7699
|
-
|
|
7700
|
-
|
|
7701
|
-
|
|
7702
|
-
|
|
7703
|
-
|
|
7704
|
-
|
|
7705
|
-
|
|
7706
|
-
|
|
7707
|
-
|
|
7708
|
-
|
|
7709
|
-
|
|
7710
|
-
|
|
7711
|
-
|
|
7712
|
-
|
|
7713
|
-
|
|
7714
|
-
|
|
7715
|
-
|
|
7716
|
-
|
|
7717
|
-
|
|
7718
|
-
|
|
7719
|
-
|
|
7411
|
+
{selectedMode !== "native" && selectedMode !== "whisper" ? (
|
|
7412
|
+
<DesktopSettingsRow
|
|
7413
|
+
label="Custom instructions"
|
|
7414
|
+
description="Guide casing, names, punctuation, and terms of art for LLM cleanup."
|
|
7415
|
+
stacked
|
|
7416
|
+
control={
|
|
7417
|
+
<textarea
|
|
7418
|
+
id="voice-instructions"
|
|
7419
|
+
className="setup-textarea"
|
|
7420
|
+
rows={4}
|
|
7421
|
+
value={voiceInstructions}
|
|
7422
|
+
onChange={(event) =>
|
|
7423
|
+
onVoiceInstructionsChange(event.target.value)
|
|
7424
|
+
}
|
|
7425
|
+
placeholder="Example: keep it casual and preserve technical terms exactly."
|
|
7426
|
+
/>
|
|
7427
|
+
}
|
|
7428
|
+
/>
|
|
7429
|
+
) : null}
|
|
7430
|
+
|
|
7431
|
+
<DesktopSettingsRow
|
|
7432
|
+
label="Shortcut"
|
|
7433
|
+
description={shortcutHint[voiceShortcut]}
|
|
7434
|
+
control={
|
|
7435
|
+
<select
|
|
7436
|
+
id="voice-shortcut"
|
|
7437
|
+
className="setup-select"
|
|
7438
|
+
value={voiceShortcut}
|
|
7439
|
+
onChange={(event) =>
|
|
7440
|
+
onVoiceShortcutChange(
|
|
7441
|
+
event.target.value as VoiceShortcutPreference,
|
|
7442
|
+
)
|
|
7720
7443
|
}
|
|
7721
|
-
|
|
7722
|
-
|
|
7723
|
-
|
|
7724
|
-
|
|
7725
|
-
|
|
7444
|
+
>
|
|
7445
|
+
<option value="cmd-shift-space">Cmd+Shift+Space</option>
|
|
7446
|
+
<option value="ctrl-shift-space">Ctrl+Shift+Space</option>
|
|
7447
|
+
<option value="custom">Custom shortcut</option>
|
|
7448
|
+
<option value="fn">
|
|
7449
|
+
Fn (globe, needs Input Monitoring)
|
|
7450
|
+
</option>
|
|
7451
|
+
<option value="both">All shortcuts (includes Fn)</option>
|
|
7452
|
+
</select>
|
|
7453
|
+
}
|
|
7454
|
+
/>
|
|
7455
|
+
{voiceShortcut === "custom" ? (
|
|
7456
|
+
<DesktopSettingsRow
|
|
7457
|
+
label="Custom shortcut"
|
|
7458
|
+
description="Record the key combination used for dictation."
|
|
7459
|
+
control={
|
|
7460
|
+
<ShortcutRecorder
|
|
7461
|
+
value={voiceCustomShortcut}
|
|
7462
|
+
placeholder="Record voice shortcut"
|
|
7463
|
+
onChange={onVoiceCustomShortcutChange}
|
|
7464
|
+
/>
|
|
7726
7465
|
}
|
|
7727
|
-
className="setup-key-input"
|
|
7728
7466
|
/>
|
|
7729
|
-
<button
|
|
7730
|
-
type="button"
|
|
7731
|
-
className="secondary setup-key-save"
|
|
7732
|
-
onClick={saveApiKey}
|
|
7733
|
-
disabled={!apiKeyValue.trim() || apiKeySaving}
|
|
7734
|
-
>
|
|
7735
|
-
{apiKeySaving
|
|
7736
|
-
? "Saving..."
|
|
7737
|
-
: providerStatus?.[byokProvider]
|
|
7738
|
-
? "Rotate"
|
|
7739
|
-
: "Save"}
|
|
7740
|
-
</button>
|
|
7741
|
-
</div>
|
|
7742
|
-
{providerStatus?.[byokProvider] ? (
|
|
7743
|
-
<p className="setup-hint">
|
|
7744
|
-
{labelForByokProvider(byokProvider)} key is set.
|
|
7745
|
-
</p>
|
|
7746
7467
|
) : null}
|
|
7747
|
-
{
|
|
7748
|
-
<
|
|
7749
|
-
|
|
7750
|
-
|
|
7751
|
-
|
|
7752
|
-
|
|
7468
|
+
{isMacPlatform() && fnShortcutSelected ? (
|
|
7469
|
+
<DesktopSettingsRow
|
|
7470
|
+
label="Input Monitoring"
|
|
7471
|
+
description="Required when the Fn / globe shortcut is enabled."
|
|
7472
|
+
control={
|
|
7473
|
+
<button
|
|
7474
|
+
type="button"
|
|
7475
|
+
className="secondary"
|
|
7476
|
+
onClick={() => openPrivacySettings("input-monitoring")}
|
|
7477
|
+
>
|
|
7478
|
+
Open settings
|
|
7479
|
+
</button>
|
|
7753
7480
|
}
|
|
7754
|
-
|
|
7755
|
-
{apiKeyMessage.text}
|
|
7756
|
-
</p>
|
|
7481
|
+
/>
|
|
7757
7482
|
) : null}
|
|
7758
|
-
|
|
7759
|
-
|
|
7760
|
-
|
|
7761
|
-
|
|
7762
|
-
|
|
7763
|
-
|
|
7764
|
-
|
|
7765
|
-
|
|
7766
|
-
|
|
7767
|
-
|
|
7768
|
-
|
|
7769
|
-
|
|
7770
|
-
|
|
7771
|
-
|
|
7772
|
-
|
|
7773
|
-
|
|
7774
|
-
|
|
7483
|
+
<DesktopSettingsRow
|
|
7484
|
+
label="Mode"
|
|
7485
|
+
description={modeHint[voiceMode]}
|
|
7486
|
+
control={
|
|
7487
|
+
<select
|
|
7488
|
+
id="voice-mode"
|
|
7489
|
+
className="setup-select"
|
|
7490
|
+
value={voiceMode}
|
|
7491
|
+
onChange={(event) =>
|
|
7492
|
+
onVoiceModeChange(event.target.value as VoiceMode)
|
|
7493
|
+
}
|
|
7494
|
+
>
|
|
7495
|
+
<option value="push-to-talk">Hold to dictate</option>
|
|
7496
|
+
<option value="toggle">
|
|
7497
|
+
Press to start, press to stop
|
|
7498
|
+
</option>
|
|
7499
|
+
</select>
|
|
7775
7500
|
}
|
|
7776
|
-
placeholder="Example: keep it casual, spell Builder.io with a dot, and preserve technical terms exactly."
|
|
7777
7501
|
/>
|
|
7778
|
-
|
|
7779
|
-
These instructions are sent only when an LLM-based provider is
|
|
7780
|
-
selected.
|
|
7781
|
-
</p>
|
|
7782
|
-
</div>
|
|
7502
|
+
</>
|
|
7783
7503
|
) : null}
|
|
7504
|
+
</DesktopSettingsGroup>
|
|
7505
|
+
</div>
|
|
7506
|
+
);
|
|
7507
|
+
}
|
|
7784
7508
|
|
|
7785
|
-
|
|
7786
|
-
|
|
7787
|
-
|
|
7788
|
-
|
|
7789
|
-
|
|
7790
|
-
|
|
7791
|
-
|
|
7792
|
-
|
|
7793
|
-
|
|
7794
|
-
|
|
7795
|
-
|
|
7796
|
-
|
|
7797
|
-
|
|
7798
|
-
|
|
7799
|
-
|
|
7509
|
+
const settingsTabs: Array<{
|
|
7510
|
+
id: SettingsTabId;
|
|
7511
|
+
label: string;
|
|
7512
|
+
group: string;
|
|
7513
|
+
}> = [
|
|
7514
|
+
{ id: "general", label: "General", group: "Personal" },
|
|
7515
|
+
{ id: "recording", label: "Recording", group: "Personal" },
|
|
7516
|
+
{ id: "meetings", label: "Meetings", group: "Workflow" },
|
|
7517
|
+
{ id: "dictation", label: "Dictation", group: "Workflow" },
|
|
7518
|
+
];
|
|
7519
|
+
const activeSettingsTab =
|
|
7520
|
+
settingsTabs.find((tab) => tab.id === settingsTab) ?? settingsTabs[0];
|
|
7521
|
+
|
|
7522
|
+
function renderSettingsTab() {
|
|
7523
|
+
switch (settingsTab) {
|
|
7524
|
+
case "recording":
|
|
7525
|
+
return renderRecordingSettings();
|
|
7526
|
+
case "meetings":
|
|
7527
|
+
return renderMeetingSettings();
|
|
7528
|
+
case "dictation":
|
|
7529
|
+
return renderDictationSettings();
|
|
7530
|
+
case "general":
|
|
7531
|
+
default:
|
|
7532
|
+
return renderGeneralSettings();
|
|
7533
|
+
}
|
|
7534
|
+
}
|
|
7535
|
+
|
|
7536
|
+
return (
|
|
7537
|
+
<div className="setup settings-page">
|
|
7538
|
+
<div
|
|
7539
|
+
className="setup-header settings-page-header"
|
|
7540
|
+
onMouseDown={handlePopoverHeaderMouseDown}
|
|
7541
|
+
>
|
|
7542
|
+
{onCancel ? (
|
|
7543
|
+
<button
|
|
7544
|
+
type="button"
|
|
7545
|
+
className="setup-back"
|
|
7546
|
+
onClick={onCancel}
|
|
7547
|
+
aria-label="Back"
|
|
7548
|
+
>
|
|
7549
|
+
<IconArrowLeft size={18} stroke={1.75} />
|
|
7550
|
+
</button>
|
|
7551
|
+
) : null}
|
|
7552
|
+
<div className="settings-page-title">
|
|
7553
|
+
<h2>Settings</h2>
|
|
7554
|
+
<p>Clips desktop preferences</p>
|
|
7555
|
+
</div>
|
|
7556
|
+
</div>
|
|
7557
|
+
|
|
7558
|
+
<div className="settings-page-layout">
|
|
7559
|
+
<nav className="settings-nav" aria-label="Settings sections">
|
|
7560
|
+
{settingsTabs.map((tab, index) => (
|
|
7561
|
+
<div
|
|
7562
|
+
key={tab.id}
|
|
7563
|
+
className={`settings-nav-group ${
|
|
7564
|
+
index === 0 || settingsTabs[index - 1]?.group !== tab.group
|
|
7565
|
+
? "settings-nav-group--start"
|
|
7566
|
+
: ""
|
|
7567
|
+
}`}
|
|
7800
7568
|
>
|
|
7801
|
-
|
|
7802
|
-
|
|
7803
|
-
|
|
7804
|
-
<option value="fn">Fn (globe, needs Input Monitoring)</option>
|
|
7805
|
-
<option value="both">All shortcuts (includes Fn)</option>
|
|
7806
|
-
</select>
|
|
7807
|
-
{voiceShortcut === "custom" ? (
|
|
7808
|
-
<ShortcutRecorder
|
|
7809
|
-
value={voiceCustomShortcut}
|
|
7810
|
-
placeholder="Record voice shortcut"
|
|
7811
|
-
onChange={onVoiceCustomShortcutChange}
|
|
7812
|
-
/>
|
|
7813
|
-
) : null}
|
|
7814
|
-
<p className="setup-hint">{shortcutHint[voiceShortcut]}</p>
|
|
7815
|
-
{isMacPlatform() && fnShortcutSelected ? (
|
|
7569
|
+
{index === 0 || settingsTabs[index - 1]?.group !== tab.group ? (
|
|
7570
|
+
<div className="settings-nav-group-label">{tab.group}</div>
|
|
7571
|
+
) : null}
|
|
7816
7572
|
<button
|
|
7817
7573
|
type="button"
|
|
7818
|
-
className="
|
|
7819
|
-
|
|
7574
|
+
className={`settings-nav-button ${settingsTab === tab.id ? "is-active" : ""}`}
|
|
7575
|
+
aria-current={settingsTab === tab.id ? "page" : undefined}
|
|
7576
|
+
onClick={() => setSettingsTab(tab.id)}
|
|
7820
7577
|
>
|
|
7821
|
-
|
|
7578
|
+
{tab.label}
|
|
7822
7579
|
</button>
|
|
7823
|
-
|
|
7824
|
-
|
|
7825
|
-
|
|
7826
|
-
|
|
7827
|
-
|
|
7828
|
-
|
|
7829
|
-
|
|
7830
|
-
|
|
7831
|
-
|
|
7832
|
-
|
|
7833
|
-
|
|
7834
|
-
|
|
7835
|
-
|
|
7836
|
-
|
|
7837
|
-
|
|
7838
|
-
}
|
|
7839
|
-
>
|
|
7840
|
-
<option value="push-to-talk">Hold to dictate</option>
|
|
7841
|
-
<option value="toggle">Press to start, press to stop</option>
|
|
7842
|
-
</select>
|
|
7843
|
-
<p className="setup-hint">{modeHint[voiceMode]}</p>
|
|
7580
|
+
</div>
|
|
7581
|
+
))}
|
|
7582
|
+
</nav>
|
|
7583
|
+
<main className="settings-page-content" tabIndex={-1}>
|
|
7584
|
+
<div className="settings-tab-heading">
|
|
7585
|
+
<h3>{activeSettingsTab.label}</h3>
|
|
7586
|
+
<p>
|
|
7587
|
+
{settingsTab === "general"
|
|
7588
|
+
? "Connection, app behavior, updates, and diagnostics."
|
|
7589
|
+
: settingsTab === "recording"
|
|
7590
|
+
? "Capture behavior, permissions, drafts, and shortcuts."
|
|
7591
|
+
: settingsTab === "meetings"
|
|
7592
|
+
? "Meeting reminders and live transcription behavior."
|
|
7593
|
+
: "Local transcription and voice dictation controls."}
|
|
7594
|
+
</p>
|
|
7844
7595
|
</div>
|
|
7845
|
-
|
|
7846
|
-
|
|
7596
|
+
{renderSettingsTab()}
|
|
7597
|
+
</main>
|
|
7598
|
+
</div>
|
|
7599
|
+
</div>
|
|
7600
|
+
);
|
|
7601
|
+
}
|
|
7847
7602
|
|
|
7848
|
-
|
|
7603
|
+
function DesktopSettingsGroup({
|
|
7604
|
+
title,
|
|
7605
|
+
description,
|
|
7606
|
+
children,
|
|
7607
|
+
}: {
|
|
7608
|
+
title: string;
|
|
7609
|
+
description?: string;
|
|
7610
|
+
children: ReactNode;
|
|
7611
|
+
}) {
|
|
7612
|
+
return (
|
|
7613
|
+
<section className="settings-group">
|
|
7614
|
+
<header className="settings-group-header">
|
|
7615
|
+
<h4>{title}</h4>
|
|
7616
|
+
{description ? <p>{description}</p> : null}
|
|
7617
|
+
</header>
|
|
7618
|
+
<div className="settings-group-body">{children}</div>
|
|
7619
|
+
</section>
|
|
7620
|
+
);
|
|
7621
|
+
}
|
|
7849
7622
|
|
|
7850
|
-
|
|
7851
|
-
|
|
7852
|
-
|
|
7853
|
-
|
|
7854
|
-
|
|
7855
|
-
|
|
7856
|
-
|
|
7857
|
-
|
|
7858
|
-
|
|
7859
|
-
|
|
7860
|
-
|
|
7861
|
-
|
|
7862
|
-
|
|
7863
|
-
|
|
7623
|
+
function DesktopSettingsRow({
|
|
7624
|
+
label,
|
|
7625
|
+
description,
|
|
7626
|
+
control,
|
|
7627
|
+
children,
|
|
7628
|
+
extraClassName,
|
|
7629
|
+
stacked = false,
|
|
7630
|
+
}: {
|
|
7631
|
+
label: string;
|
|
7632
|
+
description: ReactNode;
|
|
7633
|
+
control?: ReactNode;
|
|
7634
|
+
children?: ReactNode;
|
|
7635
|
+
extraClassName?: string;
|
|
7636
|
+
stacked?: boolean;
|
|
7637
|
+
}) {
|
|
7638
|
+
return (
|
|
7639
|
+
<div className={`settings-row ${stacked ? "settings-row--stacked" : ""}`}>
|
|
7640
|
+
<div className="settings-row-copy">
|
|
7641
|
+
<strong>{label}</strong>
|
|
7642
|
+
<span>{description}</span>
|
|
7864
7643
|
</div>
|
|
7865
|
-
{
|
|
7866
|
-
|
|
7867
|
-
|
|
7868
|
-
|
|
7869
|
-
type="button"
|
|
7870
|
-
className="link-button"
|
|
7871
|
-
onClick={onSignOut}
|
|
7872
|
-
style={{ background: "transparent", border: "none" }}
|
|
7873
|
-
>
|
|
7874
|
-
Sign out
|
|
7875
|
-
</button>
|
|
7644
|
+
{control ? <div className="settings-row-control">{control}</div> : null}
|
|
7645
|
+
{children ? (
|
|
7646
|
+
<div className={`settings-row-extra ${extraClassName ?? ""}`}>
|
|
7647
|
+
{children}
|
|
7876
7648
|
</div>
|
|
7877
7649
|
) : null}
|
|
7878
7650
|
</div>
|