@agent-native/core 0.187.0 → 0.187.1-nightly-20260925003003
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +1 -1
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +4 -3
- package/corpus/templates/analytics/changelog/2026-09-24-analytics-sidebar-footer-aligns-consistently.md +6 -0
- package/corpus/templates/analytics/changelog/2026-09-24-dashboard-emails-match-stacked-area-totals.md +6 -0
- package/corpus/templates/analytics/server/lib/report-chart-svg.ts +47 -20
- package/corpus/templates/calendar/app/components/calendar/AttendeeAutocomplete.tsx +1 -1
- package/corpus/templates/calendar/changelog/2026-09-24-guest-inputs-wrap-beneath-existing-guests-so-the-active-inpu.md +6 -0
- package/corpus/templates/clips/app/components/editor/stitch-manager.tsx +8 -3
- package/corpus/templates/clips/app/lib/recording-link.ts +21 -0
- package/corpus/templates/clips/app/routes/record.tsx +27 -8
- package/corpus/templates/clips/changelog/2026-09-24-the-camera-bubble-stays-visible-while-you-reposition-it-befo.md +6 -0
- package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +7 -0
- package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +63 -55
- package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +251 -74
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +66 -56
- package/corpus/templates/design/app/components/design/EditPanel.tsx +19 -0
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +59 -9
- package/corpus/templates/design/app/components/design/edit-panel/interaction-state-helpers.ts +4 -0
- package/corpus/templates/design/app/components/design/edit-panel/layout-properties.tsx +119 -0
- package/corpus/templates/design/app/components/design/edit-panel/stroke-properties.tsx +48 -51
- package/corpus/templates/design/app/components/design/edit-panel/style-change-types.ts +1 -0
- package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +298 -187
- package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +2 -2
- package/corpus/templates/design/app/components/design/inspector/index.ts +4 -0
- package/corpus/templates/design/app/components/design/multi-screen/types.ts +4 -2
- package/corpus/templates/design/app/i18n/en-US.ts +6 -0
- package/corpus/templates/design/app/pages/Present.tsx +101 -87
- package/corpus/templates/design/app/pages/design-editor/clone-and-pen-edit.ts +75 -14
- package/corpus/templates/design/app/pages/design-editor/commands/README.md +1 -0
- package/corpus/templates/design/app/pages/design-editor/commands/commit-relative-style-delta-to-selected-layers.ts +69 -56
- package/corpus/templates/design/app/pages/design-editor/commands/mode-change.ts +16 -0
- package/corpus/templates/design/app/pages/design-editor/commands/scale-selection.ts +17 -2
- package/corpus/templates/design/app/pages/design-editor/commands/styles-change.ts +23 -30
- package/corpus/templates/design/app/pages/design-editor/commands/swap-fill-stroke.ts +105 -0
- package/corpus/templates/design/app/pages/design-editor/pending-edits.ts +21 -0
- package/corpus/templates/design/app/pages/present-review-state.ts +7 -1
- package/corpus/templates/design/changelog/2026-09-24-a-vector-s-stroke-row-shows-position-beside-weight-instead-o.md +6 -0
- package/corpus/templates/design/changelog/2026-09-24-css-margins-can-be-adjusted-on-the-canvas-and-in-the-layout-.md +6 -0
- package/corpus/templates/design/changelog/2026-09-24-design-reviews-show-the-real-canvas-beside-its-conversation.md +6 -0
- package/corpus/templates/design/changelog/2026-09-24-double-clicking-text-or-a-vector-on-the-board-starts-editing.md +6 -0
- package/corpus/templates/design/changelog/2026-09-24-interact-opens-a-focused-responsive-preview-without-reloadin.md +6 -0
- package/corpus/templates/design/changelog/2026-09-24-keep-gradient-values-visible-and-limit-canvas-paints-to-supp.md +6 -0
- package/corpus/templates/design/changelog/2026-09-24-open-pen-paths-no-longer-paint-a-filled-triangle-after-shift.md +6 -0
- package/corpus/templates/design/changelog/2026-09-24-re-clicking-the-active-responsive-preview-returns-to-the-can.md +6 -0
- package/corpus/templates/design/changelog/2026-09-24-resized-vectors-can-be-point-edited-and-the-k-scale-tool-sho.md +6 -0
- package/corpus/templates/design/changelog/2026-09-24-while-editing-text-click-to-place-the-caret-and-double-click.md +6 -0
- package/corpus/templates/design/e2e/helpers.ts +19 -2
- package/corpus/templates/mail/app/components/email/ComposeModal.tsx +1 -1
- package/corpus/templates/mail/changelog/2026-09-24-gmail-push-ack.md +6 -0
- package/corpus/templates/mail/changelog/2026-09-24-the-composer-keeps-its-taller-layout-in-a-narrower-window.md +6 -0
- package/corpus/templates/mail/server/db/schema.ts +26 -1
- package/corpus/templates/mail/server/lib/inbox-store.ts +68 -23
- package/corpus/templates/mail/server/lib/inbox-sync.ts +43 -17
- package/corpus/templates/mail/server/plugins/db.ts +19 -0
- package/corpus/templates/mail/server/routes/api/gmail/push.post.ts +4 -15
- package/corpus/templates/slides/.agents/skills/create-deck/SKILL.md +45 -30
- package/corpus/templates/slides/.agents/skills/slide-editing/SKILL.md +18 -2
- package/corpus/templates/slides/actions/export-html.ts +28 -17
- package/corpus/templates/slides/app/hooks/use-deck-design-system.ts +27 -15
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +1 -1
- package/corpus/templates/slides/changelog/2026-09-23-decks-with-no-design-system-now-follow-a-theme-chosen-for-th.md +6 -0
- package/corpus/templates/slides/shared/slide-background.ts +3 -1
- package/dist/agent/engine/builder-engine.js +11 -0
- package/dist/agent/engine/types.d.ts +1 -0
- package/dist/agent/production-agent.d.ts +2 -0
- package/dist/agent/production-agent.js +32 -4
- package/dist/agent/run-loop-with-resume.js +6 -0
- package/dist/cli/index.js +4 -4
- package/dist/cli/process.d.ts +10 -1
- package/dist/cli/process.js +66 -0
- package/dist/cli/workspace-dev.js +2 -0
- package/dist/client/agent-chat.js +13 -14
- package/dist/client/error-format.js +4 -4
- package/dist/client/observability/ObservabilityDashboard.js +95 -65
- package/dist/client/observability/OutputPreview.d.ts +2 -1
- package/dist/client/observability/OutputPreview.js +64 -7
- package/dist/client/observability/useObservability.d.ts +2 -1
- package/dist/client/observability/useObservability.js +5 -1
- package/dist/client/settings/UsageSection.js +105 -17
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/db/client.d.ts +44 -0
- package/dist/db/client.js +62 -3
- package/dist/db/create-get-db.js +23 -1
- package/dist/db/embedded-runtime.d.ts +21 -0
- package/dist/db/embedded-runtime.js +27 -0
- package/dist/db/migrations.js +2 -0
- package/dist/db/server-runtime.d.ts +22 -0
- package/dist/db/server-runtime.js +26 -0
- package/dist/deploy/build.d.ts +29 -0
- package/dist/deploy/build.js +204 -121
- package/dist/feature-flags/index.d.ts +1 -1
- package/dist/feature-flags/index.js +1 -1
- package/dist/feature-flags/registry.d.ts +1 -0
- package/dist/feature-flags/registry.js +5 -0
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/integrations/webhook-handler.js +4 -1
- package/dist/jobs/background-automation-runner.js +6 -1
- package/dist/localization/core-messages/ar-SA.js +9 -1
- package/dist/localization/core-messages/de-DE.js +9 -1
- package/dist/localization/core-messages/en-US.d.ts +9 -1
- package/dist/localization/core-messages/en-US.js +9 -1
- package/dist/localization/core-messages/es-ES.js +9 -1
- package/dist/localization/core-messages/fr-FR.js +9 -1
- package/dist/localization/core-messages/hi-IN.js +9 -1
- package/dist/localization/core-messages/ja-JP.js +9 -1
- package/dist/localization/core-messages/ko-KR.js +9 -1
- package/dist/localization/core-messages/pt-BR.js +9 -1
- package/dist/localization/core-messages/supplemental/ar-SA.js +1 -0
- package/dist/localization/core-messages/supplemental/de-DE.js +1 -0
- package/dist/localization/core-messages/supplemental/en-US.js +1 -0
- package/dist/localization/core-messages/supplemental/es-ES.js +1 -0
- package/dist/localization/core-messages/supplemental/fr-FR.js +1 -0
- package/dist/localization/core-messages/supplemental/hi-IN.js +1 -0
- package/dist/localization/core-messages/supplemental/ja-JP.js +1 -0
- package/dist/localization/core-messages/supplemental/ko-KR.js +1 -0
- package/dist/localization/core-messages/supplemental/pt-BR.js +1 -0
- package/dist/localization/core-messages/supplemental/zh-CN.js +1 -0
- package/dist/localization/core-messages/supplemental/zh-TW.js +1 -0
- package/dist/localization/core-messages/zh-CN.js +9 -1
- package/dist/localization/core-messages/zh-TW.js +9 -1
- package/dist/localization/core-messages.d.ts +9 -1
- package/dist/localization/mcp-settings-messages.d.ts +1 -0
- package/dist/mcp/connect-route.js +45 -8
- package/dist/mcp/connect-store.d.ts +7 -5
- package/dist/mcp/connect-store.js +30 -39
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/actions/get-observability-review-detail.d.ts +7 -0
- package/dist/observability/actions/get-observability-review-detail.js +25 -0
- package/dist/observability/reviews.d.ts +9 -1
- package/dist/observability/reviews.js +20 -0
- package/dist/observability/routes.d.ts +3 -3
- package/dist/observability/types.d.ts +8 -0
- package/dist/org/handlers.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +7 -7
- package/dist/provider-api/actions/provider-api.d.ts +9 -9
- package/dist/scripts/db/query.js +6 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-engine-ollama-models-route.d.ts +2 -2
- package/dist/server/agent-teams.js +4 -1
- package/dist/server/auth.js +60 -20
- package/dist/server/better-auth-instance.js +7 -1
- package/dist/server/core-routes-plugin.d.ts +1 -1
- package/dist/server/core-routes-plugin.js +2 -0
- package/dist/server/embedded.js +6 -0
- package/dist/server/framework-request-handler.js +6 -0
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/usage/actions/get-usage-metrics.js +4 -1
- package/dist/usage/metrics-store.d.ts +19 -0
- package/dist/usage/metrics-store.js +106 -14
- package/dist/usage/store.d.ts +7 -2
- package/dist/usage/store.js +25 -4
- package/dist/vite/action-types-plugin.js +4 -0
- package/dist/vite/client.d.ts +6 -1
- package/dist/vite/client.js +40 -1
- package/package.json +4 -3
package/corpus/README.md
CHANGED
|
@@ -2665,8 +2665,8 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
|
|
|
2665
2665
|
</div>
|
|
2666
2666
|
</nav>
|
|
2667
2667
|
|
|
2668
|
-
<div className="mt-3 shrink-0 min-w-0 space-y-
|
|
2669
|
-
<nav className="flex min-w-0 flex-col space-y-0.5">
|
|
2668
|
+
<div className="mt-3 shrink-0 min-w-0 space-y-1 border-t border-border/70 pt-3">
|
|
2669
|
+
<nav className="flex min-w-0 flex-col space-y-0.5 px-2">
|
|
2670
2670
|
{bottomItems.map((item) => {
|
|
2671
2671
|
const Icon = item.icon;
|
|
2672
2672
|
const isActive = location.pathname === item.href;
|
|
@@ -2694,8 +2694,9 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
|
|
|
2694
2694
|
collapsed={false}
|
|
2695
2695
|
collapsible={false}
|
|
2696
2696
|
feedback={footerFeedback}
|
|
2697
|
+
className="space-y-1 px-2"
|
|
2697
2698
|
orgSwitcher={
|
|
2698
|
-
<OrgSwitcher className="min-w-0 flex-1 !bg-transparent !text-primary hover:!bg-accent/60 hover:!text-primary" />
|
|
2699
|
+
<OrgSwitcher className="min-w-0 flex-1 !px-2 !bg-transparent !text-primary hover:!bg-accent/60 hover:!text-primary" />
|
|
2699
2700
|
}
|
|
2700
2701
|
footerExtras={
|
|
2701
2702
|
<>
|
|
@@ -367,26 +367,29 @@ function buildAxisScale(
|
|
|
367
367
|
}
|
|
368
368
|
|
|
369
369
|
/**
|
|
370
|
-
* Segment edges for
|
|
371
|
-
*
|
|
372
|
-
* running total alone puts a mixed-sign stack's tallest bar off-canvas.
|
|
370
|
+
* Segment edges for diverging bars or signed cumulative areas. The y-domain
|
|
371
|
+
* has to cover every edge so mixed-sign segments stay inside the plot.
|
|
373
372
|
*/
|
|
374
373
|
function stackSegments(
|
|
375
374
|
series: ResolvedSeries[],
|
|
376
375
|
seriesIndexes: number[],
|
|
377
376
|
labelCount: number,
|
|
377
|
+
mode: "diverging" | "cumulative",
|
|
378
378
|
): Map<string, { base: number; top: number }> {
|
|
379
379
|
const segments = new Map<string, { base: number; top: number }>();
|
|
380
380
|
for (let labelIndex = 0; labelIndex < labelCount; labelIndex += 1) {
|
|
381
381
|
let up = 0;
|
|
382
382
|
let down = 0;
|
|
383
|
+
let total = 0;
|
|
383
384
|
for (const seriesIndex of seriesIndexes) {
|
|
384
385
|
const value = series[seriesIndex].data[labelIndex];
|
|
385
386
|
if (value === null) continue;
|
|
386
|
-
const base = value >= 0 ? up : down;
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
387
|
+
const base = mode === "cumulative" ? total : value >= 0 ? up : down;
|
|
388
|
+
const top = base + value;
|
|
389
|
+
if (mode === "cumulative") total = top;
|
|
390
|
+
else if (value >= 0) up = top;
|
|
391
|
+
else down = top;
|
|
392
|
+
segments.set(`${labelIndex}:${seriesIndex}`, { base, top });
|
|
390
393
|
}
|
|
391
394
|
}
|
|
392
395
|
return segments;
|
|
@@ -464,17 +467,29 @@ function renderCartesianChartSvg({
|
|
|
464
467
|
const plotHeight = Math.max(1, chartBottom - chartTop);
|
|
465
468
|
|
|
466
469
|
const stackedBars = stacked && type === "bar";
|
|
467
|
-
|
|
468
|
-
//
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
470
|
+
const stackedAreas = stacked && type === "area";
|
|
471
|
+
// Stacks group per axis, matching the dashboard renderer, so a dual-axis
|
|
472
|
+
// chart never sums different units onto one scale.
|
|
473
|
+
const stackedSegments =
|
|
474
|
+
stackedBars || stackedAreas
|
|
475
|
+
? new Map([
|
|
476
|
+
...stackSegments(
|
|
477
|
+
series,
|
|
478
|
+
leftAxisIndexes,
|
|
479
|
+
labels.length,
|
|
480
|
+
stackedAreas ? "cumulative" : "diverging",
|
|
481
|
+
),
|
|
482
|
+
...stackSegments(
|
|
483
|
+
series,
|
|
484
|
+
rightAxisIndexes,
|
|
485
|
+
labels.length,
|
|
486
|
+
stackedAreas ? "cumulative" : "diverging",
|
|
487
|
+
),
|
|
488
|
+
])
|
|
489
|
+
: new Map<string, { base: number; top: number }>();
|
|
475
490
|
|
|
476
491
|
const axisValues = (seriesIndexes: number[]): number[] =>
|
|
477
|
-
stackedBars
|
|
492
|
+
stackedBars || stackedAreas
|
|
478
493
|
? seriesIndexes.flatMap((seriesIndex) =>
|
|
479
494
|
labels.flatMap((_, labelIndex) => {
|
|
480
495
|
const segment = stackedSegments.get(`${labelIndex}:${seriesIndex}`);
|
|
@@ -583,15 +598,19 @@ function renderCartesianChartSvg({
|
|
|
583
598
|
const zeroText = formatCoord(zeroY);
|
|
584
599
|
const segments: Array<{
|
|
585
600
|
start: number;
|
|
586
|
-
points: Array<[string, string]>;
|
|
601
|
+
points: Array<[string, string, string]>;
|
|
587
602
|
}> = [];
|
|
588
603
|
labels.forEach((_, index) => {
|
|
589
604
|
const value = entry.data[index];
|
|
590
605
|
if (value === null) return;
|
|
606
|
+
const stack = stackedAreas
|
|
607
|
+
? stackedSegments.get(`${index}:${seriesIndex}`)
|
|
608
|
+
: undefined;
|
|
591
609
|
const x = chartLeft + slot * index + slot / 2;
|
|
592
|
-
const point: [string, string] = [
|
|
610
|
+
const point: [string, string, string] = [
|
|
593
611
|
x.toFixed(1),
|
|
594
|
-
yFor(value).toFixed(1),
|
|
612
|
+
yFor(stack?.top ?? value).toFixed(1),
|
|
613
|
+
yFor(stack?.base ?? 0).toFixed(1),
|
|
595
614
|
];
|
|
596
615
|
const open = segments[segments.length - 1];
|
|
597
616
|
if (open && open.start + open.points.length === index) {
|
|
@@ -613,7 +632,15 @@ function renderCartesianChartSvg({
|
|
|
613
632
|
.join(" ");
|
|
614
633
|
const area =
|
|
615
634
|
type === "area"
|
|
616
|
-
? `<path d="${path}
|
|
635
|
+
? `<path d="${path} ${
|
|
636
|
+
stackedAreas
|
|
637
|
+
? points
|
|
638
|
+
.slice()
|
|
639
|
+
.reverse()
|
|
640
|
+
.map(([x, , baseY]) => `L ${x},${baseY}`)
|
|
641
|
+
.join(" ")
|
|
642
|
+
: `L ${chartLeft + slot * (start + points.length - 0.5)},${zeroText} L ${chartLeft + slot * (start + 0.5)},${zeroText}`
|
|
643
|
+
} Z" fill="${entry.color}" fill-opacity="0.18"/>`
|
|
617
644
|
: "";
|
|
618
645
|
return `${area}<path d="${path}" fill="none" stroke="${entry.color}" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>`;
|
|
619
646
|
})
|
|
@@ -345,7 +345,7 @@ export const AttendeeAutocomplete = forwardRef<
|
|
|
345
345
|
className={cn(
|
|
346
346
|
variant === "box"
|
|
347
347
|
? "rounded-md border border-input bg-transparent px-2 py-1.5 text-sm focus-within:ring-1 focus-within:ring-ring"
|
|
348
|
-
: "flex min-w-0 flex-1 items-center gap-2",
|
|
348
|
+
: "flex min-w-0 flex-1 flex-wrap items-center gap-2",
|
|
349
349
|
className,
|
|
350
350
|
)}
|
|
351
351
|
onClick={() => inputRef.current?.focus()}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
useActionMutation,
|
|
3
3
|
useActionQuery,
|
|
4
|
+
useSession,
|
|
4
5
|
} from "@agent-native/core/client/hooks";
|
|
5
6
|
import { useT } from "@agent-native/core/client/i18n";
|
|
6
7
|
import {
|
|
@@ -22,7 +23,7 @@ import {
|
|
|
22
23
|
} from "@/components/ui/dialog";
|
|
23
24
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
|
24
25
|
import { exportConcat } from "@/lib/ffmpeg-export";
|
|
25
|
-
import {
|
|
26
|
+
import { copyFreshRecordingShareLink } from "@/lib/recording-link";
|
|
26
27
|
import { formatMs } from "@/lib/timestamp-mapping";
|
|
27
28
|
import { uploadFileClient } from "@/lib/upload-file-client";
|
|
28
29
|
import { cn } from "@/lib/utils";
|
|
@@ -51,6 +52,7 @@ export function StitchManager({
|
|
|
51
52
|
seedRecordingId,
|
|
52
53
|
}: StitchManagerProps) {
|
|
53
54
|
const t = useT();
|
|
55
|
+
const { session } = useSession();
|
|
54
56
|
const [queue, setQueue] = useState<RecordingLite[]>([]);
|
|
55
57
|
const [busy, setBusy] = useState(false);
|
|
56
58
|
const [progress, setProgress] = useState(0);
|
|
@@ -152,7 +154,10 @@ export function StitchManager({
|
|
|
152
154
|
});
|
|
153
155
|
const newRecordingId = (result as { id?: string } | null)?.id;
|
|
154
156
|
if (newRecordingId) {
|
|
155
|
-
const copied = await
|
|
157
|
+
const copied = await copyFreshRecordingShareLink(
|
|
158
|
+
newRecordingId,
|
|
159
|
+
session,
|
|
160
|
+
);
|
|
156
161
|
if (copied) {
|
|
157
162
|
toast.success(t("stitchManager.created"), {
|
|
158
163
|
description: t("recordRoute.linkCopied"),
|
|
@@ -162,7 +167,7 @@ export function StitchManager({
|
|
|
162
167
|
action: {
|
|
163
168
|
label: t("recordRoute.copyLinkAction"),
|
|
164
169
|
onClick: () => {
|
|
165
|
-
void
|
|
170
|
+
void copyFreshRecordingShareLink(newRecordingId, session);
|
|
166
171
|
},
|
|
167
172
|
},
|
|
168
173
|
});
|
|
@@ -30,3 +30,24 @@ export async function copyRecordingShareLink(
|
|
|
30
30
|
): Promise<boolean> {
|
|
31
31
|
return writeClipboardText(recordingShareUrl(recordingId, ownerId));
|
|
32
32
|
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Share URL for a recording the signed-in visitor just finished. Unlike the
|
|
36
|
+
* reshare dialog, which must check the viewer's role, the recorder always owns
|
|
37
|
+
* the row they just created, so no ownership check is needed. Omitting the
|
|
38
|
+
* session drops `via`, and with it `referrer_user` on the signup.
|
|
39
|
+
*/
|
|
40
|
+
export function freshRecordingShareUrl(
|
|
41
|
+
recordingId: string,
|
|
42
|
+
session: { userId?: string | null } | null | undefined,
|
|
43
|
+
): string {
|
|
44
|
+
return recordingShareUrl(recordingId, session?.userId ?? undefined);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Copy variant of {@link freshRecordingShareUrl}. */
|
|
48
|
+
export async function copyFreshRecordingShareLink(
|
|
49
|
+
recordingId: string,
|
|
50
|
+
session: { userId?: string | null } | null | undefined,
|
|
51
|
+
): Promise<boolean> {
|
|
52
|
+
return writeClipboardText(freshRecordingShareUrl(recordingId, session));
|
|
53
|
+
}
|
|
@@ -6,7 +6,11 @@ import {
|
|
|
6
6
|
agentNativePath,
|
|
7
7
|
appBasePath,
|
|
8
8
|
} from "@agent-native/core/client/api-path";
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
callAction,
|
|
11
|
+
getBrowserTabId,
|
|
12
|
+
useSession,
|
|
13
|
+
} from "@agent-native/core/client/hooks";
|
|
10
14
|
import { useT } from "@agent-native/core/client/i18n";
|
|
11
15
|
import { useLiveTranscription } from "@agent-native/core/client/transcription/use-live-transcription";
|
|
12
16
|
import type { BrowserDiagnosticsData } from "@shared/browser-diagnostics";
|
|
@@ -80,7 +84,7 @@ import {
|
|
|
80
84
|
loadRecorderPreferences,
|
|
81
85
|
saveRecorderPreferences,
|
|
82
86
|
} from "@/lib/recorder-preferences";
|
|
83
|
-
import {
|
|
87
|
+
import { copyFreshRecordingShareLink } from "@/lib/recording-link";
|
|
84
88
|
import {
|
|
85
89
|
buildCaptureTitle,
|
|
86
90
|
defaultRecordingTitle,
|
|
@@ -910,6 +914,9 @@ export default function RecordRoute() {
|
|
|
910
914
|
const t = useT();
|
|
911
915
|
const navigate = useNavigate();
|
|
912
916
|
const location = useLocation();
|
|
917
|
+
// Named distinctly from the local `session` upload-attempt counters used
|
|
918
|
+
// inside uploadFile/startFlow below — this is the signed-in visitor.
|
|
919
|
+
const { session: authSession } = useSession();
|
|
913
920
|
const {
|
|
914
921
|
dismiss: dismissUploadToast,
|
|
915
922
|
error: failUploadToast,
|
|
@@ -933,12 +940,12 @@ export default function RecordRoute() {
|
|
|
933
940
|
action: {
|
|
934
941
|
label: t("recordRoute.copyLinkAction"),
|
|
935
942
|
onClick: () => {
|
|
936
|
-
void
|
|
943
|
+
void copyFreshRecordingShareLink(recordingId, authSession);
|
|
937
944
|
},
|
|
938
945
|
},
|
|
939
946
|
});
|
|
940
947
|
},
|
|
941
|
-
[completeUploadToast, t],
|
|
948
|
+
[authSession, completeUploadToast, t],
|
|
942
949
|
);
|
|
943
950
|
const [uiState, setUiState] = useState<UiState>("idle");
|
|
944
951
|
const [savingKind, setSavingKind] = useState<"recording" | "upload" | null>(
|
|
@@ -1936,7 +1943,7 @@ export default function RecordRoute() {
|
|
|
1936
1943
|
} else if (createdId && !reportContext) {
|
|
1937
1944
|
showSavedToast(
|
|
1938
1945
|
t("recordRoute.videoUploaded"),
|
|
1939
|
-
await
|
|
1946
|
+
await copyFreshRecordingShareLink(createdId, authSession),
|
|
1940
1947
|
createdId,
|
|
1941
1948
|
);
|
|
1942
1949
|
} else {
|
|
@@ -2017,6 +2024,7 @@ export default function RecordRoute() {
|
|
|
2017
2024
|
}
|
|
2018
2025
|
},
|
|
2019
2026
|
[
|
|
2027
|
+
authSession,
|
|
2020
2028
|
completeUploadToast,
|
|
2021
2029
|
failUploadToast,
|
|
2022
2030
|
infoUploadToast,
|
|
@@ -2197,7 +2205,8 @@ export default function RecordRoute() {
|
|
|
2197
2205
|
} else {
|
|
2198
2206
|
showSavedToast(
|
|
2199
2207
|
t("recordRoute.recordingSaved"),
|
|
2200
|
-
await (pendingCopy ??
|
|
2208
|
+
await (pendingCopy ??
|
|
2209
|
+
copyFreshRecordingShareLink(recordingId, authSession)),
|
|
2201
2210
|
recordingId,
|
|
2202
2211
|
);
|
|
2203
2212
|
}
|
|
@@ -2227,7 +2236,14 @@ export default function RecordRoute() {
|
|
|
2227
2236
|
void navigate(`/r/${recordingId}`);
|
|
2228
2237
|
}, 50);
|
|
2229
2238
|
},
|
|
2230
|
-
[
|
|
2239
|
+
[
|
|
2240
|
+
authSession,
|
|
2241
|
+
completeUploadToast,
|
|
2242
|
+
infoUploadToast,
|
|
2243
|
+
navigate,
|
|
2244
|
+
showSavedToast,
|
|
2245
|
+
t,
|
|
2246
|
+
],
|
|
2231
2247
|
);
|
|
2232
2248
|
|
|
2233
2249
|
const doStop = useCallback(async () => {
|
|
@@ -2317,7 +2333,9 @@ export default function RecordRoute() {
|
|
|
2317
2333
|
const pendingCopy =
|
|
2318
2334
|
stopResult.waitingForStorage || bugReportContextRef.current
|
|
2319
2335
|
? undefined
|
|
2320
|
-
:
|
|
2336
|
+
: copyFreshRecordingShareLink(pending.id, authSession).catch(
|
|
2337
|
+
() => false,
|
|
2338
|
+
);
|
|
2321
2339
|
await diagnosticsSave;
|
|
2322
2340
|
await finishSavedRecording(pending.id, stopResult, pendingCopy);
|
|
2323
2341
|
} catch (err) {
|
|
@@ -2362,6 +2380,7 @@ export default function RecordRoute() {
|
|
|
2362
2380
|
});
|
|
2363
2381
|
}
|
|
2364
2382
|
}, [
|
|
2383
|
+
authSession,
|
|
2365
2384
|
failUploadToast,
|
|
2366
2385
|
finishSavedRecording,
|
|
2367
2386
|
liveTranscription,
|
|
@@ -251,6 +251,10 @@ fn clamp_existing_bubble_window(app: &AppHandle, window: &WebviewWindow) {
|
|
|
251
251
|
/// yield to it.
|
|
252
252
|
static BUBBLE_DRAGGING: AtomicBool = AtomicBool::new(false);
|
|
253
253
|
|
|
254
|
+
pub fn is_bubble_dragging() -> bool {
|
|
255
|
+
BUBBLE_DRAGGING.load(Ordering::SeqCst)
|
|
256
|
+
}
|
|
257
|
+
|
|
254
258
|
/// Anchor captured at the start of a hand-drag: the global cursor position and
|
|
255
259
|
/// the bubble window's top-left, both in physical px with a desktop top-left
|
|
256
260
|
/// origin (the same space `cursor_position()` / `outer_position()` report in).
|
|
@@ -3067,6 +3071,8 @@ pub async fn bubble_drag_start(app: AppHandle) -> Result<(), String> {
|
|
|
3067
3071
|
win_y: pos.y,
|
|
3068
3072
|
});
|
|
3069
3073
|
BUBBLE_DRAGGING.store(true, Ordering::SeqCst);
|
|
3074
|
+
// Invalidate a blur dismissal delivered before the drag command reached Rust.
|
|
3075
|
+
mark_popover_shown(&app);
|
|
3070
3076
|
Ok(())
|
|
3071
3077
|
}
|
|
3072
3078
|
|
|
@@ -3118,6 +3124,7 @@ pub async fn bubble_drag_end(app: AppHandle) -> Result<(), String> {
|
|
|
3118
3124
|
if let Some(window) = app.get_webview_window(BUBBLE_LABEL) {
|
|
3119
3125
|
clamp_existing_bubble_window(&app, &window);
|
|
3120
3126
|
}
|
|
3127
|
+
crate::schedule_popover_dismissal(&app);
|
|
3121
3128
|
Ok(())
|
|
3122
3129
|
}
|
|
3123
3130
|
|
|
@@ -67,9 +67,62 @@ use util::{
|
|
|
67
67
|
pub(crate) const TRAY_PNG: &[u8] = include_bytes!("../icons/tray.png");
|
|
68
68
|
|
|
69
69
|
const POPOVER_BLUR_GUARD: Duration = Duration::from_millis(1500);
|
|
70
|
+
// Focused(false) can arrive before the bubble's pointer-down reaches Rust.
|
|
71
|
+
const POPOVER_BLUR_SETTLE: Duration = Duration::from_millis(100);
|
|
70
72
|
|
|
71
|
-
fn popover_blur_delay(elapsed: Duration) -> Option<Duration> {
|
|
72
|
-
|
|
73
|
+
fn popover_blur_delay(elapsed: Duration, bubble_dragging: bool) -> Option<Duration> {
|
|
74
|
+
if bubble_dragging {
|
|
75
|
+
None
|
|
76
|
+
} else if elapsed < POPOVER_BLUR_GUARD {
|
|
77
|
+
Some(POPOVER_BLUR_GUARD - elapsed)
|
|
78
|
+
} else {
|
|
79
|
+
Some(POPOVER_BLUR_SETTLE)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
pub(crate) fn schedule_popover_dismissal(app_handle: &tauri::AppHandle) {
|
|
84
|
+
if is_recording_active(app_handle) && clips::popover_is_parked(app_handle) {
|
|
85
|
+
dlog!("[clips-tray] popover blur ignored — popover parked");
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if !config::auto_hide_popover_enabled(app_handle) {
|
|
89
|
+
dlog!("[clips-tray] popover blur ignored — auto-hide disabled");
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
let Some(handle) = app_handle.get_webview_window("popover") else {
|
|
93
|
+
return;
|
|
94
|
+
};
|
|
95
|
+
let shown_at = app_handle
|
|
96
|
+
.try_state::<PopoverShownAt>()
|
|
97
|
+
.and_then(|s| s.0.lock().ok().and_then(|g| *g));
|
|
98
|
+
let elapsed = shown_at.map(|t| t.elapsed()).unwrap_or(Duration::MAX);
|
|
99
|
+
let Some(delay) = popover_blur_delay(elapsed, clips::is_bubble_dragging()) else {
|
|
100
|
+
dlog!("[clips-tray] popover blur ignored — camera bubble drag active");
|
|
101
|
+
return;
|
|
102
|
+
};
|
|
103
|
+
dlog!(
|
|
104
|
+
"[clips-tray] popover blur, elapsed_ms={}",
|
|
105
|
+
elapsed.as_millis()
|
|
106
|
+
);
|
|
107
|
+
let delayed_handle = handle.clone();
|
|
108
|
+
let delayed_app_handle = (*app_handle).clone();
|
|
109
|
+
tauri::async_runtime::spawn(async move {
|
|
110
|
+
tokio::time::sleep(delay).await;
|
|
111
|
+
let still_current = delayed_app_handle
|
|
112
|
+
.try_state::<PopoverShownAt>()
|
|
113
|
+
.and_then(|state| state.0.lock().ok().map(|guard| *guard == shown_at))
|
|
114
|
+
.unwrap_or(false);
|
|
115
|
+
if !still_current
|
|
116
|
+
|| clips::is_bubble_dragging()
|
|
117
|
+
|| (is_recording_active(&delayed_app_handle)
|
|
118
|
+
&& clips::popover_is_parked(&delayed_app_handle))
|
|
119
|
+
|| !config::auto_hide_popover_enabled(&delayed_app_handle)
|
|
120
|
+
|| delayed_handle.is_focused().unwrap_or(true)
|
|
121
|
+
{
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
clips::hide_popover(&delayed_app_handle);
|
|
125
|
+
});
|
|
73
126
|
}
|
|
74
127
|
|
|
75
128
|
fn present_popover(app: &tauri::AppHandle) {
|
|
@@ -496,7 +549,6 @@ pub fn run() {
|
|
|
496
549
|
// itself macOS briefly steals focus from the popover, which would
|
|
497
550
|
// fire Focused(false) and hide the window we literally just showed.
|
|
498
551
|
if let Some(window) = app.get_webview_window("popover") {
|
|
499
|
-
let handle = window.clone();
|
|
500
552
|
let app_handle = app.handle().clone();
|
|
501
553
|
// NOTE: Intentionally NOT calling window.open_devtools()
|
|
502
554
|
// here. An auto-opened devtools window steals focus from
|
|
@@ -506,55 +558,7 @@ pub fn run() {
|
|
|
506
558
|
// Element if they need devtools.
|
|
507
559
|
window.on_window_event(move |event| {
|
|
508
560
|
if let tauri::WindowEvent::Focused(false) = event {
|
|
509
|
-
|
|
510
|
-
// is intentionally not a user-facing menu. Ignore
|
|
511
|
-
// its blur while native capture owns the handoff;
|
|
512
|
-
// a full-size visible popover must still dismiss even
|
|
513
|
-
// if an earlier start flow left the recording flag
|
|
514
|
-
// latched.
|
|
515
|
-
if is_recording_active(&app_handle) && clips::popover_is_parked(&app_handle) {
|
|
516
|
-
dlog!("[clips-tray] popover blur ignored — popover parked");
|
|
517
|
-
return;
|
|
518
|
-
}
|
|
519
|
-
if !config::auto_hide_popover_enabled(&app_handle) {
|
|
520
|
-
dlog!("[clips-tray] popover blur ignored — auto-hide disabled");
|
|
521
|
-
return;
|
|
522
|
-
}
|
|
523
|
-
let shown_at = app_handle
|
|
524
|
-
.try_state::<PopoverShownAt>()
|
|
525
|
-
.and_then(|s| s.0.lock().ok().and_then(|g| *g));
|
|
526
|
-
let elapsed = shown_at.map(|t| t.elapsed()).unwrap_or(Duration::MAX);
|
|
527
|
-
dlog!(
|
|
528
|
-
"[clips-tray] popover blur, elapsed_ms={}",
|
|
529
|
-
elapsed.as_millis()
|
|
530
|
-
);
|
|
531
|
-
if let Some(delay) = popover_blur_delay(elapsed) {
|
|
532
|
-
let Some(shown_at) = shown_at else {
|
|
533
|
-
return;
|
|
534
|
-
};
|
|
535
|
-
let delayed_handle = handle.clone();
|
|
536
|
-
let delayed_app_handle = app_handle.clone();
|
|
537
|
-
tauri::async_runtime::spawn(async move {
|
|
538
|
-
tokio::time::sleep(delay).await;
|
|
539
|
-
let still_current = delayed_app_handle
|
|
540
|
-
.try_state::<PopoverShownAt>()
|
|
541
|
-
.and_then(|state| {
|
|
542
|
-
state.0.lock().ok().map(|guard| *guard == Some(shown_at))
|
|
543
|
-
})
|
|
544
|
-
.unwrap_or(false);
|
|
545
|
-
if !still_current
|
|
546
|
-
|| (is_recording_active(&delayed_app_handle)
|
|
547
|
-
&& clips::popover_is_parked(&delayed_app_handle))
|
|
548
|
-
|| !config::auto_hide_popover_enabled(&delayed_app_handle)
|
|
549
|
-
|| delayed_handle.is_focused().unwrap_or(true)
|
|
550
|
-
{
|
|
551
|
-
return;
|
|
552
|
-
}
|
|
553
|
-
clips::hide_popover(&delayed_app_handle);
|
|
554
|
-
});
|
|
555
|
-
} else {
|
|
556
|
-
clips::hide_popover(&app_handle);
|
|
557
|
-
}
|
|
561
|
+
schedule_popover_dismissal(&app_handle);
|
|
558
562
|
}
|
|
559
563
|
});
|
|
560
564
|
}
|
|
@@ -661,15 +665,19 @@ pub fn run() {
|
|
|
661
665
|
|
|
662
666
|
#[cfg(test)]
|
|
663
667
|
mod tests {
|
|
664
|
-
use super::{popover_blur_delay, POPOVER_BLUR_GUARD};
|
|
668
|
+
use super::{popover_blur_delay, POPOVER_BLUR_GUARD, POPOVER_BLUR_SETTLE};
|
|
665
669
|
use std::time::Duration;
|
|
666
670
|
|
|
667
671
|
#[test]
|
|
668
672
|
fn retries_blur_until_guard_expires() {
|
|
669
673
|
assert_eq!(
|
|
670
|
-
popover_blur_delay(Duration::from_millis(400)),
|
|
674
|
+
popover_blur_delay(Duration::from_millis(400), false),
|
|
671
675
|
Some(Duration::from_millis(1100))
|
|
672
676
|
);
|
|
673
|
-
assert_eq!(
|
|
677
|
+
assert_eq!(
|
|
678
|
+
popover_blur_delay(POPOVER_BLUR_GUARD, false),
|
|
679
|
+
Some(POPOVER_BLUR_SETTLE)
|
|
680
|
+
);
|
|
681
|
+
assert_eq!(popover_blur_delay(Duration::MAX, true), None);
|
|
674
682
|
}
|
|
675
683
|
}
|