@agent-native/core 0.91.2 → 0.92.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 +2 -2
- package/corpus/core/CHANGELOG.md +17 -0
- package/corpus/core/docs/content/faq.mdx +17 -0
- package/corpus/core/docs/content/notifications.mdx +12 -6
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/production-agent.ts +140 -46
- package/corpus/core/src/agent/tool-search.ts +71 -4
- package/corpus/core/src/cli/index.ts +20 -0
- package/corpus/core/src/cli/upgrade.ts +798 -0
- package/corpus/core/src/client/AssistantChat.tsx +1 -1
- package/corpus/core/src/client/HighlightedCodeBlock.tsx +20 -3
- package/corpus/core/src/client/agent-chat-adapter.ts +31 -6
- package/corpus/core/src/client/chat/message-components.tsx +85 -8
- package/corpus/core/src/client/chat/tool-call-display.tsx +355 -144
- package/corpus/core/src/client/code-agent-chat-adapter.ts +2 -9
- package/corpus/core/src/client/session-replay.ts +3 -97
- package/corpus/core/src/client/settings/SettingsPanel.tsx +533 -389
- package/corpus/core/src/client/settings/SettingsSection.tsx +154 -17
- package/corpus/core/src/client/sse-event-processor.ts +29 -2
- package/corpus/core/src/extensions/actions.ts +113 -5
- package/corpus/core/src/notifications/channels.ts +88 -25
- package/corpus/core/src/notifications/registry.ts +17 -3
- package/corpus/core/src/notifications/types.ts +4 -1
- package/corpus/core/src/server/agent-chat-plugin.ts +7 -3
- package/corpus/core/src/server/request-context.ts +7 -0
- package/corpus/core/src/styles/agent-conversation.css +1 -1
- package/corpus/core/src/templates/default/.agents/skills/self-modifying-code/SKILL.md +20 -2
- package/corpus/core/src/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/corpus/core/src/templates/default/AGENTS.md +10 -3
- package/corpus/core/src/templates/default/package.json +2 -1
- package/corpus/core/src/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/corpus/core/src/templates/headless/AGENTS.md +9 -3
- package/corpus/core/src/templates/headless/package.json +1 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +20 -2
- package/corpus/core/src/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/corpus/core/src/templates/workspace-root/AGENTS.md +10 -4
- package/corpus/core/src/templates/workspace-root/package.json +1 -0
- package/corpus/templates/analytics/.agents/skills/self-modifying-code/SKILL.md +20 -2
- package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +5 -6
- package/corpus/templates/analytics/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/corpus/templates/analytics/AGENTS.md +7 -5
- package/corpus/templates/analytics/actions/save-monitor.ts +14 -0
- package/corpus/templates/analytics/app/components/dashboard/DataTable.tsx +41 -6
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +122 -11
- package/corpus/templates/analytics/app/pages/monitoring/MonitoringPage.tsx +8 -2
- package/corpus/templates/analytics/app/pages/monitoring/uptime/MonitorFormPage.tsx +151 -32
- package/corpus/templates/analytics/app/pages/monitoring/uptime/i18n.ts +17 -2
- package/corpus/templates/analytics/app/pages/monitoring/uptime/types.ts +4 -0
- package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +54 -190
- package/corpus/templates/analytics/changelog/2026-07-08-dashboard-tables-keep-their-size-while-loading.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-08-monitoring-uptime-and-errors-tabs-now-clearly-show-which-view-is-active.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-08-session-replay-uses-stock-rrweb-sizing-again-so-recorded.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-08-sidebar-dashboard-menus-now-open-above-the-dashboard-canvas.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-08-uptime-alerting-clarifies-in-app-inbox-vs-email-and-lets-you-paste-slack-or-webhook-urls-per-monitor.md +6 -0
- package/corpus/templates/analytics/docs/uptime-monitoring.md +13 -11
- package/corpus/templates/analytics/server/db/schema-monitoring.ts +4 -0
- package/corpus/templates/analytics/server/lib/uptime-monitors.ts +60 -12
- package/corpus/templates/analytics/server/plugins/db.ts +10 -0
- package/corpus/templates/assets/.agents/skills/self-modifying-code/SKILL.md +20 -2
- package/corpus/templates/assets/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/corpus/templates/brain/.agents/skills/self-modifying-code/SKILL.md +20 -2
- package/corpus/templates/brain/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/corpus/templates/calendar/.agents/skills/self-modifying-code/SKILL.md +20 -2
- package/corpus/templates/calendar/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/corpus/templates/chat/.agents/skills/self-modifying-code/SKILL.md +20 -2
- package/corpus/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/corpus/templates/clips/.agents/skills/self-modifying-code/SKILL.md +20 -2
- package/corpus/templates/clips/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/corpus/templates/clips/changelog/2026-07-08-desktop-meeting-notes-stay-active-from-tray-and-shortcut-controls.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-08-desktop-meeting-popovers-can-start-notes-and-join-the-meetin.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-08-meeting-notes-keep-their-recording-state-when-the-desktop.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-08-meeting-reminder-dropdowns-now-expand-fully-instead-of-being.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +123 -3
- package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +15 -1
- package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +23 -0
- package/corpus/templates/clips/desktop/src/styles.css +23 -1
- package/corpus/templates/clips/desktop/src-tauri/src/adhoc_meetings_watcher.rs +1 -3
- package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +40 -6
- package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +4 -2
- package/corpus/templates/clips/desktop/src-tauri/src/meetings_watcher.rs +3 -5
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +23 -19
- package/corpus/templates/clips/desktop/src-tauri/src/shortcuts.rs +3 -3
- package/corpus/templates/clips/desktop/src-tauri/src/silence_detector.rs +0 -1
- package/corpus/templates/clips/desktop/src-tauri/src/state.rs +5 -0
- package/corpus/templates/clips/desktop/src-tauri/src/tray.rs +18 -6
- package/corpus/templates/content/.agents/skills/self-modifying-code/SKILL.md +20 -2
- package/corpus/templates/content/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/corpus/templates/content/actions/_batch-utils.ts +8 -0
- package/corpus/templates/content/actions/_builder-cms-read-client.ts +7 -5
- package/corpus/templates/content/actions/_database-source-utils.ts +776 -319
- package/corpus/templates/content/actions/_database-utils.ts +6 -1
- package/corpus/templates/content/actions/_property-utils.ts +168 -0
- package/corpus/templates/content/actions/add-content-database-source-field-property.ts +19 -12
- package/corpus/templates/content/actions/bind-content-database-source-field.ts +19 -12
- package/corpus/templates/content/actions/delete-document.ts +9 -13
- package/corpus/templates/content/actions/refresh-content-database-source.ts +5 -5
- package/corpus/templates/content/app/components/editor/body-hydration.ts +35 -11
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +47 -5
- package/corpus/templates/content/app/hooks/use-documents.ts +8 -2
- package/corpus/templates/content/changelog/2026-07-08-builder-source-sync-now-completes-reliably-on-hosted-databases.md +6 -0
- package/corpus/templates/content/changelog/2026-07-08-fixed-builder-database-sync-so-large-sources-continue-past-t.md +6 -0
- package/corpus/templates/content/server/plugins/db.ts +19 -0
- package/corpus/templates/design/.agents/skills/self-modifying-code/SKILL.md +20 -2
- package/corpus/templates/design/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/corpus/templates/dispatch/.agents/skills/self-modifying-code/SKILL.md +20 -2
- package/corpus/templates/dispatch/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/corpus/templates/forms/.agents/skills/self-modifying-code/SKILL.md +20 -2
- package/corpus/templates/forms/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/corpus/templates/macros/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/corpus/templates/mail/.agents/skills/self-modifying-code/SKILL.md +20 -2
- package/corpus/templates/mail/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/corpus/templates/plan/.agents/skills/self-modifying-code/SKILL.md +20 -2
- package/corpus/templates/plan/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/corpus/templates/slides/.agents/skills/self-modifying-code/SKILL.md +20 -2
- package/corpus/templates/slides/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/corpus/templates/videos/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/corpus/templates/voice/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/dist/agent/production-agent.d.ts +5 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +112 -42
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/tool-search.d.ts +2 -0
- package/dist/agent/tool-search.d.ts.map +1 -1
- package/dist/agent/tool-search.js +60 -4
- package/dist/agent/tool-search.js.map +1 -1
- package/dist/cli/index.js +19 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/upgrade.d.ts +100 -0
- package/dist/cli/upgrade.d.ts.map +1 -0
- package/dist/cli/upgrade.js +678 -0
- package/dist/cli/upgrade.js.map +1 -0
- package/dist/client/AssistantChat.js +1 -1
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/HighlightedCodeBlock.d.ts +2 -0
- package/dist/client/HighlightedCodeBlock.d.ts.map +1 -1
- package/dist/client/HighlightedCodeBlock.js +10 -1
- package/dist/client/HighlightedCodeBlock.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +32 -4
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/chat/message-components.d.ts.map +1 -1
- package/dist/client/chat/message-components.js +61 -8
- package/dist/client/chat/message-components.js.map +1 -1
- package/dist/client/chat/tool-call-display.d.ts +12 -0
- package/dist/client/chat/tool-call-display.d.ts.map +1 -1
- package/dist/client/chat/tool-call-display.js +136 -42
- package/dist/client/chat/tool-call-display.js.map +1 -1
- package/dist/client/code-agent-chat-adapter.js +2 -9
- package/dist/client/code-agent-chat-adapter.js.map +1 -1
- package/dist/client/session-replay.d.ts +0 -8
- package/dist/client/session-replay.d.ts.map +1 -1
- package/dist/client/session-replay.js +3 -80
- package/dist/client/session-replay.js.map +1 -1
- package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
- package/dist/client/settings/SettingsPanel.js +52 -19
- package/dist/client/settings/SettingsPanel.js.map +1 -1
- package/dist/client/settings/SettingsSection.d.ts +19 -4
- package/dist/client/settings/SettingsSection.d.ts.map +1 -1
- package/dist/client/settings/SettingsSection.js +33 -4
- package/dist/client/settings/SettingsSection.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts +9 -0
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +20 -2
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/extensions/actions.d.ts.map +1 -1
- package/dist/extensions/actions.js +85 -5
- package/dist/extensions/actions.js.map +1 -1
- package/dist/notifications/channels.d.ts +8 -3
- package/dist/notifications/channels.d.ts.map +1 -1
- package/dist/notifications/channels.js +61 -22
- package/dist/notifications/channels.js.map +1 -1
- package/dist/notifications/registry.d.ts.map +1 -1
- package/dist/notifications/registry.js +14 -3
- package/dist/notifications/registry.js.map +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/notifications/types.d.ts +4 -1
- package/dist/notifications/types.d.ts.map +1 -1
- package/dist/notifications/types.js.map +1 -1
- package/dist/observability/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/server/agent-chat-plugin.d.ts +5 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +2 -2
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/request-context.d.ts +7 -0
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/request-context.js.map +1 -1
- package/dist/styles/agent-conversation.css +1 -1
- package/dist/templates/default/.agents/skills/self-modifying-code/SKILL.md +20 -2
- package/dist/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/dist/templates/default/AGENTS.md +10 -3
- package/dist/templates/default/package.json +2 -1
- package/dist/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/dist/templates/headless/AGENTS.md +9 -3
- package/dist/templates/headless/package.json +1 -0
- package/dist/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +20 -2
- package/dist/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/dist/templates/workspace-root/AGENTS.md +10 -4
- package/dist/templates/workspace-root/package.json +1 -0
- package/docs/content/faq.mdx +17 -0
- package/docs/content/notifications.mdx +12 -6
- package/package.json +2 -2
- package/src/templates/default/.agents/skills/self-modifying-code/SKILL.md +20 -2
- package/src/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/src/templates/default/AGENTS.md +10 -3
- package/src/templates/default/package.json +2 -1
- package/src/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/src/templates/headless/AGENTS.md +9 -3
- package/src/templates/headless/package.json +1 -0
- package/src/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +20 -2
- package/src/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/src/templates/workspace-root/AGENTS.md +10 -4
- package/src/templates/workspace-root/package.json +1 -0
- package/corpus/templates/analytics/changelog/2026-07-08-session-replay-clamps-ultra-wide-viewports-while-keeping-the.md +0 -6
|
@@ -713,6 +713,7 @@ export function App() {
|
|
|
713
713
|
const [meetingStartMessage, setMeetingStartMessage] = useState<string | null>(
|
|
714
714
|
null,
|
|
715
715
|
);
|
|
716
|
+
const [activeMeetingId, setActiveMeetingId] = useState<string | null>(null);
|
|
716
717
|
const [readinessOpen, setReadinessOpen] = useState<boolean>(
|
|
717
718
|
() => !loadBool(READINESS_REVIEWED_KEY, false),
|
|
718
719
|
);
|
|
@@ -1140,6 +1141,7 @@ export function App() {
|
|
|
1140
1141
|
}, [authStatus, callClipsAction]);
|
|
1141
1142
|
|
|
1142
1143
|
const startMeetingNotes = useCallback((meeting: PopoverMeeting) => {
|
|
1144
|
+
setActiveMeetingId(meeting.id);
|
|
1143
1145
|
setMeetingStartMessage(`Starting notes for ${meeting.title}…`);
|
|
1144
1146
|
emit("meetings:start-transcription", {
|
|
1145
1147
|
meetingId: meeting.id,
|
|
@@ -1147,10 +1149,98 @@ export function App() {
|
|
|
1147
1149
|
reason: "user",
|
|
1148
1150
|
}).catch((err) => {
|
|
1149
1151
|
console.error("[clips-popover] start meeting notes failed:", err);
|
|
1152
|
+
setActiveMeetingId(null);
|
|
1150
1153
|
setMeetingStartMessage("Could not start notes. Try again from Meetings.");
|
|
1151
1154
|
});
|
|
1152
1155
|
}, []);
|
|
1153
1156
|
|
|
1157
|
+
const startMeetingNotesAndJoin = useCallback(
|
|
1158
|
+
(meeting: PopoverMeeting) => {
|
|
1159
|
+
if (meeting.joinUrl) {
|
|
1160
|
+
openExternal(meeting.joinUrl).catch((err) => {
|
|
1161
|
+
console.error("[clips-popover] open meeting join url failed:", err);
|
|
1162
|
+
});
|
|
1163
|
+
}
|
|
1164
|
+
startMeetingNotes(meeting);
|
|
1165
|
+
},
|
|
1166
|
+
[startMeetingNotes],
|
|
1167
|
+
);
|
|
1168
|
+
|
|
1169
|
+
const showActiveMeetingPill = useCallback((meetingId: string) => {
|
|
1170
|
+
invoke("recording_pill_show", { meetingId, mode: "meeting" }).catch(
|
|
1171
|
+
(err) => {
|
|
1172
|
+
console.error("[clips-popover] show meeting pill failed:", err);
|
|
1173
|
+
},
|
|
1174
|
+
);
|
|
1175
|
+
emit("clips:pill-context", { meetingId, mode: "meeting" }).catch(() => {});
|
|
1176
|
+
}, []);
|
|
1177
|
+
|
|
1178
|
+
useEffect(() => {
|
|
1179
|
+
invoke<string | null>("get_active_meeting_id")
|
|
1180
|
+
.then((meetingId) => {
|
|
1181
|
+
if (meetingId) setActiveMeetingId(meetingId);
|
|
1182
|
+
})
|
|
1183
|
+
.catch(() => {});
|
|
1184
|
+
|
|
1185
|
+
let stopped = false;
|
|
1186
|
+
const unlistens: Array<() => void> = [];
|
|
1187
|
+
const track = (promise: Promise<() => void>) => {
|
|
1188
|
+
promise
|
|
1189
|
+
.then((unlisten) => {
|
|
1190
|
+
if (stopped) {
|
|
1191
|
+
unlisten();
|
|
1192
|
+
return;
|
|
1193
|
+
}
|
|
1194
|
+
unlistens.push(unlisten);
|
|
1195
|
+
})
|
|
1196
|
+
.catch(() => {});
|
|
1197
|
+
};
|
|
1198
|
+
track(
|
|
1199
|
+
listen<{ meetingId?: string | null }>(
|
|
1200
|
+
"meetings:transcription-started",
|
|
1201
|
+
(event) => {
|
|
1202
|
+
if (event.payload?.meetingId) {
|
|
1203
|
+
setActiveMeetingId(event.payload.meetingId);
|
|
1204
|
+
}
|
|
1205
|
+
},
|
|
1206
|
+
),
|
|
1207
|
+
);
|
|
1208
|
+
track(
|
|
1209
|
+
listen<{ meetingId?: string | null }>(
|
|
1210
|
+
"meetings:transcription-stopped",
|
|
1211
|
+
(event) => {
|
|
1212
|
+
setActiveMeetingId((current) =>
|
|
1213
|
+
!event.payload?.meetingId || event.payload.meetingId === current
|
|
1214
|
+
? null
|
|
1215
|
+
: current,
|
|
1216
|
+
);
|
|
1217
|
+
},
|
|
1218
|
+
),
|
|
1219
|
+
);
|
|
1220
|
+
track(
|
|
1221
|
+
listen<{ meetingId?: string | null }>(
|
|
1222
|
+
"meetings:transcription-error",
|
|
1223
|
+
(event) => {
|
|
1224
|
+
setActiveMeetingId((current) =>
|
|
1225
|
+
!event.payload?.meetingId || event.payload.meetingId === current
|
|
1226
|
+
? null
|
|
1227
|
+
: current,
|
|
1228
|
+
);
|
|
1229
|
+
},
|
|
1230
|
+
),
|
|
1231
|
+
);
|
|
1232
|
+
return () => {
|
|
1233
|
+
stopped = true;
|
|
1234
|
+
unlistens.forEach((unlisten) => unlisten());
|
|
1235
|
+
unlistens.length = 0;
|
|
1236
|
+
};
|
|
1237
|
+
}, []);
|
|
1238
|
+
|
|
1239
|
+
useEffect(() => {
|
|
1240
|
+
if (!popoverVisible || !activeMeetingId) return;
|
|
1241
|
+
showActiveMeetingPill(activeMeetingId);
|
|
1242
|
+
}, [activeMeetingId, popoverVisible, showActiveMeetingPill]);
|
|
1243
|
+
|
|
1154
1244
|
useMeetingTranscription({
|
|
1155
1245
|
callClipsAction,
|
|
1156
1246
|
serverUrl,
|
|
@@ -2419,6 +2509,7 @@ export function App() {
|
|
|
2419
2509
|
loading={meetingsLoading}
|
|
2420
2510
|
error={meetingsError}
|
|
2421
2511
|
startMessage={meetingStartMessage}
|
|
2512
|
+
activeMeetingId={activeMeetingId}
|
|
2422
2513
|
meetingsEnabled={featureConfig?.meetingsEnabled !== false}
|
|
2423
2514
|
onBack={() => setPopoverView("recorder")}
|
|
2424
2515
|
onRefresh={fetchUpcomingMeetings}
|
|
@@ -2428,6 +2519,8 @@ export function App() {
|
|
|
2428
2519
|
}
|
|
2429
2520
|
onOpenSettings={() => setPopoverView("settings")}
|
|
2430
2521
|
onStartNotes={startMeetingNotes}
|
|
2522
|
+
onStartNotesAndJoin={startMeetingNotesAndJoin}
|
|
2523
|
+
onShowActiveMeeting={showActiveMeetingPill}
|
|
2431
2524
|
/>
|
|
2432
2525
|
</div>
|
|
2433
2526
|
);
|
|
@@ -3326,6 +3419,7 @@ function MeetingsPopoverView({
|
|
|
3326
3419
|
loading,
|
|
3327
3420
|
error,
|
|
3328
3421
|
startMessage,
|
|
3422
|
+
activeMeetingId,
|
|
3329
3423
|
meetingsEnabled,
|
|
3330
3424
|
onBack,
|
|
3331
3425
|
onRefresh,
|
|
@@ -3333,11 +3427,14 @@ function MeetingsPopoverView({
|
|
|
3333
3427
|
onOpenMeeting,
|
|
3334
3428
|
onOpenSettings,
|
|
3335
3429
|
onStartNotes,
|
|
3430
|
+
onStartNotesAndJoin,
|
|
3431
|
+
onShowActiveMeeting,
|
|
3336
3432
|
}: {
|
|
3337
3433
|
meetings: PopoverMeeting[];
|
|
3338
3434
|
loading: boolean;
|
|
3339
3435
|
error: string | null;
|
|
3340
3436
|
startMessage: string | null;
|
|
3437
|
+
activeMeetingId: string | null;
|
|
3341
3438
|
meetingsEnabled: boolean;
|
|
3342
3439
|
onBack: () => void;
|
|
3343
3440
|
onRefresh: () => void;
|
|
@@ -3345,6 +3442,8 @@ function MeetingsPopoverView({
|
|
|
3345
3442
|
onOpenMeeting: (meetingId: string) => void;
|
|
3346
3443
|
onOpenSettings: () => void;
|
|
3347
3444
|
onStartNotes: (meeting: PopoverMeeting) => void;
|
|
3445
|
+
onStartNotesAndJoin: (meeting: PopoverMeeting) => void;
|
|
3446
|
+
onShowActiveMeeting: (meetingId: string) => void;
|
|
3348
3447
|
}) {
|
|
3349
3448
|
return (
|
|
3350
3449
|
<div className="setup popover-view">
|
|
@@ -3402,6 +3501,8 @@ function MeetingsPopoverView({
|
|
|
3402
3501
|
<div className="popover-list">
|
|
3403
3502
|
{meetings.map((meeting) => {
|
|
3404
3503
|
const canStart = meetingCanStartNotes(meeting);
|
|
3504
|
+
const hasJoin = Boolean(meeting.joinUrl);
|
|
3505
|
+
const isActive = activeMeetingId === meeting.id;
|
|
3405
3506
|
return (
|
|
3406
3507
|
<div className="popover-list-item" key={meeting.id}>
|
|
3407
3508
|
<div className="popover-list-icon">
|
|
@@ -3414,13 +3515,32 @@ function MeetingsPopoverView({
|
|
|
3414
3515
|
{meeting.platform ? ` · ${meeting.platform}` : ""}
|
|
3415
3516
|
</div>
|
|
3416
3517
|
</div>
|
|
3417
|
-
{
|
|
3518
|
+
{isActive ? (
|
|
3519
|
+
<button
|
|
3520
|
+
type="button"
|
|
3521
|
+
className="popover-list-action popover-list-action-active"
|
|
3522
|
+
onClick={() => onShowActiveMeeting(meeting.id)}
|
|
3523
|
+
title="Meeting notes are recording"
|
|
3524
|
+
>
|
|
3525
|
+
<span className="popover-list-action-dot" aria-hidden />
|
|
3526
|
+
Recording
|
|
3527
|
+
</button>
|
|
3528
|
+
) : canStart ? (
|
|
3418
3529
|
<button
|
|
3419
3530
|
type="button"
|
|
3420
3531
|
className="popover-list-action popover-list-action-primary"
|
|
3421
|
-
onClick={() =>
|
|
3532
|
+
onClick={() =>
|
|
3533
|
+
hasJoin
|
|
3534
|
+
? onStartNotesAndJoin(meeting)
|
|
3535
|
+
: onStartNotes(meeting)
|
|
3536
|
+
}
|
|
3537
|
+
title={
|
|
3538
|
+
hasJoin
|
|
3539
|
+
? "Start notes and join the meeting"
|
|
3540
|
+
: "Start meeting notes"
|
|
3541
|
+
}
|
|
3422
3542
|
>
|
|
3423
|
-
Start notes
|
|
3543
|
+
{hasJoin ? "Start + join" : "Start notes"}
|
|
3424
3544
|
</button>
|
|
3425
3545
|
) : (
|
|
3426
3546
|
<button
|
|
@@ -245,6 +245,14 @@ export function useMeetingTranscription({
|
|
|
245
245
|
const existing = sessionRef.current;
|
|
246
246
|
if (existing) {
|
|
247
247
|
if (!existing.stopping && existing.meetingId === meetingId) {
|
|
248
|
+
await invoke("recording_pill_show", {
|
|
249
|
+
meetingId: existing.meetingId,
|
|
250
|
+
mode: "meeting",
|
|
251
|
+
}).catch(() => {});
|
|
252
|
+
emit("clips:pill-context", {
|
|
253
|
+
meetingId: existing.meetingId,
|
|
254
|
+
mode: "meeting",
|
|
255
|
+
}).catch(() => {});
|
|
248
256
|
emit("meetings:hide-notification", { meetingId }).catch(() => {});
|
|
249
257
|
return;
|
|
250
258
|
}
|
|
@@ -282,7 +290,13 @@ export function useMeetingTranscription({
|
|
|
282
290
|
};
|
|
283
291
|
sessionRef.current = session;
|
|
284
292
|
await invoke("set_recording_state", { active: true }).catch(() => {});
|
|
285
|
-
await invoke("set_meeting_active", {
|
|
293
|
+
await invoke("set_meeting_active", {
|
|
294
|
+
active: true,
|
|
295
|
+
meetingId: resolvedMeetingId,
|
|
296
|
+
}).catch(() => {});
|
|
297
|
+
emit("meetings:transcription-started", {
|
|
298
|
+
meetingId: resolvedMeetingId,
|
|
299
|
+
}).catch(() => {});
|
|
286
300
|
|
|
287
301
|
const scheduleFlush = () => {
|
|
288
302
|
if (session.flushTimer) window.clearTimeout(session.flushTimer);
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
IconX,
|
|
7
7
|
} from "@tabler/icons-react";
|
|
8
8
|
import { invoke } from "@tauri-apps/api/core";
|
|
9
|
+
import { LogicalSize } from "@tauri-apps/api/dpi";
|
|
9
10
|
import { emit, listen } from "@tauri-apps/api/event";
|
|
10
11
|
import { getCurrentWindow } from "@tauri-apps/api/window";
|
|
11
12
|
import { open as openExternal } from "@tauri-apps/plugin-shell";
|
|
@@ -37,6 +38,9 @@ interface TranscriptionStatusPayload {
|
|
|
37
38
|
|
|
38
39
|
const SNOOZE_MS = 5 * 60_000;
|
|
39
40
|
const FALLBACK_AUTO_HIDE_MS = 6 * 60_000;
|
|
41
|
+
const NOTIFICATION_WINDOW_WIDTH = 440;
|
|
42
|
+
const NOTIFICATION_COLLAPSED_HEIGHT = 120;
|
|
43
|
+
const NOTIFICATION_MENU_HEIGHT = 224;
|
|
40
44
|
|
|
41
45
|
/**
|
|
42
46
|
* Open a meeting join URL via the Tauri shell plugin.
|
|
@@ -50,6 +54,17 @@ async function openJoinUrl(url: string | null | undefined): Promise<void> {
|
|
|
50
54
|
}
|
|
51
55
|
}
|
|
52
56
|
|
|
57
|
+
function resizeNotificationWindow(expanded: boolean) {
|
|
58
|
+
const height = expanded
|
|
59
|
+
? NOTIFICATION_MENU_HEIGHT
|
|
60
|
+
: NOTIFICATION_COLLAPSED_HEIGHT;
|
|
61
|
+
getCurrentWindow()
|
|
62
|
+
.setSize(new LogicalSize(NOTIFICATION_WINDOW_WIDTH, height))
|
|
63
|
+
.catch((err) => {
|
|
64
|
+
console.warn("[clips-meeting-notif] resize failed", err);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
53
68
|
function ProviderGlyph({ provider }: { provider: MeetingJoinProvider }) {
|
|
54
69
|
// Lightweight glyphs — keep the overlay free of extra assets. Zoom blue
|
|
55
70
|
// camera / Meet green / Teams purple, otherwise a generic video icon.
|
|
@@ -115,6 +130,14 @@ export function MeetingNotification() {
|
|
|
115
130
|
dataRef.current = data;
|
|
116
131
|
}, [data]);
|
|
117
132
|
|
|
133
|
+
useEffect(() => {
|
|
134
|
+
resizeNotificationWindow(Boolean(data && menuOpen));
|
|
135
|
+
}, [data, menuOpen]);
|
|
136
|
+
|
|
137
|
+
useEffect(() => {
|
|
138
|
+
return () => resizeNotificationWindow(false);
|
|
139
|
+
}, []);
|
|
140
|
+
|
|
118
141
|
useEffect(() => {
|
|
119
142
|
const win = getCurrentWindow();
|
|
120
143
|
if (data) {
|
|
@@ -1195,6 +1195,7 @@ body[data-clips-route="recording-pill"] #root {
|
|
|
1195
1195
|
display: flex;
|
|
1196
1196
|
flex-direction: column;
|
|
1197
1197
|
gap: 6px;
|
|
1198
|
+
margin-bottom: 4px;
|
|
1198
1199
|
}
|
|
1199
1200
|
|
|
1200
1201
|
.popover-list-item {
|
|
@@ -1264,6 +1265,27 @@ body[data-clips-route="recording-pill"] #root {
|
|
|
1264
1265
|
background: var(--brand-hover);
|
|
1265
1266
|
}
|
|
1266
1267
|
|
|
1268
|
+
.popover-list-action-active {
|
|
1269
|
+
display: inline-flex;
|
|
1270
|
+
align-items: center;
|
|
1271
|
+
gap: 5px;
|
|
1272
|
+
border-color: rgba(34, 197, 94, 0.36);
|
|
1273
|
+
background: rgba(34, 197, 94, 0.12);
|
|
1274
|
+
color: #4ade80;
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
.popover-list-action-active:hover {
|
|
1278
|
+
background: rgba(34, 197, 94, 0.18);
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
.popover-list-action-dot {
|
|
1282
|
+
width: 6px;
|
|
1283
|
+
height: 6px;
|
|
1284
|
+
border-radius: 999px;
|
|
1285
|
+
background: currentColor;
|
|
1286
|
+
box-shadow: 0 0 0 3px rgba(34, 197, 94, 0.12);
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1267
1289
|
.popover-kv {
|
|
1268
1290
|
display: flex;
|
|
1269
1291
|
align-items: center;
|
|
@@ -1425,7 +1447,7 @@ body[data-clips-route="recording-pill"] #root {
|
|
|
1425
1447
|
content isn't flush with the panel edge. Must follow `.setup` so it wins
|
|
1426
1448
|
over the shorthand padding above. */
|
|
1427
1449
|
.setup.popover-view {
|
|
1428
|
-
padding-bottom:
|
|
1450
|
+
padding-bottom: 26px;
|
|
1429
1451
|
}
|
|
1430
1452
|
|
|
1431
1453
|
.setup h2 {
|
|
@@ -123,9 +123,7 @@ async fn tick_macos(
|
|
|
123
123
|
config: &crate::config::FeatureConfig,
|
|
124
124
|
) -> Result<(), String> {
|
|
125
125
|
let front = crate::util::frontmost_bundle_id();
|
|
126
|
-
let matched = front
|
|
127
|
-
.as_deref()
|
|
128
|
-
.and_then(match_vc_bundle);
|
|
126
|
+
let matched = front.as_deref().and_then(match_vc_bundle);
|
|
129
127
|
|
|
130
128
|
let Some((platform, title)) = matched else {
|
|
131
129
|
// VC app left front — clear session dedupe so a later re-focus can
|
|
@@ -15,8 +15,8 @@ use tauri::{
|
|
|
15
15
|
|
|
16
16
|
use crate::dlog;
|
|
17
17
|
use crate::state::{
|
|
18
|
-
DictationActive, LastTranscript, MeetingActive, RecordingActive, TrayAnchor,
|
|
19
|
-
VoiceWakePopover,
|
|
18
|
+
ActiveMeetingId, DictationActive, LastTranscript, MeetingActive, RecordingActive, TrayAnchor,
|
|
19
|
+
VoiceTargetBundle, VoiceWakePopover,
|
|
20
20
|
};
|
|
21
21
|
use crate::util::{
|
|
22
22
|
build_overlay_url, configure_overlay_behavior, hide_voice_wake_popover, is_recording_active,
|
|
@@ -969,8 +969,11 @@ pub async fn hide_overlays(app: AppHandle) -> Result<(), String> {
|
|
|
969
969
|
let _ = w.close();
|
|
970
970
|
}
|
|
971
971
|
}
|
|
972
|
-
//
|
|
973
|
-
|
|
972
|
+
// The meeting pill belongs to the live notes session, not the popover's
|
|
973
|
+
// camera/bubble lifecycle. Keep it visible when the popover closes.
|
|
974
|
+
if !crate::util::is_meeting_active(&app) {
|
|
975
|
+
let _ = crate::recording_indicator::recording_pill_hide(app).await;
|
|
976
|
+
}
|
|
974
977
|
Ok(())
|
|
975
978
|
}
|
|
976
979
|
|
|
@@ -1887,16 +1890,37 @@ pub async fn set_recording_state(app: AppHandle, active: bool) -> Result<(), Str
|
|
|
1887
1890
|
/// teardown in `lib.rs`: quit stays instant when no meeting is active, and
|
|
1888
1891
|
/// only waits for a graceful stop when one is.
|
|
1889
1892
|
#[tauri::command]
|
|
1890
|
-
pub async fn set_meeting_active(
|
|
1891
|
-
|
|
1893
|
+
pub async fn set_meeting_active(
|
|
1894
|
+
app: AppHandle,
|
|
1895
|
+
active: bool,
|
|
1896
|
+
meeting_id: Option<String>,
|
|
1897
|
+
) -> Result<(), String> {
|
|
1898
|
+
dlog!(
|
|
1899
|
+
"[clips-tray] set_meeting_active active={} meeting_id={:?}",
|
|
1900
|
+
active,
|
|
1901
|
+
meeting_id
|
|
1902
|
+
);
|
|
1892
1903
|
if let Some(state) = app.try_state::<MeetingActive>() {
|
|
1893
1904
|
if let Ok(mut g) = state.0.lock() {
|
|
1894
1905
|
*g = active;
|
|
1895
1906
|
}
|
|
1896
1907
|
}
|
|
1908
|
+
if let Some(state) = app.try_state::<ActiveMeetingId>() {
|
|
1909
|
+
if let Ok(mut g) = state.0.lock() {
|
|
1910
|
+
*g = if active { meeting_id } else { None };
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1913
|
+
crate::tray::rebuild_tray_menu(&app);
|
|
1897
1914
|
Ok(())
|
|
1898
1915
|
}
|
|
1899
1916
|
|
|
1917
|
+
#[tauri::command]
|
|
1918
|
+
pub async fn get_active_meeting_id(app: AppHandle) -> Result<Option<String>, String> {
|
|
1919
|
+
Ok(app
|
|
1920
|
+
.try_state::<ActiveMeetingId>()
|
|
1921
|
+
.and_then(|s| s.0.lock().ok().and_then(|g| g.clone())))
|
|
1922
|
+
}
|
|
1923
|
+
|
|
1900
1924
|
/// Guards the quit-teardown handshake in `lib.rs`'s `ExitRequested` handler:
|
|
1901
1925
|
/// 0 = not requested, 1 = requested (waiting on JS), 2 = done (safe to let
|
|
1902
1926
|
/// the process exit — including the watchdog's own forced exit, which must
|
|
@@ -1937,6 +1961,16 @@ pub async fn reset_state(app: AppHandle) -> Result<(), String> {
|
|
|
1937
1961
|
*g = false;
|
|
1938
1962
|
}
|
|
1939
1963
|
}
|
|
1964
|
+
if let Some(state) = app.try_state::<MeetingActive>() {
|
|
1965
|
+
if let Ok(mut g) = state.0.lock() {
|
|
1966
|
+
*g = false;
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
if let Some(state) = app.try_state::<ActiveMeetingId>() {
|
|
1970
|
+
if let Ok(mut g) = state.0.lock() {
|
|
1971
|
+
*g = None;
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1940
1974
|
if let Some(w) = app.get_webview_window(REGION_GUIDES_LABEL) {
|
|
1941
1975
|
let _ = w.close();
|
|
1942
1976
|
}
|
|
@@ -33,8 +33,8 @@ use tauri::{Emitter, Manager};
|
|
|
33
33
|
|
|
34
34
|
use clips::{position_popover, toggle_popover};
|
|
35
35
|
use state::{
|
|
36
|
-
DictationActive, DictationEnabled, LastTranscript, MeetingActive,
|
|
37
|
-
RecordingActive, TrayAnchor, TrayMeetings, VoiceTargetBundle, VoiceWakePopover,
|
|
36
|
+
ActiveMeetingId, DictationActive, DictationEnabled, LastTranscript, MeetingActive,
|
|
37
|
+
PopoverShownAt, RecordingActive, TrayAnchor, TrayMeetings, VoiceTargetBundle, VoiceWakePopover,
|
|
38
38
|
};
|
|
39
39
|
use util::{
|
|
40
40
|
configure_overlay_behavior, is_recording_active, present_interactive_window,
|
|
@@ -95,6 +95,7 @@ pub fn run() {
|
|
|
95
95
|
clips::paste_last_dictation,
|
|
96
96
|
clips::set_recording_state,
|
|
97
97
|
clips::set_meeting_active,
|
|
98
|
+
clips::get_active_meeting_id,
|
|
98
99
|
clips::quit_teardown_done,
|
|
99
100
|
clips::reset_state,
|
|
100
101
|
clips::save_bubble_position,
|
|
@@ -204,6 +205,7 @@ pub fn run() {
|
|
|
204
205
|
.manage(PopoverShownAt::default())
|
|
205
206
|
.manage(RecordingActive::default())
|
|
206
207
|
.manage(MeetingActive::default())
|
|
208
|
+
.manage(ActiveMeetingId::default())
|
|
207
209
|
.manage(DictationEnabled::default())
|
|
208
210
|
.manage(DictationActive::default())
|
|
209
211
|
.manage(VoiceWakePopover::default())
|
|
@@ -289,9 +289,7 @@ async fn tick_once(app: &AppHandle, client: &reqwest::Client) -> Result<(), Stri
|
|
|
289
289
|
let limit = MEETING_POLL_LIMIT.to_string();
|
|
290
290
|
// Include meetings that started within the hold window so a late-open
|
|
291
291
|
// desktop still surfaces the reminder until 5 minutes after start.
|
|
292
|
-
let within_min = ((NOTIFY_LEAD_SECS + NOTIFY_HOLD_AFTER_START_SECS) / 60
|
|
293
|
-
+ 1)
|
|
294
|
-
.to_string();
|
|
292
|
+
let within_min = ((NOTIFY_LEAD_SECS + NOTIFY_HOLD_AFTER_START_SECS) / 60 + 1).to_string();
|
|
295
293
|
let mut req = client.get(&url).query(&[
|
|
296
294
|
("view", "upcoming"),
|
|
297
295
|
("limit", limit.as_str()),
|
|
@@ -368,8 +366,8 @@ async fn tick_once(app: &AppHandle, client: &reqwest::Client) -> Result<(), Stri
|
|
|
368
366
|
|
|
369
367
|
// Eligible from 1 min before start through 5 min after start.
|
|
370
368
|
// secs_until > 0 => still upcoming; negative => already started.
|
|
371
|
-
let in_window =
|
|
372
|
-
&& secs_until >= -NOTIFY_HOLD_AFTER_START_SECS;
|
|
369
|
+
let in_window =
|
|
370
|
+
secs_until <= NOTIFY_LEAD_SECS && secs_until >= -NOTIFY_HOLD_AFTER_START_SECS;
|
|
373
371
|
|
|
374
372
|
let eligible = match g.snoozed_until.get(&m.id).copied() {
|
|
375
373
|
Some(until) if now_ts < until => false, // still snoozed
|
|
@@ -65,22 +65,22 @@ const AUDIO_DENOISE_FILTER: &str = "afftdn=nr=10:nf=-50:tn=1";
|
|
|
65
65
|
// explicit output rate the AAC track ends up at 192 kHz and plays back slow.
|
|
66
66
|
const AUDIO_OUTPUT_SAMPLE_RATE: u32 = 48000;
|
|
67
67
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
68
|
+
// Pre-gain for mic-only native captures. ScreenCaptureKit records without
|
|
69
|
+
// WebRTC AGC (unlike Loom / the browser path), so speech often lands well
|
|
70
|
+
// below -16 LUFS. 12 dB before loudnorm matches Loom-ish perceptual loudness
|
|
71
|
+
// for MacBook mics; loudnorm still clamps true peaks at TP=-1.5.
|
|
72
72
|
const AUDIO_MIC_PREGAIN_FILTER: &str = "volume=12dB";
|
|
73
73
|
// After the mic+system centered downmix (0.5*L+0.5*R) each source is ~
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
74
|
+
// 6 dB quieter. Restore energy before loudnorm so dual-capture clips are not
|
|
75
|
+
// systematically quieter than mic-only. Dual still mixes two sources so peaks
|
|
76
|
+
// may compress more under loudnorm than mic-only — by design when both sides
|
|
77
|
+
// compete for the same LUFS budget.
|
|
78
78
|
const AUDIO_DOWNMIX_MAKEUP_FILTER: &str = "volume=6dB";
|
|
79
79
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
80
|
+
// Loudness normalization, optionally preceded by the centered-stereo downmix
|
|
81
|
+
// that repairs the mic+system L/R split and denoise for native mic captures.
|
|
82
|
+
// Pair with `-ar AUDIO_OUTPUT_SAMPLE_RATE` so loudnorm's 192 kHz output is
|
|
83
|
+
// resampled back.
|
|
84
84
|
fn audio_filter_chain(downmix: bool, denoise: bool, mic_pregain: bool) -> String {
|
|
85
85
|
let mut filters = Vec::new();
|
|
86
86
|
if downmix {
|
|
@@ -4479,7 +4479,11 @@ fn normalize_audio_with_ffmpeg(
|
|
|
4479
4479
|
.arg("-b:a")
|
|
4480
4480
|
.arg(audio_bitrate)
|
|
4481
4481
|
.arg("-af")
|
|
4482
|
-
.arg(audio_filter_chain(
|
|
4482
|
+
.arg(audio_filter_chain(
|
|
4483
|
+
downmix_audio,
|
|
4484
|
+
denoise_audio,
|
|
4485
|
+
mic_pregain,
|
|
4486
|
+
))
|
|
4483
4487
|
.arg("-ac")
|
|
4484
4488
|
.arg("2")
|
|
4485
4489
|
.arg("-ar")
|
|
@@ -4568,9 +4572,11 @@ fn transcode_with_ffmpeg(
|
|
|
4568
4572
|
}
|
|
4569
4573
|
|
|
4570
4574
|
if normalize_audio {
|
|
4571
|
-
command
|
|
4572
|
-
|
|
4573
|
-
|
|
4575
|
+
command.arg("-af").arg(audio_filter_chain(
|
|
4576
|
+
downmix_audio,
|
|
4577
|
+
denoise_audio,
|
|
4578
|
+
mic_pregain,
|
|
4579
|
+
));
|
|
4574
4580
|
}
|
|
4575
4581
|
|
|
4576
4582
|
let duration_rate_limit =
|
|
@@ -5055,9 +5061,7 @@ mod audio_track_probe_tests {
|
|
|
5055
5061
|
);
|
|
5056
5062
|
assert_eq!(
|
|
5057
5063
|
audio_filter_chain(false, true, true),
|
|
5058
|
-
format!(
|
|
5059
|
-
"{AUDIO_DENOISE_FILTER},{AUDIO_MIC_PREGAIN_FILTER},{AUDIO_LOUDNESS_FILTER}"
|
|
5060
|
-
)
|
|
5064
|
+
format!("{AUDIO_DENOISE_FILTER},{AUDIO_MIC_PREGAIN_FILTER},{AUDIO_LOUDNESS_FILTER}")
|
|
5061
5065
|
);
|
|
5062
5066
|
assert_eq!(
|
|
5063
5067
|
audio_filter_chain(true, true, false),
|
|
@@ -10,8 +10,8 @@ use crate::clips::{remember_voice_target, toggle_popover};
|
|
|
10
10
|
use crate::dlog;
|
|
11
11
|
use crate::state::{DictationActive, VoiceWakePopover};
|
|
12
12
|
use crate::util::{
|
|
13
|
-
hide_voice_wake_popover, is_dictation_active,
|
|
14
|
-
show_without_activation,
|
|
13
|
+
hide_voice_wake_popover, is_dictation_active, is_meeting_active, is_recording_active,
|
|
14
|
+
set_dictation_active, show_without_activation,
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
fn escape_shortcut() -> Shortcut {
|
|
@@ -513,7 +513,7 @@ pub fn build_shortcut_plugin() -> tauri_plugin_global_shortcut::Builder<tauri::W
|
|
|
513
513
|
// global shortcut stops it rather than re-opening the
|
|
514
514
|
// popover. Keeps parity with the tray-icon click
|
|
515
515
|
// behaviour in `on_tray_icon_event`.
|
|
516
|
-
if is_recording_active(app) {
|
|
516
|
+
if is_recording_active(app) && !is_meeting_active(app) {
|
|
517
517
|
let _ = app.emit("clips:recorder-stop", ());
|
|
518
518
|
} else {
|
|
519
519
|
toggle_popover(app);
|
|
@@ -34,6 +34,11 @@ pub struct RecordingActive(pub Mutex<bool>);
|
|
|
34
34
|
#[derive(Default)]
|
|
35
35
|
pub struct MeetingActive(pub Mutex<bool>);
|
|
36
36
|
|
|
37
|
+
/// Active meeting id, when meeting notes are currently running. Kept separate
|
|
38
|
+
/// from `MeetingActive` so older boolean-only state checks stay simple.
|
|
39
|
+
#[derive(Default)]
|
|
40
|
+
pub struct ActiveMeetingId(pub Mutex<Option<String>>);
|
|
41
|
+
|
|
37
42
|
#[allow(dead_code)]
|
|
38
43
|
/// Whether dictation is toggled on.
|
|
39
44
|
#[derive(Default)]
|
|
@@ -8,7 +8,7 @@ use crate::clips::{force_show_popover, toggle_popover};
|
|
|
8
8
|
use crate::dlog;
|
|
9
9
|
use crate::state::{TrayAnchor, TrayMeetings};
|
|
10
10
|
use crate::tray_meetings::{build_meetings_section, handle_meeting_menu_click, MeetingItem};
|
|
11
|
-
use crate::util::is_recording_active;
|
|
11
|
+
use crate::util::{is_meeting_active, is_recording_active};
|
|
12
12
|
use crate::TRAY_PNG;
|
|
13
13
|
|
|
14
14
|
/// Build the full tray menu with the given upcoming-meetings list. Used both
|
|
@@ -22,15 +22,18 @@ fn build_menu_with_meetings(
|
|
|
22
22
|
let meetings_submenu = build_meetings_section(app, meetings)?;
|
|
23
23
|
let show_item = MenuItem::with_id(app, "show", "Show popover", true, None::<&str>)?;
|
|
24
24
|
let recording_active = is_recording_active(app);
|
|
25
|
+
let meeting_active = is_meeting_active(app);
|
|
25
26
|
let stop_item = MenuItem::with_id(
|
|
26
27
|
app,
|
|
27
28
|
"stop",
|
|
28
|
-
if
|
|
29
|
+
if meeting_active {
|
|
30
|
+
"Stop meeting notes"
|
|
31
|
+
} else if recording_active {
|
|
29
32
|
"Stop recording"
|
|
30
33
|
} else {
|
|
31
34
|
"No active recording"
|
|
32
35
|
},
|
|
33
|
-
recording_active,
|
|
36
|
+
recording_active || meeting_active,
|
|
34
37
|
None::<&str>,
|
|
35
38
|
)?;
|
|
36
39
|
let has_last_dictation = app
|
|
@@ -132,7 +135,11 @@ pub fn build_tray(app: &tauri::App) -> Result<(), Box<dyn std::error::Error>> {
|
|
|
132
135
|
match id_ref {
|
|
133
136
|
"show" => force_show_popover(app),
|
|
134
137
|
"stop" => {
|
|
135
|
-
|
|
138
|
+
if is_meeting_active(app) {
|
|
139
|
+
let _ = app.emit("clips:pill-stop", serde_json::json!({}));
|
|
140
|
+
} else {
|
|
141
|
+
let _ = app.emit("clips:recorder-stop", ());
|
|
142
|
+
}
|
|
136
143
|
}
|
|
137
144
|
"paste-last-dictation" => {
|
|
138
145
|
let app = app.clone();
|
|
@@ -213,8 +220,13 @@ pub fn build_tray(app: &tauri::App) -> Result<(), Box<dyn std::error::Error>> {
|
|
|
213
220
|
{
|
|
214
221
|
let app = tray.app_handle();
|
|
215
222
|
let active = is_recording_active(app);
|
|
216
|
-
|
|
217
|
-
|
|
223
|
+
let meeting_active = is_meeting_active(app);
|
|
224
|
+
dlog!(
|
|
225
|
+
"[clips-tray] tray click — is_recording_active={} is_meeting_active={}",
|
|
226
|
+
active,
|
|
227
|
+
meeting_active
|
|
228
|
+
);
|
|
229
|
+
if active && !meeting_active {
|
|
218
230
|
let _ = app.emit("clips:recorder-stop", ());
|
|
219
231
|
} else {
|
|
220
232
|
toggle_popover(app);
|