@agent-native/core 0.98.8 → 0.98.9
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/core/CHANGELOG.md +6 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/mcp/build-server.ts +99 -26
- package/corpus/templates/calendar/.agents/skills/event-management/SKILL.md +31 -2
- package/corpus/templates/calendar/AGENTS.md +11 -0
- package/corpus/templates/calendar/README.md +2 -1
- package/corpus/templates/calendar/actions/create-event.ts +7 -6
- package/corpus/templates/calendar/actions/event-action-helpers.ts +49 -0
- package/corpus/templates/calendar/actions/update-event.ts +225 -14
- package/corpus/templates/calendar/app/components/calendar/DayView.tsx +185 -70
- package/corpus/templates/calendar/app/components/calendar/EventCard.tsx +26 -4
- package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +112 -69
- package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +621 -524
- package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +275 -164
- package/corpus/templates/calendar/app/components/calendar/WorkingLocationEditor.tsx +222 -0
- package/corpus/templates/calendar/app/hooks/use-events.ts +151 -79
- package/corpus/templates/calendar/app/i18n/zh-TW.ts +6 -0
- package/corpus/templates/calendar/app/i18n-data.ts +60 -0
- package/corpus/templates/calendar/app/lib/all-day-layout.ts +126 -0
- package/corpus/templates/calendar/app/lib/event-form-utils.ts +18 -1
- package/corpus/templates/calendar/app/lib/event-mutation-inputs.ts +1 -1
- package/corpus/templates/calendar/app/lib/working-location.ts +163 -0
- package/corpus/templates/calendar/app/pages/CalendarView.tsx +21 -2
- package/corpus/templates/calendar/changelog/2026-07-07-working-locations-from-google-calendar-now-appear-as-native-.md +6 -0
- package/corpus/templates/calendar/server/lib/calendar-availability.ts +2 -0
- package/corpus/templates/calendar/server/lib/google-api.ts +6 -1
- package/corpus/templates/calendar/server/lib/google-calendar.ts +49 -19
- package/corpus/templates/calendar/shared/api.ts +2 -0
- package/dist/mcp/build-server.d.ts.map +1 -1
- package/dist/mcp/build-server.js +82 -28
- package/dist/mcp/build-server.js.map +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/secrets/routes.d.ts +9 -9
- package/package.json +1 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useT } from "@agent-native/core/client";
|
|
1
2
|
import type { CalendarEvent } from "@shared/api";
|
|
2
3
|
import { IconAlertTriangleFilled } from "@tabler/icons-react";
|
|
3
4
|
|
|
@@ -8,6 +9,12 @@ import {
|
|
|
8
9
|
} from "@/lib/event-colors";
|
|
9
10
|
import { EventStatusIcon } from "@/lib/rsvp-status";
|
|
10
11
|
import { cn } from "@/lib/utils";
|
|
12
|
+
import {
|
|
13
|
+
createWorkingLocationDisplayLabels,
|
|
14
|
+
getWorkingLocationChipLabel,
|
|
15
|
+
getWorkingLocationTitle,
|
|
16
|
+
isWorkingLocationEvent,
|
|
17
|
+
} from "@/lib/working-location";
|
|
11
18
|
|
|
12
19
|
interface EventCardProps {
|
|
13
20
|
event: CalendarEvent;
|
|
@@ -30,8 +37,13 @@ export function EventCard({
|
|
|
30
37
|
dimmed = false,
|
|
31
38
|
colorPreferences,
|
|
32
39
|
}: EventCardProps) {
|
|
40
|
+
const t = useT();
|
|
41
|
+
const workingLocationLabels = createWorkingLocationDisplayLabels(t);
|
|
33
42
|
const accentColor = getEventDisplayColor(event, colorPreferences);
|
|
34
43
|
const ownerLabel = event.ownerName || event.overlayEmail;
|
|
44
|
+
const title = getWorkingLocationChipLabel(event, workingLocationLabels);
|
|
45
|
+
const ariaTitle = getWorkingLocationTitle(event, workingLocationLabels);
|
|
46
|
+
const isWorkingLocation = isWorkingLocationEvent(event);
|
|
35
47
|
|
|
36
48
|
const handleDragStart = (e: React.DragEvent) => {
|
|
37
49
|
e.dataTransfer.setData("text/plain", event.id);
|
|
@@ -55,7 +67,7 @@ export function EventCard({
|
|
|
55
67
|
event.ownerColor && "pr-3.5",
|
|
56
68
|
)}
|
|
57
69
|
aria-label={
|
|
58
|
-
ownerLabel ? `${
|
|
70
|
+
ownerLabel ? `${ariaTitle}, ${ownerLabel}'s calendar` : ariaTitle
|
|
59
71
|
}
|
|
60
72
|
style={{
|
|
61
73
|
backgroundColor: `${accentColor}25`,
|
|
@@ -73,7 +85,12 @@ export function EventCard({
|
|
|
73
85
|
/>
|
|
74
86
|
)}
|
|
75
87
|
<EventStatusIcon event={event} />
|
|
76
|
-
<span className="truncate font-medium">{
|
|
88
|
+
<span className="truncate font-medium">{title}</span>
|
|
89
|
+
{isWorkingLocation && (
|
|
90
|
+
<span className="hidden shrink-0 text-[10px] font-normal text-foreground/65 sm:inline">
|
|
91
|
+
{t("eventForm.workingLocation")}
|
|
92
|
+
</span>
|
|
93
|
+
)}
|
|
77
94
|
{event.ownerColor && (
|
|
78
95
|
<span
|
|
79
96
|
aria-hidden="true"
|
|
@@ -98,7 +115,7 @@ export function EventCard({
|
|
|
98
115
|
event.ownerColor && "pr-4",
|
|
99
116
|
)}
|
|
100
117
|
aria-label={
|
|
101
|
-
ownerLabel ? `${
|
|
118
|
+
ownerLabel ? `${ariaTitle}, ${ownerLabel}'s calendar` : ariaTitle
|
|
102
119
|
}
|
|
103
120
|
style={{
|
|
104
121
|
backgroundColor: `${accentColor}25`,
|
|
@@ -113,8 +130,13 @@ export function EventCard({
|
|
|
113
130
|
/>
|
|
114
131
|
)}
|
|
115
132
|
<EventStatusIcon event={event} />
|
|
116
|
-
<span className="truncate font-medium">{
|
|
133
|
+
<span className="truncate font-medium">{title}</span>
|
|
117
134
|
</div>
|
|
135
|
+
{isWorkingLocation && (
|
|
136
|
+
<span className="truncate text-foreground/70">
|
|
137
|
+
{t("eventForm.workingLocation")}
|
|
138
|
+
</span>
|
|
139
|
+
)}
|
|
118
140
|
{!event.allDay && (
|
|
119
141
|
<span className="text-foreground/70">
|
|
120
142
|
{new Date(event.start).toLocaleTimeString([], {
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
AutoGrowTextarea,
|
|
24
24
|
} from "@/components/calendar/EventDescription";
|
|
25
25
|
import { useGuestNotificationPrompt } from "@/components/calendar/GuestNotificationDialog";
|
|
26
|
+
import { WorkingLocationEditor } from "@/components/calendar/WorkingLocationEditor";
|
|
26
27
|
import { useCalendarContext } from "@/components/layout/AppLayout";
|
|
27
28
|
import { Button } from "@/components/ui/button";
|
|
28
29
|
import {
|
|
@@ -34,6 +35,13 @@ import {
|
|
|
34
35
|
import { useUpdateEvent } from "@/hooks/use-events";
|
|
35
36
|
import { useViewPreferences } from "@/hooks/use-view-preferences";
|
|
36
37
|
import { cn } from "@/lib/utils";
|
|
38
|
+
import {
|
|
39
|
+
buildWorkingLocationUpdate,
|
|
40
|
+
createWorkingLocationDisplayLabels,
|
|
41
|
+
getWorkingLocationTitle,
|
|
42
|
+
isWorkingLocationEvent,
|
|
43
|
+
type WorkingLocationSelection,
|
|
44
|
+
} from "@/lib/working-location";
|
|
37
45
|
|
|
38
46
|
function buildEventDetailSlotContext(event: CalendarEvent) {
|
|
39
47
|
return {
|
|
@@ -111,6 +119,7 @@ export function EventDetailPanel({
|
|
|
111
119
|
onTitleSave,
|
|
112
120
|
}: EventDetailPanelProps) {
|
|
113
121
|
const t = useT();
|
|
122
|
+
const workingLocationLabels = createWorkingLocationDisplayLabels(t);
|
|
114
123
|
const { setEventDetailSidebar } = useCalendarContext();
|
|
115
124
|
useViewPreferences();
|
|
116
125
|
const isOpen = event !== null;
|
|
@@ -125,6 +134,10 @@ export function EventDetailPanel({
|
|
|
125
134
|
const { promptGuestNotification, guestNotificationDialog } =
|
|
126
135
|
useGuestNotificationPrompt();
|
|
127
136
|
const isOverlay = !!event?.overlayEmail;
|
|
137
|
+
const isWorkingLocation = event ? isWorkingLocationEvent(event) : false;
|
|
138
|
+
const isRecurringEvent = !!(
|
|
139
|
+
event?.recurringEventId || event?.recurrence?.length
|
|
140
|
+
);
|
|
128
141
|
const lastSavedDescriptionRef = useRef(event?.description || "");
|
|
129
142
|
const meetingLink = event ? extractMeetingLink(event) : null;
|
|
130
143
|
const ownerLabel = event?.ownerName || event?.overlayEmail;
|
|
@@ -247,6 +260,16 @@ export function EventDetailPanel({
|
|
|
247
260
|
[event, promptGuestNotification, updateEvent],
|
|
248
261
|
);
|
|
249
262
|
|
|
263
|
+
const handleSaveWorkingLocation = useCallback(
|
|
264
|
+
(selection: WorkingLocationSelection) => {
|
|
265
|
+
if (!event) return;
|
|
266
|
+
updateEvent.mutate(buildWorkingLocationUpdate(event, selection), {
|
|
267
|
+
onError: () => toast.error(t("calendarView.failedUpdateEvent")),
|
|
268
|
+
});
|
|
269
|
+
},
|
|
270
|
+
[event, t, updateEvent],
|
|
271
|
+
);
|
|
272
|
+
|
|
250
273
|
return (
|
|
251
274
|
<TooltipProvider>
|
|
252
275
|
{isOpen && (
|
|
@@ -268,7 +291,9 @@ export function EventDetailPanel({
|
|
|
268
291
|
{/* Header */}
|
|
269
292
|
<div className="flex items-center justify-between px-4 py-3 border-b border-border">
|
|
270
293
|
<span className="text-xs font-medium uppercase tracking-wider text-muted-foreground">
|
|
271
|
-
{
|
|
294
|
+
{isWorkingLocation
|
|
295
|
+
? t("eventForm.workingLocation")
|
|
296
|
+
: t("eventForm.event")}
|
|
272
297
|
</span>
|
|
273
298
|
<div className="flex items-center gap-0.5">
|
|
274
299
|
<Tooltip>
|
|
@@ -300,7 +325,7 @@ export function EventDetailPanel({
|
|
|
300
325
|
{/* Content */}
|
|
301
326
|
<div className="flex-1 overflow-y-auto px-4 py-4 space-y-4">
|
|
302
327
|
{/* Title — click to edit */}
|
|
303
|
-
{isEditingTitle ? (
|
|
328
|
+
{isEditingTitle && !isWorkingLocation ? (
|
|
304
329
|
<input
|
|
305
330
|
ref={titleInputRef}
|
|
306
331
|
value={editingTitle}
|
|
@@ -331,13 +356,17 @@ export function EventDetailPanel({
|
|
|
331
356
|
/>
|
|
332
357
|
) : (
|
|
333
358
|
<h2
|
|
334
|
-
className=
|
|
359
|
+
className={cn(
|
|
360
|
+
"-mx-0.5 rounded px-0.5 text-lg font-semibold leading-tight text-foreground",
|
|
361
|
+
!isWorkingLocation && "cursor-text hover:bg-muted/50",
|
|
362
|
+
)}
|
|
335
363
|
onClick={() => {
|
|
364
|
+
if (isWorkingLocation) return;
|
|
336
365
|
setEditingTitle(event.title);
|
|
337
366
|
setIsEditingTitle(true);
|
|
338
367
|
}}
|
|
339
368
|
>
|
|
340
|
-
{event
|
|
369
|
+
{getWorkingLocationTitle(event, workingLocationLabels)}
|
|
341
370
|
</h2>
|
|
342
371
|
)}
|
|
343
372
|
|
|
@@ -368,13 +397,20 @@ export function EventDetailPanel({
|
|
|
368
397
|
</div>
|
|
369
398
|
</div>
|
|
370
399
|
|
|
371
|
-
{
|
|
372
|
-
|
|
400
|
+
{isWorkingLocation ? (
|
|
401
|
+
<WorkingLocationEditor
|
|
402
|
+
event={event}
|
|
403
|
+
isRecurring={isRecurringEvent}
|
|
404
|
+
readOnly={isOverlay}
|
|
405
|
+
disabled={updateEvent.isPending}
|
|
406
|
+
onSave={handleSaveWorkingLocation}
|
|
407
|
+
/>
|
|
408
|
+
) : event.location ? (
|
|
373
409
|
<div className="flex items-start gap-2.5 text-sm text-muted-foreground">
|
|
374
410
|
<IconMapPin className="mt-0.5 h-4 w-4 shrink-0" />
|
|
375
411
|
<span>{event.location}</span>
|
|
376
412
|
</div>
|
|
377
|
-
)}
|
|
413
|
+
) : null}
|
|
378
414
|
|
|
379
415
|
{event.overlayEmail && ownerLabel && (
|
|
380
416
|
<div className="flex items-center gap-2.5 text-sm text-muted-foreground">
|
|
@@ -391,32 +427,33 @@ export function EventDetailPanel({
|
|
|
391
427
|
</div>
|
|
392
428
|
)}
|
|
393
429
|
|
|
394
|
-
{
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
430
|
+
{!isWorkingLocation &&
|
|
431
|
+
(meetingLink ? (
|
|
432
|
+
<a
|
|
433
|
+
href={safeUrl(meetingLink)}
|
|
434
|
+
target="_blank"
|
|
435
|
+
rel="noopener noreferrer"
|
|
436
|
+
className="flex items-center justify-center rounded-lg bg-[#4965E0] px-3 py-2 text-sm font-semibold text-white hover:bg-[#5A75F0]"
|
|
437
|
+
>
|
|
438
|
+
<IconVideo className="mr-2 h-4 w-4 opacity-80" />
|
|
439
|
+
{t("eventForm.joinMeeting")}
|
|
440
|
+
</a>
|
|
441
|
+
) : !isOverlay ? (
|
|
442
|
+
<Button
|
|
443
|
+
type="button"
|
|
444
|
+
variant="outline"
|
|
445
|
+
size="sm"
|
|
446
|
+
className="w-full justify-center gap-1.5"
|
|
447
|
+
disabled={updateEvent.isPending}
|
|
448
|
+
onClick={handleAddGoogleMeet}
|
|
449
|
+
>
|
|
450
|
+
<IconVideo className="h-4 w-4" />
|
|
451
|
+
{t("eventForm.googleMeet")}
|
|
452
|
+
</Button>
|
|
453
|
+
) : null)}
|
|
417
454
|
|
|
418
455
|
{/* Description — always shown, editable; hidden for overlay events with no description */}
|
|
419
|
-
{(!isOverlay || event.description) && (
|
|
456
|
+
{!isWorkingLocation && (!isOverlay || event.description) && (
|
|
420
457
|
<div className="flex items-start gap-2.5">
|
|
421
458
|
<IconAlignLeft className="mt-1.5 h-4 w-4 shrink-0 text-muted-foreground" />
|
|
422
459
|
{isOverlay ? (
|
|
@@ -446,49 +483,55 @@ export function EventDetailPanel({
|
|
|
446
483
|
)}
|
|
447
484
|
|
|
448
485
|
{/* Attachments */}
|
|
449
|
-
{
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
486
|
+
{!isWorkingLocation &&
|
|
487
|
+
event.attachments &&
|
|
488
|
+
event.attachments.length > 0 && (
|
|
489
|
+
<div className="space-y-1">
|
|
490
|
+
{event.attachments.map((att, i) => (
|
|
491
|
+
<a
|
|
492
|
+
key={i}
|
|
493
|
+
href={safeUrl(att.fileUrl)}
|
|
494
|
+
target="_blank"
|
|
495
|
+
rel="noopener noreferrer"
|
|
496
|
+
className="flex items-center gap-2.5 rounded-lg px-2 py-1.5 text-sm hover:bg-muted/50 group"
|
|
497
|
+
>
|
|
498
|
+
{att.iconLink ? (
|
|
499
|
+
<img
|
|
500
|
+
src={safeUrl(att.iconLink)}
|
|
501
|
+
alt=""
|
|
502
|
+
className="h-4 w-4 shrink-0"
|
|
503
|
+
/>
|
|
504
|
+
) : (
|
|
505
|
+
<IconFileText className="h-4 w-4 shrink-0 text-muted-foreground" />
|
|
506
|
+
)}
|
|
507
|
+
<span className="truncate text-foreground">
|
|
508
|
+
{att.title}
|
|
509
|
+
</span>
|
|
510
|
+
<IconExternalLink className="ml-auto h-3 w-3 shrink-0 text-muted-foreground opacity-0 group-hover:opacity-100" />
|
|
511
|
+
</a>
|
|
512
|
+
))}
|
|
513
|
+
</div>
|
|
514
|
+
)}
|
|
476
515
|
|
|
477
516
|
{/* Attendees */}
|
|
478
|
-
{
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
517
|
+
{!isWorkingLocation &&
|
|
518
|
+
event.attendees &&
|
|
519
|
+
event.attendees.length > 0 && (
|
|
520
|
+
<EventAttendeesSection
|
|
521
|
+
event={event}
|
|
522
|
+
canEditOptional={!isOverlay}
|
|
523
|
+
onToggleOptional={handleToggleAttendeeOptional}
|
|
524
|
+
/>
|
|
525
|
+
)}
|
|
485
526
|
|
|
486
527
|
{/* Research Meeting */}
|
|
487
|
-
{
|
|
488
|
-
|
|
489
|
-
|
|
528
|
+
{!isWorkingLocation &&
|
|
529
|
+
event.attendees &&
|
|
530
|
+
event.attendees.length > 0 && (
|
|
531
|
+
<ResearchMeetingButton event={event} />
|
|
532
|
+
)}
|
|
490
533
|
|
|
491
|
-
{eventDetailSlotContext && (
|
|
534
|
+
{!isWorkingLocation && eventDetailSlotContext && (
|
|
492
535
|
<ExtensionSlot
|
|
493
536
|
id="calendar.event-detail.bottom"
|
|
494
537
|
context={eventDetailSlotContext}
|