@agent-native/core 0.180.0-nightly-20260915184036 → 0.180.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/CHANGELOG.md +10 -0
  3. package/corpus/templates/calendar/CHANGELOG.md +39 -0
  4. package/corpus/templates/calendar/INTERACTION-MATRIX.md +19 -17
  5. package/corpus/templates/calendar/actions/update-calendar-visual-preferences.ts +21 -0
  6. package/corpus/templates/calendar/actions/view-screen.ts +10 -2
  7. package/corpus/templates/calendar/app/components/calendar/CreateEventDialog.tsx +11 -0
  8. package/corpus/templates/calendar/app/components/calendar/MonthView.tsx +174 -145
  9. package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +20 -16
  10. package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +1 -0
  11. package/corpus/templates/calendar/app/hooks/use-events.ts +16 -0
  12. package/corpus/templates/calendar/app/hooks/use-view-preferences.ts +126 -14
  13. package/corpus/templates/calendar/app/i18n-data.ts +90 -0
  14. package/corpus/templates/calendar/app/lib/calendar-navigation.ts +29 -9
  15. package/corpus/templates/calendar/app/lib/calendar-timezone.ts +2 -0
  16. package/corpus/templates/calendar/app/lib/event-time-range.ts +34 -0
  17. package/corpus/templates/calendar/app/pages/CalendarView.tsx +245 -35
  18. package/corpus/templates/calendar/changelog/2026-09-13-calendar-sidebar-overflow-is-easier-to-discover.md +6 -0
  19. package/corpus/templates/calendar/changelog/2026-09-15-calendar-mirrors-notion-calendar-s-customizable-day-ranges-a.md +6 -0
  20. package/corpus/templates/calendar/shared/calendar-view-preferences.ts +36 -0
  21. package/corpus/templates/calendar/shared/timezone.ts +9 -2
  22. package/corpus/templates/clips/.agents/skills/video-sharing/SKILL.md +2 -0
  23. package/corpus/templates/clips/CHANGELOG.md +19 -0
  24. package/corpus/templates/clips/actions/save-browser-diagnostics.ts +43 -4
  25. package/corpus/templates/clips/app/components/player/browser-diagnostics-panel.tsx +114 -1
  26. package/corpus/templates/clips/app/i18n/en-US.ts +7 -0
  27. package/corpus/templates/clips/app/lib/browser-diagnostics-capture.ts +108 -0
  28. package/corpus/templates/clips/app/routes/record.tsx +1 -0
  29. package/corpus/templates/clips/changelog/2026-09-15-recordings-include-a-structured-timeline-of-browser-interact.md +6 -0
  30. package/corpus/templates/clips/chrome-extension/public/manifest.json +7 -0
  31. package/corpus/templates/clips/chrome-extension/src/background.ts +235 -9
  32. package/corpus/templates/clips/chrome-extension/src/content-history-bridge.ts +80 -0
  33. package/corpus/templates/clips/chrome-extension/src/content-script.ts +166 -5
  34. package/corpus/templates/clips/chrome-extension/vite.config.ts +4 -0
  35. package/corpus/templates/clips/server/db/schema.ts +3 -0
  36. package/corpus/templates/clips/server/lib/public-agent-context.ts +40 -2
  37. package/corpus/templates/clips/server/plugins/db.ts +5 -0
  38. package/corpus/templates/clips/shared/browser-diagnostics.ts +171 -0
  39. package/corpus/templates/content/.agents/skills/content/SKILL.md +4 -2
  40. package/corpus/templates/content/.agents/skills/document-editing/SKILL.md +8 -1
  41. package/corpus/templates/content/AGENTS.md +5 -6
  42. package/corpus/templates/content/CHANGELOG.md +24 -0
  43. package/corpus/templates/content/actions/_content-database-row-migration.ts +67 -36
  44. package/corpus/templates/content/actions/_document-edit-mutation.ts +74 -23
  45. package/corpus/templates/content/actions/edit-document.ts +48 -8
  46. package/corpus/templates/content/actions/migrate-content-database-rows.ts +69 -28
  47. package/corpus/templates/content/server/plugins/agent-chat.ts +1 -1
  48. package/corpus/templates/design/CHANGELOG.md +22 -0
  49. package/corpus/templates/factory/CHANGELOG.md +11 -0
  50. package/corpus/templates/forms/CHANGELOG.md +6 -0
  51. package/corpus/templates/mail/.agents/skills/mail-interaction-parity/references/interaction-matrix.md +24 -4
  52. package/corpus/templates/mail/CHANGELOG.md +68 -0
  53. package/corpus/templates/mail/app/components/email/ComposeBubbleToolbar.tsx +8 -0
  54. package/corpus/templates/mail/app/components/email/ComposeModal.tsx +33 -13
  55. package/corpus/templates/mail/app/components/email/EmailListItem.tsx +23 -4
  56. package/corpus/templates/mail/app/components/email/EmailThread.tsx +56 -13
  57. package/corpus/templates/mail/app/components/email/InlineReplyComposer.tsx +23 -4
  58. package/corpus/templates/mail/app/components/email/RecipientInput.tsx +9 -0
  59. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +8 -1
  60. package/corpus/templates/mail/app/i18n/en-US.ts +31 -0
  61. package/corpus/templates/mail/changelog/2026-09-15-mail-labels-recipient-and-account-controls-for-keyboard-and-.md +6 -0
  62. package/corpus/templates/slides/CHANGELOG.md +32 -0
  63. package/dist/client/CommandMenu.js +14 -2
  64. package/dist/client/settings/AccountSettingsCard.js +3 -2
  65. package/dist/client/settings/SettingsTabsPage.js +9 -1
  66. package/dist/client/settings/agent-settings-search.js +5 -1
  67. package/dist/client/sign-out.d.ts +1 -0
  68. package/dist/client/sign-out.js +8 -0
  69. package/dist/collab/routes.d.ts +1 -1
  70. package/dist/file-upload/actions/upload-image.d.ts +2 -2
  71. package/dist/observability/routes.d.ts +3 -3
  72. package/dist/org/handlers.d.ts +1 -1
  73. package/dist/resources/handlers.d.ts +1 -1
  74. package/dist/secrets/routes.d.ts +3 -3
  75. package/dist/server/agent-engine-api-key-route.d.ts +2 -2
  76. package/dist/server/transcribe-voice.d.ts +1 -1
  77. package/package.json +4 -4
@@ -21,7 +21,6 @@ import { useQueryClient } from "@tanstack/react-query";
21
21
  import {
22
22
  format,
23
23
  startOfWeek,
24
- endOfWeek,
25
24
  addMonths,
26
25
  subMonths,
27
26
  addWeeks,
@@ -51,14 +50,25 @@ import { WeekView } from "@/components/calendar/WeekView";
51
50
  import { useCalendarContext } from "@/components/layout/AppLayout";
52
51
  import type { ViewMode } from "@/components/layout/AppLayout";
53
52
  import { Button } from "@/components/ui/button";
53
+ import {
54
+ Dialog,
55
+ DialogContent,
56
+ DialogFooter,
57
+ DialogHeader,
58
+ DialogTitle,
59
+ } from "@/components/ui/dialog";
54
60
  import {
55
61
  DropdownMenu,
62
+ DropdownMenuCheckboxItem,
56
63
  DropdownMenuContent,
57
64
  DropdownMenuItem,
58
- DropdownMenuLabel,
59
65
  DropdownMenuSeparator,
66
+ DropdownMenuSub,
67
+ DropdownMenuSubContent,
68
+ DropdownMenuSubTrigger,
60
69
  DropdownMenuTrigger,
61
70
  } from "@/components/ui/dropdown-menu";
71
+ import { Input } from "@/components/ui/input";
62
72
  import { Spinner } from "@/components/ui/spinner";
63
73
  import {
64
74
  Tooltip,
@@ -73,6 +83,7 @@ import {
73
83
  useDeleteEvent,
74
84
  useRsvpEvent,
75
85
  findEventByCurrentOrReplacedId,
86
+ findVisibleSelectedEvent,
76
87
  prefetchEvents,
77
88
  shouldShowEventsSkeleton,
78
89
  } from "@/hooks/use-events";
@@ -106,6 +117,13 @@ import {
106
117
  moveEventToCalendarDate,
107
118
  normalizeTimezone,
108
119
  } from "@/lib/calendar-timezone";
120
+ import {
121
+ DEFAULT_CALENDAR_DAYS,
122
+ isEventVisibleForDeclinedPreference,
123
+ MAX_CALENDAR_DAYS,
124
+ MIN_CALENDAR_DAYS,
125
+ normalizeNumberOfDays,
126
+ } from "@/lib/calendar-view-preferences";
109
127
  import { resolveEventAccountEmail } from "@/lib/event-account-selection";
110
128
  import { getGoogleEventColorHex } from "@/lib/event-colors";
111
129
  import {
@@ -443,6 +461,10 @@ export default function CalendarView() {
443
461
  new Map(),
444
462
  );
445
463
  const [commandPaletteOpen, setCommandPaletteOpen] = useState(false);
464
+ const [customDaysOpen, setCustomDaysOpen] = useState(false);
465
+ const [customDaysInput, setCustomDaysInput] = useState(
466
+ String(DEFAULT_CALENDAR_DAYS),
467
+ );
446
468
  const openCommandPalette = useCallback(() => {
447
469
  if (commandPaletteOpen) return;
448
470
  trackEvent("calendar_search_opened", {
@@ -586,8 +608,20 @@ export default function CalendarView() {
586
608
  // timezone is only a temporary fallback while settings are loading.
587
609
  const { from, to } = useMemo(
588
610
  () =>
589
- getViewDateRange(viewMode, selectedDate, displayTimezone, weekStartsOn),
590
- [displayTimezone, selectedDate, viewMode, weekStartsOn],
611
+ getViewDateRange(
612
+ viewMode,
613
+ selectedDate,
614
+ displayTimezone,
615
+ weekStartsOn,
616
+ viewPrefs.numberOfDays,
617
+ ),
618
+ [
619
+ displayTimezone,
620
+ selectedDate,
621
+ viewMode,
622
+ viewPrefs.numberOfDays,
623
+ weekStartsOn,
624
+ ],
591
625
  );
592
626
 
593
627
  const {
@@ -646,12 +680,28 @@ export default function CalendarView() {
646
680
  );
647
681
  }
648
682
  case "week": {
649
- // Two weeks forward so rapid `j j` stays instant, plus one back.
650
- const next = addWeeks(selectedDate, 1);
651
- const next2 = addWeeks(selectedDate, 2);
652
- const prev = subWeeks(selectedDate, 1);
683
+ // Warm two displayed periods so rapid `j j` stays instant, plus one
684
+ // period back. A custom day count advances by that same count.
685
+ const step = normalizeNumberOfDays(viewPrefs.numberOfDays);
686
+ const currentPeriodStart =
687
+ step === 7
688
+ ? dateKeyToDate(
689
+ dateToCalendarDateKey(
690
+ startOfWeek(selectedDate, { weekStartsOn }),
691
+ ),
692
+ )
693
+ : selectedDate;
694
+ const next = addDays(currentPeriodStart, step);
695
+ const next2 = addDays(currentPeriodStart, step * 2);
696
+ const prev = subDays(currentPeriodStart, step);
653
697
  return [next, next2, prev].map((date) =>
654
- getViewDateRange("week", date, displayTimezone, weekStartsOn),
698
+ getViewDateRange(
699
+ "week",
700
+ date,
701
+ displayTimezone,
702
+ weekStartsOn,
703
+ viewPrefs.numberOfDays,
704
+ ),
655
705
  );
656
706
  }
657
707
  case "day": {
@@ -680,6 +730,7 @@ export default function CalendarView() {
680
730
  queryClient,
681
731
  selectedDate,
682
732
  viewMode,
733
+ viewPrefs.numberOfDays,
683
734
  weekStartsOn,
684
735
  ]);
685
736
 
@@ -740,6 +791,14 @@ export default function CalendarView() {
740
791
  ) {
741
792
  return false;
742
793
  }
794
+ if (
795
+ !isEventVisibleForDeclinedPreference(
796
+ e.responseStatus,
797
+ viewPrefs.showDeclinedEvents,
798
+ )
799
+ ) {
800
+ return false;
801
+ }
743
802
  // Hide events from hidden external calendars
744
803
  if (e.source === "ical") {
745
804
  const hiddenMatch = hiddenCalendars.external.some((calId) =>
@@ -755,6 +814,7 @@ export default function CalendarView() {
755
814
  overlayPeople,
756
815
  hiddenCalendars,
757
816
  quickEditTempIds,
817
+ viewPrefs.showDeclinedEvents,
758
818
  viewPrefs.googleCalendarVisibility,
759
819
  ]);
760
820
 
@@ -1055,25 +1115,54 @@ export default function CalendarView() {
1055
1115
 
1056
1116
  useEffect(() => {
1057
1117
  if (sidebarEvent) {
1058
- const rebound = findEventByCurrentOrReplacedId(events, sidebarEvent);
1059
- if (rebound && rebound.id !== sidebarEvent.id) setSidebarEvent(rebound);
1118
+ const rebound = findVisibleSelectedEvent(
1119
+ events,
1120
+ sidebarEvent,
1121
+ viewPrefs.showDeclinedEvents,
1122
+ );
1123
+ if (!rebound) setSidebarEvent(null);
1124
+ else if (rebound.id !== sidebarEvent.id) setSidebarEvent(rebound);
1060
1125
  }
1061
1126
  if (focusedEvent) {
1062
- const rebound = findEventByCurrentOrReplacedId(events, focusedEvent);
1063
- if (rebound && rebound.id !== focusedEvent.id) setFocusedEvent(rebound);
1127
+ const rebound = findVisibleSelectedEvent(
1128
+ events,
1129
+ focusedEvent,
1130
+ viewPrefs.showDeclinedEvents,
1131
+ );
1132
+ if (!rebound) setFocusedEvent(null);
1133
+ else if (rebound.id !== focusedEvent.id) setFocusedEvent(rebound);
1064
1134
  }
1065
- }, [events, focusedEvent, setFocusedEvent, setSidebarEvent, sidebarEvent]);
1135
+ }, [
1136
+ events,
1137
+ focusedEvent,
1138
+ setFocusedEvent,
1139
+ setSidebarEvent,
1140
+ sidebarEvent,
1141
+ viewPrefs.showDeclinedEvents,
1142
+ ]);
1066
1143
 
1067
1144
  const selectedEvent = useMemo(() => {
1068
1145
  const candidate = sidebarEvent ?? focusedEvent;
1069
1146
  if (!candidate) return null;
1070
- return findEventByCurrentOrReplacedId(events, candidate) ?? candidate;
1071
- }, [events, sidebarEvent, focusedEvent]);
1147
+ return (
1148
+ findVisibleSelectedEvent(
1149
+ events,
1150
+ candidate,
1151
+ viewPrefs.showDeclinedEvents,
1152
+ ) ?? null
1153
+ );
1154
+ }, [events, sidebarEvent, focusedEvent, viewPrefs.showDeclinedEvents]);
1072
1155
 
1073
1156
  const refreshedSidebarEvent = useMemo(() => {
1074
1157
  if (!sidebarEvent) return null;
1075
- return findEventByCurrentOrReplacedId(events, sidebarEvent) ?? sidebarEvent;
1076
- }, [events, sidebarEvent]);
1158
+ return (
1159
+ findVisibleSelectedEvent(
1160
+ events,
1161
+ sidebarEvent,
1162
+ viewPrefs.showDeclinedEvents,
1163
+ ) ?? null
1164
+ );
1165
+ }, [events, sidebarEvent, viewPrefs.showDeclinedEvents]);
1077
1166
 
1078
1167
  function handleNavigate(direction: "prev" | "next") {
1079
1168
  trackEvent("calendar_date_navigated", {
@@ -1083,7 +1172,13 @@ export default function CalendarView() {
1083
1172
  view_type: viewMode,
1084
1173
  });
1085
1174
  setSelectedDate(
1086
- navigateCalendarDate(viewMode, selectedDate, direction, weekStartsOn),
1175
+ navigateCalendarDate(
1176
+ viewMode,
1177
+ selectedDate,
1178
+ direction,
1179
+ weekStartsOn,
1180
+ viewPrefs.numberOfDays,
1181
+ ),
1087
1182
  );
1088
1183
  }
1089
1184
 
@@ -1883,8 +1978,12 @@ export default function CalendarView() {
1883
1978
  ? format(selectedDate, "MMM yyyy")
1884
1979
  : format(selectedDate, "MMMM yyyy");
1885
1980
  case "week": {
1886
- const ws = startOfWeek(selectedDate, { weekStartsOn });
1887
- const we = endOfWeek(selectedDate, { weekStartsOn });
1981
+ const displayedDays = normalizeNumberOfDays(viewPrefs.numberOfDays);
1982
+ const ws =
1983
+ displayedDays === 7
1984
+ ? startOfWeek(selectedDate, { weekStartsOn })
1985
+ : selectedDate;
1986
+ const we = addDays(ws, displayedDays - 1);
1888
1987
  return isMobile
1889
1988
  ? `${format(ws, "MMM d")} – ${format(we, "d")}`
1890
1989
  : `${format(ws, "MMM d")} – ${format(we, "d, yyyy")}`;
@@ -1896,6 +1995,18 @@ export default function CalendarView() {
1896
1995
  }
1897
1996
  })();
1898
1997
 
1998
+ function applyCustomDays() {
1999
+ const value = Number(customDaysInput);
2000
+ if (!Number.isInteger(value)) return;
2001
+ setViewPrefs({
2002
+ numberOfDays: Math.min(
2003
+ MAX_CALENDAR_DAYS,
2004
+ Math.max(MIN_CALENDAR_DAYS, value),
2005
+ ),
2006
+ });
2007
+ setCustomDaysOpen(false);
2008
+ }
2009
+
1899
2010
  return (
1900
2011
  <TooltipProvider delayDuration={500}>
1901
2012
  <div className="flex h-full min-w-0">
@@ -1962,20 +2073,83 @@ export default function CalendarView() {
1962
2073
  </kbd>
1963
2074
  </DropdownMenuItem>
1964
2075
  <DropdownMenuSeparator />
1965
- <DropdownMenuLabel className="text-[10px] font-normal uppercase tracking-wider text-muted-foreground">
1966
- {t("calendarView.display")}
1967
- </DropdownMenuLabel>
1968
- <DropdownMenuItem
1969
- onSelect={(e) => {
1970
- e.preventDefault();
1971
- setViewPrefs({ hideWeekends: !viewPrefs.hideWeekends });
1972
- }}
1973
- >
1974
- {t("calendarView.hideWeekends")}
1975
- {viewPrefs.hideWeekends && (
1976
- <IconCheck className="ml-auto h-3.5 w-3.5 text-muted-foreground" />
1977
- )}
1978
- </DropdownMenuItem>
2076
+ <DropdownMenuSub>
2077
+ <DropdownMenuSubTrigger>
2078
+ {t("calendarView.numberOfDays")}
2079
+ </DropdownMenuSubTrigger>
2080
+ <DropdownMenuSubContent>
2081
+ {Array.from({ length: 8 }, (_, index) => index + 2).map(
2082
+ (count) => (
2083
+ <DropdownMenuItem
2084
+ key={count}
2085
+ onSelect={() =>
2086
+ setViewPrefs({ numberOfDays: count })
2087
+ }
2088
+ >
2089
+ {t("calendarView.daysCount", { count })}
2090
+ {viewPrefs.numberOfDays === count && (
2091
+ <IconCheck className="ml-auto h-3.5 w-3.5 text-muted-foreground" />
2092
+ )}
2093
+ </DropdownMenuItem>
2094
+ ),
2095
+ )}
2096
+ <DropdownMenuSeparator />
2097
+ <DropdownMenuItem
2098
+ onSelect={() => {
2099
+ setCustomDaysInput(String(viewPrefs.numberOfDays));
2100
+ setCustomDaysOpen(true);
2101
+ }}
2102
+ >
2103
+ {t("calendarView.other")}
2104
+ {!Array.from(
2105
+ { length: 8 },
2106
+ (_, index) => index + 2,
2107
+ ).includes(viewPrefs.numberOfDays) && (
2108
+ <IconCheck className="ml-auto h-3.5 w-3.5 text-muted-foreground" />
2109
+ )}
2110
+ </DropdownMenuItem>
2111
+ </DropdownMenuSubContent>
2112
+ </DropdownMenuSub>
2113
+ <DropdownMenuSub>
2114
+ <DropdownMenuSubTrigger>
2115
+ {t("calendarView.viewSettings")}
2116
+ </DropdownMenuSubTrigger>
2117
+ <DropdownMenuSubContent>
2118
+ <DropdownMenuCheckboxItem
2119
+ checked={!viewPrefs.hideWeekends}
2120
+ onCheckedChange={(checked) =>
2121
+ setViewPrefs({ hideWeekends: !checked })
2122
+ }
2123
+ >
2124
+ {t("calendarView.weekends")}
2125
+ </DropdownMenuCheckboxItem>
2126
+ <DropdownMenuCheckboxItem
2127
+ checked={viewPrefs.showDeclinedEvents}
2128
+ onCheckedChange={(checked) =>
2129
+ setViewPrefs({ showDeclinedEvents: checked })
2130
+ }
2131
+ >
2132
+ {t("calendarView.declinedEvents")}
2133
+ </DropdownMenuCheckboxItem>
2134
+ <DropdownMenuCheckboxItem
2135
+ checked={viewPrefs.showWeekNumbers}
2136
+ onCheckedChange={(checked) =>
2137
+ setViewPrefs({ showWeekNumbers: checked })
2138
+ }
2139
+ >
2140
+ {t("calendarView.weekNumbers")}
2141
+ </DropdownMenuCheckboxItem>
2142
+ <DropdownMenuSeparator />
2143
+ <DropdownMenuItem asChild>
2144
+ <Link
2145
+ to="/settings"
2146
+ className="flex w-full items-center"
2147
+ >
2148
+ {t("calendarView.generalSettings")}
2149
+ </Link>
2150
+ </DropdownMenuItem>
2151
+ </DropdownMenuSubContent>
2152
+ </DropdownMenuSub>
1979
2153
  </DropdownMenuContent>
1980
2154
  </DropdownMenu>
1981
2155
  </div>
@@ -2116,6 +2290,7 @@ export default function CalendarView() {
2116
2290
  onDraftDiscard={discardDraftEvent}
2117
2291
  isLoading={eventsLoading}
2118
2292
  weekStartsOn={weekStartsOn}
2293
+ numberOfDays={viewPrefs.numberOfDays}
2119
2294
  />
2120
2295
  )}
2121
2296
  {viewMode === "day" && (
@@ -2265,6 +2440,41 @@ export default function CalendarView() {
2265
2440
  }}
2266
2441
  />
2267
2442
  {guestNotificationDialog}
2443
+ <Dialog open={customDaysOpen} onOpenChange={setCustomDaysOpen}>
2444
+ <DialogContent className="sm:max-w-[320px]">
2445
+ <DialogHeader>
2446
+ <DialogTitle>{t("calendarView.numberOfDays")}</DialogTitle>
2447
+ </DialogHeader>
2448
+ <form
2449
+ className="grid gap-4"
2450
+ onSubmit={(event) => {
2451
+ event.preventDefault();
2452
+ applyCustomDays();
2453
+ }}
2454
+ >
2455
+ <Input
2456
+ type="number"
2457
+ min={MIN_CALENDAR_DAYS}
2458
+ max={MAX_CALENDAR_DAYS}
2459
+ step={1}
2460
+ value={customDaysInput}
2461
+ onChange={(event) => setCustomDaysInput(event.target.value)}
2462
+ autoFocus
2463
+ aria-label={t("calendarView.numberOfDays")}
2464
+ />
2465
+ <DialogFooter>
2466
+ <Button
2467
+ type="button"
2468
+ variant="outline"
2469
+ onClick={() => setCustomDaysOpen(false)}
2470
+ >
2471
+ {t("eventForm.cancel")}
2472
+ </Button>
2473
+ <Button type="submit">{t("eventForm.save")}</Button>
2474
+ </DialogFooter>
2475
+ </form>
2476
+ </DialogContent>
2477
+ </Dialog>
2268
2478
  </div>
2269
2479
  </TooltipProvider>
2270
2480
  );
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-09-13
4
+ ---
5
+
6
+ Calendar sidebar overflow is easier to discover with visible scroll controls and edge cues.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-09-15
4
+ ---
5
+
6
+ Calendar mirrors Notion Calendar's customizable day ranges and view settings
@@ -16,11 +16,21 @@ export const CALENDAR_COLORS = [
16
16
 
17
17
  export type CalendarColorMode = "multi" | "single";
18
18
 
19
+ export const MIN_CALENDAR_DAYS = 1;
20
+ export const MAX_CALENDAR_DAYS = 31;
21
+ export const DEFAULT_CALENDAR_DAYS = 7;
22
+
19
23
  /** Account-scoped key: a Google account email, or `"ics:<externalCalendarId>"`. */
20
24
  export type CalendarColorSourceKey = string;
21
25
 
22
26
  export interface CalendarViewPreferences {
23
27
  hideWeekends: boolean;
28
+ /** Number of days shown by the week-style calendar view. */
29
+ numberOfDays: number;
30
+ /** Whether events declined by the current account remain visible. */
31
+ showDeclinedEvents: boolean;
32
+ /** Whether month view reserves a leading column for week numbers. */
33
+ showWeekNumbers: boolean;
24
34
  /** @deprecated kept for back-compat migration; use accountColorModes */
25
35
  colorMode: CalendarColorMode;
26
36
  /** @deprecated kept for back-compat migration; use accountColors */
@@ -37,6 +47,9 @@ export interface CalendarViewPreferences {
37
47
 
38
48
  export const DEFAULT_CALENDAR_VIEW_PREFERENCES: CalendarViewPreferences = {
39
49
  hideWeekends: false,
50
+ numberOfDays: DEFAULT_CALENDAR_DAYS,
51
+ showDeclinedEvents: true,
52
+ showWeekNumbers: false,
40
53
  colorMode: "multi",
41
54
  singleColor: CALENDAR_COLORS[0],
42
55
  accountColorModes: {},
@@ -45,6 +58,19 @@ export const DEFAULT_CALENDAR_VIEW_PREFERENCES: CalendarViewPreferences = {
45
58
  googleCalendarColors: {},
46
59
  };
47
60
 
61
+ export function normalizeNumberOfDays(value: unknown): number {
62
+ return typeof value === "number" && Number.isInteger(value)
63
+ ? Math.min(MAX_CALENDAR_DAYS, Math.max(MIN_CALENDAR_DAYS, value))
64
+ : DEFAULT_CALENDAR_DAYS;
65
+ }
66
+
67
+ export function isEventVisibleForDeclinedPreference(
68
+ responseStatus: string | undefined,
69
+ showDeclinedEvents: boolean,
70
+ ): boolean {
71
+ return showDeclinedEvents || responseStatus !== "declined";
72
+ }
73
+
48
74
  export function isValidCalendarColorMode(
49
75
  value: unknown,
50
76
  ): value is CalendarColorMode {
@@ -114,6 +140,13 @@ export function normalizeCalendarViewPreferences(
114
140
  if (typeof input.hideWeekends === "boolean") {
115
141
  next.hideWeekends = input.hideWeekends;
116
142
  }
143
+ next.numberOfDays = normalizeNumberOfDays(input.numberOfDays);
144
+ if (typeof input.showDeclinedEvents === "boolean") {
145
+ next.showDeclinedEvents = input.showDeclinedEvents;
146
+ }
147
+ if (typeof input.showWeekNumbers === "boolean") {
148
+ next.showWeekNumbers = input.showWeekNumbers;
149
+ }
117
150
  if (isValidCalendarColorMode(input.colorMode)) {
118
151
  next.colorMode = input.colorMode;
119
152
  }
@@ -135,6 +168,9 @@ export function calendarViewPreferencesEqual(
135
168
  ): boolean {
136
169
  return (
137
170
  a.hideWeekends === b.hideWeekends &&
171
+ a.numberOfDays === b.numberOfDays &&
172
+ a.showDeclinedEvents === b.showDeclinedEvents &&
173
+ a.showWeekNumbers === b.showWeekNumbers &&
138
174
  a.colorMode === b.colorMode &&
139
175
  a.singleColor === b.singleColor &&
140
176
  recordsEqual(a.accountColorModes, b.accountColorModes) &&
@@ -138,6 +138,7 @@ export function getCalendarViewDateRange(
138
138
  selectedDate: string,
139
139
  timezone: string,
140
140
  weekStartsOn: 0 | 1 = 0,
141
+ numberOfDays = 7,
141
142
  ): { from: string; to: string } {
142
143
  const selectedMonthStart = `${selectedDate.slice(0, 7)}-01`;
143
144
  const [year, month] = selectedMonthStart.split("-").map(Number);
@@ -148,8 +149,14 @@ export function getCalendarViewDateRange(
148
149
  let rangeStart = selectedDate;
149
150
  let rangeEndExclusive = addDaysToDateKey(selectedDate, 1);
150
151
  if (viewMode === "week") {
151
- rangeStart = startOfCalendarWeek(selectedDate, weekStartsOn);
152
- rangeEndExclusive = addDaysToDateKey(rangeStart, 7);
152
+ const displayedDays = Number.isInteger(numberOfDays)
153
+ ? Math.min(31, Math.max(1, numberOfDays))
154
+ : 7;
155
+ rangeStart =
156
+ displayedDays === 7
157
+ ? startOfCalendarWeek(selectedDate, weekStartsOn)
158
+ : selectedDate;
159
+ rangeEndExclusive = addDaysToDateKey(rangeStart, displayedDays);
153
160
  } else if (viewMode === "month") {
154
161
  const monthEnd = addDaysToDateKey(nextMonthStart, -1);
155
162
  rangeStart = startOfCalendarWeek(selectedMonthStart, weekStartsOn);
@@ -228,6 +228,8 @@ temporary agent-link path:
228
228
  raw provider URLs.
229
229
 
230
230
  Public agent context also exposes the recording's redacted browser diagnostics:
231
+ the bounded relative event sequence as `browserDiagnostics.timeline`, including
232
+ navigation, click/input targets, console events, and request/response markers;
231
233
  the console stream (all levels) as `browserDiagnostics.consoleLogs` and the
232
234
  fetch/XHR stream as `browserDiagnostics.networkRequests` (method, sanitized URL
233
235
  with query values redacted, status, duration), plus `consoleIssues` and
@@ -3,6 +3,25 @@
3
3
  All notable user-facing changes to Clips are documented here. Open it any time
4
4
  from the command menu (Cmd+K → "What's new") or from Settings.
5
5
 
6
+ ## 2026-09-15
7
+
8
+ ### Improved
9
+
10
+ - Trash now supports paging through more than 100 deleted recordings
11
+
12
+ ### Fixed
13
+
14
+ - Fixed the volume slider knob alignment in shared recordings
15
+ - Shared Clips now focus the agent when asked, show clear viewer-safe transcript states, and keep workspace branding visible.
16
+
17
+ ## 2026-09-14
18
+
19
+ ### Fixed
20
+
21
+ - Comment edits and reactions stay consistent during rapid updates.
22
+ - Comment threads keep overlapping replies, reactions, and deletions consistent.
23
+ - Selected recording checkboxes remain visible while selecting multiple clips
24
+
6
25
  ## 2026-09-13
7
26
 
8
27
  ### Improved
@@ -2,8 +2,8 @@
2
2
  * Save redacted browser diagnostics captured during a recording session.
3
3
  *
4
4
  * Called by the recorder UI after stop/finalize. Diagnostics are intentionally
5
- * bounded and body/header-free: console text plus method/path/status/duration
6
- * for fetch/XHR requests.
5
+ * bounded and body/header-free: interaction markers, console text, plus
6
+ * method/path/status/duration for fetch/XHR requests.
7
7
  */
8
8
 
9
9
  import { defineAction } from "@agent-native/core/action";
@@ -16,15 +16,18 @@ import { getDb, schema } from "../server/db/index.js";
16
16
  import { nanoid } from "../server/lib/recordings.js";
17
17
  import {
18
18
  MAX_BROWSER_DIAGNOSTIC_CONSOLE_LOGS,
19
+ MAX_BROWSER_DIAGNOSTIC_INTERACTION_EVENTS,
19
20
  MAX_BROWSER_DIAGNOSTIC_MESSAGE_LENGTH,
20
21
  MAX_BROWSER_DIAGNOSTIC_NETWORK_REQUESTS,
22
+ MAX_BROWSER_DIAGNOSTIC_TARGET_LENGTH,
21
23
  MAX_BROWSER_DIAGNOSTIC_URL_LENGTH,
22
24
  redactBrowserDiagnosticString,
25
+ sanitizeBrowserDiagnosticNavigationUrl,
23
26
  summarizeBrowserDiagnostics,
24
27
  type BrowserDiagnosticConsoleLevel,
25
28
  } from "../shared/browser-diagnostics.js";
26
29
 
27
- const REDACTION_VERSION = 2;
30
+ const REDACTION_VERSION = 3;
28
31
 
29
32
  const consoleLevelSchema = z.enum(["debug", "log", "info", "warn", "error"]);
30
33
 
@@ -49,6 +52,14 @@ const networkRequestSchema = z.object({
49
52
  error: z.string().max(20_000).optional(),
50
53
  });
51
54
 
55
+ const interactionEventSchema = z.object({
56
+ timestampMs: z.number().finite().nonnegative(),
57
+ elapsedMs: z.number().finite().nonnegative(),
58
+ kind: z.enum(["navigation", "click", "input", "scroll"]),
59
+ target: z.string().max(MAX_BROWSER_DIAGNOSTIC_TARGET_LENGTH).optional(),
60
+ url: z.string().max(8_000).optional(),
61
+ });
62
+
52
63
  function truncate(value: string, maxLength: number): string {
53
64
  return value.length > maxLength ? `${value.slice(0, maxLength)}...` : value;
54
65
  }
@@ -141,9 +152,29 @@ function sanitizeNetworkRequest(entry: z.infer<typeof networkRequestSchema>) {
141
152
  };
142
153
  }
143
154
 
155
+ function sanitizeInteractionEvent(
156
+ entry: z.infer<typeof interactionEventSchema>,
157
+ ) {
158
+ const target = entry.target
159
+ ? truncate(
160
+ redactString(entry.target, { redactQueryValues: true }),
161
+ MAX_BROWSER_DIAGNOSTIC_TARGET_LENGTH,
162
+ )
163
+ : "";
164
+ return {
165
+ timestampMs: Math.round(entry.timestampMs),
166
+ elapsedMs: Math.round(entry.elapsedMs),
167
+ kind: entry.kind,
168
+ ...(target ? { target } : {}),
169
+ ...(entry.url
170
+ ? { url: sanitizeBrowserDiagnosticNavigationUrl(entry.url) }
171
+ : {}),
172
+ };
173
+ }
174
+
144
175
  export default defineAction({
145
176
  description:
146
- "Save redacted console and network diagnostics captured during a Clips recording session. UI/internal use only.",
177
+ "Save redacted browser timeline, console, and network diagnostics captured during a Clips recording session. UI/internal use only.",
147
178
  agentTool: false,
148
179
  schema: z.object({
149
180
  recordingId: z.string().describe("Recording ID"),
@@ -164,6 +195,10 @@ export default defineAction({
164
195
  .array(networkRequestSchema)
165
196
  .max(MAX_BROWSER_DIAGNOSTIC_NETWORK_REQUESTS)
166
197
  .default([]),
198
+ interactionEvents: z
199
+ .array(interactionEventSchema)
200
+ .max(MAX_BROWSER_DIAGNOSTIC_INTERACTION_EVENTS)
201
+ .default([]),
167
202
  }),
168
203
  run: async (args) => {
169
204
  const access = await assertAccess("recording", args.recordingId, "editor");
@@ -178,6 +213,9 @@ export default defineAction({
178
213
  const networkRequests = args.networkRequests
179
214
  .slice(-MAX_BROWSER_DIAGNOSTIC_NETWORK_REQUESTS)
180
215
  .map(sanitizeNetworkRequest);
216
+ const interactionEvents = args.interactionEvents
217
+ .slice(-MAX_BROWSER_DIAGNOSTIC_INTERACTION_EVENTS)
218
+ .map(sanitizeInteractionEvent);
181
219
  const summary = summarizeBrowserDiagnostics({
182
220
  consoleLogs,
183
221
  networkRequests,
@@ -199,6 +237,7 @@ export default defineAction({
199
237
  endedAt,
200
238
  consoleLogsJson: JSON.stringify(consoleLogs),
201
239
  networkRequestsJson: JSON.stringify(networkRequests),
240
+ interactionEventsJson: JSON.stringify(interactionEvents),
202
241
  redactionVersion: REDACTION_VERSION,
203
242
  updatedAt: now,
204
243
  };