@agent-native/core 0.114.9 → 0.114.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +19 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/engine/ai-sdk-engine.ts +31 -3
- package/corpus/core/src/agent/engine/anthropic-engine.ts +20 -3
- package/corpus/core/src/agent/engine/builder-engine.ts +56 -9
- package/corpus/core/src/agent/engine/first-event-timeout.ts +64 -0
- package/corpus/core/src/cli/pr-visual-recap-workflow.ts +2 -3
- package/corpus/core/src/client/AssistantChat.tsx +7 -3
- package/corpus/core/src/client/resources/ResourcesPanel.tsx +1 -1
- package/corpus/core/src/extensions/actions.ts +219 -0
- package/corpus/core/src/integrations/computer-supervision-store.ts +4 -4
- package/corpus/core/src/integrations/pending-tasks-store.ts +1 -1
- package/corpus/core/src/integrations/remote-commands-store.ts +5 -5
- package/corpus/core/src/integrations/remote-devices-store.ts +3 -3
- package/corpus/core/src/integrations/remote-push-store.ts +3 -3
- package/corpus/core/src/observability/traces.ts +38 -1
- package/corpus/core/src/server/action-routes.ts +23 -1
- package/corpus/core/src/server/http-response-telemetry.ts +6 -1
- package/corpus/core/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +19 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/observability/SKILL.md +1 -1
- package/corpus/core/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +1 -1
- package/corpus/templates/analytics/actions/account-deep-dive.ts +41 -6
- package/corpus/templates/analytics/changelog/2026-07-20-account-deep-dives-finish-with-partial-data-when-hubspot-lo.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-20-dashboard-email-reports-now-include-an-image-when-a-single-p.md +6 -0
- package/corpus/templates/analytics/server/lib/dashboard-report.ts +11 -3
- package/corpus/templates/calendar/actions/create-event.ts +5 -0
- package/corpus/templates/calendar/app/components/ThemeToggle.tsx +4 -1
- package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +0 -1
- package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +309 -421
- package/corpus/templates/calendar/app/components/calendar/InlineEventPickers.tsx +349 -0
- package/corpus/templates/calendar/app/i18n-data.ts +110 -0
- package/corpus/templates/calendar/app/lib/event-form-utils.ts +6 -0
- package/corpus/templates/calendar/app/pages/CalendarView.tsx +3 -0
- package/corpus/templates/calendar/app/root.tsx +4 -2
- package/corpus/templates/calendar/changelog/2026-07-20-events-can-be-updated-inline-with-date-time-timezone-and-rep.md +6 -0
- package/corpus/templates/calendar/changelog/2026-07-20-public-booking-pages-no-longer-hydrate-with-a-theme-mismatch.md +6 -0
- package/corpus/templates/calendar/shared/api.ts +1 -0
- package/corpus/templates/clips/AGENTS.md +7 -0
- package/corpus/templates/clips/actions/create-recording.ts +51 -10
- package/corpus/templates/clips/actions/get-recording-player-data.ts +34 -5
- package/corpus/templates/clips/actions/lib/transcript-preview.ts +48 -0
- package/corpus/templates/clips/actions/list-recordings.ts +15 -4
- package/corpus/templates/clips/actions/regenerate-summary.ts +32 -0
- package/corpus/templates/clips/actions/search-recordings.ts +28 -6
- package/corpus/templates/clips/actions/view-screen.ts +54 -47
- package/corpus/templates/clips/app/components/library/library-grid.tsx +1 -5
- package/corpus/templates/clips/app/components/library/recording-card.tsx +29 -32
- package/corpus/templates/clips/app/components/meetings/quick-ask-sidebar.tsx +13 -0
- package/corpus/templates/clips/app/components/player/video-player.tsx +48 -0
- package/corpus/templates/clips/app/lib/fmp4.ts +26 -4
- package/corpus/templates/clips/app/lib/mse-video-loader.ts +13 -2
- package/corpus/templates/clips/changelog/2026-07-20-agent-summaries-use-bounded-transcript-context.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-20-clip-summaries-now-finish-without-sending-a-long-transcript-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-20-clips-agents-only-discover-recordings-you-own-or-have-alread.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-20-hosted-recordings-no-longer-start-with-an-impossible-buffered-upload.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-20-meeting-recordings-now-offer-a-cleaner-transcript-view-with-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-20-rewind-capture-status.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-20-shared-clips-can-be-shared-with-limited-access-and-cards-sho.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +81 -71
- package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +12 -8
- package/corpus/templates/clips/desktop/src/lib/rewind-status.ts +103 -0
- package/corpus/templates/clips/desktop/src/overlays/pill-logo.tsx +2 -15
- package/corpus/templates/clips/desktop/src/overlays/recording-pill.tsx +107 -340
- package/corpus/templates/clips/desktop/src/styles.css +37 -145
- package/corpus/templates/clips/desktop/src-tauri/src/recording_indicator.rs +6 -7
- package/corpus/templates/clips/server/lib/agent-recording-access.ts +73 -0
- package/corpus/templates/clips/server/plugins/agent-chat.ts +5 -2
- package/corpus/templates/plan/app/lib/plan-local-bridge.ts +18 -0
- package/corpus/templates/plan/app/pages/PlansPage.tsx +10 -11
- package/corpus/templates/plan/changelog/2026-07-20-bridged-local-plans-now-load-comments-from-the-local-workspace.md +6 -0
- package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
- package/dist/agent/engine/ai-sdk-engine.js +27 -4
- package/dist/agent/engine/ai-sdk-engine.js.map +1 -1
- package/dist/agent/engine/anthropic-engine.d.ts.map +1 -1
- package/dist/agent/engine/anthropic-engine.js +19 -4
- package/dist/agent/engine/anthropic-engine.js.map +1 -1
- package/dist/agent/engine/builder-engine.d.ts.map +1 -1
- package/dist/agent/engine/builder-engine.js +40 -7
- package/dist/agent/engine/builder-engine.js.map +1 -1
- package/dist/agent/engine/first-event-timeout.d.ts +25 -0
- package/dist/agent/engine/first-event-timeout.d.ts.map +1 -0
- package/dist/agent/engine/first-event-timeout.js +49 -0
- package/dist/agent/engine/first-event-timeout.js.map +1 -0
- package/dist/cli/pr-visual-recap-workflow.d.ts +2 -2
- package/dist/cli/pr-visual-recap-workflow.d.ts.map +1 -1
- package/dist/cli/pr-visual-recap-workflow.js +2 -2
- package/dist/cli/pr-visual-recap-workflow.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +7 -3
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/resources/ResourcesPanel.js +1 -1
- package/dist/client/resources/ResourcesPanel.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/collab/struct-routes.d.ts +1 -1
- package/dist/extensions/actions.d.ts.map +1 -1
- package/dist/extensions/actions.js +193 -1
- package/dist/extensions/actions.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/integrations/computer-supervision-store.js +4 -4
- package/dist/integrations/computer-supervision-store.js.map +1 -1
- package/dist/integrations/pending-tasks-store.js +1 -1
- package/dist/integrations/pending-tasks-store.js.map +1 -1
- package/dist/integrations/remote-commands-store.js +5 -5
- package/dist/integrations/remote-commands-store.js.map +1 -1
- package/dist/integrations/remote-devices-store.js +3 -3
- package/dist/integrations/remote-devices-store.js.map +1 -1
- package/dist/integrations/remote-push-store.js +3 -3
- package/dist/integrations/remote-push-store.js.map +1 -1
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/observability/routes.d.ts +3 -3
- package/dist/observability/traces.d.ts.map +1 -1
- package/dist/observability/traces.js +23 -1
- package/dist/observability/traces.js.map +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +3 -3
- package/dist/server/action-routes.d.ts.map +1 -1
- package/dist/server/action-routes.js +23 -1
- package/dist/server/action-routes.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/http-response-telemetry.d.ts +2 -0
- package/dist/server/http-response-telemetry.d.ts.map +1 -1
- package/dist/server/http-response-telemetry.js +5 -1
- package/dist/server/http-response-telemetry.js.map +1 -1
- package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +19 -0
- package/dist/templates/workspace-core/.agents/skills/observability/SKILL.md +1 -1
- package/dist/templates/workspace-core/.agents/skills/tracking/SKILL.md +1 -1
- package/package.json +2 -2
- package/src/agent/engine/ai-sdk-engine.ts +31 -3
- package/src/agent/engine/anthropic-engine.ts +20 -3
- package/src/agent/engine/builder-engine.ts +56 -9
- package/src/agent/engine/first-event-timeout.ts +64 -0
- package/src/cli/pr-visual-recap-workflow.ts +2 -3
- package/src/client/AssistantChat.tsx +7 -3
- package/src/client/resources/ResourcesPanel.tsx +1 -1
- package/src/extensions/actions.ts +219 -0
- package/src/integrations/computer-supervision-store.ts +4 -4
- package/src/integrations/pending-tasks-store.ts +1 -1
- package/src/integrations/remote-commands-store.ts +5 -5
- package/src/integrations/remote-devices-store.ts +3 -3
- package/src/integrations/remote-push-store.ts +3 -3
- package/src/observability/traces.ts +38 -1
- package/src/server/action-routes.ts +23 -1
- package/src/server/http-response-telemetry.ts +6 -1
- package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +19 -0
- package/src/templates/workspace-core/.agents/skills/observability/SKILL.md +1 -1
- package/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +1 -1
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
import { useT } from "@agent-native/core/client/i18n";
|
|
2
|
+
import {
|
|
3
|
+
IconCheck,
|
|
4
|
+
IconClock,
|
|
5
|
+
IconRefresh,
|
|
6
|
+
IconWorld,
|
|
7
|
+
} from "@tabler/icons-react";
|
|
8
|
+
import { format } from "date-fns";
|
|
9
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
10
|
+
|
|
11
|
+
import { TimezoneCombobox } from "@/components/TimezoneCombobox";
|
|
12
|
+
import { Button } from "@/components/ui/button";
|
|
13
|
+
import { Input } from "@/components/ui/input";
|
|
14
|
+
import {
|
|
15
|
+
Popover,
|
|
16
|
+
PopoverContent,
|
|
17
|
+
PopoverTrigger,
|
|
18
|
+
} from "@/components/ui/popover";
|
|
19
|
+
import {
|
|
20
|
+
formatTimezoneLabel,
|
|
21
|
+
type RecurrencePreset,
|
|
22
|
+
} from "@/lib/event-form-utils";
|
|
23
|
+
import { cn } from "@/lib/utils";
|
|
24
|
+
|
|
25
|
+
const TIME_OPTIONS = Array.from({ length: 24 * 4 }, (_, index) => {
|
|
26
|
+
const hour = Math.floor(index / 4);
|
|
27
|
+
const minute = (index % 4) * 15;
|
|
28
|
+
return `${String(hour).padStart(2, "0")}:${String(minute).padStart(2, "0")}`;
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
function formatTimeValue(value: string) {
|
|
32
|
+
const [hourValue, minuteValue] = value.split(":").map(Number);
|
|
33
|
+
if (!Number.isFinite(hourValue) || !Number.isFinite(minuteValue)) {
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
const period = hourValue >= 12 ? "PM" : "AM";
|
|
37
|
+
const hour = hourValue % 12 || 12;
|
|
38
|
+
return minuteValue === 0
|
|
39
|
+
? `${hour} ${period}`
|
|
40
|
+
: `${hour}:${String(minuteValue).padStart(2, "0")} ${period}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function formatDateValue(value: string) {
|
|
44
|
+
const parsed = new Date(`${value}T12:00:00`);
|
|
45
|
+
return Number.isNaN(parsed.getTime()) ? value : format(parsed, "EEE MMM d");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function TimePickerPopover({
|
|
49
|
+
value,
|
|
50
|
+
onChange,
|
|
51
|
+
label,
|
|
52
|
+
getOptionMeta,
|
|
53
|
+
className,
|
|
54
|
+
}: {
|
|
55
|
+
value: string;
|
|
56
|
+
onChange: (value: string) => void;
|
|
57
|
+
label: string;
|
|
58
|
+
getOptionMeta?: (value: string) => string | undefined;
|
|
59
|
+
className?: string;
|
|
60
|
+
}) {
|
|
61
|
+
const [open, setOpen] = useState(false);
|
|
62
|
+
const selectedRef = useRef<HTMLButtonElement>(null);
|
|
63
|
+
const options = useMemo(
|
|
64
|
+
() =>
|
|
65
|
+
TIME_OPTIONS.includes(value) ? TIME_OPTIONS : [value, ...TIME_OPTIONS],
|
|
66
|
+
[value],
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
if (open) {
|
|
71
|
+
requestAnimationFrame(() =>
|
|
72
|
+
selectedRef.current?.scrollIntoView({ block: "center" }),
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
}, [open]);
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<Popover open={open} onOpenChange={setOpen}>
|
|
79
|
+
<PopoverTrigger asChild>
|
|
80
|
+
<Button
|
|
81
|
+
type="button"
|
|
82
|
+
variant="ghost"
|
|
83
|
+
size="sm"
|
|
84
|
+
className={cn(
|
|
85
|
+
"h-auto rounded px-1.5 py-0.5 text-base font-normal text-foreground hover:bg-muted",
|
|
86
|
+
className,
|
|
87
|
+
)}
|
|
88
|
+
aria-label={label}
|
|
89
|
+
>
|
|
90
|
+
{formatTimeValue(value)}
|
|
91
|
+
</Button>
|
|
92
|
+
</PopoverTrigger>
|
|
93
|
+
<PopoverContent
|
|
94
|
+
align="start"
|
|
95
|
+
sideOffset={6}
|
|
96
|
+
className="w-52 p-1"
|
|
97
|
+
data-time-picker-popover
|
|
98
|
+
>
|
|
99
|
+
<div className="flex items-center gap-2 px-2.5 py-2 text-xs font-medium text-muted-foreground">
|
|
100
|
+
<IconClock className="size-3.5" />
|
|
101
|
+
{label}
|
|
102
|
+
</div>
|
|
103
|
+
<div className="max-h-64 overflow-y-auto">
|
|
104
|
+
{options.map((option) => {
|
|
105
|
+
const selected = option === value;
|
|
106
|
+
return (
|
|
107
|
+
<button
|
|
108
|
+
key={option}
|
|
109
|
+
ref={selected ? selectedRef : undefined}
|
|
110
|
+
type="button"
|
|
111
|
+
className={cn(
|
|
112
|
+
"flex w-full items-center justify-between rounded-md px-2.5 py-2 text-left text-sm transition-colors hover:bg-accent hover:text-accent-foreground",
|
|
113
|
+
selected && "bg-accent text-accent-foreground",
|
|
114
|
+
)}
|
|
115
|
+
onClick={() => {
|
|
116
|
+
onChange(option);
|
|
117
|
+
setOpen(false);
|
|
118
|
+
}}
|
|
119
|
+
>
|
|
120
|
+
<span>{formatTimeValue(option)}</span>
|
|
121
|
+
{getOptionMeta?.(option) && (
|
|
122
|
+
<span className="text-muted-foreground">
|
|
123
|
+
{getOptionMeta(option)}
|
|
124
|
+
</span>
|
|
125
|
+
)}
|
|
126
|
+
{selected && <IconCheck className="ml-2 size-3.5 shrink-0" />}
|
|
127
|
+
</button>
|
|
128
|
+
);
|
|
129
|
+
})}
|
|
130
|
+
</div>
|
|
131
|
+
</PopoverContent>
|
|
132
|
+
</Popover>
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function DatePickerPopover({
|
|
137
|
+
value,
|
|
138
|
+
onChange,
|
|
139
|
+
label,
|
|
140
|
+
className,
|
|
141
|
+
}: {
|
|
142
|
+
value: string;
|
|
143
|
+
onChange: (value: string) => void;
|
|
144
|
+
label: string;
|
|
145
|
+
className?: string;
|
|
146
|
+
}) {
|
|
147
|
+
const [open, setOpen] = useState(false);
|
|
148
|
+
|
|
149
|
+
return (
|
|
150
|
+
<Popover open={open} onOpenChange={setOpen}>
|
|
151
|
+
<PopoverTrigger asChild>
|
|
152
|
+
<Button
|
|
153
|
+
type="button"
|
|
154
|
+
variant="ghost"
|
|
155
|
+
size="sm"
|
|
156
|
+
className={cn(
|
|
157
|
+
"h-auto rounded px-1.5 py-0.5 text-base font-normal text-foreground hover:bg-muted",
|
|
158
|
+
className,
|
|
159
|
+
)}
|
|
160
|
+
aria-label={label}
|
|
161
|
+
>
|
|
162
|
+
{formatDateValue(value)}
|
|
163
|
+
</Button>
|
|
164
|
+
</PopoverTrigger>
|
|
165
|
+
<PopoverContent
|
|
166
|
+
align="start"
|
|
167
|
+
sideOffset={6}
|
|
168
|
+
className="w-auto p-3"
|
|
169
|
+
data-time-picker-popover
|
|
170
|
+
>
|
|
171
|
+
<Input
|
|
172
|
+
type="date"
|
|
173
|
+
value={value}
|
|
174
|
+
aria-label={label}
|
|
175
|
+
autoFocus
|
|
176
|
+
onChange={(event) => {
|
|
177
|
+
if (!event.target.value) return;
|
|
178
|
+
onChange(event.target.value);
|
|
179
|
+
setOpen(false);
|
|
180
|
+
}}
|
|
181
|
+
/>
|
|
182
|
+
</PopoverContent>
|
|
183
|
+
</Popover>
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function TimezonePickerPopover({
|
|
188
|
+
value,
|
|
189
|
+
onChange,
|
|
190
|
+
label,
|
|
191
|
+
}: {
|
|
192
|
+
value: string;
|
|
193
|
+
onChange: (value: string) => void;
|
|
194
|
+
label: string;
|
|
195
|
+
}) {
|
|
196
|
+
const t = useT();
|
|
197
|
+
const [open, setOpen] = useState(false);
|
|
198
|
+
|
|
199
|
+
return (
|
|
200
|
+
<Popover open={open} onOpenChange={setOpen}>
|
|
201
|
+
<PopoverTrigger asChild>
|
|
202
|
+
<Button
|
|
203
|
+
type="button"
|
|
204
|
+
variant="ghost"
|
|
205
|
+
size="sm"
|
|
206
|
+
className="h-auto justify-start rounded px-1.5 py-0.5 text-sm font-normal text-muted-foreground hover:bg-muted hover:text-foreground"
|
|
207
|
+
aria-label={label}
|
|
208
|
+
>
|
|
209
|
+
<IconWorld className="mr-2 size-4 shrink-0" />
|
|
210
|
+
{formatTimezoneLabel(value)}
|
|
211
|
+
</Button>
|
|
212
|
+
</PopoverTrigger>
|
|
213
|
+
<PopoverContent
|
|
214
|
+
align="start"
|
|
215
|
+
sideOffset={6}
|
|
216
|
+
className="w-[min(20rem,calc(100vw-2rem))] p-3"
|
|
217
|
+
data-time-picker-popover
|
|
218
|
+
>
|
|
219
|
+
<TimezoneCombobox
|
|
220
|
+
id="inline-event-timezone"
|
|
221
|
+
value={value}
|
|
222
|
+
onChange={(nextValue) => {
|
|
223
|
+
onChange(nextValue);
|
|
224
|
+
setOpen(false);
|
|
225
|
+
}}
|
|
226
|
+
/>
|
|
227
|
+
<p className="mt-2 text-xs text-muted-foreground">
|
|
228
|
+
{t("eventForm.timezone")}
|
|
229
|
+
</p>
|
|
230
|
+
</PopoverContent>
|
|
231
|
+
</Popover>
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export function RepeatPicker({
|
|
236
|
+
preset,
|
|
237
|
+
referenceDate,
|
|
238
|
+
onChange,
|
|
239
|
+
}: {
|
|
240
|
+
preset: RecurrencePreset;
|
|
241
|
+
referenceDate: string;
|
|
242
|
+
onChange: (preset: RecurrencePreset) => void;
|
|
243
|
+
}) {
|
|
244
|
+
const t = useT();
|
|
245
|
+
const [open, setOpen] = useState(false);
|
|
246
|
+
const reference = new Date(referenceDate);
|
|
247
|
+
const weekday = Number.isNaN(reference.getTime())
|
|
248
|
+
? ""
|
|
249
|
+
: format(reference, "EEE");
|
|
250
|
+
const monthDay = Number.isNaN(reference.getTime())
|
|
251
|
+
? ""
|
|
252
|
+
: format(reference, "do");
|
|
253
|
+
const options: Array<{
|
|
254
|
+
value: RecurrencePreset;
|
|
255
|
+
label: string;
|
|
256
|
+
meta?: string;
|
|
257
|
+
disabled?: boolean;
|
|
258
|
+
}> = [
|
|
259
|
+
{ value: "none", label: t("eventForm.doesNotRepeat") },
|
|
260
|
+
{ value: "daily", label: t("eventForm.daily") },
|
|
261
|
+
{
|
|
262
|
+
value: "weekdays",
|
|
263
|
+
label: t("eventForm.everyWeekday"),
|
|
264
|
+
meta: t("eventForm.weekdaysShort"),
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
value: "weekly",
|
|
268
|
+
label: t("eventForm.weekly"),
|
|
269
|
+
meta: weekday ? t("eventForm.onDay", { day: weekday }) : undefined,
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
value: "biweekly",
|
|
273
|
+
label: t("eventForm.everyTwoWeeks"),
|
|
274
|
+
meta: weekday ? t("eventForm.onDay", { day: weekday }) : undefined,
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
value: "monthly",
|
|
278
|
+
label: t("eventForm.monthly"),
|
|
279
|
+
meta: monthDay ? t("eventForm.onMonthDay", { day: monthDay }) : undefined,
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
value: "yearly",
|
|
283
|
+
label: t("eventForm.yearly"),
|
|
284
|
+
meta: monthDay
|
|
285
|
+
? t("eventForm.onDate", { date: format(reference, "MMM d") })
|
|
286
|
+
: undefined,
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
value: "custom",
|
|
290
|
+
label: t("eventForm.customSchedule"),
|
|
291
|
+
disabled: true,
|
|
292
|
+
},
|
|
293
|
+
];
|
|
294
|
+
|
|
295
|
+
const selectedOption = options.find((option) => option.value === preset);
|
|
296
|
+
const triggerLabel =
|
|
297
|
+
preset === "none" ? t("eventForm.repeat") : selectedOption?.label;
|
|
298
|
+
|
|
299
|
+
return (
|
|
300
|
+
<Popover open={open} onOpenChange={setOpen}>
|
|
301
|
+
<PopoverTrigger asChild>
|
|
302
|
+
<Button
|
|
303
|
+
type="button"
|
|
304
|
+
variant="ghost"
|
|
305
|
+
size="sm"
|
|
306
|
+
className="h-auto w-full justify-start rounded-md px-1.5 py-1 text-sm font-normal text-muted-foreground hover:bg-muted hover:text-foreground"
|
|
307
|
+
aria-label={t("eventForm.repeat")}
|
|
308
|
+
>
|
|
309
|
+
<IconRefresh className="mr-2 size-4 shrink-0" />
|
|
310
|
+
{triggerLabel}
|
|
311
|
+
</Button>
|
|
312
|
+
</PopoverTrigger>
|
|
313
|
+
<PopoverContent
|
|
314
|
+
align="start"
|
|
315
|
+
sideOffset={6}
|
|
316
|
+
className="w-64 p-1"
|
|
317
|
+
data-time-picker-popover
|
|
318
|
+
>
|
|
319
|
+
{options.map((option) => {
|
|
320
|
+
const selected = option.value === preset;
|
|
321
|
+
return (
|
|
322
|
+
<button
|
|
323
|
+
key={option.value}
|
|
324
|
+
type="button"
|
|
325
|
+
disabled={option.disabled}
|
|
326
|
+
className={cn(
|
|
327
|
+
"flex w-full items-center justify-between rounded-md px-2.5 py-2 text-left text-sm transition-colors hover:bg-accent hover:text-accent-foreground",
|
|
328
|
+
selected && "bg-accent text-accent-foreground",
|
|
329
|
+
option.disabled &&
|
|
330
|
+
"cursor-not-allowed opacity-50 hover:bg-transparent",
|
|
331
|
+
)}
|
|
332
|
+
onClick={() => {
|
|
333
|
+
if (option.disabled) return;
|
|
334
|
+
onChange(option.value);
|
|
335
|
+
setOpen(false);
|
|
336
|
+
}}
|
|
337
|
+
>
|
|
338
|
+
<span>{option.label}</span>
|
|
339
|
+
<span className="ml-3 flex items-center gap-2 text-muted-foreground">
|
|
340
|
+
{option.meta}
|
|
341
|
+
{selected && <IconCheck className="size-3.5 shrink-0" />}
|
|
342
|
+
</span>
|
|
343
|
+
</button>
|
|
344
|
+
);
|
|
345
|
+
})}
|
|
346
|
+
</PopoverContent>
|
|
347
|
+
</Popover>
|
|
348
|
+
);
|
|
349
|
+
}
|
|
@@ -703,6 +703,7 @@ const enUS = {
|
|
|
703
703
|
addCalendar: "Add Calendar",
|
|
704
704
|
addCalendarFailed: "Failed to add calendar",
|
|
705
705
|
addCalendarFromUrl: "Add calendar from URL",
|
|
706
|
+
addGuest: "Add guest",
|
|
706
707
|
addGuests: "Add guests",
|
|
707
708
|
addNote: "Add note",
|
|
708
709
|
addLocation: "Add location",
|
|
@@ -766,6 +767,7 @@ const enUS = {
|
|
|
766
767
|
eventTitlePlaceholder: "Event title",
|
|
767
768
|
events: "Events",
|
|
768
769
|
everyWeekday: "Every weekday",
|
|
770
|
+
everyTwoWeeks: "Every 2 weeks",
|
|
769
771
|
findTime: "Find a time",
|
|
770
772
|
focusTime: "Focus time",
|
|
771
773
|
focusTimeTimedOnly:
|
|
@@ -821,6 +823,7 @@ const enUS = {
|
|
|
821
823
|
quickCreate: "Quick create",
|
|
822
824
|
recurringResponseScope: "Recurring response scope",
|
|
823
825
|
repeats: "Repeats",
|
|
826
|
+
repeat: "Repeat",
|
|
824
827
|
reviewInvite: "Review Invite",
|
|
825
828
|
responseAwaitingCount: "{{count}} awaiting",
|
|
826
829
|
responseMaybeCount: "{{count}} maybe",
|
|
@@ -872,6 +875,10 @@ const enUS = {
|
|
|
872
875
|
viewingOwnerCalendar: "Viewing {{owner}}'s calendar",
|
|
873
876
|
yourResponse: "Your response: {{status}}",
|
|
874
877
|
visibility: "Visibility",
|
|
878
|
+
onDate: "on {{date}}",
|
|
879
|
+
onDay: "on {{day}}",
|
|
880
|
+
onMonthDay: "on the {{day}}",
|
|
881
|
+
weekdaysShort: "Mon–Fri",
|
|
875
882
|
weekly: "Weekly",
|
|
876
883
|
workingFrom: "Working from",
|
|
877
884
|
workingLocation: "Working location",
|
|
@@ -6122,6 +6129,108 @@ const translatedCalendarDebtTranslations = {
|
|
|
6122
6129
|
},
|
|
6123
6130
|
} satisfies Partial<Record<LocaleCode, PartialMessages>>;
|
|
6124
6131
|
|
|
6132
|
+
const translatedCalendarInlineEditorLabels = {
|
|
6133
|
+
"zh-CN": {
|
|
6134
|
+
eventForm: {
|
|
6135
|
+
addGuest: "添加客人",
|
|
6136
|
+
everyTwoWeeks: "每两周",
|
|
6137
|
+
repeat: "重复",
|
|
6138
|
+
onDate: "于 {{date}}",
|
|
6139
|
+
onDay: "于 {{day}}",
|
|
6140
|
+
onMonthDay: "于 {{day}}日",
|
|
6141
|
+
weekdaysShort: "周一至周五",
|
|
6142
|
+
},
|
|
6143
|
+
},
|
|
6144
|
+
"es-ES": {
|
|
6145
|
+
eventForm: {
|
|
6146
|
+
addGuest: "Añadir invitado",
|
|
6147
|
+
everyTwoWeeks: "Cada 2 semanas",
|
|
6148
|
+
repeat: "Repetir",
|
|
6149
|
+
onDate: "el {{date}}",
|
|
6150
|
+
onDay: "el {{day}}",
|
|
6151
|
+
onMonthDay: "el día {{day}}",
|
|
6152
|
+
weekdaysShort: "lun–vie",
|
|
6153
|
+
},
|
|
6154
|
+
},
|
|
6155
|
+
"fr-FR": {
|
|
6156
|
+
eventForm: {
|
|
6157
|
+
addGuest: "Ajouter un invité",
|
|
6158
|
+
everyTwoWeeks: "Toutes les 2 semaines",
|
|
6159
|
+
repeat: "Répéter",
|
|
6160
|
+
onDate: "le {{date}}",
|
|
6161
|
+
onDay: "le {{day}}",
|
|
6162
|
+
onMonthDay: "le {{day}}",
|
|
6163
|
+
weekdaysShort: "lun–ven",
|
|
6164
|
+
},
|
|
6165
|
+
},
|
|
6166
|
+
"de-DE": {
|
|
6167
|
+
eventForm: {
|
|
6168
|
+
addGuest: "Gast hinzufügen",
|
|
6169
|
+
everyTwoWeeks: "Alle 2 Wochen",
|
|
6170
|
+
repeat: "Wiederholen",
|
|
6171
|
+
onDate: "am {{date}}",
|
|
6172
|
+
onDay: "am {{day}}",
|
|
6173
|
+
onMonthDay: "am {{day}}.",
|
|
6174
|
+
weekdaysShort: "Mo–Fr",
|
|
6175
|
+
},
|
|
6176
|
+
},
|
|
6177
|
+
"ja-JP": {
|
|
6178
|
+
eventForm: {
|
|
6179
|
+
addGuest: "ゲストを追加",
|
|
6180
|
+
everyTwoWeeks: "2週間ごと",
|
|
6181
|
+
repeat: "繰り返し",
|
|
6182
|
+
onDate: "{{date}}",
|
|
6183
|
+
onDay: "{{day}}に",
|
|
6184
|
+
onMonthDay: "{{day}}日",
|
|
6185
|
+
weekdaysShort: "月〜金",
|
|
6186
|
+
},
|
|
6187
|
+
},
|
|
6188
|
+
"ko-KR": {
|
|
6189
|
+
eventForm: {
|
|
6190
|
+
addGuest: "게스트 추가",
|
|
6191
|
+
everyTwoWeeks: "2주마다",
|
|
6192
|
+
repeat: "반복",
|
|
6193
|
+
onDate: "{{date}}",
|
|
6194
|
+
onDay: "{{day}}에",
|
|
6195
|
+
onMonthDay: "{{day}}일",
|
|
6196
|
+
weekdaysShort: "월–금",
|
|
6197
|
+
},
|
|
6198
|
+
},
|
|
6199
|
+
"pt-BR": {
|
|
6200
|
+
eventForm: {
|
|
6201
|
+
addGuest: "Adicionar convidado",
|
|
6202
|
+
everyTwoWeeks: "A cada 2 semanas",
|
|
6203
|
+
repeat: "Repetir",
|
|
6204
|
+
onDate: "em {{date}}",
|
|
6205
|
+
onDay: "em {{day}}",
|
|
6206
|
+
onMonthDay: "no dia {{day}}",
|
|
6207
|
+
weekdaysShort: "seg–sex",
|
|
6208
|
+
},
|
|
6209
|
+
},
|
|
6210
|
+
"hi-IN": {
|
|
6211
|
+
eventForm: {
|
|
6212
|
+
addGuest: "अतिथि जोड़ें",
|
|
6213
|
+
everyTwoWeeks: "हर 2 सप्ताह",
|
|
6214
|
+
repeat: "दोहराएं",
|
|
6215
|
+
onDate: "{{date}} को",
|
|
6216
|
+
onDay: "{{day}} को",
|
|
6217
|
+
onMonthDay: "{{day}} को",
|
|
6218
|
+
weekdaysShort: "सोम–शुक्र",
|
|
6219
|
+
},
|
|
6220
|
+
},
|
|
6221
|
+
"ar-SA": {
|
|
6222
|
+
eventForm: {
|
|
6223
|
+
addGuest: "إضافة ضيف",
|
|
6224
|
+
everyTwoWeeks: "كل أسبوعين",
|
|
6225
|
+
repeat: "تكرار",
|
|
6226
|
+
onDate: "في {{date}}",
|
|
6227
|
+
onDay: "في {{day}}",
|
|
6228
|
+
onMonthDay: "في اليوم {{day}}",
|
|
6229
|
+
weekdaysShort: "الإثنين–الجمعة",
|
|
6230
|
+
},
|
|
6231
|
+
},
|
|
6232
|
+
} satisfies Partial<Record<LocaleCode, PartialMessages>>;
|
|
6233
|
+
|
|
6125
6234
|
const translatedCalendarRemainingRaw = {
|
|
6126
6235
|
"zh-CN": {
|
|
6127
6236
|
agentSidebar: {
|
|
@@ -9555,6 +9664,7 @@ function applyTranslatedCalendarOverrides(
|
|
|
9555
9664
|
|
|
9556
9665
|
applyTranslatedCalendarOverrides(translatedCalendarExtras);
|
|
9557
9666
|
applyTranslatedCalendarOverrides(translatedCalendarDebtTranslations);
|
|
9667
|
+
applyTranslatedCalendarOverrides(translatedCalendarInlineEditorLabels);
|
|
9558
9668
|
applyTranslatedCalendarOverrides(translatedCalendarRemainingRaw);
|
|
9559
9669
|
applyTranslatedCalendarOverrides(translatedCalendarRawBurnDown);
|
|
9560
9670
|
applyTranslatedCalendarOverrides(translatedCalendarExactCleanup);
|
|
@@ -7,6 +7,7 @@ export type RecurrencePreset =
|
|
|
7
7
|
| "daily"
|
|
8
8
|
| "weekdays"
|
|
9
9
|
| "weekly"
|
|
10
|
+
| "biweekly"
|
|
10
11
|
| "monthly"
|
|
11
12
|
| "yearly"
|
|
12
13
|
| "custom";
|
|
@@ -334,6 +335,7 @@ export function getRecurrencePreset(recurrence?: string[]): RecurrencePreset {
|
|
|
334
335
|
const interval = recurrenceField(rule, "INTERVAL") || "1";
|
|
335
336
|
const byDay = recurrenceField(rule, "BYDAY");
|
|
336
337
|
|
|
338
|
+
if (freq === "WEEKLY" && interval === "2") return "biweekly";
|
|
337
339
|
if (interval !== "1") return "custom";
|
|
338
340
|
if (freq === "DAILY" && !byDay) return "daily";
|
|
339
341
|
if ((freq === "DAILY" || freq === "WEEKLY") && byDay === "MO,TU,WE,TH,FR") {
|
|
@@ -361,6 +363,10 @@ export function buildRecurrenceRules(
|
|
|
361
363
|
const day = eventWeekdayCode(startIso, timeZone);
|
|
362
364
|
return [`RRULE:FREQ=WEEKLY;BYDAY=${day}`];
|
|
363
365
|
}
|
|
366
|
+
case "biweekly": {
|
|
367
|
+
const day = eventWeekdayCode(startIso, timeZone);
|
|
368
|
+
return [`RRULE:FREQ=WEEKLY;INTERVAL=2;BYDAY=${day}`];
|
|
369
|
+
}
|
|
364
370
|
case "monthly":
|
|
365
371
|
return ["RRULE:FREQ=MONTHLY"];
|
|
366
372
|
case "yearly":
|
|
@@ -199,6 +199,7 @@ function draftToCalendarEvent(
|
|
|
199
199
|
transparency: draft.transparency,
|
|
200
200
|
visibility: draft.visibility,
|
|
201
201
|
eventType: draft.eventType ?? "default",
|
|
202
|
+
recurrence: draft.recurrence,
|
|
202
203
|
attendees: draft.attendees,
|
|
203
204
|
reminders: draft.reminders,
|
|
204
205
|
remindersUseDefault: draft.remindersUseDefault,
|
|
@@ -239,6 +240,7 @@ function applyDraftPatch(
|
|
|
239
240
|
copy("transparency");
|
|
240
241
|
copy("visibility");
|
|
241
242
|
copy("colorId");
|
|
243
|
+
copy("recurrence");
|
|
242
244
|
copy("reminders");
|
|
243
245
|
copy("remindersUseDefault");
|
|
244
246
|
copy("attachments");
|
|
@@ -677,6 +679,7 @@ export default function CalendarView() {
|
|
|
677
679
|
? getGoogleEventColorHex(draft.colorId)
|
|
678
680
|
: undefined,
|
|
679
681
|
colorId: draft.colorId,
|
|
682
|
+
recurrence: draft.recurrence,
|
|
680
683
|
attachments: draft.attachments,
|
|
681
684
|
attendees: draft.attendees,
|
|
682
685
|
};
|
|
@@ -24,7 +24,7 @@ import { resolveLocaleFromRequest } from "@agent-native/core/server";
|
|
|
24
24
|
import { IconBrain, IconSun, IconMoon } from "@tabler/icons-react";
|
|
25
25
|
import { useQueryClient } from "@tanstack/react-query";
|
|
26
26
|
import { useTheme } from "next-themes";
|
|
27
|
-
import { useCallback, useState } from "react";
|
|
27
|
+
import { useCallback, useEffect, useState } from "react";
|
|
28
28
|
import {
|
|
29
29
|
Links,
|
|
30
30
|
Meta,
|
|
@@ -174,8 +174,10 @@ function DbSyncSetup() {
|
|
|
174
174
|
|
|
175
175
|
function ThemeToggleItem() {
|
|
176
176
|
const { resolvedTheme, setTheme } = useTheme();
|
|
177
|
+
const [mounted, setMounted] = useState(false);
|
|
178
|
+
useEffect(() => setMounted(true), []);
|
|
177
179
|
const t = useT();
|
|
178
|
-
const isDark = resolvedTheme === "dark";
|
|
180
|
+
const isDark = mounted && resolvedTheme === "dark";
|
|
179
181
|
return (
|
|
180
182
|
<CommandMenu.Item
|
|
181
183
|
onSelect={() => setTheme(isDark ? "light" : "dark")}
|
|
@@ -142,6 +142,7 @@ export interface CalendarEventDraft {
|
|
|
142
142
|
transparency?: "opaque" | "transparent";
|
|
143
143
|
visibility?: "default" | "public" | "private" | "confidential";
|
|
144
144
|
colorId?: string;
|
|
145
|
+
recurrence?: string[];
|
|
145
146
|
reminders?: CalendarEvent["reminders"];
|
|
146
147
|
remindersUseDefault?: boolean;
|
|
147
148
|
attachments?: CalendarEvent["attachments"];
|
|
@@ -110,6 +110,13 @@ ladder.
|
|
|
110
110
|
- Use `list-recordings --view=shared` for the current user's "Shared with me"
|
|
111
111
|
collection. It returns recordings admitted by sharing access that are owned
|
|
112
112
|
by someone else; public-link-only clips remain out of this list.
|
|
113
|
+
- Public recordings are unlisted-by-link for agent purposes: an agent may
|
|
114
|
+
discover only recordings the current user owns or has already viewed. Do not
|
|
115
|
+
use `list-recordings` or `search-recordings` to discover another user's
|
|
116
|
+
public clips, answer a time/date question about the clip already in context,
|
|
117
|
+
or recover from a failed direct lookup. If the user supplies another clip's
|
|
118
|
+
share URL or id, use that explicit reference; otherwise stop and report the
|
|
119
|
+
lookup failure.
|
|
113
120
|
- Public recordings expose AI-readable URLs for external agents:
|
|
114
121
|
`/api/agent-context.json?id=<recordingId>` for metadata, transcript, and frame
|
|
115
122
|
API discovery; `/api/agent-transcript.json?id=<recordingId>` for transcript
|