@agent-native/core 0.149.5 → 0.151.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +1 -1
- package/corpus/templates/analytics/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/analytics/.agents/skills/frontend-design/SKILL.md +5 -0
- package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +83 -45
- package/corpus/templates/analytics/changelog/2026-08-11-analytics-dashboards-keep-the-selected-tab-when-reopened.md +6 -0
- package/corpus/templates/assets/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/assets/.agents/skills/frontend-design/SKILL.md +5 -0
- package/corpus/templates/brain/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/brain/.agents/skills/frontend-design/SKILL.md +5 -0
- package/corpus/templates/calendar/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/calendar/.agents/skills/frontend-design/SKILL.md +5 -0
- package/corpus/templates/chat/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/chat/.agents/skills/frontend-design/SKILL.md +5 -0
- package/corpus/templates/clips/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/clips/.agents/skills/frontend-design/SKILL.md +5 -0
- package/corpus/templates/clips/changelog/2026-08-11-clips-recovers-from-transient-updates-and-avoids-macos-priva.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +32 -176
- package/corpus/templates/clips/desktop/src/lib/updater.ts +52 -37
- package/corpus/templates/clips/desktop/src/styles.css +1 -166
- package/corpus/templates/clips/desktop/src-tauri/Cargo.toml +2 -0
- package/corpus/templates/clips/desktop/src-tauri/Info.plist +4 -0
- package/corpus/templates/clips/desktop/src-tauri/src/eventkit.rs +28 -1
- package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +2 -0
- package/corpus/templates/clips/desktop/src-tauri/src/native_speech.rs +48 -1
- package/corpus/templates/clips/desktop/src-tauri/src/permission_status.rs +3 -0
- package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +5 -0
- package/corpus/templates/clips/server/plugins/auth.ts +3 -0
- package/corpus/templates/content/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/content/.agents/skills/frontend-design/SKILL.md +5 -0
- package/corpus/templates/content/actions/_document-discovery-query.ts +127 -0
- package/corpus/templates/content/actions/list-documents.ts +189 -198
- package/corpus/templates/content/actions/search-documents.ts +95 -24
- package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +2 -1
- package/corpus/templates/content/app/hooks/use-documents.ts +84 -6
- package/corpus/templates/content/shared/api.ts +10 -0
- package/corpus/templates/design/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/design/.agents/skills/frontend-design/SKILL.md +5 -0
- package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +5 -1
- package/corpus/templates/design/changelog/2026-08-11-design-agent-prompts-copy-successfully-in-the-desktop-app.md +6 -0
- package/corpus/templates/dispatch/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/dispatch/.agents/skills/frontend-design/SKILL.md +5 -0
- package/corpus/templates/dispatch/app/global.css +2 -2
- package/corpus/templates/dispatch/changelog/2026-08-10-desktop-workspace-sign-in.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-08-11-dispatch-no-longer-shows-the-generic-chat-starter-among-defa.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-08-11-integrations-and-scheduled-work-open-as-first-class-electron-pages.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-08-11-sidebar-text-is-slightly-easier-to-read-in-light-and-dark-mo.md +6 -0
- package/corpus/templates/dispatch/server/lib/identity-sso.ts +57 -31
- package/corpus/templates/dispatch/server/plugins/feature-flags.ts +5 -0
- package/corpus/templates/dispatch/server/plugins/identity-sso.ts +82 -33
- package/corpus/templates/dispatch/server/plugins/org-apps-directory.ts +7 -15
- package/corpus/templates/dispatch/server/plugins/setup-dispatch.ts +9 -1
- package/corpus/templates/dispatch/shared/feature-flags.ts +15 -0
- package/corpus/templates/factory/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/factory/.agents/skills/frontend-design/SKILL.md +5 -0
- package/corpus/templates/forms/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/forms/.agents/skills/form-responses/SKILL.md +8 -7
- package/corpus/templates/forms/.agents/skills/frontend-design/SKILL.md +5 -0
- package/corpus/templates/forms/app/hooks/use-forms.ts +8 -1
- package/corpus/templates/forms/changelog/2026-08-11-fixed-public-forms-so-conditional-questions-stay-hidden-unti.md +6 -0
- package/corpus/templates/forms/server/handlers/submissions.ts +2 -1
- package/corpus/templates/forms/server/lib/public-form-ssr.ts +37 -2
- package/corpus/templates/forms/shared/page-url.ts +54 -0
- package/corpus/templates/forms/shared/types.ts +4 -4
- package/corpus/templates/macros/.agents/skills/frontend-design/SKILL.md +5 -0
- package/corpus/templates/mail/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/mail/.agents/skills/frontend-design/SKILL.md +5 -0
- package/corpus/templates/plan/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/plan/.agents/skills/frontend-design/SKILL.md +5 -0
- package/corpus/templates/slides/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/slides/.agents/skills/frontend-design/SKILL.md +5 -0
- package/corpus/templates/slides/actions/add-slide.ts +0 -27
- package/corpus/templates/slides/actions/import-file.ts +28 -9
- package/corpus/templates/slides/actions/view-screen.ts +4 -4
- package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +8 -1
- package/corpus/templates/slides/app/components/editor/AiEditingMarker.tsx +25 -0
- package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +43 -48
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +9 -6
- package/corpus/templates/slides/app/components/editor/GeneratingSlidePreview.tsx +1 -9
- package/corpus/templates/slides/app/components/editor/ImageDropPromptPopover.tsx +2 -0
- package/corpus/templates/slides/app/components/editor/ImageOverlay.tsx +0 -26
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +904 -217
- package/corpus/templates/slides/app/components/editor/canvas-interactions/index.ts +1 -0
- package/corpus/templates/slides/app/components/editor/canvas-interactions/slides-canvas-adapter.ts +11 -0
- package/corpus/templates/slides/app/components/editor/inline-edit-session.ts +16 -0
- package/corpus/templates/slides/app/components/editor/slide-object-interactions.ts +22 -6
- package/corpus/templates/slides/app/components/editor/style-clipboard.ts +61 -0
- package/corpus/templates/slides/app/components/layout/Layout.tsx +26 -2
- package/corpus/templates/slides/app/components/ui/context-menu.tsx +1 -0
- package/corpus/templates/slides/app/context/DeckContext.tsx +75 -25
- package/corpus/templates/slides/app/global.css +1 -1
- package/corpus/templates/slides/app/i18n/en-US.ts +6 -1
- package/corpus/templates/slides/app/lib/create-deck-generation.ts +1 -1
- package/corpus/templates/slides/app/lib/image-drop-to-agent.ts +12 -12
- package/corpus/templates/slides/app/lib/slide-agent-context.ts +52 -0
- package/corpus/templates/slides/app/lib/slide-image-replacement.ts +61 -0
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +55 -116
- package/corpus/templates/slides/app/pages/Index.tsx +28 -3
- package/corpus/templates/slides/changelog/2026-08-11-agent-chat-now-follows-the-current-slide-and-selection.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-11-deck-titles-now-use-slide-content-instead-of-placeholder-filenames.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-11-history-now-opens-from-the-deck-overflow-menu.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-11-images-can-be-dropped-directly-onto-slides-and-resized-or-re.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-11-new-decks-now-transition-directly-to-a-full-page-editor-load.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-11-selected-slide-layers-can-be-nudged-precisely-with-arrow-key.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-11-slides-can-copy-and-paste-element-styles-with-cmd-option-c-v.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-11-slides-shows-one-compact-ai-marker-and-keeps-your-current-sl.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-11-text-edits-preserve-slide-layout-when-no-changes-are-made.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-11-the-style-toolbar-now-uses-the-full-available-width.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-11-undo-and-redo-now-move-one-visible-slide-state-at-a-time.md +6 -0
- package/corpus/templates/slides/server/lib/chat-attachments.ts +6 -2
- package/corpus/templates/slides/shared/deck-title.ts +56 -0
- package/corpus/templates/tasks/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/corpus/templates/tasks/.agents/skills/frontend-design/SKILL.md +5 -0
- package/dist/agent/run-store.js +6 -1
- package/dist/agent/thread-data-builder.js +19 -44
- package/dist/agent/types.d.ts +13 -0
- package/dist/catalog.json +1 -0
- package/dist/cli/create.js +1 -1
- package/dist/client/AssistantChat.js +58 -10
- package/dist/client/FileStorageSetupCard.d.ts +6 -0
- package/dist/client/FileStorageSetupCard.js +18 -0
- package/dist/client/NewWorkspaceAppFlow.js +10 -0
- package/dist/client/agent-chat-adapter.js +87 -11
- package/dist/client/agent-chat.d.ts +1 -1
- package/dist/client/agent-chat.js +8 -3
- package/dist/client/chat/attachment-adapters.js +10 -0
- package/dist/client/chat/message-components.d.ts +4 -1
- package/dist/client/chat/message-components.js +37 -13
- package/dist/client/chat/tool-call-display.js +14 -0
- package/dist/client/chat-first/apps-rail.d.ts +1 -1
- package/dist/client/chat-first/apps-rail.js +6 -6
- package/dist/client/chat-first/copy.js +1 -0
- package/dist/client/chat-first/types.d.ts +1 -0
- package/dist/client/extensions/EmbeddedExtension.js +1 -1
- package/dist/client/extensions/ExtensionViewer.js +1 -1
- package/dist/client/extensions/InlineExtensionFrame.js +1 -1
- package/dist/client/onboarding/FirstRunOnboarding.js +17 -2
- package/dist/client/settings/FileStorageSettingsForm.d.ts +2 -0
- package/dist/client/settings/FileStorageSettingsForm.js +113 -0
- package/dist/client/settings/SettingsPanel.js +2 -1
- package/dist/client/sse-event-processor.js +21 -0
- package/dist/extensions/actions.js +2 -2
- package/dist/extensions/html-shell.js +3 -1
- package/dist/file-upload/pre-upload-attachments.d.ts +15 -10
- package/dist/file-upload/pre-upload-attachments.js +68 -24
- package/dist/file-upload/registry.d.ts +4 -5
- package/dist/file-upload/registry.js +7 -14
- package/dist/file-upload/s3.d.ts +9 -0
- package/dist/file-upload/s3.js +168 -0
- package/dist/file-upload/types.d.ts +3 -3
- package/dist/file-upload/types.js +2 -2
- package/dist/onboarding/app-profile.js +23 -1
- package/dist/onboarding/default-steps.js +69 -0
- package/dist/onboarding/types.d.ts +2 -0
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
- package/dist/provider-api/actions/provider-api.d.ts +8 -8
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +6 -6
- package/dist/server/agent-chat/browser-team-tools.js +20 -0
- package/dist/server/agent-chat-plugin.js +12 -3
- package/dist/server/auth.js +45 -21
- package/dist/server/better-auth-instance.d.ts +2 -0
- package/dist/server/better-auth-instance.js +2 -2
- package/dist/server/core-routes-plugin.d.ts +1 -0
- package/dist/server/core-routes-plugin.js +26 -16
- package/dist/server/identity-sso-store.d.ts +3 -0
- package/dist/server/identity-sso-store.js +45 -0
- package/dist/server/identity-sso.d.ts +20 -10
- package/dist/server/identity-sso.js +79 -31
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +1 -1
- package/dist/templates/chat/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/dist/templates/chat/.agents/skills/frontend-design/SKILL.md +5 -0
- package/dist/templates/default/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/dist/templates/default/.agents/skills/frontend-design/SKILL.md +5 -0
- package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +24 -1
- package/dist/templates/workspace-core/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +6 -2
- package/dist/templates/workspace-core/.agents/skills/extensions/references/api.md +1 -1
- package/dist/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +5 -0
- package/dist/templates/workspace-core/.agents/skills/generative-ui/SKILL.md +3 -1
- package/docs/content/authentication.mdx +27 -0
- package/docs/content/cross-app-sso.mdx +38 -8
- package/docs/content/extensions.mdx +15 -15
- package/docs/content/file-uploads.mdx +10 -12
- package/docs/content/generative-ui.mdx +6 -1
- package/package.json +2 -2
- package/src/templates/chat/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/src/templates/chat/.agents/skills/frontend-design/SKILL.md +5 -0
- package/src/templates/default/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/src/templates/default/.agents/skills/frontend-design/SKILL.md +5 -0
- package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +24 -1
- package/src/templates/workspace-core/.agents/skills/delegate-to-agent/SKILL.md +28 -0
- package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +6 -2
- package/src/templates/workspace-core/.agents/skills/extensions/references/api.md +1 -1
- package/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +5 -0
- package/src/templates/workspace-core/.agents/skills/generative-ui/SKILL.md +3 -1
|
@@ -2,8 +2,6 @@ import {
|
|
|
2
2
|
IconAlertTriangle,
|
|
3
3
|
IconArrowLeft,
|
|
4
4
|
IconCalendarEvent,
|
|
5
|
-
IconChevronDown,
|
|
6
|
-
IconChevronRight,
|
|
7
5
|
IconCircleCheck,
|
|
8
6
|
IconCopy,
|
|
9
7
|
IconDownload,
|
|
@@ -973,10 +971,6 @@ export function App() {
|
|
|
973
971
|
const [rewindSettingsReturnView, setRewindSettingsReturnView] = useState<
|
|
974
972
|
"recorder" | "settings"
|
|
975
973
|
>("recorder");
|
|
976
|
-
const [homeScreenMemoryStatus, setHomeScreenMemoryStatus] =
|
|
977
|
-
useState<ScreenMemoryStatus | null>(null);
|
|
978
|
-
const [homeScreenMemoryBusy, setHomeScreenMemoryBusy] = useState(false);
|
|
979
|
-
const homeScreenMemoryRefreshVersionRef = useRef(0);
|
|
980
974
|
const [rewindAgentPromptCopied, setRewindAgentPromptCopied] = useState(false);
|
|
981
975
|
const [agentHandoff, setAgentHandoff] =
|
|
982
976
|
useState<RewindAgentHandoffRequest | null>(null);
|
|
@@ -1002,7 +996,6 @@ export function App() {
|
|
|
1002
996
|
const [readinessOpen, setReadinessOpen] = useState<boolean>(
|
|
1003
997
|
() => !loadBool(READINESS_REVIEWED_KEY, false),
|
|
1004
998
|
);
|
|
1005
|
-
const [rewindHomeOpen, setRewindHomeOpen] = useState(false);
|
|
1006
999
|
const [recorder, setRecorder] = useState<RecorderHandle | null>(null);
|
|
1007
1000
|
const recordingStartAbortRef = useRef<AbortController | null>(null);
|
|
1008
1001
|
const [recError, setRecError] = useState<string | null>(null);
|
|
@@ -1036,57 +1029,6 @@ export function App() {
|
|
|
1036
1029
|
const isRecording = recorder !== null;
|
|
1037
1030
|
// Whether the popover window is shown; driven by the visibility effect below.
|
|
1038
1031
|
const [popoverVisible, setPopoverVisible] = useState(false);
|
|
1039
|
-
const homeRewindPresentation = getRewindStatusPresentation({
|
|
1040
|
-
status: homeScreenMemoryStatus,
|
|
1041
|
-
config: featureConfig?.screenMemory ?? DEFAULT_SCREEN_MEMORY_CONFIG,
|
|
1042
|
-
clipRecordingActive: isRecording || recordingFlowActive,
|
|
1043
|
-
});
|
|
1044
|
-
const homeRewindOn =
|
|
1045
|
-
featureConfig?.screenMemory?.enabled === true &&
|
|
1046
|
-
featureConfig.screenMemory.paused !== true;
|
|
1047
|
-
const refreshHomeScreenMemoryStatus = useCallback(() => {
|
|
1048
|
-
const version = ++homeScreenMemoryRefreshVersionRef.current;
|
|
1049
|
-
invoke<ScreenMemoryStatus>("screen_memory_status")
|
|
1050
|
-
.then((status) => {
|
|
1051
|
-
if (version === homeScreenMemoryRefreshVersionRef.current) {
|
|
1052
|
-
setHomeScreenMemoryStatus(status);
|
|
1053
|
-
}
|
|
1054
|
-
})
|
|
1055
|
-
.catch(() => {});
|
|
1056
|
-
}, []);
|
|
1057
|
-
useEffect(() => {
|
|
1058
|
-
if (featureConfig?.screenMemory?.enabled !== true) {
|
|
1059
|
-
homeScreenMemoryRefreshVersionRef.current += 1;
|
|
1060
|
-
setHomeScreenMemoryStatus(null);
|
|
1061
|
-
return;
|
|
1062
|
-
}
|
|
1063
|
-
let cancelled = false;
|
|
1064
|
-
const refresh = () => {
|
|
1065
|
-
if (!cancelled) refreshHomeScreenMemoryStatus();
|
|
1066
|
-
};
|
|
1067
|
-
refresh();
|
|
1068
|
-
const timer = window.setInterval(refresh, popoverVisible ? 5_000 : 30_000);
|
|
1069
|
-
let unlisten: (() => void) | undefined;
|
|
1070
|
-
listen("clips:screen-memory-changed", refresh)
|
|
1071
|
-
.then((stopListening) => {
|
|
1072
|
-
if (cancelled) {
|
|
1073
|
-
stopListening();
|
|
1074
|
-
return;
|
|
1075
|
-
}
|
|
1076
|
-
unlisten = stopListening;
|
|
1077
|
-
})
|
|
1078
|
-
.catch(() => {});
|
|
1079
|
-
return () => {
|
|
1080
|
-
cancelled = true;
|
|
1081
|
-
homeScreenMemoryRefreshVersionRef.current += 1;
|
|
1082
|
-
window.clearInterval(timer);
|
|
1083
|
-
unlisten?.();
|
|
1084
|
-
};
|
|
1085
|
-
}, [
|
|
1086
|
-
featureConfig?.screenMemory?.enabled,
|
|
1087
|
-
popoverVisible,
|
|
1088
|
-
refreshHomeScreenMemoryStatus,
|
|
1089
|
-
]);
|
|
1090
1032
|
const recordShortcutHandlerRef = useRef<() => void>(() => {});
|
|
1091
1033
|
const handleStartRecordingRef = useRef<
|
|
1092
1034
|
(options?: {
|
|
@@ -3576,47 +3518,6 @@ export function App() {
|
|
|
3576
3518
|
const showCameraRow = mode !== "screen"; // screen-only has no camera
|
|
3577
3519
|
const showSourceRow = mode !== "camera"; // camera-only has no screen source
|
|
3578
3520
|
|
|
3579
|
-
async function setHomeRewindRemembering(remembering: boolean) {
|
|
3580
|
-
if (homeScreenMemoryBusy) return;
|
|
3581
|
-
if (remembering && featureConfig?.screenMemory?.enabled !== true) {
|
|
3582
|
-
setRewindSettingsReturnView("recorder");
|
|
3583
|
-
setPromptRewindEnable(true);
|
|
3584
|
-
setPopoverView("rewind-settings");
|
|
3585
|
-
return;
|
|
3586
|
-
}
|
|
3587
|
-
setHomeScreenMemoryBusy(true);
|
|
3588
|
-
try {
|
|
3589
|
-
const current = await invoke<FeatureConfig>("get_feature_config");
|
|
3590
|
-
await invoke("set_feature_config", {
|
|
3591
|
-
config: {
|
|
3592
|
-
...current,
|
|
3593
|
-
screenMemory: {
|
|
3594
|
-
...DEFAULT_SCREEN_MEMORY_CONFIG,
|
|
3595
|
-
...current.screenMemory,
|
|
3596
|
-
// Once Rewind has been set up, the everyday Home switch is a
|
|
3597
|
-
// pause/resume control. Full disable and capture permissions live
|
|
3598
|
-
// in Rewind Settings, so an accidental Home click never tears
|
|
3599
|
-
// down the configured memory system or asks for consent again.
|
|
3600
|
-
enabled: true,
|
|
3601
|
-
paused: !remembering,
|
|
3602
|
-
},
|
|
3603
|
-
},
|
|
3604
|
-
});
|
|
3605
|
-
// The native producer can take a moment to finish pausing. Do not keep
|
|
3606
|
-
// the Home switch locked while that status request waits; the existing
|
|
3607
|
-
// change event and bounded poll will reconcile it, and this best-effort
|
|
3608
|
-
// refresh can do the same without blocking the next resume action.
|
|
3609
|
-
refreshHomeScreenMemoryStatus();
|
|
3610
|
-
} catch (err) {
|
|
3611
|
-
console.error(
|
|
3612
|
-
"[clips-tray] update Rewind remembering state failed:",
|
|
3613
|
-
err,
|
|
3614
|
-
);
|
|
3615
|
-
} finally {
|
|
3616
|
-
setHomeScreenMemoryBusy(false);
|
|
3617
|
-
}
|
|
3618
|
-
}
|
|
3619
|
-
|
|
3620
3521
|
const pendingUploadBanner = recordingStopFinalizing ? (
|
|
3621
3522
|
<FinalizingUploadBanner />
|
|
3622
3523
|
) : pendingUploads.length > 0 ? (
|
|
@@ -3831,6 +3732,9 @@ export function App() {
|
|
|
3831
3732
|
setServerUrl(url.replace(/\/+$/, ""));
|
|
3832
3733
|
setPopoverView("recorder");
|
|
3833
3734
|
}}
|
|
3735
|
+
rewindAgentPromptCopied={rewindAgentPromptCopied}
|
|
3736
|
+
onCopyRewindAgentPrompt={copyRewindAgentPrompt}
|
|
3737
|
+
onOpenRewindDocs={openRewindDocs}
|
|
3834
3738
|
onOpenRewind={() => setPopoverView("rewind-settings")}
|
|
3835
3739
|
onOpenMemory={() => setPopoverView("memory")}
|
|
3836
3740
|
onCancel={() => {
|
|
@@ -3877,6 +3781,9 @@ export function App() {
|
|
|
3877
3781
|
setServerUrl(url.replace(/\/+$/, ""));
|
|
3878
3782
|
setPopoverView("recorder");
|
|
3879
3783
|
}}
|
|
3784
|
+
rewindAgentPromptCopied={rewindAgentPromptCopied}
|
|
3785
|
+
onCopyRewindAgentPrompt={copyRewindAgentPrompt}
|
|
3786
|
+
onOpenRewindDocs={openRewindDocs}
|
|
3880
3787
|
onOpenRewind={() => {
|
|
3881
3788
|
setPromptRewindEnable(false);
|
|
3882
3789
|
setRewindSettingsReturnView("settings");
|
|
@@ -4093,83 +4000,6 @@ export function App() {
|
|
|
4093
4000
|
onOpenChange={updateReadinessOpen}
|
|
4094
4001
|
onOpenPermission={openPrivacySettings}
|
|
4095
4002
|
/>
|
|
4096
|
-
|
|
4097
|
-
<section className="rewind-home-card" aria-label="Rewind">
|
|
4098
|
-
<button
|
|
4099
|
-
type="button"
|
|
4100
|
-
className="rewind-home-summary"
|
|
4101
|
-
aria-expanded={rewindHomeOpen}
|
|
4102
|
-
onClick={() => setRewindHomeOpen((open) => !open)}
|
|
4103
|
-
>
|
|
4104
|
-
<span className="rewind-home-title">Rewind</span>
|
|
4105
|
-
<span className="rewind-home-state">
|
|
4106
|
-
{homeRewindOn ? "On" : "Off"}
|
|
4107
|
-
{rewindHomeOpen ? (
|
|
4108
|
-
<IconChevronDown size={11} stroke={2} />
|
|
4109
|
-
) : (
|
|
4110
|
-
<IconChevronRight size={11} stroke={2} />
|
|
4111
|
-
)}
|
|
4112
|
-
</span>
|
|
4113
|
-
</button>
|
|
4114
|
-
{rewindHomeOpen ? (
|
|
4115
|
-
<div className="rewind-home-details">
|
|
4116
|
-
<p>{homeRewindPresentation.detail}</p>
|
|
4117
|
-
<div className="rewind-home-detail-actions">
|
|
4118
|
-
<button
|
|
4119
|
-
type="button"
|
|
4120
|
-
className="rewind-docs-link"
|
|
4121
|
-
onClick={openRewindDocs}
|
|
4122
|
-
>
|
|
4123
|
-
Learn about Rewind
|
|
4124
|
-
<IconExternalLink size={13} stroke={1.9} />
|
|
4125
|
-
</button>
|
|
4126
|
-
<div className="rewind-home-controls">
|
|
4127
|
-
<Tooltip>
|
|
4128
|
-
<TooltipTrigger asChild>
|
|
4129
|
-
<button
|
|
4130
|
-
type="button"
|
|
4131
|
-
className="rewind-agent-prompt-copy"
|
|
4132
|
-
onClick={() => void copyRewindAgentPrompt()}
|
|
4133
|
-
aria-label={
|
|
4134
|
-
rewindAgentPromptCopied
|
|
4135
|
-
? "Setup prompt copied — paste it into your agent once"
|
|
4136
|
-
: "Copy setup prompt for your agent"
|
|
4137
|
-
}
|
|
4138
|
-
>
|
|
4139
|
-
{rewindAgentPromptCopied ? (
|
|
4140
|
-
<IconCircleCheck size={15} stroke={2} />
|
|
4141
|
-
) : (
|
|
4142
|
-
<IconCopy size={15} stroke={1.9} />
|
|
4143
|
-
)}
|
|
4144
|
-
</button>
|
|
4145
|
-
</TooltipTrigger>
|
|
4146
|
-
<TooltipContent>
|
|
4147
|
-
{rewindAgentPromptCopied
|
|
4148
|
-
? "Setup prompt copied"
|
|
4149
|
-
: "Copy setup prompt for your agent"}
|
|
4150
|
-
</TooltipContent>
|
|
4151
|
-
</Tooltip>
|
|
4152
|
-
<Switch
|
|
4153
|
-
on={homeRewindOn}
|
|
4154
|
-
disabled={
|
|
4155
|
-
homeScreenMemoryBusy ||
|
|
4156
|
-
isRecording ||
|
|
4157
|
-
recordingFlowActive
|
|
4158
|
-
}
|
|
4159
|
-
onChange={(remembering) =>
|
|
4160
|
-
void setHomeRewindRemembering(remembering)
|
|
4161
|
-
}
|
|
4162
|
-
label={
|
|
4163
|
-
featureConfig?.screenMemory?.enabled === true
|
|
4164
|
-
? "Remember with Rewind"
|
|
4165
|
-
: "Set up Rewind"
|
|
4166
|
-
}
|
|
4167
|
-
/>
|
|
4168
|
-
</div>
|
|
4169
|
-
</div>
|
|
4170
|
-
</div>
|
|
4171
|
-
) : null}
|
|
4172
|
-
</section>
|
|
4173
4003
|
</div>
|
|
4174
4004
|
|
|
4175
4005
|
{!isRecording ? (
|
|
@@ -5445,6 +5275,9 @@ function Setup({
|
|
|
5445
5275
|
onVoiceProviderChange,
|
|
5446
5276
|
onVoiceInstructionsChange,
|
|
5447
5277
|
onConnect,
|
|
5278
|
+
rewindAgentPromptCopied,
|
|
5279
|
+
onCopyRewindAgentPrompt,
|
|
5280
|
+
onOpenRewindDocs,
|
|
5448
5281
|
onOpenRewind,
|
|
5449
5282
|
onOpenMemory,
|
|
5450
5283
|
onCancel,
|
|
@@ -5472,6 +5305,9 @@ function Setup({
|
|
|
5472
5305
|
onVoiceProviderChange: (value: VoiceProvider) => void;
|
|
5473
5306
|
onVoiceInstructionsChange: (value: string) => void;
|
|
5474
5307
|
onConnect: (url: string) => void;
|
|
5308
|
+
rewindAgentPromptCopied: boolean;
|
|
5309
|
+
onCopyRewindAgentPrompt: () => void;
|
|
5310
|
+
onOpenRewindDocs: () => void;
|
|
5475
5311
|
onOpenRewind?: () => void;
|
|
5476
5312
|
onOpenMemory?: () => void;
|
|
5477
5313
|
onCancel?: () => void;
|
|
@@ -6669,6 +6505,26 @@ function Setup({
|
|
|
6669
6505
|
Rewind's reusable instructions and repairs the local
|
|
6670
6506
|
connection, so later you can simply say “Look at Rewind.”
|
|
6671
6507
|
</p>
|
|
6508
|
+
<div className="setup-button-row">
|
|
6509
|
+
<button
|
|
6510
|
+
type="button"
|
|
6511
|
+
className="secondary"
|
|
6512
|
+
onClick={onCopyRewindAgentPrompt}
|
|
6513
|
+
>
|
|
6514
|
+
<IconCopy size={14} stroke={1.9} />
|
|
6515
|
+
{rewindAgentPromptCopied
|
|
6516
|
+
? "Setup prompt copied"
|
|
6517
|
+
: "Copy setup prompt"}
|
|
6518
|
+
</button>
|
|
6519
|
+
<button
|
|
6520
|
+
type="button"
|
|
6521
|
+
className="secondary"
|
|
6522
|
+
onClick={onOpenRewindDocs}
|
|
6523
|
+
>
|
|
6524
|
+
<IconExternalLink size={14} stroke={1.9} />
|
|
6525
|
+
Learn about Rewind
|
|
6526
|
+
</button>
|
|
6527
|
+
</div>
|
|
6672
6528
|
</div>
|
|
6673
6529
|
</div>
|
|
6674
6530
|
<details className="setup-advanced rewind-agent-repair">
|
|
@@ -31,6 +31,11 @@ let lastCheckStartedAt = 0;
|
|
|
31
31
|
|
|
32
32
|
const UPDATE_CHECK_INTERVAL_MS = 60 * 60 * 1000;
|
|
33
33
|
const UPDATE_FOCUS_CHECK_MIN_INTERVAL_MS = 15 * 60 * 1000;
|
|
34
|
+
const UPDATE_RETRY_DELAYS_MS = [1000, 3000];
|
|
35
|
+
|
|
36
|
+
function waitForRetry(delayMs: number) {
|
|
37
|
+
return new Promise<void>((resolve) => setTimeout(resolve, delayMs));
|
|
38
|
+
}
|
|
34
39
|
|
|
35
40
|
function canRunUpdateChecks() {
|
|
36
41
|
// Dev and local release builds are for testing the current checkout. Do not
|
|
@@ -43,6 +48,44 @@ function setStatus(next: UpdateStatus) {
|
|
|
43
48
|
for (const l of listeners) l(next);
|
|
44
49
|
}
|
|
45
50
|
|
|
51
|
+
async function runCheckAttempt(staged: string | null) {
|
|
52
|
+
if (!staged) setStatus({ state: "checking" });
|
|
53
|
+
const update = await check();
|
|
54
|
+
if (!update) {
|
|
55
|
+
// The endpoint answering "current" while a newer bundle sits staged is a
|
|
56
|
+
// contradiction, not a reason to drop the download. Keep it staged.
|
|
57
|
+
if (!staged) setStatus({ state: "not-available" });
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (staged && update.version === staged) return;
|
|
61
|
+
const version = update.version;
|
|
62
|
+
const notes = update.body ?? undefined;
|
|
63
|
+
setStatus({ state: "available", version, notes });
|
|
64
|
+
|
|
65
|
+
// Download ONLY — do not install here. On macOS `install` swaps the .app
|
|
66
|
+
// bundle on disk while this process keeps running, which invalidates the
|
|
67
|
+
// running process's Screen Recording (TCC) grant and breaks capture until
|
|
68
|
+
// relaunch. We defer the swap to `installAndRestart()` so a downloaded-but-
|
|
69
|
+
// not-yet-installed update leaves the running app fully functional; the
|
|
70
|
+
// user records normally until they choose to restart.
|
|
71
|
+
let total = 0;
|
|
72
|
+
let downloaded = 0;
|
|
73
|
+
await update.download((event) => {
|
|
74
|
+
if (event.event === "Started") {
|
|
75
|
+
total = event.data.contentLength ?? 0;
|
|
76
|
+
downloaded = 0;
|
|
77
|
+
setStatus({ state: "downloading", version, notes, percent: 0 });
|
|
78
|
+
} else if (event.event === "Progress") {
|
|
79
|
+
downloaded += event.data.chunkLength;
|
|
80
|
+
const percent =
|
|
81
|
+
total > 0 ? Math.min(100, Math.round((downloaded / total) * 100)) : 0;
|
|
82
|
+
setStatus({ state: "downloading", version, notes, percent });
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
pendingUpdate = update;
|
|
86
|
+
setStatus({ state: "downloaded", version, notes });
|
|
87
|
+
}
|
|
88
|
+
|
|
46
89
|
async function runCheck() {
|
|
47
90
|
if (checkInFlight) return checkInFlight;
|
|
48
91
|
|
|
@@ -56,44 +99,16 @@ async function runCheck() {
|
|
|
56
99
|
lastCheckStartedAt = Date.now();
|
|
57
100
|
checkInFlight = (async () => {
|
|
58
101
|
try {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
if (staged && update.version === staged) return;
|
|
68
|
-
pendingUpdate = update;
|
|
69
|
-
const version = update.version;
|
|
70
|
-
const notes = update.body ?? undefined;
|
|
71
|
-
setStatus({ state: "available", version, notes });
|
|
72
|
-
|
|
73
|
-
// Download ONLY — do not install here. On macOS `install` swaps the .app
|
|
74
|
-
// bundle on disk while this process keeps running, which invalidates the
|
|
75
|
-
// running process's Screen Recording (TCC) grant and breaks capture until
|
|
76
|
-
// relaunch. We defer the swap to `installAndRestart()` so a downloaded-but-
|
|
77
|
-
// not-yet-installed update leaves the running app fully functional; the
|
|
78
|
-
// user records normally until they choose to restart.
|
|
79
|
-
let total = 0;
|
|
80
|
-
let downloaded = 0;
|
|
81
|
-
await update.download((event) => {
|
|
82
|
-
if (event.event === "Started") {
|
|
83
|
-
total = event.data.contentLength ?? 0;
|
|
84
|
-
downloaded = 0;
|
|
85
|
-
setStatus({ state: "downloading", version, notes, percent: 0 });
|
|
86
|
-
} else if (event.event === "Progress") {
|
|
87
|
-
downloaded += event.data.chunkLength;
|
|
88
|
-
const percent =
|
|
89
|
-
total > 0
|
|
90
|
-
? Math.min(100, Math.round((downloaded / total) * 100))
|
|
91
|
-
: 0;
|
|
92
|
-
setStatus({ state: "downloading", version, notes, percent });
|
|
93
|
-
} else if (event.event === "Finished") {
|
|
94
|
-
setStatus({ state: "downloaded", version, notes });
|
|
102
|
+
for (let attempt = 0; ; attempt += 1) {
|
|
103
|
+
try {
|
|
104
|
+
await runCheckAttempt(staged);
|
|
105
|
+
return;
|
|
106
|
+
} catch (err) {
|
|
107
|
+
const delay = UPDATE_RETRY_DELAYS_MS[attempt];
|
|
108
|
+
if (delay === undefined) throw err;
|
|
109
|
+
await waitForRetry(delay);
|
|
95
110
|
}
|
|
96
|
-
}
|
|
111
|
+
}
|
|
97
112
|
} catch (err) {
|
|
98
113
|
const message = err instanceof Error ? err.message : String(err);
|
|
99
114
|
setStatus({ state: "error", message });
|
|
@@ -1010,8 +1010,7 @@ body[data-clips-route="recording-pill"] #root {
|
|
|
1010
1010
|
line-height: 1;
|
|
1011
1011
|
}
|
|
1012
1012
|
|
|
1013
|
-
.readiness-action svg
|
|
1014
|
-
.rewind-home-state svg {
|
|
1013
|
+
.readiness-action svg {
|
|
1015
1014
|
width: 11px;
|
|
1016
1015
|
height: 11px;
|
|
1017
1016
|
}
|
|
@@ -1386,110 +1385,6 @@ body[data-clips-route="recording-pill"] #root {
|
|
|
1386
1385
|
font-size: 11px;
|
|
1387
1386
|
}
|
|
1388
1387
|
|
|
1389
|
-
/* Rewind is a calm everyday status first; configuration is a drill-in. */
|
|
1390
|
-
.rewind-home-card {
|
|
1391
|
-
display: grid;
|
|
1392
|
-
gap: 0;
|
|
1393
|
-
padding: 0;
|
|
1394
|
-
border: none;
|
|
1395
|
-
border-radius: 0;
|
|
1396
|
-
background: transparent;
|
|
1397
|
-
}
|
|
1398
|
-
|
|
1399
|
-
.rewind-home-summary {
|
|
1400
|
-
display: grid;
|
|
1401
|
-
grid-template-columns: minmax(0, 1fr) auto;
|
|
1402
|
-
align-items: center;
|
|
1403
|
-
gap: 8px;
|
|
1404
|
-
width: 100%;
|
|
1405
|
-
min-height: 36px;
|
|
1406
|
-
padding: 7px 12px;
|
|
1407
|
-
border: none;
|
|
1408
|
-
border-radius: var(--radius-sm);
|
|
1409
|
-
background: transparent;
|
|
1410
|
-
color: var(--fg-muted);
|
|
1411
|
-
cursor: pointer;
|
|
1412
|
-
text-align: left;
|
|
1413
|
-
transition: color 120ms;
|
|
1414
|
-
}
|
|
1415
|
-
|
|
1416
|
-
.rewind-home-summary:hover,
|
|
1417
|
-
.rewind-home-summary:focus-visible {
|
|
1418
|
-
color: var(--fg);
|
|
1419
|
-
}
|
|
1420
|
-
|
|
1421
|
-
.rewind-home-summary:focus-visible {
|
|
1422
|
-
outline: none;
|
|
1423
|
-
box-shadow: 0 0 0 3px var(--brand-ring);
|
|
1424
|
-
}
|
|
1425
|
-
|
|
1426
|
-
.rewind-home-title {
|
|
1427
|
-
font-size: 13px;
|
|
1428
|
-
font-weight: 500;
|
|
1429
|
-
line-height: 1;
|
|
1430
|
-
}
|
|
1431
|
-
|
|
1432
|
-
.rewind-home-state {
|
|
1433
|
-
display: inline-flex;
|
|
1434
|
-
align-items: center;
|
|
1435
|
-
gap: 4px;
|
|
1436
|
-
color: inherit;
|
|
1437
|
-
font-size: 11px;
|
|
1438
|
-
font-weight: 600;
|
|
1439
|
-
line-height: 1;
|
|
1440
|
-
}
|
|
1441
|
-
|
|
1442
|
-
.rewind-home-details {
|
|
1443
|
-
display: grid;
|
|
1444
|
-
gap: 10px;
|
|
1445
|
-
padding: 0 12px 10px;
|
|
1446
|
-
}
|
|
1447
|
-
|
|
1448
|
-
.rewind-home-details p {
|
|
1449
|
-
margin: 0;
|
|
1450
|
-
color: var(--fg-muted);
|
|
1451
|
-
font-size: 11px;
|
|
1452
|
-
line-height: 1.4;
|
|
1453
|
-
}
|
|
1454
|
-
|
|
1455
|
-
.rewind-home-detail-actions {
|
|
1456
|
-
display: flex;
|
|
1457
|
-
align-items: center;
|
|
1458
|
-
justify-content: space-between;
|
|
1459
|
-
gap: 10px;
|
|
1460
|
-
}
|
|
1461
|
-
|
|
1462
|
-
.rewind-home-controls {
|
|
1463
|
-
display: inline-flex;
|
|
1464
|
-
align-items: center;
|
|
1465
|
-
gap: 8px;
|
|
1466
|
-
margin-left: auto;
|
|
1467
|
-
}
|
|
1468
|
-
|
|
1469
|
-
.rewind-docs-link {
|
|
1470
|
-
display: inline-flex;
|
|
1471
|
-
align-items: center;
|
|
1472
|
-
gap: 4px;
|
|
1473
|
-
padding: 0;
|
|
1474
|
-
border: none;
|
|
1475
|
-
background: transparent;
|
|
1476
|
-
color: var(--fg-muted);
|
|
1477
|
-
cursor: pointer;
|
|
1478
|
-
font-size: 11px;
|
|
1479
|
-
font-weight: 600;
|
|
1480
|
-
white-space: nowrap;
|
|
1481
|
-
}
|
|
1482
|
-
|
|
1483
|
-
.rewind-docs-link:hover {
|
|
1484
|
-
color: var(--fg);
|
|
1485
|
-
}
|
|
1486
|
-
|
|
1487
|
-
.rewind-docs-link:focus-visible {
|
|
1488
|
-
outline: none;
|
|
1489
|
-
box-shadow: 0 0 0 3px var(--brand-ring);
|
|
1490
|
-
border-radius: 4px;
|
|
1491
|
-
}
|
|
1492
|
-
|
|
1493
1388
|
.rewind-settings-entry-main {
|
|
1494
1389
|
display: grid;
|
|
1495
1390
|
grid-template-columns: auto minmax(0, 1fr);
|
|
@@ -1497,7 +1392,6 @@ body[data-clips-route="recording-pill"] #root {
|
|
|
1497
1392
|
gap: 9px;
|
|
1498
1393
|
}
|
|
1499
1394
|
|
|
1500
|
-
.rewind-home-lock-note,
|
|
1501
1395
|
.rewind-capture-lock-note {
|
|
1502
1396
|
margin: 0;
|
|
1503
1397
|
color: var(--fg-muted);
|
|
@@ -1511,28 +1405,6 @@ body[data-clips-route="recording-pill"] #root {
|
|
|
1511
1405
|
background: var(--surface);
|
|
1512
1406
|
}
|
|
1513
1407
|
|
|
1514
|
-
.rewind-agent-prompt-copy {
|
|
1515
|
-
width: 28px;
|
|
1516
|
-
height: 28px;
|
|
1517
|
-
display: grid;
|
|
1518
|
-
place-items: center;
|
|
1519
|
-
padding: 0;
|
|
1520
|
-
border: none;
|
|
1521
|
-
border-radius: 7px;
|
|
1522
|
-
background: transparent;
|
|
1523
|
-
color: var(--fg-muted);
|
|
1524
|
-
cursor: pointer;
|
|
1525
|
-
}
|
|
1526
|
-
|
|
1527
|
-
.rewind-agent-prompt-copy:hover {
|
|
1528
|
-
color: var(--fg);
|
|
1529
|
-
}
|
|
1530
|
-
|
|
1531
|
-
.rewind-agent-prompt-copy:focus-visible {
|
|
1532
|
-
outline: none;
|
|
1533
|
-
box-shadow: 0 0 0 3px var(--brand-ring);
|
|
1534
|
-
}
|
|
1535
|
-
|
|
1536
1408
|
.rewind-home-dot {
|
|
1537
1409
|
width: 9px;
|
|
1538
1410
|
height: 9px;
|
|
@@ -1548,38 +1420,6 @@ body[data-clips-route="recording-pill"] #root {
|
|
|
1548
1420
|
box-shadow: 0 0 0 4px color-mix(in srgb, var(--status-ok) 15%, transparent);
|
|
1549
1421
|
}
|
|
1550
1422
|
|
|
1551
|
-
.rewind-home-actions {
|
|
1552
|
-
display: grid;
|
|
1553
|
-
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
1554
|
-
gap: 6px;
|
|
1555
|
-
}
|
|
1556
|
-
|
|
1557
|
-
.rewind-home-actions button {
|
|
1558
|
-
min-width: 0;
|
|
1559
|
-
min-height: 31px;
|
|
1560
|
-
display: inline-flex;
|
|
1561
|
-
align-items: center;
|
|
1562
|
-
justify-content: center;
|
|
1563
|
-
gap: 5px;
|
|
1564
|
-
border: 1px solid var(--border-strong);
|
|
1565
|
-
border-radius: var(--radius-sm);
|
|
1566
|
-
background: var(--bg);
|
|
1567
|
-
color: var(--fg);
|
|
1568
|
-
cursor: pointer;
|
|
1569
|
-
font-size: 10.5px;
|
|
1570
|
-
font-weight: 600;
|
|
1571
|
-
}
|
|
1572
|
-
|
|
1573
|
-
.rewind-home-actions button:hover:not(:disabled) {
|
|
1574
|
-
background: var(--surface-hover);
|
|
1575
|
-
}
|
|
1576
|
-
|
|
1577
|
-
.rewind-home-actions button:disabled {
|
|
1578
|
-
opacity: 0.42;
|
|
1579
|
-
cursor: default;
|
|
1580
|
-
}
|
|
1581
|
-
|
|
1582
|
-
.rewind-home-actions button:focus-visible,
|
|
1583
1423
|
.rewind-consent-choices button:focus-visible,
|
|
1584
1424
|
.rewind-consent-primary:focus-visible,
|
|
1585
1425
|
.rewind-quiet-button:focus-visible,
|
|
@@ -1594,11 +1434,6 @@ body[data-clips-route="recording-pill"] #root {
|
|
|
1594
1434
|
box-shadow: 0 0 0 3px var(--brand-ring);
|
|
1595
1435
|
}
|
|
1596
1436
|
|
|
1597
|
-
.rewind-home-actions .icon {
|
|
1598
|
-
width: 14px;
|
|
1599
|
-
height: 14px;
|
|
1600
|
-
}
|
|
1601
|
-
|
|
1602
1437
|
.rewind-consent {
|
|
1603
1438
|
min-height: 430px;
|
|
1604
1439
|
align-items: center;
|
|
@@ -99,6 +99,8 @@ unicode-segmentation = "1.13"
|
|
|
99
99
|
# Powers the "browser" voice-dictation provider (free, instant, no API
|
|
100
100
|
# key, no server round-trip — same engine WisprFlow uses for offline).
|
|
101
101
|
objc2-foundation = { version = "0.3", features = [
|
|
102
|
+
"NSBundle",
|
|
103
|
+
"NSDictionary",
|
|
102
104
|
"NSString",
|
|
103
105
|
"NSError",
|
|
104
106
|
"NSLocale",
|
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
<string>Clips uses your microphone to record audio with screen recordings.</string>
|
|
9
9
|
<key>NSScreenCaptureUsageDescription</key>
|
|
10
10
|
<string>Clips records your screen and system audio when you start a Clip.</string>
|
|
11
|
+
<key>NSCalendarsUsageDescription</key>
|
|
12
|
+
<string>Clips uses your calendars to show upcoming meetings and offer meeting capture.</string>
|
|
13
|
+
<key>NSCalendarsFullAccessUsageDescription</key>
|
|
14
|
+
<string>Clips uses your calendars to show upcoming meetings and offer meeting capture.</string>
|
|
11
15
|
<key>NSSpeechRecognitionUsageDescription</key>
|
|
12
16
|
<string>Clips uses speech recognition for on-device voice dictation.</string>
|
|
13
17
|
<key>NSAccessibilityUsageDescription</key>
|
|
@@ -73,13 +73,38 @@ mod macos {
|
|
|
73
73
|
use objc2::rc::Retained;
|
|
74
74
|
use objc2::runtime::{AnyClass, AnyObject, Bool};
|
|
75
75
|
use objc2::{class, msg_send};
|
|
76
|
-
use objc2_foundation::{NSArray, NSString};
|
|
76
|
+
use objc2_foundation::{NSArray, NSBundle, NSString};
|
|
77
77
|
use tauri::AppHandle;
|
|
78
78
|
|
|
79
79
|
use super::EventKitEvent;
|
|
80
80
|
|
|
81
81
|
/// EKEntityType::Event == 0
|
|
82
82
|
const EK_ENTITY_TYPE_EVENT: usize = 0;
|
|
83
|
+
const CALENDAR_USAGE_DESCRIPTION_KEYS: [&str; 2] = [
|
|
84
|
+
"NSCalendarsUsageDescription",
|
|
85
|
+
"NSCalendarsFullAccessUsageDescription",
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
fn has_calendar_usage_descriptions() -> bool {
|
|
89
|
+
let bundle = NSBundle::mainBundle();
|
|
90
|
+
let Some(info) = bundle.infoDictionary() else {
|
|
91
|
+
return false;
|
|
92
|
+
};
|
|
93
|
+
CALENDAR_USAGE_DESCRIPTION_KEYS.iter().all(|key| {
|
|
94
|
+
let key = NSString::from_str(key);
|
|
95
|
+
info.objectForKey(&*key)
|
|
96
|
+
.and_then(|value| value.downcast::<NSString>().ok())
|
|
97
|
+
.is_some_and(|value| !value.is_empty())
|
|
98
|
+
})
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
fn ensure_calendar_usage_descriptions() -> Result<(), String> {
|
|
102
|
+
if has_calendar_usage_descriptions() {
|
|
103
|
+
Ok(())
|
|
104
|
+
} else {
|
|
105
|
+
Err("Clips cannot access macOS calendars because the app bundle is missing calendar usage descriptions.".into())
|
|
106
|
+
}
|
|
107
|
+
}
|
|
83
108
|
|
|
84
109
|
/// Returns Some(NSObject*) for the given class name, or None if the
|
|
85
110
|
/// runtime doesn't know about it (e.g. the EventKit framework is not
|
|
@@ -104,6 +129,7 @@ mod macos {
|
|
|
104
129
|
}
|
|
105
130
|
|
|
106
131
|
pub async fn request_access_impl(_app: AppHandle) -> Result<bool, String> {
|
|
132
|
+
ensure_calendar_usage_descriptions()?;
|
|
107
133
|
let store =
|
|
108
134
|
unsafe { new_event_store() }.ok_or_else(|| "EventKit not available".to_string())?;
|
|
109
135
|
let (tx, rx) = mpsc::sync_channel::<bool>(1);
|
|
@@ -141,6 +167,7 @@ mod macos {
|
|
|
141
167
|
_app: AppHandle,
|
|
142
168
|
within_hours: u32,
|
|
143
169
|
) -> Result<Vec<EventKitEvent>, String> {
|
|
170
|
+
ensure_calendar_usage_descriptions()?;
|
|
144
171
|
unsafe {
|
|
145
172
|
let store = new_event_store().ok_or_else(|| "EventKit not available".to_string())?;
|
|
146
173
|
|
|
@@ -553,6 +553,8 @@ pub fn run() {
|
|
|
553
553
|
// otherwise skips Rust destructors — make sure a live
|
|
554
554
|
// `screencapture` fallback child doesn't survive us.
|
|
555
555
|
if let tauri::RunEvent::Exit = _event {
|
|
556
|
+
whisper_speech::shutdown(_app_handle);
|
|
557
|
+
native_speech::shutdown();
|
|
556
558
|
let state = _app_handle.state::<native_screen::NativeFullscreenRecordingState>();
|
|
557
559
|
native_screen::kill_active_screencapture_child(&state);
|
|
558
560
|
}
|