@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.
- package/corpus/README.md +1 -1
- package/corpus/templates/analytics/CHANGELOG.md +10 -0
- package/corpus/templates/calendar/CHANGELOG.md +39 -0
- package/corpus/templates/calendar/INTERACTION-MATRIX.md +19 -17
- package/corpus/templates/calendar/actions/update-calendar-visual-preferences.ts +21 -0
- package/corpus/templates/calendar/actions/view-screen.ts +10 -2
- package/corpus/templates/calendar/app/components/calendar/CreateEventDialog.tsx +11 -0
- package/corpus/templates/calendar/app/components/calendar/MonthView.tsx +174 -145
- package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +20 -16
- package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +1 -0
- package/corpus/templates/calendar/app/hooks/use-events.ts +16 -0
- package/corpus/templates/calendar/app/hooks/use-view-preferences.ts +126 -14
- package/corpus/templates/calendar/app/i18n-data.ts +90 -0
- package/corpus/templates/calendar/app/lib/calendar-navigation.ts +29 -9
- package/corpus/templates/calendar/app/lib/calendar-timezone.ts +2 -0
- package/corpus/templates/calendar/app/lib/event-time-range.ts +34 -0
- package/corpus/templates/calendar/app/pages/CalendarView.tsx +245 -35
- package/corpus/templates/calendar/changelog/2026-09-13-calendar-sidebar-overflow-is-easier-to-discover.md +6 -0
- package/corpus/templates/calendar/changelog/2026-09-15-calendar-mirrors-notion-calendar-s-customizable-day-ranges-a.md +6 -0
- package/corpus/templates/calendar/shared/calendar-view-preferences.ts +36 -0
- package/corpus/templates/calendar/shared/timezone.ts +9 -2
- package/corpus/templates/clips/.agents/skills/video-sharing/SKILL.md +2 -0
- package/corpus/templates/clips/CHANGELOG.md +19 -0
- package/corpus/templates/clips/actions/save-browser-diagnostics.ts +43 -4
- package/corpus/templates/clips/app/components/player/browser-diagnostics-panel.tsx +114 -1
- package/corpus/templates/clips/app/i18n/en-US.ts +7 -0
- package/corpus/templates/clips/app/lib/browser-diagnostics-capture.ts +108 -0
- package/corpus/templates/clips/app/routes/record.tsx +1 -0
- package/corpus/templates/clips/changelog/2026-09-15-recordings-include-a-structured-timeline-of-browser-interact.md +6 -0
- package/corpus/templates/clips/chrome-extension/public/manifest.json +7 -0
- package/corpus/templates/clips/chrome-extension/src/background.ts +235 -9
- package/corpus/templates/clips/chrome-extension/src/content-history-bridge.ts +80 -0
- package/corpus/templates/clips/chrome-extension/src/content-script.ts +166 -5
- package/corpus/templates/clips/chrome-extension/vite.config.ts +4 -0
- package/corpus/templates/clips/server/db/schema.ts +3 -0
- package/corpus/templates/clips/server/lib/public-agent-context.ts +40 -2
- package/corpus/templates/clips/server/plugins/db.ts +5 -0
- package/corpus/templates/clips/shared/browser-diagnostics.ts +171 -0
- package/corpus/templates/content/.agents/skills/content/SKILL.md +4 -2
- package/corpus/templates/content/.agents/skills/document-editing/SKILL.md +8 -1
- package/corpus/templates/content/AGENTS.md +5 -6
- package/corpus/templates/content/CHANGELOG.md +24 -0
- package/corpus/templates/content/actions/_content-database-row-migration.ts +67 -36
- package/corpus/templates/content/actions/_document-edit-mutation.ts +74 -23
- package/corpus/templates/content/actions/edit-document.ts +48 -8
- package/corpus/templates/content/actions/migrate-content-database-rows.ts +69 -28
- package/corpus/templates/content/server/plugins/agent-chat.ts +1 -1
- package/corpus/templates/design/CHANGELOG.md +22 -0
- package/corpus/templates/factory/CHANGELOG.md +11 -0
- package/corpus/templates/forms/CHANGELOG.md +6 -0
- package/corpus/templates/mail/.agents/skills/mail-interaction-parity/references/interaction-matrix.md +24 -4
- package/corpus/templates/mail/CHANGELOG.md +68 -0
- package/corpus/templates/mail/app/components/email/ComposeBubbleToolbar.tsx +8 -0
- package/corpus/templates/mail/app/components/email/ComposeModal.tsx +33 -13
- package/corpus/templates/mail/app/components/email/EmailListItem.tsx +23 -4
- package/corpus/templates/mail/app/components/email/EmailThread.tsx +56 -13
- package/corpus/templates/mail/app/components/email/InlineReplyComposer.tsx +23 -4
- package/corpus/templates/mail/app/components/email/RecipientInput.tsx +9 -0
- package/corpus/templates/mail/app/components/layout/AppLayout.tsx +8 -1
- package/corpus/templates/mail/app/i18n/en-US.ts +31 -0
- package/corpus/templates/mail/changelog/2026-09-15-mail-labels-recipient-and-account-controls-for-keyboard-and-.md +6 -0
- package/corpus/templates/slides/CHANGELOG.md +32 -0
- package/dist/client/CommandMenu.js +14 -2
- package/dist/client/settings/AccountSettingsCard.js +3 -2
- package/dist/client/settings/SettingsTabsPage.js +9 -1
- package/dist/client/settings/agent-settings-search.js +5 -1
- package/dist/client/sign-out.d.ts +1 -0
- package/dist/client/sign-out.js +8 -0
- package/dist/collab/routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +2 -2
- package/dist/observability/routes.d.ts +3 -3
- package/dist/org/handlers.d.ts +1 -1
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +3 -3
- package/dist/server/agent-engine-api-key-route.d.ts +2 -2
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/package.json +4 -4
|
@@ -8,11 +8,12 @@ import {
|
|
|
8
8
|
startOfWeek,
|
|
9
9
|
endOfWeek,
|
|
10
10
|
eachDayOfInterval,
|
|
11
|
+
getWeek,
|
|
11
12
|
isSameMonth,
|
|
12
13
|
isSameDay,
|
|
13
14
|
format,
|
|
14
15
|
} from "date-fns";
|
|
15
|
-
import { memo, useState, useMemo, useRef } from "react";
|
|
16
|
+
import { Fragment, memo, useState, useMemo, useRef } from "react";
|
|
16
17
|
|
|
17
18
|
import {
|
|
18
19
|
Tooltip,
|
|
@@ -131,6 +132,7 @@ export const MonthView = memo(function MonthView({
|
|
|
131
132
|
const headers = getWeekdayOrder(weekStartsOn)
|
|
132
133
|
.filter((day) => !prefs.hideWeekends || (day !== 0 && day !== 6))
|
|
133
134
|
.map((day) => headerLabels[day]);
|
|
135
|
+
const gridColumnCount = colCount + (prefs.showWeekNumbers ? 1 : 0);
|
|
134
136
|
|
|
135
137
|
// Pre-group events by every day they overlap (not just their start day) so
|
|
136
138
|
// multi-day events keep appearing as the grid moves past their start date.
|
|
@@ -203,8 +205,15 @@ export const MonthView = memo(function MonthView({
|
|
|
203
205
|
{/* Weekday headers */}
|
|
204
206
|
<div
|
|
205
207
|
className="grid border-b border-border bg-card"
|
|
206
|
-
style={{
|
|
208
|
+
style={{
|
|
209
|
+
gridTemplateColumns: `repeat(${gridColumnCount}, minmax(0, 1fr))`,
|
|
210
|
+
}}
|
|
207
211
|
>
|
|
212
|
+
{prefs.showWeekNumbers && (
|
|
213
|
+
<div className="py-2 text-center text-[10px] font-medium tracking-wide text-muted-foreground sm:py-2.5 sm:text-xs">
|
|
214
|
+
{t("calendarView.week")}
|
|
215
|
+
</div>
|
|
216
|
+
)}
|
|
208
217
|
{headers.map((day, i) => (
|
|
209
218
|
<div
|
|
210
219
|
key={`${day}-${i}`}
|
|
@@ -218,9 +227,11 @@ export const MonthView = memo(function MonthView({
|
|
|
218
227
|
{/* Day grid */}
|
|
219
228
|
<div
|
|
220
229
|
className="grid flex-1 auto-rows-fr"
|
|
221
|
-
style={{
|
|
230
|
+
style={{
|
|
231
|
+
gridTemplateColumns: `repeat(${gridColumnCount}, minmax(0, 1fr))`,
|
|
232
|
+
}}
|
|
222
233
|
>
|
|
223
|
-
{days.map((day) => {
|
|
234
|
+
{days.map((day, index) => {
|
|
224
235
|
const dayOccurrences =
|
|
225
236
|
eventsByDay.get(dateToCalendarDateKey(day)) ?? [];
|
|
226
237
|
const inMonth = isSameMonth(day, selectedDate);
|
|
@@ -232,154 +243,172 @@ export const MonthView = memo(function MonthView({
|
|
|
232
243
|
const isDragTarget = dragOverDay === dayKey;
|
|
233
244
|
|
|
234
245
|
return (
|
|
235
|
-
<
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
e.preventDefault();
|
|
246
|
-
setDragOverDay(dayKey);
|
|
247
|
-
}}
|
|
248
|
-
onDragLeave={(e) => {
|
|
249
|
-
// Only clear if leaving to outside this cell
|
|
250
|
-
if (!e.currentTarget.contains(e.relatedTarget as Node)) {
|
|
251
|
-
setDragOverDay(null);
|
|
252
|
-
}
|
|
253
|
-
}}
|
|
254
|
-
onDrop={(e) => handleDrop(e, day)}
|
|
255
|
-
className={cn(
|
|
256
|
-
"group relative min-h-[60px] cursor-pointer border-b border-r border-border p-1 transition-colors sm:min-h-[90px] sm:p-1.5",
|
|
257
|
-
!inMonth && "opacity-35",
|
|
258
|
-
isDragTarget
|
|
259
|
-
? "bg-primary/10 ring-2 ring-inset ring-primary/50"
|
|
260
|
-
: "hover:bg-accent/40",
|
|
261
|
-
)}
|
|
262
|
-
>
|
|
263
|
-
{/* Date number */}
|
|
264
|
-
<div className="flex items-center justify-between">
|
|
265
|
-
<span
|
|
266
|
-
className={cn(
|
|
267
|
-
"flex h-6 w-6 items-center justify-center rounded-full text-xs font-medium sm:h-7 sm:w-7 sm:text-sm",
|
|
268
|
-
today && "bg-primary text-primary-foreground font-semibold",
|
|
269
|
-
selected && !today && "bg-accent text-accent-foreground",
|
|
270
|
-
!today && !selected && "text-foreground",
|
|
271
|
-
)}
|
|
246
|
+
<Fragment key={dayKey}>
|
|
247
|
+
{prefs.showWeekNumbers && index % colCount === 0 && (
|
|
248
|
+
<div
|
|
249
|
+
aria-label={t("calendarView.weekNumber", {
|
|
250
|
+
number: getWeek(day, {
|
|
251
|
+
weekStartsOn,
|
|
252
|
+
firstWeekContainsDate: 1,
|
|
253
|
+
}),
|
|
254
|
+
})}
|
|
255
|
+
className="border-b border-r border-border bg-muted/20 px-1 py-2 text-center text-[10px] font-medium text-muted-foreground sm:text-xs"
|
|
272
256
|
>
|
|
273
|
-
{
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
257
|
+
{getWeek(day, {
|
|
258
|
+
weekStartsOn,
|
|
259
|
+
firstWeekContainsDate: 1,
|
|
260
|
+
})}
|
|
261
|
+
</div>
|
|
262
|
+
)}
|
|
263
|
+
<div
|
|
264
|
+
data-calendar-create-surface="true"
|
|
265
|
+
onClick={(e) => {
|
|
266
|
+
if ((e.target as HTMLElement).closest("button")) return;
|
|
267
|
+
if (shouldSuppressAfterPopoverClose()) return;
|
|
268
|
+
onDateSelect(day);
|
|
269
|
+
}}
|
|
270
|
+
onDragOver={(e) => handleDragOver(e, dayKey)}
|
|
271
|
+
onDragEnter={(e) => {
|
|
272
|
+
e.preventDefault();
|
|
273
|
+
setDragOverDay(dayKey);
|
|
274
|
+
}}
|
|
275
|
+
onDragLeave={(e) => {
|
|
276
|
+
// Only clear if leaving to outside this cell
|
|
277
|
+
if (!e.currentTarget.contains(e.relatedTarget as Node)) {
|
|
278
|
+
setDragOverDay(null);
|
|
279
|
+
}
|
|
280
|
+
}}
|
|
281
|
+
onDrop={(e) => handleDrop(e, day)}
|
|
282
|
+
className={cn(
|
|
283
|
+
"group relative min-h-[60px] cursor-pointer border-b border-r border-border p-1 transition-colors sm:min-h-[90px] sm:p-1.5",
|
|
284
|
+
!inMonth && "opacity-35",
|
|
285
|
+
isDragTarget
|
|
286
|
+
? "bg-primary/10 ring-2 ring-inset ring-primary/50"
|
|
287
|
+
: "hover:bg-accent/40",
|
|
295
288
|
)}
|
|
296
|
-
|
|
289
|
+
>
|
|
290
|
+
{/* Date number */}
|
|
291
|
+
<div className="flex items-center justify-between">
|
|
292
|
+
<span
|
|
293
|
+
className={cn(
|
|
294
|
+
"flex h-6 w-6 items-center justify-center rounded-full text-xs font-medium sm:h-7 sm:w-7 sm:text-sm",
|
|
295
|
+
today &&
|
|
296
|
+
"bg-primary text-primary-foreground font-semibold",
|
|
297
|
+
selected && !today && "bg-accent text-accent-foreground",
|
|
298
|
+
!today && !selected && "text-foreground",
|
|
299
|
+
)}
|
|
300
|
+
>
|
|
301
|
+
{format(day, "d")}
|
|
302
|
+
</span>
|
|
297
303
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
{!isLoading &&
|
|
309
|
-
dayOccurrences
|
|
310
|
-
.slice(0, isMobile ? 2 : 3)
|
|
311
|
-
.map(({ event, isStart, continuesNext }) => (
|
|
312
|
-
<EventDetailPopover
|
|
313
|
-
key={getCalendarEventRenderKey(event)}
|
|
314
|
-
event={event}
|
|
315
|
-
timezone={timezone}
|
|
316
|
-
onDelete={onDeleteEvent ?? (() => {})}
|
|
317
|
-
isDraft={draftEventIds.includes(event.id)}
|
|
318
|
-
onDraftUpdate={onDraftUpdate}
|
|
319
|
-
onDraftCreate={onDraftCreate}
|
|
320
|
-
onDraftDiscard={onDraftDiscard}
|
|
321
|
-
>
|
|
322
|
-
<div
|
|
323
|
-
onClick={(e) => e.stopPropagation()}
|
|
324
|
-
onDragStart={(e) => {
|
|
325
|
-
if (!isStart) return;
|
|
326
|
-
const ghost = e.currentTarget.querySelector(
|
|
327
|
-
"button",
|
|
328
|
-
) as HTMLElement | null;
|
|
329
|
-
if (ghost) {
|
|
330
|
-
e.dataTransfer.setDragImage(ghost, 12, 12);
|
|
331
|
-
}
|
|
304
|
+
{inMonth && onCreateWorkingLocation && (
|
|
305
|
+
<Tooltip>
|
|
306
|
+
<TooltipTrigger asChild>
|
|
307
|
+
<button
|
|
308
|
+
type="button"
|
|
309
|
+
aria-label={t("calendarView.addWorkingLocation")}
|
|
310
|
+
className="mr-0.5 flex size-5 items-center justify-center rounded text-muted-foreground opacity-0 transition-opacity hover:bg-accent hover:text-foreground focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring group-hover:opacity-100"
|
|
311
|
+
onClick={(event) => {
|
|
312
|
+
event.stopPropagation();
|
|
313
|
+
onCreateWorkingLocation(day);
|
|
332
314
|
}}
|
|
333
|
-
className={cn(
|
|
334
|
-
"relative",
|
|
335
|
-
!isStart &&
|
|
336
|
-
"-ml-1 -mr-1 border-l-2 border-dashed border-current pl-[calc(0.25rem-2px)] opacity-90 sm:-ml-1.5 sm:-mr-1.5 sm:pl-[calc(0.375rem-2px)]",
|
|
337
|
-
)}
|
|
338
315
|
>
|
|
339
|
-
<
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
/>
|
|
359
|
-
{continuesNext && (
|
|
360
|
-
<span
|
|
361
|
-
aria-hidden="true"
|
|
362
|
-
className="pointer-events-none absolute right-0.5 top-1/2 -translate-y-1/2 text-[9px] leading-none text-current opacity-60"
|
|
363
|
-
>
|
|
364
|
-
›
|
|
365
|
-
</span>
|
|
366
|
-
)}
|
|
367
|
-
</div>
|
|
368
|
-
</EventDetailPopover>
|
|
316
|
+
<IconPlus aria-hidden="true" className="size-3.5" />
|
|
317
|
+
</button>
|
|
318
|
+
</TooltipTrigger>
|
|
319
|
+
<TooltipContent side="top">
|
|
320
|
+
{t("calendarView.addWorkingLocation")}
|
|
321
|
+
</TooltipContent>
|
|
322
|
+
</Tooltip>
|
|
323
|
+
)}
|
|
324
|
+
</div>
|
|
325
|
+
|
|
326
|
+
{/* Events / Skeleton */}
|
|
327
|
+
<div className="mt-1 space-y-0.5 overflow-hidden">
|
|
328
|
+
{isLoading &&
|
|
329
|
+
MONTH_SKELETON_WIDTHS[day.getDay()].map((w, i) => (
|
|
330
|
+
<div
|
|
331
|
+
key={i}
|
|
332
|
+
className="h-4 animate-pulse rounded bg-muted"
|
|
333
|
+
style={{ width: w }}
|
|
334
|
+
/>
|
|
369
335
|
))}
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
336
|
+
{!isLoading &&
|
|
337
|
+
dayOccurrences
|
|
338
|
+
.slice(0, isMobile ? 2 : 3)
|
|
339
|
+
.map(({ event, isStart, continuesNext }) => (
|
|
340
|
+
<EventDetailPopover
|
|
341
|
+
key={getCalendarEventRenderKey(event)}
|
|
342
|
+
event={event}
|
|
343
|
+
timezone={timezone}
|
|
344
|
+
onDelete={onDeleteEvent ?? (() => {})}
|
|
345
|
+
isDraft={draftEventIds.includes(event.id)}
|
|
346
|
+
onDraftUpdate={onDraftUpdate}
|
|
347
|
+
onDraftCreate={onDraftCreate}
|
|
348
|
+
onDraftDiscard={onDraftDiscard}
|
|
349
|
+
>
|
|
350
|
+
<div
|
|
351
|
+
onClick={(e) => e.stopPropagation()}
|
|
352
|
+
onDragStart={(e) => {
|
|
353
|
+
if (!isStart) return;
|
|
354
|
+
const ghost = e.currentTarget.querySelector(
|
|
355
|
+
"button",
|
|
356
|
+
) as HTMLElement | null;
|
|
357
|
+
if (ghost) {
|
|
358
|
+
e.dataTransfer.setDragImage(ghost, 12, 12);
|
|
359
|
+
}
|
|
360
|
+
}}
|
|
361
|
+
className={cn(
|
|
362
|
+
"relative",
|
|
363
|
+
!isStart &&
|
|
364
|
+
"-ml-1 -mr-1 border-l-2 border-dashed border-current pl-[calc(0.25rem-2px)] opacity-90 sm:-ml-1.5 sm:-mr-1.5 sm:pl-[calc(0.375rem-2px)]",
|
|
365
|
+
)}
|
|
366
|
+
>
|
|
367
|
+
<EventCard
|
|
368
|
+
event={event}
|
|
369
|
+
colorPreferences={prefs}
|
|
370
|
+
compact
|
|
371
|
+
draggable={isStart}
|
|
372
|
+
onDragStart={(draggedEvent) => {
|
|
373
|
+
draggedEventRef.current = draggedEvent;
|
|
374
|
+
setDraggingEvent(draggedEvent);
|
|
375
|
+
}}
|
|
376
|
+
onDragEnd={() => {
|
|
377
|
+
draggedEventRef.current = null;
|
|
378
|
+
setDraggingEvent(null);
|
|
379
|
+
setDragOverDay(null);
|
|
380
|
+
}}
|
|
381
|
+
dimmed={
|
|
382
|
+
draggingEvent !== null &&
|
|
383
|
+
getCalendarEventRenderKey(draggingEvent) ===
|
|
384
|
+
getCalendarEventRenderKey(event)
|
|
385
|
+
}
|
|
386
|
+
/>
|
|
387
|
+
{continuesNext && (
|
|
388
|
+
<span
|
|
389
|
+
aria-hidden="true"
|
|
390
|
+
className="pointer-events-none absolute right-0.5 top-1/2 -translate-y-1/2 text-[9px] leading-none text-current opacity-60"
|
|
391
|
+
>
|
|
392
|
+
›
|
|
393
|
+
</span>
|
|
394
|
+
)}
|
|
395
|
+
</div>
|
|
396
|
+
</EventDetailPopover>
|
|
397
|
+
))}
|
|
398
|
+
{!isLoading && dayOccurrences.length > (isMobile ? 2 : 3) && (
|
|
399
|
+
<button
|
|
400
|
+
onClick={(e) => {
|
|
401
|
+
e.stopPropagation();
|
|
402
|
+
onDateSelect(day);
|
|
403
|
+
}}
|
|
404
|
+
className="block w-full rounded px-1 py-0.5 text-left text-[10px] text-muted-foreground hover:bg-accent/50 sm:px-1.5 sm:text-xs"
|
|
405
|
+
>
|
|
406
|
+
+{dayOccurrences.length - (isMobile ? 2 : 3)} more
|
|
407
|
+
</button>
|
|
408
|
+
)}
|
|
409
|
+
</div>
|
|
381
410
|
</div>
|
|
382
|
-
</
|
|
411
|
+
</Fragment>
|
|
383
412
|
);
|
|
384
413
|
})}
|
|
385
414
|
</div>
|
|
@@ -10,12 +10,11 @@ import {
|
|
|
10
10
|
} from "@tabler/icons-react";
|
|
11
11
|
import {
|
|
12
12
|
startOfWeek,
|
|
13
|
-
endOfWeek,
|
|
14
|
-
eachDayOfInterval,
|
|
15
13
|
eachHourOfInterval,
|
|
16
14
|
format,
|
|
17
15
|
set,
|
|
18
16
|
addMinutes,
|
|
17
|
+
addDays,
|
|
19
18
|
} from "date-fns";
|
|
20
19
|
import { useState, useEffect, useRef, useMemo, useCallback, memo } from "react";
|
|
21
20
|
|
|
@@ -41,6 +40,7 @@ import {
|
|
|
41
40
|
partitionAllDayEvents,
|
|
42
41
|
} from "@/lib/all-day-layout";
|
|
43
42
|
import { getCalendarEventRenderKey } from "@/lib/calendar-event-identity";
|
|
43
|
+
import { getVisibleCalendarDays } from "@/lib/calendar-navigation";
|
|
44
44
|
import {
|
|
45
45
|
dateToCalendarDateKey,
|
|
46
46
|
getBrowserTimezone,
|
|
@@ -49,6 +49,7 @@ import {
|
|
|
49
49
|
getEventSegmentForCalendarDay,
|
|
50
50
|
isAllDayCalendarEvent,
|
|
51
51
|
} from "@/lib/calendar-timezone";
|
|
52
|
+
import { normalizeNumberOfDays } from "@/lib/calendar-view-preferences";
|
|
52
53
|
import { getEventDisplayColor, allOtherDeclined } from "@/lib/event-colors";
|
|
53
54
|
import {
|
|
54
55
|
computeTimedEventLayout,
|
|
@@ -113,6 +114,7 @@ interface WeekViewProps {
|
|
|
113
114
|
onDraftDiscard?: (eventId: string) => void;
|
|
114
115
|
isLoading?: boolean;
|
|
115
116
|
weekStartsOn?: 0 | 1;
|
|
117
|
+
numberOfDays?: number;
|
|
116
118
|
}
|
|
117
119
|
|
|
118
120
|
// [startHour, startMin, durationMin, widthPct] per day column (Sun–Sat)
|
|
@@ -555,6 +557,7 @@ export const WeekView = memo(function WeekView({
|
|
|
555
557
|
onDraftDiscard,
|
|
556
558
|
isLoading = false,
|
|
557
559
|
weekStartsOn = 0,
|
|
560
|
+
numberOfDays = 7,
|
|
558
561
|
}: WeekViewProps) {
|
|
559
562
|
const t = useT();
|
|
560
563
|
const workingLocationLabels = useMemo(
|
|
@@ -602,30 +605,31 @@ export const WeekView = memo(function WeekView({
|
|
|
602
605
|
}, []);
|
|
603
606
|
|
|
604
607
|
const { prefs } = useViewPreferences();
|
|
605
|
-
const
|
|
606
|
-
|
|
607
|
-
|
|
608
|
+
const displayedDayCount = normalizeNumberOfDays(numberOfDays);
|
|
609
|
+
const periodStart = useMemo(
|
|
610
|
+
() =>
|
|
611
|
+
displayedDayCount === 7
|
|
612
|
+
? startOfWeek(selectedDate, { weekStartsOn })
|
|
613
|
+
: selectedDate,
|
|
614
|
+
[displayedDayCount, selectedDate, weekStartsOn],
|
|
608
615
|
);
|
|
609
|
-
const
|
|
610
|
-
() =>
|
|
611
|
-
[
|
|
616
|
+
const periodEnd = useMemo(
|
|
617
|
+
() => addDays(periodStart, displayedDayCount - 1),
|
|
618
|
+
[displayedDayCount, periodStart],
|
|
612
619
|
);
|
|
613
620
|
// Stable day/hour arrays — recomputed only when the week or weekend
|
|
614
621
|
// visibility actually changes, so memoized children (event buttons) don't
|
|
615
622
|
// see a new array identity on every drag/focus re-render.
|
|
616
623
|
const days = useMemo(() => {
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
? fullWeek.filter((d) => d.getDay() !== 0 && d.getDay() !== 6)
|
|
620
|
-
: fullWeek;
|
|
621
|
-
}, [weekStart, weekEnd, prefs.hideWeekends]);
|
|
624
|
+
return getVisibleCalendarDays(periodStart, periodEnd, prefs.hideWeekends);
|
|
625
|
+
}, [periodEnd, periodStart, prefs.hideWeekends]);
|
|
622
626
|
const hours = useMemo(
|
|
623
627
|
() =>
|
|
624
628
|
eachHourOfInterval({
|
|
625
|
-
start: set(
|
|
626
|
-
end: set(
|
|
629
|
+
start: set(periodStart, { hours: START_HOUR, minutes: 0 }),
|
|
630
|
+
end: set(periodStart, { hours: END_HOUR - 1, minutes: 0 }),
|
|
627
631
|
}),
|
|
628
|
-
[
|
|
632
|
+
[periodStart],
|
|
629
633
|
);
|
|
630
634
|
|
|
631
635
|
// Separate all-day and timed events
|
|
@@ -769,6 +769,7 @@ export function Sidebar({
|
|
|
769
769
|
collapsed={collapsed}
|
|
770
770
|
collapsible={Boolean(onCollapsedChange)}
|
|
771
771
|
onCollapsedChange={onCollapsedChange}
|
|
772
|
+
overflowAffordances
|
|
772
773
|
brandName={t("navigation.brand")}
|
|
773
774
|
brandHref="/home"
|
|
774
775
|
secondaryItems={secondaryItems}
|
|
@@ -15,6 +15,7 @@ import { nanoid } from "nanoid";
|
|
|
15
15
|
import { useMemo } from "react";
|
|
16
16
|
|
|
17
17
|
import type { CalendarEventSourceIdentity } from "@/lib/calendar-event-identity";
|
|
18
|
+
import { isEventVisibleForDeclinedPreference } from "@/lib/calendar-view-preferences";
|
|
18
19
|
import { dateTimeInTimezoneToIso } from "@/lib/event-form-utils";
|
|
19
20
|
import {
|
|
20
21
|
isSharedCalendarDemo,
|
|
@@ -625,6 +626,21 @@ export function findEventByCurrentOrReplacedId(
|
|
|
625
626
|
return findCalendarEventForSelection(events, selectedEvent);
|
|
626
627
|
}
|
|
627
628
|
|
|
629
|
+
export function findVisibleSelectedEvent(
|
|
630
|
+
events: CalendarEvent[],
|
|
631
|
+
selectedEvent: CalendarEvent,
|
|
632
|
+
showDeclinedEvents: boolean,
|
|
633
|
+
): CalendarEvent | undefined {
|
|
634
|
+
const currentEvent =
|
|
635
|
+
findEventByCurrentOrReplacedId(events, selectedEvent) ?? selectedEvent;
|
|
636
|
+
return isEventVisibleForDeclinedPreference(
|
|
637
|
+
currentEvent.responseStatus,
|
|
638
|
+
showDeclinedEvents,
|
|
639
|
+
)
|
|
640
|
+
? currentEvent
|
|
641
|
+
: undefined;
|
|
642
|
+
}
|
|
643
|
+
|
|
628
644
|
export function useDeleteEvent() {
|
|
629
645
|
const queryClient = useQueryClient();
|
|
630
646
|
return useActionMutation<
|