@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.
Files changed (197) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  3. package/corpus/templates/analytics/.agents/skills/frontend-design/SKILL.md +5 -0
  4. package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +83 -45
  5. package/corpus/templates/analytics/changelog/2026-08-11-analytics-dashboards-keep-the-selected-tab-when-reopened.md +6 -0
  6. package/corpus/templates/assets/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  7. package/corpus/templates/assets/.agents/skills/frontend-design/SKILL.md +5 -0
  8. package/corpus/templates/brain/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  9. package/corpus/templates/brain/.agents/skills/frontend-design/SKILL.md +5 -0
  10. package/corpus/templates/calendar/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  11. package/corpus/templates/calendar/.agents/skills/frontend-design/SKILL.md +5 -0
  12. package/corpus/templates/chat/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  13. package/corpus/templates/chat/.agents/skills/frontend-design/SKILL.md +5 -0
  14. package/corpus/templates/clips/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  15. package/corpus/templates/clips/.agents/skills/frontend-design/SKILL.md +5 -0
  16. package/corpus/templates/clips/changelog/2026-08-11-clips-recovers-from-transient-updates-and-avoids-macos-priva.md +6 -0
  17. package/corpus/templates/clips/desktop/src/app.tsx +32 -176
  18. package/corpus/templates/clips/desktop/src/lib/updater.ts +52 -37
  19. package/corpus/templates/clips/desktop/src/styles.css +1 -166
  20. package/corpus/templates/clips/desktop/src-tauri/Cargo.toml +2 -0
  21. package/corpus/templates/clips/desktop/src-tauri/Info.plist +4 -0
  22. package/corpus/templates/clips/desktop/src-tauri/src/eventkit.rs +28 -1
  23. package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +2 -0
  24. package/corpus/templates/clips/desktop/src-tauri/src/native_speech.rs +48 -1
  25. package/corpus/templates/clips/desktop/src-tauri/src/permission_status.rs +3 -0
  26. package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +5 -0
  27. package/corpus/templates/clips/server/plugins/auth.ts +3 -0
  28. package/corpus/templates/content/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  29. package/corpus/templates/content/.agents/skills/frontend-design/SKILL.md +5 -0
  30. package/corpus/templates/content/actions/_document-discovery-query.ts +127 -0
  31. package/corpus/templates/content/actions/list-documents.ts +189 -198
  32. package/corpus/templates/content/actions/search-documents.ts +95 -24
  33. package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +2 -1
  34. package/corpus/templates/content/app/hooks/use-documents.ts +84 -6
  35. package/corpus/templates/content/shared/api.ts +10 -0
  36. package/corpus/templates/design/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  37. package/corpus/templates/design/.agents/skills/frontend-design/SKILL.md +5 -0
  38. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +5 -1
  39. package/corpus/templates/design/changelog/2026-08-11-design-agent-prompts-copy-successfully-in-the-desktop-app.md +6 -0
  40. package/corpus/templates/dispatch/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  41. package/corpus/templates/dispatch/.agents/skills/frontend-design/SKILL.md +5 -0
  42. package/corpus/templates/dispatch/app/global.css +2 -2
  43. package/corpus/templates/dispatch/changelog/2026-08-10-desktop-workspace-sign-in.md +6 -0
  44. package/corpus/templates/dispatch/changelog/2026-08-11-dispatch-no-longer-shows-the-generic-chat-starter-among-defa.md +6 -0
  45. package/corpus/templates/dispatch/changelog/2026-08-11-integrations-and-scheduled-work-open-as-first-class-electron-pages.md +6 -0
  46. package/corpus/templates/dispatch/changelog/2026-08-11-sidebar-text-is-slightly-easier-to-read-in-light-and-dark-mo.md +6 -0
  47. package/corpus/templates/dispatch/server/lib/identity-sso.ts +57 -31
  48. package/corpus/templates/dispatch/server/plugins/feature-flags.ts +5 -0
  49. package/corpus/templates/dispatch/server/plugins/identity-sso.ts +82 -33
  50. package/corpus/templates/dispatch/server/plugins/org-apps-directory.ts +7 -15
  51. package/corpus/templates/dispatch/server/plugins/setup-dispatch.ts +9 -1
  52. package/corpus/templates/dispatch/shared/feature-flags.ts +15 -0
  53. package/corpus/templates/factory/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  54. package/corpus/templates/factory/.agents/skills/frontend-design/SKILL.md +5 -0
  55. package/corpus/templates/forms/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  56. package/corpus/templates/forms/.agents/skills/form-responses/SKILL.md +8 -7
  57. package/corpus/templates/forms/.agents/skills/frontend-design/SKILL.md +5 -0
  58. package/corpus/templates/forms/app/hooks/use-forms.ts +8 -1
  59. package/corpus/templates/forms/changelog/2026-08-11-fixed-public-forms-so-conditional-questions-stay-hidden-unti.md +6 -0
  60. package/corpus/templates/forms/server/handlers/submissions.ts +2 -1
  61. package/corpus/templates/forms/server/lib/public-form-ssr.ts +37 -2
  62. package/corpus/templates/forms/shared/page-url.ts +54 -0
  63. package/corpus/templates/forms/shared/types.ts +4 -4
  64. package/corpus/templates/macros/.agents/skills/frontend-design/SKILL.md +5 -0
  65. package/corpus/templates/mail/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  66. package/corpus/templates/mail/.agents/skills/frontend-design/SKILL.md +5 -0
  67. package/corpus/templates/plan/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  68. package/corpus/templates/plan/.agents/skills/frontend-design/SKILL.md +5 -0
  69. package/corpus/templates/slides/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  70. package/corpus/templates/slides/.agents/skills/frontend-design/SKILL.md +5 -0
  71. package/corpus/templates/slides/actions/add-slide.ts +0 -27
  72. package/corpus/templates/slides/actions/import-file.ts +28 -9
  73. package/corpus/templates/slides/actions/view-screen.ts +4 -4
  74. package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +8 -1
  75. package/corpus/templates/slides/app/components/editor/AiEditingMarker.tsx +25 -0
  76. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +43 -48
  77. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +9 -6
  78. package/corpus/templates/slides/app/components/editor/GeneratingSlidePreview.tsx +1 -9
  79. package/corpus/templates/slides/app/components/editor/ImageDropPromptPopover.tsx +2 -0
  80. package/corpus/templates/slides/app/components/editor/ImageOverlay.tsx +0 -26
  81. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +904 -217
  82. package/corpus/templates/slides/app/components/editor/canvas-interactions/index.ts +1 -0
  83. package/corpus/templates/slides/app/components/editor/canvas-interactions/slides-canvas-adapter.ts +11 -0
  84. package/corpus/templates/slides/app/components/editor/inline-edit-session.ts +16 -0
  85. package/corpus/templates/slides/app/components/editor/slide-object-interactions.ts +22 -6
  86. package/corpus/templates/slides/app/components/editor/style-clipboard.ts +61 -0
  87. package/corpus/templates/slides/app/components/layout/Layout.tsx +26 -2
  88. package/corpus/templates/slides/app/components/ui/context-menu.tsx +1 -0
  89. package/corpus/templates/slides/app/context/DeckContext.tsx +75 -25
  90. package/corpus/templates/slides/app/global.css +1 -1
  91. package/corpus/templates/slides/app/i18n/en-US.ts +6 -1
  92. package/corpus/templates/slides/app/lib/create-deck-generation.ts +1 -1
  93. package/corpus/templates/slides/app/lib/image-drop-to-agent.ts +12 -12
  94. package/corpus/templates/slides/app/lib/slide-agent-context.ts +52 -0
  95. package/corpus/templates/slides/app/lib/slide-image-replacement.ts +61 -0
  96. package/corpus/templates/slides/app/pages/DeckEditor.tsx +55 -116
  97. package/corpus/templates/slides/app/pages/Index.tsx +28 -3
  98. package/corpus/templates/slides/changelog/2026-08-11-agent-chat-now-follows-the-current-slide-and-selection.md +6 -0
  99. package/corpus/templates/slides/changelog/2026-08-11-deck-titles-now-use-slide-content-instead-of-placeholder-filenames.md +6 -0
  100. package/corpus/templates/slides/changelog/2026-08-11-history-now-opens-from-the-deck-overflow-menu.md +6 -0
  101. package/corpus/templates/slides/changelog/2026-08-11-images-can-be-dropped-directly-onto-slides-and-resized-or-re.md +6 -0
  102. package/corpus/templates/slides/changelog/2026-08-11-new-decks-now-transition-directly-to-a-full-page-editor-load.md +6 -0
  103. package/corpus/templates/slides/changelog/2026-08-11-selected-slide-layers-can-be-nudged-precisely-with-arrow-key.md +6 -0
  104. package/corpus/templates/slides/changelog/2026-08-11-slides-can-copy-and-paste-element-styles-with-cmd-option-c-v.md +6 -0
  105. package/corpus/templates/slides/changelog/2026-08-11-slides-shows-one-compact-ai-marker-and-keeps-your-current-sl.md +6 -0
  106. package/corpus/templates/slides/changelog/2026-08-11-text-edits-preserve-slide-layout-when-no-changes-are-made.md +6 -0
  107. package/corpus/templates/slides/changelog/2026-08-11-the-style-toolbar-now-uses-the-full-available-width.md +6 -0
  108. package/corpus/templates/slides/changelog/2026-08-11-undo-and-redo-now-move-one-visible-slide-state-at-a-time.md +6 -0
  109. package/corpus/templates/slides/server/lib/chat-attachments.ts +6 -2
  110. package/corpus/templates/slides/shared/deck-title.ts +56 -0
  111. package/corpus/templates/tasks/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  112. package/corpus/templates/tasks/.agents/skills/frontend-design/SKILL.md +5 -0
  113. package/dist/agent/run-store.js +6 -1
  114. package/dist/agent/thread-data-builder.js +19 -44
  115. package/dist/agent/types.d.ts +13 -0
  116. package/dist/catalog.json +1 -0
  117. package/dist/cli/create.js +1 -1
  118. package/dist/client/AssistantChat.js +58 -10
  119. package/dist/client/FileStorageSetupCard.d.ts +6 -0
  120. package/dist/client/FileStorageSetupCard.js +18 -0
  121. package/dist/client/NewWorkspaceAppFlow.js +10 -0
  122. package/dist/client/agent-chat-adapter.js +87 -11
  123. package/dist/client/agent-chat.d.ts +1 -1
  124. package/dist/client/agent-chat.js +8 -3
  125. package/dist/client/chat/attachment-adapters.js +10 -0
  126. package/dist/client/chat/message-components.d.ts +4 -1
  127. package/dist/client/chat/message-components.js +37 -13
  128. package/dist/client/chat/tool-call-display.js +14 -0
  129. package/dist/client/chat-first/apps-rail.d.ts +1 -1
  130. package/dist/client/chat-first/apps-rail.js +6 -6
  131. package/dist/client/chat-first/copy.js +1 -0
  132. package/dist/client/chat-first/types.d.ts +1 -0
  133. package/dist/client/extensions/EmbeddedExtension.js +1 -1
  134. package/dist/client/extensions/ExtensionViewer.js +1 -1
  135. package/dist/client/extensions/InlineExtensionFrame.js +1 -1
  136. package/dist/client/onboarding/FirstRunOnboarding.js +17 -2
  137. package/dist/client/settings/FileStorageSettingsForm.d.ts +2 -0
  138. package/dist/client/settings/FileStorageSettingsForm.js +113 -0
  139. package/dist/client/settings/SettingsPanel.js +2 -1
  140. package/dist/client/sse-event-processor.js +21 -0
  141. package/dist/extensions/actions.js +2 -2
  142. package/dist/extensions/html-shell.js +3 -1
  143. package/dist/file-upload/pre-upload-attachments.d.ts +15 -10
  144. package/dist/file-upload/pre-upload-attachments.js +68 -24
  145. package/dist/file-upload/registry.d.ts +4 -5
  146. package/dist/file-upload/registry.js +7 -14
  147. package/dist/file-upload/s3.d.ts +9 -0
  148. package/dist/file-upload/s3.js +168 -0
  149. package/dist/file-upload/types.d.ts +3 -3
  150. package/dist/file-upload/types.js +2 -2
  151. package/dist/onboarding/app-profile.js +23 -1
  152. package/dist/onboarding/default-steps.js +69 -0
  153. package/dist/onboarding/types.d.ts +2 -0
  154. package/dist/progress/routes.d.ts +1 -1
  155. package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
  156. package/dist/provider-api/actions/provider-api.d.ts +8 -8
  157. package/dist/resources/handlers.d.ts +1 -1
  158. package/dist/secrets/routes.d.ts +6 -6
  159. package/dist/server/agent-chat/browser-team-tools.js +20 -0
  160. package/dist/server/agent-chat-plugin.js +12 -3
  161. package/dist/server/auth.js +45 -21
  162. package/dist/server/better-auth-instance.d.ts +2 -0
  163. package/dist/server/better-auth-instance.js +2 -2
  164. package/dist/server/core-routes-plugin.d.ts +1 -0
  165. package/dist/server/core-routes-plugin.js +26 -16
  166. package/dist/server/identity-sso-store.d.ts +3 -0
  167. package/dist/server/identity-sso-store.js +45 -0
  168. package/dist/server/identity-sso.d.ts +20 -10
  169. package/dist/server/identity-sso.js +79 -31
  170. package/dist/server/index.d.ts +1 -1
  171. package/dist/server/index.js +1 -1
  172. package/dist/templates/chat/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  173. package/dist/templates/chat/.agents/skills/frontend-design/SKILL.md +5 -0
  174. package/dist/templates/default/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  175. package/dist/templates/default/.agents/skills/frontend-design/SKILL.md +5 -0
  176. package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +24 -1
  177. package/dist/templates/workspace-core/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  178. package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +6 -2
  179. package/dist/templates/workspace-core/.agents/skills/extensions/references/api.md +1 -1
  180. package/dist/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +5 -0
  181. package/dist/templates/workspace-core/.agents/skills/generative-ui/SKILL.md +3 -1
  182. package/docs/content/authentication.mdx +27 -0
  183. package/docs/content/cross-app-sso.mdx +38 -8
  184. package/docs/content/extensions.mdx +15 -15
  185. package/docs/content/file-uploads.mdx +10 -12
  186. package/docs/content/generative-ui.mdx +6 -1
  187. package/package.json +2 -2
  188. package/src/templates/chat/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  189. package/src/templates/chat/.agents/skills/frontend-design/SKILL.md +5 -0
  190. package/src/templates/default/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  191. package/src/templates/default/.agents/skills/frontend-design/SKILL.md +5 -0
  192. package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +24 -1
  193. package/src/templates/workspace-core/.agents/skills/delegate-to-agent/SKILL.md +28 -0
  194. package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +6 -2
  195. package/src/templates/workspace-core/.agents/skills/extensions/references/api.md +1 -1
  196. package/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +5 -0
  197. 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
- if (!staged) setStatus({ state: "checking" });
60
- const update = await check();
61
- if (!update) {
62
- // The endpoint answering "current" while a newer bundle sits staged is
63
- // a contradiction, not a reason to drop the download. Keep it staged.
64
- if (!staged) setStatus({ state: "not-available" });
65
- return;
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
  }