@agent-native/core 0.90.3 → 0.90.4
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/bin/agent-native.js +25 -3
- package/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +9 -0
- package/corpus/core/bin/agent-native.js +25 -3
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/engine/index.ts +1 -0
- package/corpus/core/src/agent/engine/registry.ts +63 -2
- package/corpus/core/src/cli/create.ts +35 -0
- package/corpus/core/src/cli/design-connect.ts +248 -0
- package/corpus/core/src/cli/index.ts +84 -7
- package/corpus/core/src/cli/workspacify.ts +34 -0
- package/corpus/core/src/client/history/VersionHistoryPanel.tsx +77 -47
- package/corpus/core/src/client/history/index.ts +3 -0
- package/corpus/core/src/client/history/use-history.ts +28 -0
- package/corpus/core/src/client/index.ts +8 -0
- package/corpus/core/src/client/review/index.ts +5 -0
- package/corpus/core/src/client/review/use-review.ts +40 -2
- package/corpus/core/src/history/actions/create-resource-version.ts +11 -8
- package/corpus/core/src/history/index.ts +0 -1
- package/corpus/core/src/history/registry.ts +24 -12
- package/corpus/core/src/history/store.ts +84 -53
- package/corpus/core/src/review/actions/consume-review-feedback.ts +15 -5
- package/corpus/core/src/review/actions/delete-review-comment.ts +15 -3
- package/corpus/core/src/review/actions/resolve-review-thread.ts +12 -5
- package/corpus/core/src/review/index.ts +0 -5
- package/corpus/core/src/review/registry.ts +24 -12
- package/corpus/core/src/review/store.ts +10 -7
- package/corpus/core/src/server/agent-chat-plugin.ts +6 -0
- package/corpus/core/src/server/credential-provider.ts +13 -3
- package/corpus/templates/assets/actions/_preset-skeleton-validation.ts +11 -11
- package/corpus/templates/assets/actions/generate-image.ts +64 -28
- package/corpus/templates/assets/app/components/generation/GenerationResults.tsx +1 -1
- package/corpus/templates/assets/app/routes/brand-kits.$id_.presets.$presetId.tsx +91 -74
- package/corpus/templates/assets/server/lib/generation.ts +29 -4
- package/corpus/templates/assets/server/lib/image-processing.ts +131 -0
- package/corpus/templates/calendar/.agents/skills/event-management/SKILL.md +24 -2
- package/corpus/templates/calendar/AGENTS.md +3 -0
- package/corpus/templates/calendar/actions/create-event.ts +8 -34
- package/corpus/templates/calendar/actions/event-action-helpers.ts +52 -0
- package/corpus/templates/calendar/actions/manage-event-draft.ts +7 -34
- package/corpus/templates/calendar/actions/update-event.ts +10 -23
- package/corpus/templates/calendar/app/components/calendar/AttendeeAutocomplete.tsx +47 -0
- package/corpus/templates/calendar/app/components/calendar/CreateEventDialog.tsx +23 -0
- package/corpus/templates/calendar/app/components/calendar/EventAttendeesSection.tsx +59 -2
- package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +40 -1
- package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +34 -1
- package/corpus/templates/calendar/app/hooks/use-events.ts +6 -0
- package/corpus/templates/calendar/app/i18n/zh-TW.ts +4 -0
- package/corpus/templates/calendar/app/i18n-data.ts +40 -0
- package/corpus/templates/calendar/changelog/2026-07-07-you-can-mark-event-guests-as-optional-while-inviting-them-or.md +6 -0
- package/corpus/templates/calendar/server/handlers/events.ts +1 -0
- package/corpus/templates/calendar/server/lib/google-calendar.ts +4 -0
- package/corpus/templates/calendar/server/plugins/agent-chat.ts +1 -1
- package/corpus/templates/calendar/shared/api.ts +2 -0
- package/corpus/templates/clips/.agents/skills/ai-video-tools/SKILL.md +29 -3
- package/corpus/templates/clips/actions/generate-workflow.ts +19 -8
- package/corpus/templates/clips/actions/get-clips-ai-prefs.ts +36 -0
- package/corpus/templates/clips/actions/lib/clips-ai-prefs.ts +28 -0
- package/corpus/templates/clips/actions/regenerate-chapters.ts +21 -7
- package/corpus/templates/clips/actions/regenerate-summary.ts +26 -8
- package/corpus/templates/clips/actions/regenerate-title.ts +44 -7
- package/corpus/templates/clips/actions/update-clips-ai-prefs.ts +50 -0
- package/corpus/templates/clips/app/components/meetings/meeting-card.tsx +7 -2
- package/corpus/templates/clips/app/hooks/use-auto-title.ts +25 -10
- package/corpus/templates/clips/app/i18n/ar-SA.ts +28 -6
- package/corpus/templates/clips/app/i18n/de-DE.ts +28 -6
- package/corpus/templates/clips/app/i18n/en-US.ts +29 -6
- package/corpus/templates/clips/app/i18n/es-ES.ts +29 -6
- package/corpus/templates/clips/app/i18n/fr-FR.ts +30 -6
- package/corpus/templates/clips/app/i18n/hi-IN.ts +26 -6
- package/corpus/templates/clips/app/i18n/ja-JP.ts +27 -6
- package/corpus/templates/clips/app/i18n/ko-KR.ts +28 -6
- package/corpus/templates/clips/app/i18n/pt-BR.ts +28 -6
- package/corpus/templates/clips/app/i18n/zh-CN.ts +26 -6
- package/corpus/templates/clips/app/i18n/zh-TW.ts +25 -6
- package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +73 -0
- package/corpus/templates/clips/app/routes/_app.settings._index.tsx +1 -0
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +70 -2
- package/corpus/templates/clips/changelog/2026-07-07-ai-tools-can-include-the-full-video-not-just-the-transcript-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-07-clips-desktop-now-surfaces-meetings-and-dictate-directly-in-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-07-meeting-notes-now-explain-exactly-how-to-start-granola-style.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-07-meeting-start-alerts-now-include-separate-start-notes-and-jo.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-07-recordings-start-more-predictably-and-newly-saved-clips-refr.md +6 -0
- package/corpus/templates/clips/chrome-extension/public/manifest.json +1 -1
- package/corpus/templates/clips/desktop/src/app.tsx +435 -105
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +27 -21
- package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +23 -22
- package/corpus/templates/clips/desktop/src/styles.css +167 -2
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +1 -1
- package/corpus/templates/clips/desktop/src-tauri/src/notifications.rs +3 -7
- package/corpus/templates/clips/desktop/src-tauri/src/tray_meetings.rs +5 -4
- package/corpus/templates/clips/server/routes/_agent-native/clips/user-prefs.put.ts +16 -8
- package/corpus/templates/clips/shared/clips-ai-prefs.ts +81 -0
- package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +16 -1
- package/corpus/templates/design/actions/add-localhost-screens.ts +1 -0
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +345 -11
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +89 -14
- package/corpus/templates/design/app/components/design/bridge-security.ts +7 -1
- package/corpus/templates/design/app/components/design/multi-screen/types.ts +1 -1
- package/corpus/templates/design/app/i18n/zh-TW.ts +3 -0
- package/corpus/templates/design/app/i18n-data.ts +41 -0
- package/corpus/templates/design/changelog/2026-07-07-local-visual-edit-uses-live-app-iframes-for-click-to-edit-pr.md +6 -0
- package/dist/agent/engine/index.d.ts +1 -1
- package/dist/agent/engine/index.d.ts.map +1 -1
- package/dist/agent/engine/index.js +1 -1
- package/dist/agent/engine/index.js.map +1 -1
- package/dist/agent/engine/registry.d.ts +14 -0
- package/dist/agent/engine/registry.d.ts.map +1 -1
- package/dist/agent/engine/registry.js +56 -2
- package/dist/agent/engine/registry.js.map +1 -1
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +31 -0
- package/dist/cli/create.js.map +1 -1
- package/dist/cli/design-connect.d.ts.map +1 -1
- package/dist/cli/design-connect.js +186 -0
- package/dist/cli/design-connect.js.map +1 -1
- package/dist/cli/index.js +76 -9
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/workspacify.d.ts.map +1 -1
- package/dist/cli/workspacify.js +30 -0
- package/dist/cli/workspacify.js.map +1 -1
- package/dist/client/history/VersionHistoryPanel.d.ts +1 -1
- package/dist/client/history/VersionHistoryPanel.d.ts.map +1 -1
- package/dist/client/history/VersionHistoryPanel.js +12 -5
- package/dist/client/history/VersionHistoryPanel.js.map +1 -1
- package/dist/client/history/index.d.ts +1 -1
- package/dist/client/history/index.d.ts.map +1 -1
- package/dist/client/history/index.js +1 -1
- package/dist/client/history/index.js.map +1 -1
- package/dist/client/history/use-history.d.ts +12 -0
- package/dist/client/history/use-history.d.ts.map +1 -1
- package/dist/client/history/use-history.js +6 -0
- package/dist/client/history/use-history.js.map +1 -1
- package/dist/client/index.d.ts +2 -2
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +2 -2
- package/dist/client/index.js.map +1 -1
- package/dist/client/review/index.d.ts +1 -1
- package/dist/client/review/index.d.ts.map +1 -1
- package/dist/client/review/index.js +1 -1
- package/dist/client/review/index.js.map +1 -1
- package/dist/client/review/use-review.d.ts +23 -0
- package/dist/client/review/use-review.d.ts.map +1 -1
- package/dist/client/review/use-review.js +8 -0
- package/dist/client/review/use-review.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/history/actions/create-resource-version.js +7 -4
- package/dist/history/actions/create-resource-version.js.map +1 -1
- package/dist/history/index.d.ts +1 -1
- package/dist/history/index.d.ts.map +1 -1
- package/dist/history/index.js +1 -1
- package/dist/history/index.js.map +1 -1
- package/dist/history/registry.d.ts.map +1 -1
- package/dist/history/registry.js +14 -12
- package/dist/history/registry.js.map +1 -1
- package/dist/history/store.d.ts.map +1 -1
- package/dist/history/store.js +74 -49
- package/dist/history/store.js.map +1 -1
- package/dist/observability/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/review/actions/consume-review-feedback.d.ts +1 -0
- package/dist/review/actions/consume-review-feedback.js +8 -2
- package/dist/review/actions/consume-review-feedback.js.map +1 -1
- package/dist/review/actions/delete-review-comment.d.ts +2 -1
- package/dist/review/actions/delete-review-comment.js +11 -3
- package/dist/review/actions/delete-review-comment.js.map +1 -1
- package/dist/review/actions/resolve-review-thread.d.ts +2 -1
- package/dist/review/actions/resolve-review-thread.js +5 -2
- package/dist/review/actions/resolve-review-thread.js.map +1 -1
- package/dist/review/index.d.ts +1 -1
- package/dist/review/index.d.ts.map +1 -1
- package/dist/review/index.js +1 -1
- package/dist/review/index.js.map +1 -1
- package/dist/review/registry.d.ts.map +1 -1
- package/dist/review/registry.js +14 -12
- package/dist/review/registry.js.map +1 -1
- package/dist/review/store.d.ts +3 -3
- package/dist/review/store.d.ts.map +1 -1
- package/dist/review/store.js +7 -4
- package/dist/review/store.js.map +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +5 -0
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/credential-provider.d.ts +2 -0
- package/dist/server/credential-provider.d.ts.map +1 -1
- package/dist/server/credential-provider.js +13 -3
- package/dist/server/credential-provider.js.map +1 -1
- package/package.json +1 -1
|
@@ -999,7 +999,6 @@ async function captureTitleForRecording(params: {
|
|
|
999
999
|
}
|
|
1000
1000
|
|
|
1001
1001
|
const COUNTDOWN_EVENT_TIMEOUT_MS = 5000;
|
|
1002
|
-
const COUNTDOWN_OVERLAY_SETTLE_MS = 120;
|
|
1003
1002
|
|
|
1004
1003
|
function wait(ms: number): Promise<void> {
|
|
1005
1004
|
return new Promise((resolve) => window.setTimeout(resolve, ms));
|
|
@@ -1652,10 +1651,6 @@ async function runRecordingCountdown(wantsScreen: boolean) {
|
|
|
1652
1651
|
console.warn("[clips-recorder] countdown timed out — proceeding");
|
|
1653
1652
|
return;
|
|
1654
1653
|
}
|
|
1655
|
-
// The countdown webview emits before it finishes closing. Give macOS a
|
|
1656
|
-
// brief beat to remove the overlay and any shortcut handling before capture
|
|
1657
|
-
// starts, so the first frame/audio sample is the real recording.
|
|
1658
|
-
await wait(COUNTDOWN_OVERLAY_SETTLE_MS);
|
|
1659
1654
|
}
|
|
1660
1655
|
|
|
1661
1656
|
function showFinalizingFeedback() {
|
|
@@ -1852,29 +1847,35 @@ async function startNativeFullscreenRecording(
|
|
|
1852
1847
|
}).catch((err) => {
|
|
1853
1848
|
console.warn("[clips-recorder] mic warm failed:", err);
|
|
1854
1849
|
});
|
|
1855
|
-
const countdownPromise = runRecordingCountdown(true);
|
|
1856
1850
|
if (localOnly) {
|
|
1851
|
+
const countdownPromise = runRecordingCountdown(true);
|
|
1857
1852
|
id = localFolderName;
|
|
1858
1853
|
await Promise.all([countdownPromise, warmMic(id)]);
|
|
1859
1854
|
} else {
|
|
1860
|
-
const
|
|
1855
|
+
const captureTitlePromise = captureTitleForRecording({
|
|
1861
1856
|
mode: params.mode,
|
|
1862
1857
|
source: params.source,
|
|
1863
1858
|
});
|
|
1864
|
-
|
|
1865
|
-
const recordingPromise =
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1859
|
+
const countdownPromise = runRecordingCountdown(true);
|
|
1860
|
+
const recordingPromise = (async () => {
|
|
1861
|
+
const captureTitle = await captureTitlePromise;
|
|
1862
|
+
console.time("[clips-recorder] createServerRecording duration");
|
|
1863
|
+
try {
|
|
1864
|
+
return await createServerRecording(
|
|
1865
|
+
params.serverUrl,
|
|
1866
|
+
wantsCamera,
|
|
1867
|
+
wantsRecordedAudio,
|
|
1868
|
+
captureTitle,
|
|
1869
|
+
{
|
|
1870
|
+
mimeType: NATIVE_FULLSCREEN_MIME_TYPE,
|
|
1871
|
+
requestStreaming: true,
|
|
1872
|
+
streamingUploadClient: "desktop-native",
|
|
1873
|
+
},
|
|
1874
|
+
);
|
|
1875
|
+
} finally {
|
|
1876
|
+
console.timeEnd("[clips-recorder] createServerRecording duration");
|
|
1877
|
+
}
|
|
1878
|
+
})();
|
|
1878
1879
|
// The recording id usually lands well before the countdown ends — warm
|
|
1879
1880
|
// native capture as soon as it does so setup overlaps the 3-2-1.
|
|
1880
1881
|
const warmAndId = (async () => {
|
|
@@ -1918,6 +1919,11 @@ async function startNativeFullscreenRecording(
|
|
|
1918
1919
|
// Capture is now live — after rebasing the transcript timeline, stamp the
|
|
1919
1920
|
// timer baseline so the toolbar clock lines up with the real start.
|
|
1920
1921
|
startedAt = Date.now();
|
|
1922
|
+
emit("clips:toolbar-enabled", true).catch(() => {});
|
|
1923
|
+
emit("clips:recorder-state", {
|
|
1924
|
+
paused: false,
|
|
1925
|
+
elapsedMs: 0,
|
|
1926
|
+
}).catch(() => {});
|
|
1921
1927
|
localCameraExport?.start(2_000);
|
|
1922
1928
|
} catch (err) {
|
|
1923
1929
|
await localCameraExport?.cancel().catch(() => {});
|
|
@@ -23,9 +23,8 @@ const DEFAULT_AUTO_HIDE_MS = 30_000;
|
|
|
23
23
|
const SNOOZE_MS = 5 * 60_000;
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
|
-
* Open a meeting join URL via the Tauri shell plugin.
|
|
27
|
-
*
|
|
28
|
-
* `meetings:start-transcription` event use the same path.
|
|
26
|
+
* Open a meeting join URL via the Tauri shell plugin. Used by the
|
|
27
|
+
* notification's dedicated Join CTA (notes start is a separate action).
|
|
29
28
|
*/
|
|
30
29
|
async function openJoinUrl(url: string | null | undefined): Promise<void> {
|
|
31
30
|
if (!url) return;
|
|
@@ -41,7 +40,7 @@ async function openJoinUrl(url: string | null | undefined): Promise<void> {
|
|
|
41
40
|
* Variants:
|
|
42
41
|
*
|
|
43
42
|
* - Calendar event: solid left bar (green), meeting title, time,
|
|
44
|
-
* "Start notes" + "Snooze 5 min" buttons.
|
|
43
|
+
* "Start notes" + optional "Join" + "Snooze 5 min" buttons.
|
|
45
44
|
* - Ad-hoc call: dashed left bar (slate), "Call detected", app name,
|
|
46
45
|
* same controls.
|
|
47
46
|
*
|
|
@@ -170,11 +169,6 @@ export function MeetingNotification() {
|
|
|
170
169
|
if (!data || pending) return;
|
|
171
170
|
setPending(true);
|
|
172
171
|
setError(null);
|
|
173
|
-
// Open the join URL right away so the user feels the click — the
|
|
174
|
-
// recording action runs in parallel.
|
|
175
|
-
if (data.joinUrl) {
|
|
176
|
-
openJoinUrl(data.joinUrl);
|
|
177
|
-
}
|
|
178
172
|
emit("meetings:start-transcription", {
|
|
179
173
|
meetingId: data.meetingId,
|
|
180
174
|
joinUrl: data.joinUrl,
|
|
@@ -185,25 +179,20 @@ export function MeetingNotification() {
|
|
|
185
179
|
});
|
|
186
180
|
}
|
|
187
181
|
|
|
182
|
+
async function joinMeeting() {
|
|
183
|
+
if (!data?.joinUrl) return;
|
|
184
|
+
await openJoinUrl(data.joinUrl);
|
|
185
|
+
}
|
|
186
|
+
|
|
188
187
|
function snooze() {
|
|
189
188
|
if (!data) return;
|
|
190
189
|
// Hand the snooze to the Rust watcher so the reminder re-fires after the
|
|
191
190
|
// delay even though this overlay window closes right away. A setTimeout
|
|
192
|
-
// here would be torn down with the window and never fire
|
|
193
|
-
// immediately (as before) just re-showed the same banner instantly.
|
|
191
|
+
// here would be torn down with the window and never fire.
|
|
194
192
|
invoke("meetings_snooze", {
|
|
195
193
|
meetingId: data.meetingId,
|
|
196
194
|
minutes: Math.round(SNOOZE_MS / 60_000),
|
|
197
195
|
}).catch(() => {});
|
|
198
|
-
const payload = data;
|
|
199
|
-
// Hide the current banner and re-fire the same payload after the snooze
|
|
200
|
-
// delay. We re-emit through Tauri so the in-app banner overlay window
|
|
201
|
-
// (which is what hosts this React component) can rerender with a fresh
|
|
202
|
-
// auto-hide timer.
|
|
203
|
-
emit("meetings:show-notification", {
|
|
204
|
-
...payload,
|
|
205
|
-
subtitle: `${payload.subtitle} (snoozed)`,
|
|
206
|
-
} as NotificationData).catch(() => {});
|
|
207
196
|
hideNotification();
|
|
208
197
|
}
|
|
209
198
|
|
|
@@ -212,6 +201,7 @@ export function MeetingNotification() {
|
|
|
212
201
|
}
|
|
213
202
|
|
|
214
203
|
const isCalendar = data.type === "calendar";
|
|
204
|
+
const hasJoin = Boolean(data.joinUrl);
|
|
215
205
|
|
|
216
206
|
return (
|
|
217
207
|
<div
|
|
@@ -227,7 +217,9 @@ export function MeetingNotification() {
|
|
|
227
217
|
scheduleAutoHide(DEFAULT_AUTO_HIDE_MS);
|
|
228
218
|
}}
|
|
229
219
|
>
|
|
230
|
-
<div
|
|
220
|
+
<div
|
|
221
|
+
className={`meeting-notification${hasJoin ? " meeting-notification-with-join" : ""}`}
|
|
222
|
+
>
|
|
231
223
|
<div
|
|
232
224
|
className={`meeting-notification-bar ${isCalendar ? "meeting-notification-bar-calendar" : "meeting-notification-bar-adhoc"}`}
|
|
233
225
|
/>
|
|
@@ -243,13 +235,22 @@ export function MeetingNotification() {
|
|
|
243
235
|
</div>
|
|
244
236
|
<div className="meeting-notification-actions">
|
|
245
237
|
<button
|
|
246
|
-
className="meeting-notification-btn"
|
|
238
|
+
className="meeting-notification-btn meeting-notification-btn-primary"
|
|
247
239
|
onClick={takeNotes}
|
|
248
240
|
disabled={pending}
|
|
249
241
|
data-no-drag
|
|
250
242
|
>
|
|
251
243
|
{pending ? "Starting…" : "Start notes"}
|
|
252
244
|
</button>
|
|
245
|
+
{hasJoin ? (
|
|
246
|
+
<button
|
|
247
|
+
className="meeting-notification-btn meeting-notification-btn-secondary"
|
|
248
|
+
onClick={joinMeeting}
|
|
249
|
+
data-no-drag
|
|
250
|
+
>
|
|
251
|
+
Join
|
|
252
|
+
</button>
|
|
253
|
+
) : null}
|
|
253
254
|
<button
|
|
254
255
|
className="meeting-notification-btn meeting-notification-btn-secondary"
|
|
255
256
|
onClick={snooze}
|
|
@@ -1096,7 +1096,7 @@ body[data-clips-route="recording-pill"] #root {
|
|
|
1096
1096
|
|
|
1097
1097
|
.bottom-row {
|
|
1098
1098
|
display: grid;
|
|
1099
|
-
grid-template-columns: repeat(
|
|
1099
|
+
grid-template-columns: repeat(4, 1fr);
|
|
1100
1100
|
gap: 4px;
|
|
1101
1101
|
}
|
|
1102
1102
|
|
|
@@ -1138,6 +1138,145 @@ body[data-clips-route="recording-pill"] #root {
|
|
|
1138
1138
|
font-weight: 500;
|
|
1139
1139
|
}
|
|
1140
1140
|
|
|
1141
|
+
/* ------------------------------------------------------------------------- */
|
|
1142
|
+
/* Popover drill-in views */
|
|
1143
|
+
/* ------------------------------------------------------------------------- */
|
|
1144
|
+
|
|
1145
|
+
.app-popover-view {
|
|
1146
|
+
gap: 0;
|
|
1147
|
+
padding: 0;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
.popover-view {
|
|
1151
|
+
min-height: 260px;
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
.popover-view-header {
|
|
1155
|
+
margin-bottom: 2px;
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
.popover-view-header-spacer {
|
|
1159
|
+
flex: 1;
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
.popover-view-link {
|
|
1163
|
+
font-size: 11px;
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
.popover-empty-card {
|
|
1167
|
+
display: flex;
|
|
1168
|
+
flex-direction: column;
|
|
1169
|
+
gap: 8px;
|
|
1170
|
+
padding: 12px;
|
|
1171
|
+
border: 1px solid var(--border);
|
|
1172
|
+
border-radius: var(--radius);
|
|
1173
|
+
background: var(--surface);
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
.popover-empty-card strong {
|
|
1177
|
+
font-size: 13px;
|
|
1178
|
+
color: var(--fg);
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
.popover-card-heading {
|
|
1182
|
+
display: flex;
|
|
1183
|
+
align-items: center;
|
|
1184
|
+
gap: 8px;
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
.popover-list {
|
|
1188
|
+
display: flex;
|
|
1189
|
+
flex-direction: column;
|
|
1190
|
+
gap: 6px;
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
.popover-list-item {
|
|
1194
|
+
display: flex;
|
|
1195
|
+
align-items: center;
|
|
1196
|
+
gap: 9px;
|
|
1197
|
+
padding: 9px;
|
|
1198
|
+
border: 1px solid var(--border);
|
|
1199
|
+
border-radius: var(--radius);
|
|
1200
|
+
background: var(--surface);
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
.popover-list-icon {
|
|
1204
|
+
display: inline-flex;
|
|
1205
|
+
align-items: center;
|
|
1206
|
+
justify-content: center;
|
|
1207
|
+
color: var(--fg-muted);
|
|
1208
|
+
flex-shrink: 0;
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
.popover-list-main {
|
|
1212
|
+
flex: 1;
|
|
1213
|
+
min-width: 0;
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
.popover-list-title {
|
|
1217
|
+
font-size: 12px;
|
|
1218
|
+
font-weight: 600;
|
|
1219
|
+
color: var(--fg);
|
|
1220
|
+
white-space: nowrap;
|
|
1221
|
+
overflow: hidden;
|
|
1222
|
+
text-overflow: ellipsis;
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
.popover-list-sub {
|
|
1226
|
+
margin-top: 1px;
|
|
1227
|
+
font-size: 11px;
|
|
1228
|
+
color: var(--fg-subtle);
|
|
1229
|
+
white-space: nowrap;
|
|
1230
|
+
overflow: hidden;
|
|
1231
|
+
text-overflow: ellipsis;
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
.popover-list-action {
|
|
1235
|
+
flex-shrink: 0;
|
|
1236
|
+
border: 1px solid var(--border-strong);
|
|
1237
|
+
border-radius: var(--radius-sm);
|
|
1238
|
+
background: var(--bg);
|
|
1239
|
+
color: var(--fg);
|
|
1240
|
+
padding: 6px 8px;
|
|
1241
|
+
font-size: 11px;
|
|
1242
|
+
font-weight: 600;
|
|
1243
|
+
cursor: pointer;
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
.popover-list-action:hover {
|
|
1247
|
+
background: var(--surface-hover);
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
.popover-list-action-primary {
|
|
1251
|
+
border-color: var(--brand);
|
|
1252
|
+
background: var(--brand);
|
|
1253
|
+
color: var(--bg);
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
.popover-list-action-primary:hover {
|
|
1257
|
+
background: var(--brand-hover);
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
.popover-kv {
|
|
1261
|
+
display: flex;
|
|
1262
|
+
align-items: center;
|
|
1263
|
+
justify-content: space-between;
|
|
1264
|
+
gap: 12px;
|
|
1265
|
+
padding-top: 6px;
|
|
1266
|
+
border-top: 1px solid var(--border);
|
|
1267
|
+
font-size: 11px;
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
.popover-kv span {
|
|
1271
|
+
color: var(--fg-subtle);
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
.popover-kv strong {
|
|
1275
|
+
min-width: 0;
|
|
1276
|
+
text-align: right;
|
|
1277
|
+
font-size: 11px;
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1141
1280
|
.badge {
|
|
1142
1281
|
position: absolute;
|
|
1143
1282
|
top: -4px;
|
|
@@ -3468,7 +3607,19 @@ body[data-clips-route="recording-pill"] #root {
|
|
|
3468
3607
|
color: white;
|
|
3469
3608
|
pointer-events: auto;
|
|
3470
3609
|
min-width: 280px;
|
|
3471
|
-
max-width:
|
|
3610
|
+
max-width: 400px;
|
|
3611
|
+
}
|
|
3612
|
+
|
|
3613
|
+
.meeting-notification-with-join {
|
|
3614
|
+
flex-wrap: wrap;
|
|
3615
|
+
align-items: flex-start;
|
|
3616
|
+
row-gap: 10px;
|
|
3617
|
+
}
|
|
3618
|
+
|
|
3619
|
+
.meeting-notification-with-join .meeting-notification-actions {
|
|
3620
|
+
flex: 1 1 100%;
|
|
3621
|
+
justify-content: flex-end;
|
|
3622
|
+
padding-left: 13px; /* align under content past the bar */
|
|
3472
3623
|
}
|
|
3473
3624
|
|
|
3474
3625
|
.meeting-notification-bar {
|
|
@@ -3536,6 +3687,20 @@ body[data-clips-route="recording-pill"] #root {
|
|
|
3536
3687
|
cursor: progress;
|
|
3537
3688
|
}
|
|
3538
3689
|
|
|
3690
|
+
.meeting-notification-btn-primary {
|
|
3691
|
+
background: #ffffff;
|
|
3692
|
+
color: #111827;
|
|
3693
|
+
}
|
|
3694
|
+
|
|
3695
|
+
.meeting-notification-btn-primary:hover {
|
|
3696
|
+
background: #f3f4f6;
|
|
3697
|
+
}
|
|
3698
|
+
|
|
3699
|
+
.meeting-notification-btn-primary:disabled {
|
|
3700
|
+
background: rgba(255, 255, 255, 0.7);
|
|
3701
|
+
color: #374151;
|
|
3702
|
+
}
|
|
3703
|
+
|
|
3539
3704
|
.meeting-notification-actions {
|
|
3540
3705
|
display: inline-flex;
|
|
3541
3706
|
align-items: center;
|
|
@@ -648,7 +648,7 @@ pub async fn native_fullscreen_recording_available() -> Result<bool, String> {
|
|
|
648
648
|
/// the countdown by now; this is just a safety net so a slow/denied mic can't
|
|
649
649
|
/// stall the start.
|
|
650
650
|
#[cfg(target_os = "macos")]
|
|
651
|
-
const MIC_WARM_TIMEOUT_MS: u64 =
|
|
651
|
+
const MIC_WARM_TIMEOUT_MS: u64 = 250;
|
|
652
652
|
|
|
653
653
|
/// Acquire a backend (ScreenCaptureKit, or the screencapture fallback) and
|
|
654
654
|
/// store it as the active session. Shared by the immediate-start command and
|
|
@@ -16,8 +16,8 @@ use crate::util::{
|
|
|
16
16
|
};
|
|
17
17
|
|
|
18
18
|
const MEETING_NOTIFICATION_LABEL: &str = "meeting-notif";
|
|
19
|
-
const NOTIFICATION_W_LOGICAL: u32 =
|
|
20
|
-
const NOTIFICATION_H_LOGICAL: u32 =
|
|
19
|
+
const NOTIFICATION_W_LOGICAL: u32 = 420;
|
|
20
|
+
const NOTIFICATION_H_LOGICAL: u32 = 148;
|
|
21
21
|
const NOTIFICATION_TOP_MARGIN_LOGICAL: u32 = 44;
|
|
22
22
|
const NOTIFICATION_RIGHT_MARGIN_LOGICAL: u32 = 24;
|
|
23
23
|
|
|
@@ -125,11 +125,7 @@ pub async fn notify_meeting_starting(
|
|
|
125
125
|
} else {
|
|
126
126
|
format!("in {} min", (starts_in_secs / 60).max(1))
|
|
127
127
|
};
|
|
128
|
-
let body =
|
|
129
|
-
format!("{} — Start notes", pretty_in)
|
|
130
|
-
} else {
|
|
131
|
-
format!("Starts {}", pretty_in)
|
|
132
|
-
};
|
|
128
|
+
let body = format!("Starts {}", pretty_in);
|
|
133
129
|
dlog!(
|
|
134
130
|
"[clips-tray] notify_meeting_starting id={} title={} body={}",
|
|
135
131
|
meeting_id,
|
|
@@ -25,21 +25,22 @@ pub struct MeetingItem {
|
|
|
25
25
|
|
|
26
26
|
pub const MEETING_ID_PREFIX: &str = "meeting:";
|
|
27
27
|
|
|
28
|
-
/// Build the "
|
|
28
|
+
/// Build the "Start Meeting Notes" submenu populated with up to 3 events. If
|
|
29
29
|
/// the list is empty, returns a submenu containing a single disabled "No
|
|
30
|
-
///
|
|
30
|
+
/// meetings ready" item — keeps the menu structure stable. Selecting a real
|
|
31
|
+
/// meeting starts live notes immediately.
|
|
31
32
|
pub fn build_meetings_section(
|
|
32
33
|
app: &AppHandle,
|
|
33
34
|
upcoming: Vec<MeetingItem>,
|
|
34
35
|
) -> Result<Submenu<Wry>, Box<dyn std::error::Error>> {
|
|
35
36
|
let mut builder: SubmenuBuilder<'_, Wry, AppHandle> =
|
|
36
|
-
SubmenuBuilder::new(app, "
|
|
37
|
+
SubmenuBuilder::new(app, "Start Meeting Notes");
|
|
37
38
|
|
|
38
39
|
if upcoming.is_empty() {
|
|
39
40
|
let placeholder = MenuItem::with_id(
|
|
40
41
|
app,
|
|
41
42
|
"meeting:none",
|
|
42
|
-
"No
|
|
43
|
+
"No meetings ready",
|
|
43
44
|
false,
|
|
44
45
|
None::<&str>,
|
|
45
46
|
)?;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getSession } from "@agent-native/core/server";
|
|
2
|
-
import { putUserSetting } from "@agent-native/core/settings";
|
|
2
|
+
import { getUserSetting, putUserSetting } from "@agent-native/core/settings";
|
|
3
3
|
import { defineEventHandler, getHeader, readBody, setResponseStatus } from "h3";
|
|
4
4
|
|
|
5
5
|
const CLIPS_USER_PREFS_KEY = "clips-user-prefs";
|
|
@@ -17,11 +17,19 @@ export default defineEventHandler(async (event) => {
|
|
|
17
17
|
return { error: "Invalid settings payload" };
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
20
|
+
// Merge so partial Settings saves (playback, notifications, …) don't wipe
|
|
21
|
+
// AI-tool prefs written from the recording page popover.
|
|
22
|
+
const existing =
|
|
23
|
+
(await getUserSetting(session.email, CLIPS_USER_PREFS_KEY)) ?? {};
|
|
24
|
+
const next = {
|
|
25
|
+
...(typeof existing === "object" && existing && !Array.isArray(existing)
|
|
26
|
+
? existing
|
|
27
|
+
: {}),
|
|
28
|
+
...(body as Record<string, unknown>),
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
await putUserSetting(session.email, CLIPS_USER_PREFS_KEY, next, {
|
|
32
|
+
requestSource: getHeader(event, "x-request-source") || undefined,
|
|
33
|
+
});
|
|
34
|
+
return next;
|
|
27
35
|
});
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clips user preferences that affect AI video tools.
|
|
3
|
+
*
|
|
4
|
+
* Stored under the shared `clips-user-prefs` user-setting key so Settings and
|
|
5
|
+
* the AI tools popover both read/write the same object.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export const CLIPS_USER_PREFS_KEY = "clips-user-prefs";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Preferred chat model when Include full video is on. Full-recording video
|
|
12
|
+
* input only works with Gemini (Builder Gemini or Google/Gemini BYOK) —
|
|
13
|
+
* Claude and OpenAI cannot ingest the MP4/WebM.
|
|
14
|
+
*/
|
|
15
|
+
export const CLIPS_FULL_VIDEO_AI_ENGINE = "builder";
|
|
16
|
+
export const CLIPS_FULL_VIDEO_AI_MODEL = "gemini-3-5-flash";
|
|
17
|
+
|
|
18
|
+
export type ClipsAiPrefs = {
|
|
19
|
+
/**
|
|
20
|
+
* When true, Clips AI tools (default title/description, regenerate title/
|
|
21
|
+
* description/chapters, workflows) must watch the full recording — not just
|
|
22
|
+
* the transcript — before writing results. Off by default for cost/latency.
|
|
23
|
+
* Sending the full video requires a Gemini model.
|
|
24
|
+
*/
|
|
25
|
+
includeFullVideoInAi?: boolean;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export type ClipsUserPrefs = ClipsAiPrefs & {
|
|
29
|
+
defaultPlaybackSpeed?: string;
|
|
30
|
+
emailNotifications?: boolean;
|
|
31
|
+
displayName?: string;
|
|
32
|
+
transcriptCleanupEnabled?: boolean;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export function isIncludeFullVideoInAiEnabled(
|
|
36
|
+
prefs: ClipsAiPrefs | Record<string, unknown> | null | undefined,
|
|
37
|
+
): boolean {
|
|
38
|
+
return prefs?.includeFullVideoInAi === true;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Extra agent instructions when the user wants AI tools to watch the clip,
|
|
43
|
+
* not just read the audio transcript. Full-video understanding requires Gemini.
|
|
44
|
+
*/
|
|
45
|
+
export function buildFullVideoAiInstructions(recordingId: string): string {
|
|
46
|
+
return (
|
|
47
|
+
`The user enabled "Include full video" for Clips AI. Do NOT rely on the ` +
|
|
48
|
+
`transcript alone — audio is often incomplete for accurate titles, ` +
|
|
49
|
+
`descriptions, chapters, and workflow docs. ` +
|
|
50
|
+
`IMPORTANT: sending / understanding the full recording only works with ` +
|
|
51
|
+
`Gemini (Builder Gemini or a Google Gemini key). Use a Gemini model for ` +
|
|
52
|
+
`this turn — Claude and OpenAI cannot ingest the full video file. ` +
|
|
53
|
+
`Prefer attaching or uploading the recording video itself to Gemini when ` +
|
|
54
|
+
`available. Otherwise use \`get-recording-player-data --recordingId=${recordingId}\` ` +
|
|
55
|
+
`(preferred in-app) or \`create-recording-agent-link --recordingId=${recordingId}\` ` +
|
|
56
|
+
`and fetch that context URL, then sample the timeline via recommendedFrames / ` +
|
|
57
|
+
`the frame API. Combine on-screen UI text, product names, and visual context ` +
|
|
58
|
+
`with any transcript. If the transcript is thin or missing, lean harder on ` +
|
|
59
|
+
`the video.`
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function withFullVideoAiInstructions(
|
|
64
|
+
message: string,
|
|
65
|
+
recordingId: string,
|
|
66
|
+
includeFullVideo: boolean,
|
|
67
|
+
): string {
|
|
68
|
+
if (!includeFullVideo) return message;
|
|
69
|
+
return `${message} ${buildFullVideoAiInstructions(recordingId)}`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Model selection for sendToAgentChat when Include full video is enabled. */
|
|
73
|
+
export function fullVideoAiModelSelection(): {
|
|
74
|
+
engine: string;
|
|
75
|
+
model: string;
|
|
76
|
+
} {
|
|
77
|
+
return {
|
|
78
|
+
engine: CLIPS_FULL_VIDEO_AI_ENGINE,
|
|
79
|
+
model: CLIPS_FULL_VIDEO_AI_MODEL,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
@@ -21,6 +21,19 @@ iframe-backed screens on the infinite canvas.
|
|
|
21
21
|
- Each screen is a URL-backed iframe, not copied HTML.
|
|
22
22
|
- Each screen keeps its own URL metadata: `connectionId`, `routeId`, `path`,
|
|
23
23
|
`url`, `bridgeUrl`, title, and viewport size.
|
|
24
|
+
- Localhost Edit mode renders the running app through the local bridge as a live
|
|
25
|
+
iframe with the same editor bridge used by HTML designs. It is not a frozen
|
|
26
|
+
static DOM snapshot.
|
|
27
|
+
- The live editor is same-origin through the local bridge proxy. This boots
|
|
28
|
+
CSR apps and root-relative assets, but it is still a localhost editing proxy:
|
|
29
|
+
app-origin cookies, WebSockets/HMR, SSE, and non-GET app API calls may need a
|
|
30
|
+
future dev-server/plugin integration for perfect parity with the app's own
|
|
31
|
+
origin.
|
|
32
|
+
- Interact mode renders the app's normal URL so app navigation, scrolling,
|
|
33
|
+
links, and form controls behave as they would in the browser.
|
|
34
|
+
- While a localhost screen has pending live visual edits, do not switch back to
|
|
35
|
+
Interact until the user either applies the edits to source or explicitly
|
|
36
|
+
aborts/discards the preview.
|
|
24
37
|
- Start in Design's screen overview mode. The user can edit/select/drop on any
|
|
25
38
|
visible screen from the overview canvas.
|
|
26
39
|
- In overview, screens are static like Figma frames: no iframe scrolling or app
|
|
@@ -168,7 +181,9 @@ explicitly asks to freeze a snapshot.
|
|
|
168
181
|
Once a connection is registered, the design editor's Code panel (left rail →
|
|
169
182
|
Code, or `navigate --view editor --designId <id> --leftPanel code`) shows a
|
|
170
183
|
local-files workspace root for that connection next to the design's own files.
|
|
171
|
-
|
|
184
|
+
Treat that root like VS Code opened at the connected project directory: file
|
|
185
|
+
tree, search, open/edit, and save are backed by the real local files. It lists
|
|
186
|
+
the connected app's text/code files through the bridge
|
|
172
187
|
(`list-local-files` / `read-local-file`); build output, `node_modules`,
|
|
173
188
|
`.git`, and secret-looking paths (`.env*`, key files) are always excluded.
|
|
174
189
|
|
|
@@ -468,6 +468,7 @@ export default defineAction({
|
|
|
468
468
|
sourceFile: screen.sourceFile,
|
|
469
469
|
path: screen.path,
|
|
470
470
|
bridgeUrl: connection.bridgeUrl ?? undefined,
|
|
471
|
+
bridgeToken: connection.bridgeToken ?? undefined,
|
|
471
472
|
};
|
|
472
473
|
previousMetadata[screen.id] = metadata;
|
|
473
474
|
previousLocalhostScreens[screen.id] = metadata;
|