@agent-native/core 0.175.4 → 0.175.5-nightly-20260827014436
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/templates/analytics/actions/get-explorer-dashboard.ts +1 -0
- package/corpus/templates/analytics/app/components/auth/AuthProvider.tsx +8 -6
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +32 -0
- package/corpus/templates/analytics/app/components/layout/Layout.tsx +1 -1
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +66 -7
- package/corpus/templates/analytics/app/lib/prefetch-keys.ts +10 -0
- package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +9 -7
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +10 -11
- package/corpus/templates/analytics/changelog/2026-08-24-feature-flag-changes-recover-from-temporary-workspace-direct.md +6 -0
- package/corpus/templates/analytics/changelog/2026-08-26-analytics-distinguishes-daily-from-weekly-activity-shows-sta.md +6 -0
- package/corpus/templates/analytics/changelog/2026-08-26-dashboard-metadata-now-shows-the-original-creator-separately.md +6 -0
- package/corpus/templates/analytics/changelog/2026-08-26-dashboard-names-stay-editable-when-the-analytics-sidebar-ref.md +6 -0
- package/corpus/templates/analytics/changelog/2026-08-26-fixed-the-agent-re-asking-dashboard-scope-questions-you-had-.md +6 -0
- package/corpus/templates/analytics/server/db/schema.ts +2 -0
- package/corpus/templates/analytics/server/lib/agent-readable-resource-context.ts +8 -2
- package/corpus/templates/analytics/server/lib/dashboards-store.ts +5 -0
- package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +6 -4
- package/corpus/templates/analytics/server/lib/migrate-analytics-artifacts.ts +19 -1
- package/corpus/templates/analytics/server/lib/workspace-feature-flags.ts +40 -9
- package/corpus/templates/analytics/server/plugins/db.ts +10 -0
- package/corpus/templates/analytics/server/routes/api/dashboard-agent-context.json.get.ts +1 -0
- package/corpus/templates/analytics/shared/feature-flags.ts +11 -1
- package/corpus/templates/calendar/app/components/calendar/GoogleConnectBanner.tsx +2 -9
- package/corpus/templates/clips/.agents/skills/ai-video-tools/SKILL.md +8 -7
- package/corpus/templates/clips/.agents/skills/recording/SKILL.md +9 -8
- package/corpus/templates/clips/actions/import-loom-recording.ts +1 -1
- package/corpus/templates/clips/actions/lib/loom-import-job.ts +52 -35
- package/corpus/templates/clips/actions/request-transcript.ts +6 -183
- package/corpus/templates/clips/app/components/clips-avatar.tsx +46 -0
- package/corpus/templates/clips/app/components/library/recording-card.tsx +8 -7
- package/corpus/templates/clips/app/components/library/space-card.tsx +8 -5
- package/corpus/templates/clips/app/components/meetings/attendee-stack.tsx +7 -8
- package/corpus/templates/clips/app/components/meetings/transcript-bubbles.tsx +19 -12
- package/corpus/templates/clips/app/components/player/transcript-panel.tsx +128 -94
- package/corpus/templates/clips/app/components/recorder/microphone-visualizer.tsx +25 -104
- package/corpus/templates/clips/app/components/workspace/members-list.tsx +8 -6
- package/corpus/templates/clips/app/components/workspace/notifications-list.tsx +8 -6
- package/corpus/templates/clips/app/components/workspace/top-creators-table.tsx +8 -6
- package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +8 -7
- package/corpus/templates/clips/app/routes/_app.settings._index.tsx +0 -26
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +1 -4
- package/corpus/templates/clips/changelog/2026-08-26-detected-calls-no-longer-duplicate-meetings-or-strand-the-prompt.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +8 -10
- package/corpus/templates/clips/desktop/src/components/live-waveform.tsx +99 -0
- package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +248 -55
- package/corpus/templates/clips/desktop/src/lib/agent-steps.ts +477 -0
- package/corpus/templates/clips/desktop/src/lib/ask-sheet-layout.ts +71 -0
- package/corpus/templates/clips/desktop/src/lib/meeting-ask.ts +297 -0
- package/corpus/templates/clips/desktop/src/lib/url.ts +24 -0
- package/corpus/templates/clips/desktop/src/overlays/ask-steps.tsx +155 -0
- package/corpus/templates/clips/desktop/src/overlays/flow-bar.tsx +7 -81
- package/corpus/templates/clips/desktop/src/overlays/live-transcript.tsx +29 -2
- package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +80 -22
- package/corpus/templates/clips/desktop/src/overlays/record-pill.tsx +13 -53
- package/corpus/templates/clips/desktop/src/overlays/recording-pill.tsx +1017 -82
- package/corpus/templates/clips/desktop/src/styles.css +796 -57
- package/corpus/templates/clips/desktop/src/tailwind.css +23 -2
- package/corpus/templates/clips/desktop/src-tauri/src/adhoc_meetings_watcher.rs +1765 -219
- package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +6 -0
- package/corpus/templates/clips/desktop/src-tauri/src/meetings_watcher.rs +45 -30
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen/custom_capture.rs +5 -3
- package/corpus/templates/clips/desktop/src-tauri/src/notifications.rs +91 -0
- package/corpus/templates/clips/desktop/src-tauri/src/recording_indicator.rs +123 -27
- package/corpus/templates/clips/server/routes/_agent-native/google/callback.get.ts +16 -0
- package/corpus/templates/clips/shared/audio-meter.ts +145 -0
- package/corpus/templates/clips/shared/clips-ai-prefs.ts +0 -1
- package/corpus/templates/clips/shared/live-waveform.tsx +119 -0
- package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +44 -22
- package/corpus/templates/content/app/components/ui/avatar.tsx +1 -0
- package/corpus/templates/design/app/components/visual-editor/ReviewCanvasPins.tsx +4 -1
- package/corpus/templates/design/app/hooks/use-question-flow.ts +9 -2
- package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +111 -37
- package/corpus/templates/design/changelog/2026-08-26-design-no-longer-asks-users-to-confirm-guided-questions-they-have-already-answered.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-08-26-signing-out-goes-straight-to-the-sign-in-page.md +5 -0
- package/corpus/templates/dispatch/server/lib/org-apps-directory.ts +96 -47
- package/corpus/templates/dispatch/server/plugins/org-apps-directory.ts +73 -29
- package/corpus/templates/factory/.env.example +6 -2
- package/corpus/templates/factory/AGENTS.md +3 -3
- package/corpus/templates/factory/README.md +4 -2
- package/corpus/templates/factory/server/connectors/credentials.ts +70 -4
- package/corpus/templates/mail/actions/get-email.ts +68 -21
- package/corpus/templates/mail/actions/get-thread.ts +92 -51
- package/corpus/templates/mail/app/components/GoogleConnectBanner.tsx +2 -9
- package/corpus/templates/mail/app/hooks/use-emails.ts +7 -4
- package/corpus/templates/mail/server/lib/mail-connector-catalog.ts +7 -4
- package/corpus/templates/plan/app/pages/PlansPage.tsx +23 -15
- package/corpus/templates/slides/actions/add-deck.ts +20 -0
- package/corpus/templates/slides/actions/create-deck.ts +17 -7
- package/corpus/templates/slides/actions/delete-deck.ts +45 -5
- package/corpus/templates/slides/actions/get-deck.ts +77 -11
- package/corpus/templates/slides/actions/patch-deck.ts +14 -2
- package/corpus/templates/slides/actions/save-deck.ts +20 -0
- package/corpus/templates/slides/app/components/comments/SlideCommentsPanel.tsx +18 -7
- package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +38 -14
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +2 -2
- package/corpus/templates/slides/app/components/ui/avatar.tsx +1 -0
- package/corpus/templates/slides/server/handlers/decks.ts +10 -0
- package/corpus/templates/slides/shared/slide-ids.ts +133 -0
- package/corpus/templates/tasks/DEVELOPING.md +8 -0
- package/corpus/templates/tasks/drizzle.config.ts +3 -1
- package/corpus/templates/tasks/drizzle.local.config.ts +6 -0
- package/corpus/templates/tasks/package.json +2 -1
- package/corpus/templates/tasks/server/db/migrations/meta/0000_snapshot.json +234 -0
- package/corpus/templates/tasks/server/db/migrations/meta/_journal.json +13 -0
- package/corpus/templates/tasks/server/db/migrations.ts +110 -0
- package/corpus/templates/tasks/server/plugins/db.ts +3 -87
- package/dist/client/AssistantChat.js +3 -10
- package/dist/client/guided-questions.d.ts +11 -1
- package/dist/client/guided-questions.js +24 -7
- package/dist/client/hooks/index.d.ts +2 -1
- package/dist/client/hooks/index.js +2 -1
- package/dist/client/org/OrgSwitcher.js +3 -17
- package/dist/client/review/ReviewThreadPanel.js +4 -2
- package/dist/client/sign-out.d.ts +15 -0
- package/dist/client/sign-out.js +82 -0
- package/dist/client/use-action.d.ts +2 -0
- package/dist/client/use-action.js +4 -2
- package/dist/client/use-session.d.ts +23 -1
- package/dist/client/use-session.js +46 -0
- package/dist/collab/client.js +20 -5
- package/dist/collab/presence.d.ts +3 -1
- package/dist/collab/presence.js +48 -45
- package/dist/collab/routes.d.ts +1 -1
- package/dist/db/drizzle-config.d.ts +7 -0
- package/dist/db/drizzle-config.js +35 -9
- package/dist/db/drizzle-migrations.d.ts +28 -0
- package/dist/db/drizzle-migrations.js +81 -0
- package/dist/db/index.d.ts +1 -1
- package/dist/db/migrations.d.ts +13 -1
- package/dist/db/migrations.js +60 -31
- package/dist/db/schema.js +4 -1
- package/dist/deploy/build.d.ts +6 -0
- package/dist/deploy/build.js +41 -0
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/mcp/index.d.ts +1 -1
- package/dist/mcp/index.js +1 -1
- package/dist/mcp/org-directory.d.ts +19 -7
- package/dist/mcp/org-directory.js +92 -68
- package/dist/mcp/screen-memory-stdio.d.ts +7 -7
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/org/context.d.ts +6 -0
- package/dist/org/context.js +22 -0
- package/dist/org/index.d.ts +1 -1
- package/dist/org/index.js +1 -1
- package/dist/resources/store.d.ts +12 -0
- package/dist/resources/store.js +55 -0
- package/dist/server/agent-discovery.d.ts +18 -2
- package/dist/server/agent-discovery.js +191 -22
- package/dist/server/auth.js +3 -2
- package/dist/server/builder-browser.d.ts +0 -4
- package/dist/server/builder-browser.js +23 -8
- package/dist/server/google-oauth.d.ts +18 -0
- package/dist/server/google-oauth.js +3 -3
- package/dist/server/onboarding-html.js +3 -3
- package/dist/server/poll.d.ts +4 -2
- package/dist/server/poll.js +8 -3
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/short-lived-token.d.ts +1 -1
- package/dist/server/short-lived-token.js +1 -1
- package/dist/shared/environment-lanes.d.ts +1 -0
- package/dist/shared/environment-lanes.js +1 -0
- package/dist/shared/google-profile-image.d.ts +2 -0
- package/dist/shared/google-profile-image.js +24 -0
- package/dist/shared/index.d.ts +1 -0
- package/dist/shared/index.js +1 -0
- package/dist/styles/agent-conversation.css +13 -0
- package/docs/content/creating-templates.mdx +10 -1
- package/docs/content/server-database.mdx +49 -3
- package/docs/content/server-plugins.mdx +39 -3
- package/docs/content/webmcp.mdx +2 -0
- package/package.json +2 -1
- package/corpus/templates/analytics/data/explorer-configs/_autosave.json +0 -18
- package/corpus/templates/analytics/data/explorer-configs/steve-chart.json +0 -18
- package/corpus/templates/analytics/data/explorer-dashboards/dashboard-1773260837897.json +0 -10
- package/corpus/templates/analytics/data/sync-config.json +0 -8
- package/corpus/templates/brain/data/sync-config.json +0 -1
- package/corpus/templates/calendar/data/availability.json +0 -32
- package/corpus/templates/calendar/data/settings.json +0 -6
- package/corpus/templates/calendar/data/sync-config.json +0 -10
- package/corpus/templates/chat/data/sync-config.json +0 -1
- package/corpus/templates/clips/data/sync-config.json +0 -1
- package/corpus/templates/clips/desktop/src/lib/audio-meter.ts +0 -80
- package/corpus/templates/clips/desktop/src/overlays/live-audio-bars.tsx +0 -113
- package/corpus/templates/design/data/sync-config.json +0 -1
- package/corpus/templates/factory/data/sync-config.json +0 -1
- package/corpus/templates/forms/data/settings.json +0 -8
- package/corpus/templates/forms/data/sync-config.json +0 -3
- package/corpus/templates/macros/data/sync-config.json +0 -4
- package/corpus/templates/mail/data/emails.defaults.json +0 -1
- package/corpus/templates/mail/data/labels.defaults.json +0 -59
- package/corpus/templates/mail/data/settings.defaults.json +0 -9
- package/corpus/templates/mail/data/sync-config.json +0 -8
- package/corpus/templates/slides/data/sync-config.json +0 -8
- package/corpus/templates/tasks/data/sync-config.json +0 -1
- package/dist/templates/chat/data/.gitkeep +0 -0
- package/dist/templates/chat/data/sync-config.json +0 -1
- package/dist/templates/default/data/.gitkeep +0 -0
- package/dist/templates/default/data/sync-config.json +0 -1
- package/src/templates/chat/data/.gitkeep +0 -0
- package/src/templates/chat/data/sync-config.json +0 -1
- package/src/templates/default/data/.gitkeep +0 -0
- package/src/templates/default/data/sync-config.json +0 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useT } from "@agent-native/core/client/i18n";
|
|
2
|
+
import { LiveWaveform } from "@shared/live-waveform";
|
|
2
3
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
3
4
|
|
|
4
5
|
import { Button } from "@/components/ui/button";
|
|
@@ -142,13 +143,13 @@ export function MicrophoneVisualizer({
|
|
|
142
143
|
onSignalChange,
|
|
143
144
|
}: MicrophoneVisualizerProps) {
|
|
144
145
|
const t = useT();
|
|
145
|
-
const canvasRef = useRef<HTMLCanvasElement | null>(null);
|
|
146
146
|
const rafRef = useRef<number | null>(null);
|
|
147
147
|
const runIdRef = useRef(0);
|
|
148
148
|
const streamRef = useRef<MediaStream | null>(null);
|
|
149
149
|
const audioContextRef = useRef<AudioContext | null>(null);
|
|
150
150
|
const sourceRef = useRef<MediaStreamAudioSourceNode | null>(null);
|
|
151
151
|
const signalRef = useRef(false);
|
|
152
|
+
const [level, setLevel] = useState<number | null>(null);
|
|
152
153
|
const lastSignalAtRef = useRef(0);
|
|
153
154
|
const previousDeviceIdRef = useRef(deviceId);
|
|
154
155
|
|
|
@@ -166,49 +167,6 @@ export function MicrophoneVisualizer({
|
|
|
166
167
|
[onSignalChange],
|
|
167
168
|
);
|
|
168
169
|
|
|
169
|
-
const syncCanvasSize = useCallback((canvas: HTMLCanvasElement) => {
|
|
170
|
-
const rect = canvas.getBoundingClientRect();
|
|
171
|
-
const dpr = window.devicePixelRatio || 1;
|
|
172
|
-
const width = Math.max(1, Math.floor(rect.width * dpr));
|
|
173
|
-
const height = Math.max(1, Math.floor(rect.height * dpr));
|
|
174
|
-
if (canvas.width !== width || canvas.height !== height) {
|
|
175
|
-
canvas.width = width;
|
|
176
|
-
canvas.height = height;
|
|
177
|
-
}
|
|
178
|
-
return { width, height, dpr };
|
|
179
|
-
}, []);
|
|
180
|
-
|
|
181
|
-
const drawIdle = useCallback(() => {
|
|
182
|
-
const canvas = canvasRef.current;
|
|
183
|
-
if (!canvas) return;
|
|
184
|
-
const ctx = canvas.getContext("2d");
|
|
185
|
-
if (!ctx) return;
|
|
186
|
-
const { width, height, dpr } = syncCanvasSize(canvas);
|
|
187
|
-
ctx.clearRect(0, 0, width, height);
|
|
188
|
-
|
|
189
|
-
ctx.strokeStyle = "rgba(14, 165, 233, 0.24)";
|
|
190
|
-
ctx.lineWidth = Math.max(1, dpr);
|
|
191
|
-
ctx.beginPath();
|
|
192
|
-
ctx.moveTo(0, height / 2);
|
|
193
|
-
ctx.lineTo(width, height / 2);
|
|
194
|
-
ctx.stroke();
|
|
195
|
-
|
|
196
|
-
ctx.strokeStyle = "rgba(14, 165, 233, 0.42)";
|
|
197
|
-
ctx.lineWidth = Math.max(1.75, 1.75 * dpr);
|
|
198
|
-
ctx.beginPath();
|
|
199
|
-
const points = 72;
|
|
200
|
-
for (let i = 0; i <= points; i++) {
|
|
201
|
-
const x = (i / points) * width;
|
|
202
|
-
const envelope = Math.sin((i / points) * Math.PI);
|
|
203
|
-
const y =
|
|
204
|
-
height / 2 +
|
|
205
|
-
Math.sin((i / points) * Math.PI * 6) * envelope * height * 0.13;
|
|
206
|
-
if (i === 0) ctx.moveTo(x, y);
|
|
207
|
-
else ctx.lineTo(x, y);
|
|
208
|
-
}
|
|
209
|
-
ctx.stroke();
|
|
210
|
-
}, [syncCanvasSize]);
|
|
211
|
-
|
|
212
170
|
const stopCurrent = useCallback(
|
|
213
171
|
(emitSignal = true) => {
|
|
214
172
|
if (rafRef.current !== null) {
|
|
@@ -234,21 +192,15 @@ export function MicrophoneVisualizer({
|
|
|
234
192
|
} else {
|
|
235
193
|
signalRef.current = false;
|
|
236
194
|
}
|
|
237
|
-
drawIdle();
|
|
238
195
|
},
|
|
239
|
-
[
|
|
196
|
+
[setSignal],
|
|
240
197
|
);
|
|
241
198
|
|
|
242
199
|
const drawLive = useCallback(
|
|
243
200
|
(analyser: AnalyserNode) => {
|
|
244
|
-
const canvas = canvasRef.current;
|
|
245
|
-
if (!canvas) return;
|
|
246
|
-
const ctx = canvas.getContext("2d");
|
|
247
|
-
if (!ctx) return;
|
|
248
201
|
const data = new Uint8Array(analyser.fftSize);
|
|
249
202
|
|
|
250
203
|
const draw = () => {
|
|
251
|
-
const { width, height, dpr } = syncCanvasSize(canvas);
|
|
252
204
|
analyser.getByteTimeDomainData(data);
|
|
253
205
|
|
|
254
206
|
let sum = 0;
|
|
@@ -265,44 +217,16 @@ export function MicrophoneVisualizer({
|
|
|
265
217
|
setSignal(false);
|
|
266
218
|
}
|
|
267
219
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
ctx.moveTo(0, height / 2);
|
|
273
|
-
ctx.lineTo(width, height / 2);
|
|
274
|
-
ctx.stroke();
|
|
275
|
-
|
|
276
|
-
const gradient = ctx.createLinearGradient(0, 0, width, 0);
|
|
277
|
-
gradient.addColorStop(0, "rgba(14, 165, 233, 0.74)");
|
|
278
|
-
gradient.addColorStop(0.5, "rgba(34, 211, 238, 1)");
|
|
279
|
-
gradient.addColorStop(1, "rgba(37, 99, 235, 0.82)");
|
|
280
|
-
ctx.strokeStyle = gradient;
|
|
281
|
-
ctx.lineWidth = Math.max(2.25 * dpr, Math.min(6 * dpr, rms * 34 * dpr));
|
|
282
|
-
ctx.shadowColor = "rgba(14, 165, 233, 0.35)";
|
|
283
|
-
ctx.shadowBlur = Math.max(4 * dpr, Math.min(12 * dpr, rms * 80 * dpr));
|
|
284
|
-
ctx.lineJoin = "round";
|
|
285
|
-
ctx.lineCap = "round";
|
|
286
|
-
ctx.beginPath();
|
|
287
|
-
const step = width / Math.max(1, data.length - 1);
|
|
288
|
-
const gain = Math.min(5.5, 2.6 + rms * 24);
|
|
289
|
-
for (let i = 0; i < data.length; i++) {
|
|
290
|
-
const x = i * step;
|
|
291
|
-
const normalized = ((data[i] - 128) / 128) * gain;
|
|
292
|
-
const y =
|
|
293
|
-
height / 2 + Math.max(-1, Math.min(1, normalized)) * height * 0.42;
|
|
294
|
-
if (i === 0) ctx.moveTo(x, y);
|
|
295
|
-
else ctx.lineTo(x, y);
|
|
296
|
-
}
|
|
297
|
-
ctx.stroke();
|
|
298
|
-
ctx.shadowBlur = 0;
|
|
299
|
-
|
|
220
|
+
// The analyser is the only part of the meter this app owns; the shape
|
|
221
|
+
// and the level math are shared with the desktop app so a microphone
|
|
222
|
+
// reads the same here as it does in the recorder and the meeting pill.
|
|
223
|
+
setLevel(rms);
|
|
300
224
|
rafRef.current = requestAnimationFrame(draw);
|
|
301
225
|
};
|
|
302
226
|
|
|
303
227
|
draw();
|
|
304
228
|
},
|
|
305
|
-
[setSignal
|
|
229
|
+
[setSignal],
|
|
306
230
|
);
|
|
307
231
|
|
|
308
232
|
const stopTest = useCallback(() => {
|
|
@@ -415,17 +339,8 @@ export function MicrophoneVisualizer({
|
|
|
415
339
|
setError(message);
|
|
416
340
|
setStatus("error");
|
|
417
341
|
onStatusChange?.("error", { error: message });
|
|
418
|
-
drawIdle();
|
|
419
342
|
}
|
|
420
|
-
}, [
|
|
421
|
-
deviceId,
|
|
422
|
-
disabled,
|
|
423
|
-
drawIdle,
|
|
424
|
-
drawLive,
|
|
425
|
-
onStatusChange,
|
|
426
|
-
setSignal,
|
|
427
|
-
stopCurrent,
|
|
428
|
-
]);
|
|
343
|
+
}, [deviceId, disabled, drawLive, onStatusChange, setSignal, stopCurrent]);
|
|
429
344
|
|
|
430
345
|
useEffect(() => {
|
|
431
346
|
if (disabled) {
|
|
@@ -433,7 +348,6 @@ export function MicrophoneVisualizer({
|
|
|
433
348
|
if (status === "live" || status === "starting") {
|
|
434
349
|
stopTest();
|
|
435
350
|
} else {
|
|
436
|
-
drawIdle();
|
|
437
351
|
}
|
|
438
352
|
return;
|
|
439
353
|
}
|
|
@@ -441,14 +355,14 @@ export function MicrophoneVisualizer({
|
|
|
441
355
|
previousDeviceIdRef.current = deviceId;
|
|
442
356
|
if (status === "live" || status === "starting") {
|
|
443
357
|
void startTest();
|
|
444
|
-
} else {
|
|
445
|
-
drawIdle();
|
|
446
358
|
}
|
|
447
|
-
}, [deviceId, disabled,
|
|
359
|
+
}, [deviceId, disabled, startTest, status, stopTest]);
|
|
448
360
|
|
|
361
|
+
// Idle and error both rest the meter by holding `level` at null, so there is
|
|
362
|
+
// nothing left to draw on a state change.
|
|
449
363
|
useEffect(() => {
|
|
450
|
-
if (status === "idle" || status === "error")
|
|
451
|
-
}, [
|
|
364
|
+
if (status === "idle" || status === "error") setLevel(null);
|
|
365
|
+
}, [status]);
|
|
452
366
|
|
|
453
367
|
useEffect(() => {
|
|
454
368
|
return () => {
|
|
@@ -480,11 +394,18 @@ export function MicrophoneVisualizer({
|
|
|
480
394
|
live && hasSignal ? "border-foreground/35" : "border-border",
|
|
481
395
|
)}
|
|
482
396
|
>
|
|
483
|
-
<
|
|
484
|
-
|
|
397
|
+
<span
|
|
398
|
+
role="img"
|
|
485
399
|
aria-label={t("clipsFinalRaw.selectedMicrophoneWaveform")}
|
|
486
|
-
className="
|
|
487
|
-
|
|
400
|
+
className="absolute inset-0 flex items-center justify-center"
|
|
401
|
+
>
|
|
402
|
+
<LiveWaveform
|
|
403
|
+
level={live ? level : null}
|
|
404
|
+
bars={18}
|
|
405
|
+
barWidth={2}
|
|
406
|
+
barGap={3}
|
|
407
|
+
/>
|
|
408
|
+
</span>
|
|
488
409
|
{statusLabel ? (
|
|
489
410
|
<span
|
|
490
411
|
className={cn(
|
|
@@ -5,6 +5,7 @@ import { useQueryClient } from "@tanstack/react-query";
|
|
|
5
5
|
import { useState } from "react";
|
|
6
6
|
import { toast } from "sonner";
|
|
7
7
|
|
|
8
|
+
import { ClipsAvatar } from "@/components/clips-avatar";
|
|
8
9
|
import {
|
|
9
10
|
AlertDialog,
|
|
10
11
|
AlertDialogAction,
|
|
@@ -15,7 +16,6 @@ import {
|
|
|
15
16
|
AlertDialogHeader,
|
|
16
17
|
AlertDialogTitle,
|
|
17
18
|
} from "@/components/ui/alert-dialog";
|
|
18
|
-
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
|
|
19
19
|
import { Badge } from "@/components/ui/badge";
|
|
20
20
|
import { Button } from "@/components/ui/button";
|
|
21
21
|
import {
|
|
@@ -152,11 +152,13 @@ export function MembersList({
|
|
|
152
152
|
<TableRow key={m.id}>
|
|
153
153
|
<TableCell>
|
|
154
154
|
<div className="flex items-center gap-2 min-w-0">
|
|
155
|
-
<
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
155
|
+
<ClipsAvatar
|
|
156
|
+
email={m.email}
|
|
157
|
+
alt={m.email}
|
|
158
|
+
fallback={initials(m.email)}
|
|
159
|
+
className="h-8 w-8 flex-shrink-0"
|
|
160
|
+
fallbackClassName="text-xs bg-primary text-primary-foreground"
|
|
161
|
+
/>
|
|
160
162
|
<div className="min-w-0">
|
|
161
163
|
<div className="truncate font-medium flex items-center gap-1.5">
|
|
162
164
|
{m.email}
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
} from "@tabler/icons-react";
|
|
10
10
|
import { Link } from "react-router";
|
|
11
11
|
|
|
12
|
-
import {
|
|
12
|
+
import { ClipsAvatar } from "@/components/clips-avatar";
|
|
13
13
|
|
|
14
14
|
export type NotificationKind = "comment" | "reaction" | "mention" | "share";
|
|
15
15
|
|
|
@@ -62,11 +62,13 @@ export function NotificationsList({ items, onReply }: NotificationsListProps) {
|
|
|
62
62
|
<ul className="divide-y">
|
|
63
63
|
{items.map((item) => (
|
|
64
64
|
<li key={item.id} className="py-3 flex items-start gap-3">
|
|
65
|
-
<
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
65
|
+
<ClipsAvatar
|
|
66
|
+
email={item.authorEmail}
|
|
67
|
+
alt={item.authorEmail ?? t("clipsFinalRaw.someone")}
|
|
68
|
+
fallback={initials(item.authorEmail)}
|
|
69
|
+
className="h-9 w-9 flex-shrink-0"
|
|
70
|
+
fallbackClassName="text-xs bg-primary text-primary-foreground"
|
|
71
|
+
/>
|
|
70
72
|
<div className="min-w-0 flex-1">
|
|
71
73
|
<div className="flex items-center gap-2 text-sm">
|
|
72
74
|
<KindIcon kind={item.kind} />
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useT } from "@agent-native/core/client/i18n";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { ClipsAvatar } from "@/components/clips-avatar";
|
|
4
4
|
import {
|
|
5
5
|
Table,
|
|
6
6
|
TableBody,
|
|
@@ -58,11 +58,13 @@ export function TopCreatorsTable({ rows }: TopCreatorsTableProps) {
|
|
|
58
58
|
<TableRow key={row.email}>
|
|
59
59
|
<TableCell>
|
|
60
60
|
<div className="flex items-center gap-2 min-w-0">
|
|
61
|
-
<
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
61
|
+
<ClipsAvatar
|
|
62
|
+
email={row.email}
|
|
63
|
+
alt={row.email}
|
|
64
|
+
fallback={initials(row.email)}
|
|
65
|
+
className="h-7 w-7 flex-shrink-0"
|
|
66
|
+
fallbackClassName="text-xs bg-primary text-primary-foreground"
|
|
67
|
+
/>
|
|
66
68
|
<span className="truncate">{row.email}</span>
|
|
67
69
|
</div>
|
|
68
70
|
</TableCell>
|
|
@@ -28,6 +28,7 @@ import { useEffect, useMemo, useRef, useState } from "react";
|
|
|
28
28
|
import { NavLink, useNavigate, useParams } from "react-router";
|
|
29
29
|
import { toast } from "sonner";
|
|
30
30
|
|
|
31
|
+
import { ClipsAvatar } from "@/components/clips-avatar";
|
|
31
32
|
import { PageHeader } from "@/components/library/page-header";
|
|
32
33
|
import {
|
|
33
34
|
AttendeeStack,
|
|
@@ -52,7 +53,6 @@ import {
|
|
|
52
53
|
AlertDialogHeader,
|
|
53
54
|
AlertDialogTitle,
|
|
54
55
|
} from "@/components/ui/alert-dialog";
|
|
55
|
-
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
|
56
56
|
import { Badge } from "@/components/ui/badge";
|
|
57
57
|
import { Button } from "@/components/ui/button";
|
|
58
58
|
import {
|
|
@@ -258,12 +258,13 @@ function ActionItemsByPerson({
|
|
|
258
258
|
{grouped.map(([who, list]) => (
|
|
259
259
|
<div key={who} className="space-y-1.5">
|
|
260
260
|
<div className="flex items-center gap-2">
|
|
261
|
-
<
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
261
|
+
<ClipsAvatar
|
|
262
|
+
email={who || null}
|
|
263
|
+
alt={who || t("meetingDetail.unassigned")}
|
|
264
|
+
fallback={attendeeInitials(who || t("meetingDetail.unassigned"))}
|
|
265
|
+
className="h-5 w-5"
|
|
266
|
+
fallbackClassName="text-[9px]"
|
|
267
|
+
/>
|
|
267
268
|
<span className="text-xs font-medium">
|
|
268
269
|
{who || t("meetingDetail.unassigned")}
|
|
269
270
|
</span>
|
|
@@ -48,7 +48,6 @@ const SPEEDS = ["1", "1.2", "1.5", "1.75", "2"];
|
|
|
48
48
|
interface ClipsUserSettings {
|
|
49
49
|
defaultPlaybackSpeed?: string;
|
|
50
50
|
emailNotifications?: boolean;
|
|
51
|
-
transcriptCleanupEnabled?: boolean;
|
|
52
51
|
includeFullVideoInAi?: boolean;
|
|
53
52
|
defaultRecordingVisibility?: ClipsDefaultVisibility;
|
|
54
53
|
}
|
|
@@ -133,9 +132,6 @@ export default function SettingsIndexRoute() {
|
|
|
133
132
|
const [emailNotifications, setEmailNotifications] = useState(true);
|
|
134
133
|
const [defaultVisibility, setDefaultVisibility] =
|
|
135
134
|
useState<ClipsDefaultVisibility>(DEFAULT_CLIPS_RECORDING_VISIBILITY);
|
|
136
|
-
const [transcriptCleanupEnabled, setTranscriptCleanupEnabled] =
|
|
137
|
-
useState(true);
|
|
138
|
-
|
|
139
135
|
useEffect(() => {
|
|
140
136
|
let cancelled = false;
|
|
141
137
|
loadSettings().then((v) => {
|
|
@@ -145,7 +141,6 @@ export default function SettingsIndexRoute() {
|
|
|
145
141
|
setDefaultVisibility(
|
|
146
142
|
v.defaultRecordingVisibility ?? DEFAULT_CLIPS_RECORDING_VISIBILITY,
|
|
147
143
|
);
|
|
148
|
-
setTranscriptCleanupEnabled(v.transcriptCleanupEnabled !== false);
|
|
149
144
|
setLoading(false);
|
|
150
145
|
});
|
|
151
146
|
return () => {
|
|
@@ -194,7 +189,6 @@ export default function SettingsIndexRoute() {
|
|
|
194
189
|
await saveSettings({
|
|
195
190
|
defaultPlaybackSpeed: defaultSpeed,
|
|
196
191
|
emailNotifications,
|
|
197
|
-
transcriptCleanupEnabled,
|
|
198
192
|
defaultRecordingVisibility: defaultVisibility,
|
|
199
193
|
});
|
|
200
194
|
toast.success(t("settings.saved"));
|
|
@@ -249,12 +243,6 @@ export default function SettingsIndexRoute() {
|
|
|
249
243
|
keywords: "playback speed video default",
|
|
250
244
|
hash: "playback",
|
|
251
245
|
},
|
|
252
|
-
{
|
|
253
|
-
id: "clips-transcript",
|
|
254
|
-
label: t("settings.transcript"),
|
|
255
|
-
keywords: "transcript cleanup captions",
|
|
256
|
-
hash: "transcript",
|
|
257
|
-
},
|
|
258
246
|
{
|
|
259
247
|
id: "clips-sharing",
|
|
260
248
|
label: t("settings.sharing"),
|
|
@@ -356,20 +344,6 @@ export default function SettingsIndexRoute() {
|
|
|
356
344
|
</Select>
|
|
357
345
|
}
|
|
358
346
|
/>
|
|
359
|
-
<SettingsRow
|
|
360
|
-
id="transcript"
|
|
361
|
-
label={t("settings.transcriptCleanup")}
|
|
362
|
-
description={t("settings.transcriptCleanupDescription")}
|
|
363
|
-
control={
|
|
364
|
-
<Switch
|
|
365
|
-
id="transcript-cleanup"
|
|
366
|
-
aria-label={t("settings.transcriptCleanup")}
|
|
367
|
-
checked={transcriptCleanupEnabled}
|
|
368
|
-
onCheckedChange={setTranscriptCleanupEnabled}
|
|
369
|
-
disabled={loading}
|
|
370
|
-
/>
|
|
371
|
-
}
|
|
372
|
-
/>
|
|
373
347
|
<SettingsRow
|
|
374
348
|
id="notifications"
|
|
375
349
|
label={t("settings.emailNotifications")}
|
|
@@ -388,7 +388,6 @@ export default function RecordingPage() {
|
|
|
388
388
|
// Also keep polling while a transcript is pending so "Transcribing…"
|
|
389
389
|
// auto-flips to the ready transcript (or to the failure card).
|
|
390
390
|
if (data?.transcript?.status === "pending") return 3000;
|
|
391
|
-
if (data?.transcript?.cleanup?.status === "running") return 2000;
|
|
392
391
|
// And keep polling while the title is still the server-seeded
|
|
393
392
|
// default — the agent will land a generated title via
|
|
394
393
|
// `update-recording` and we want the skeleton to swap in promptly.
|
|
@@ -460,7 +459,6 @@ export default function RecordingPage() {
|
|
|
460
459
|
const transcriptFullText = playerDataQ.data?.transcript?.fullText ?? null;
|
|
461
460
|
const transcriptStatus = playerDataQ.data?.transcript?.status;
|
|
462
461
|
const transcriptFailureReason = playerDataQ.data?.transcript?.failureReason;
|
|
463
|
-
const transcriptCleanup = playerDataQ.data?.transcript?.cleanup ?? null;
|
|
464
462
|
const ctas = playerDataQ.data?.ctas ?? [];
|
|
465
463
|
const canEdit = role === "owner" || role === "admin" || role === "editor";
|
|
466
464
|
// Reaching this page already requires a signed-in session with at least
|
|
@@ -1265,7 +1263,7 @@ export default function RecordingPage() {
|
|
|
1265
1263
|
</TabsContent>
|
|
1266
1264
|
<TabsContent
|
|
1267
1265
|
value="transcript"
|
|
1268
|
-
className="flex-1 min-h-0
|
|
1266
|
+
className="mt-0 flex-1 min-h-0 data-[state=inactive]:hidden"
|
|
1269
1267
|
>
|
|
1270
1268
|
<TranscriptPanel
|
|
1271
1269
|
segments={transcriptSegments}
|
|
@@ -1279,7 +1277,6 @@ export default function RecordingPage() {
|
|
|
1279
1277
|
: transcriptStatus
|
|
1280
1278
|
}
|
|
1281
1279
|
failureReason={transcriptFailureReason}
|
|
1282
|
-
cleanup={transcriptCleanup}
|
|
1283
1280
|
recordingTitle={recording.title}
|
|
1284
1281
|
onRetry={
|
|
1285
1282
|
canEdit
|
|
@@ -160,7 +160,11 @@ import {
|
|
|
160
160
|
retryUpdateCheck,
|
|
161
161
|
useUpdateStatus,
|
|
162
162
|
} from "./lib/updater";
|
|
163
|
-
import {
|
|
163
|
+
import {
|
|
164
|
+
DEFAULT_SERVER_URL,
|
|
165
|
+
normalizeServerUrl,
|
|
166
|
+
SERVER_URL_STORAGE_KEY,
|
|
167
|
+
} from "./lib/url";
|
|
164
168
|
import { cn } from "./lib/utils";
|
|
165
169
|
import {
|
|
166
170
|
installDesktopVoiceDictation,
|
|
@@ -392,7 +396,8 @@ function isStorageSetupFailureMessage(message: string | null | undefined) {
|
|
|
392
396
|
return STORAGE_SETUP_FAILURE_RE.test(message ?? "");
|
|
393
397
|
}
|
|
394
398
|
|
|
395
|
-
|
|
399
|
+
// Shared with overlays via lib/url.ts — the meeting pill reads the same key.
|
|
400
|
+
const STORAGE_KEY = SERVER_URL_STORAGE_KEY;
|
|
396
401
|
const MODE_KEY = "clips:last-mode";
|
|
397
402
|
const VOICE_SHORTCUT_KEY = "clips:voice-shortcut";
|
|
398
403
|
const VOICE_SHORTCUT_CONFIGURED_KEY = "clips:voice-shortcut-configured";
|
|
@@ -414,14 +419,7 @@ const VIDEO_STORAGE_CONFIGURED_KEY = "clips:video-storage-configured";
|
|
|
414
419
|
const REWIND_DOCS_URL =
|
|
415
420
|
"https://www.agent-native.com/docs/template-clips-capture-everywhere#rewind";
|
|
416
421
|
|
|
417
|
-
|
|
418
|
-
// Dev builds point at the local dev server; production builds point at the
|
|
419
|
-
// hosted Clips instance. The user can still override from Settings.
|
|
420
|
-
// Dev points at the Clips dev server (shared-app-config says 8094).
|
|
421
|
-
// Prod points at the hosted Clips instance. User can override from Settings.
|
|
422
|
-
const DEFAULT_URL = import.meta.env.DEV
|
|
423
|
-
? "http://localhost:8094"
|
|
424
|
-
: "https://clips.agent-native.com";
|
|
422
|
+
const DEFAULT_URL = DEFAULT_SERVER_URL;
|
|
425
423
|
|
|
426
424
|
function normalizeCaptureSource(value: string): CaptureSource {
|
|
427
425
|
if (value === "region" && isMacPlatform()) return "region";
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shared meter, wired to desktop capture.
|
|
3
|
+
*
|
|
4
|
+
* The shape and the level math live in `shared/live-waveform.tsx` with the web
|
|
5
|
+
* app; only the transport is here, because that is the one part that genuinely
|
|
6
|
+
* differs — Tauri emits `voice:audio-level`, the browser reads an
|
|
7
|
+
* `AnalyserNode`. Anything visual belongs in the shared component so the two
|
|
8
|
+
* apps cannot drift apart again.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { listen } from "@tauri-apps/api/event";
|
|
12
|
+
import { useEffect, useRef, useState } from "react";
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
combinedMeterLevel,
|
|
16
|
+
EMPTY_METER_SOURCES,
|
|
17
|
+
foldMeterSources,
|
|
18
|
+
type MeterSource,
|
|
19
|
+
type MeterSourceLevels,
|
|
20
|
+
} from "../../../shared/audio-meter";
|
|
21
|
+
import { LiveWaveform as SharedLiveWaveform } from "../../../shared/live-waveform";
|
|
22
|
+
|
|
23
|
+
interface LiveWaveformProps {
|
|
24
|
+
className?: string;
|
|
25
|
+
bars?: number;
|
|
26
|
+
barWidth?: number;
|
|
27
|
+
barGap?: number;
|
|
28
|
+
/** Drive from a caller-supplied level instead of capture events (demos). */
|
|
29
|
+
level?: number | null;
|
|
30
|
+
dimmed?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Which capture to meter. "all" rides whichever of mic and system audio is
|
|
33
|
+
* louder, which is what a meeting is; "mic" answers the narrower question a
|
|
34
|
+
* recorder asks — am I being heard.
|
|
35
|
+
*/
|
|
36
|
+
sources?: "all" | "mic";
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function LiveWaveform({
|
|
40
|
+
className,
|
|
41
|
+
bars,
|
|
42
|
+
barWidth,
|
|
43
|
+
barGap,
|
|
44
|
+
level = null,
|
|
45
|
+
dimmed = false,
|
|
46
|
+
sources = "all",
|
|
47
|
+
}: LiveWaveformProps) {
|
|
48
|
+
const [captured, setCaptured] = useState<number | null>(null);
|
|
49
|
+
const sourcesRef = useRef<MeterSourceLevels>(EMPTY_METER_SOURCES);
|
|
50
|
+
const external = level !== null && level !== undefined;
|
|
51
|
+
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
if (external) return;
|
|
54
|
+
let stopped = false;
|
|
55
|
+
let unlisten: (() => void) | null = null;
|
|
56
|
+
// Mic and system audio interleave on one event and each decays on its own
|
|
57
|
+
// frames: a shared level lets a silent mic halve whoever is actually
|
|
58
|
+
// talking on every other event, which pins the meter to its floor.
|
|
59
|
+
listen<{ level?: number; source?: MeterSource }>(
|
|
60
|
+
"voice:audio-level",
|
|
61
|
+
(event) => {
|
|
62
|
+
const source: MeterSource =
|
|
63
|
+
event.payload?.source === "system" ? "system" : "mic";
|
|
64
|
+
if (sources === "mic" && source !== "mic") return;
|
|
65
|
+
sourcesRef.current = foldMeterSources(
|
|
66
|
+
sourcesRef.current,
|
|
67
|
+
source,
|
|
68
|
+
Number(event.payload?.level),
|
|
69
|
+
);
|
|
70
|
+
setCaptured(
|
|
71
|
+
sources === "mic"
|
|
72
|
+
? sourcesRef.current.mic
|
|
73
|
+
: combinedMeterLevel(sourcesRef.current),
|
|
74
|
+
);
|
|
75
|
+
},
|
|
76
|
+
)
|
|
77
|
+
.then((cleanup) => {
|
|
78
|
+
if (stopped) cleanup();
|
|
79
|
+
else unlisten = cleanup;
|
|
80
|
+
})
|
|
81
|
+
.catch(() => {});
|
|
82
|
+
|
|
83
|
+
return () => {
|
|
84
|
+
stopped = true;
|
|
85
|
+
unlisten?.();
|
|
86
|
+
};
|
|
87
|
+
}, [external, sources]);
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<SharedLiveWaveform
|
|
91
|
+
level={external ? level : captured}
|
|
92
|
+
className={className}
|
|
93
|
+
bars={bars}
|
|
94
|
+
barWidth={barWidth}
|
|
95
|
+
barGap={barGap}
|
|
96
|
+
dimmed={dimmed}
|
|
97
|
+
/>
|
|
98
|
+
);
|
|
99
|
+
}
|