@elizaos/plugin-simple-views 2.0.3-beta.5

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/dist/ui.js ADDED
@@ -0,0 +1,1041 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { AgentButton, useAgentElement } from "@elizaos/ui/agent-surface";
3
+ import {
4
+ CalendarDays,
5
+ ChevronLeft,
6
+ ChevronRight,
7
+ Plus,
8
+ Settings,
9
+ StickyNote,
10
+ Trash2
11
+ } from "lucide-react";
12
+ import {
13
+ useCallback,
14
+ useEffect,
15
+ useMemo,
16
+ useRef,
17
+ useState
18
+ } from "react";
19
+ import { interact } from "./simple-views.interact.js";
20
+ import {
21
+ applySimpleViewsSnapshot,
22
+ isSimpleViewsSnapshot,
23
+ normalizeDateKey,
24
+ readSelectedDate,
25
+ SIMPLE_VIEWS_EVENT,
26
+ simpleViewsSnapshot,
27
+ todayDateKey
28
+ } from "./storage.js";
29
+ const COLORS = ["yellow", "green", "rose", "slate"];
30
+ const NOTE_COLORS = {
31
+ yellow: { background: "#fff5bf", borderColor: "#d9b84b" },
32
+ green: { background: "#dff6df", borderColor: "#7dbb77" },
33
+ rose: { background: "#ffe4ec", borderColor: "#d991aa" },
34
+ slate: { background: "#e8edf2", borderColor: "#9aa8b5" }
35
+ };
36
+ const EVENT_DOTS = {
37
+ yellow: "#b88719",
38
+ green: "#2f7d46",
39
+ rose: "#b84065",
40
+ slate: "#586879"
41
+ };
42
+ const shellStyle = {
43
+ display: "flex",
44
+ flexDirection: "column",
45
+ minHeight: "100%",
46
+ color: "#15171c",
47
+ background: "#f6f7f4",
48
+ fontFamily: "Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif"
49
+ };
50
+ const headerStyle = {
51
+ display: "flex",
52
+ alignItems: "center",
53
+ justifyContent: "space-between",
54
+ gap: 16,
55
+ padding: "18px 20px 14px",
56
+ borderBottom: "1px solid #dde1d8",
57
+ background: "#ffffff"
58
+ };
59
+ const titleWrapStyle = {
60
+ display: "flex",
61
+ alignItems: "center",
62
+ gap: 10,
63
+ minWidth: 0
64
+ };
65
+ const iconBoxStyle = {
66
+ display: "grid",
67
+ placeItems: "center",
68
+ width: 34,
69
+ height: 34,
70
+ border: "1px solid #d6dbd2",
71
+ borderRadius: 8,
72
+ background: "#f2f4ef",
73
+ color: "#263326"
74
+ };
75
+ const h1Style = {
76
+ margin: 0,
77
+ fontSize: 20,
78
+ lineHeight: "26px",
79
+ fontWeight: 700,
80
+ letterSpacing: 0
81
+ };
82
+ const subTextStyle = {
83
+ margin: 0,
84
+ color: "#60695d",
85
+ fontSize: 13,
86
+ lineHeight: "18px"
87
+ };
88
+ const toolbarStyle = {
89
+ display: "flex",
90
+ alignItems: "center",
91
+ flexWrap: "wrap",
92
+ gap: 8,
93
+ justifyContent: "flex-end"
94
+ };
95
+ const buttonStyle = {
96
+ display: "inline-flex",
97
+ alignItems: "center",
98
+ justifyContent: "center",
99
+ gap: 7,
100
+ minHeight: 34,
101
+ border: "1px solid #c9d0c4",
102
+ borderRadius: 8,
103
+ padding: "0 12px",
104
+ background: "#ffffff",
105
+ color: "#1f271e",
106
+ fontSize: 13,
107
+ fontWeight: 600,
108
+ cursor: "pointer"
109
+ };
110
+ const iconButtonStyle = {
111
+ ...buttonStyle,
112
+ width: 34,
113
+ padding: 0
114
+ };
115
+ const primaryButtonStyle = {
116
+ ...buttonStyle,
117
+ borderColor: "#476b3b",
118
+ background: "#476b3b",
119
+ color: "#ffffff"
120
+ };
121
+ const fieldStyle = {
122
+ width: "100%",
123
+ minHeight: 36,
124
+ border: "1px solid #c9d0c4",
125
+ borderRadius: 8,
126
+ padding: "8px 10px",
127
+ background: "#ffffff",
128
+ color: "#15171c",
129
+ font: "inherit",
130
+ fontSize: 14,
131
+ lineHeight: "20px",
132
+ boxSizing: "border-box"
133
+ };
134
+ const panelStyle = {
135
+ border: "1px solid #dde1d8",
136
+ borderRadius: 8,
137
+ background: "#ffffff"
138
+ };
139
+ async function fetchSimpleViewsSnapshot() {
140
+ if (typeof fetch !== "function") return null;
141
+ try {
142
+ const response = await fetch("/api/simple-views/state", {
143
+ headers: { Accept: "application/json" }
144
+ });
145
+ if (!response.ok) return null;
146
+ const body = await response.json();
147
+ return isSimpleViewsSnapshot(body) ? body : null;
148
+ } catch {
149
+ return null;
150
+ }
151
+ }
152
+ async function callSimpleViewsCapability(capability, params) {
153
+ if (typeof fetch === "function") {
154
+ try {
155
+ const response = await fetch("/api/simple-views/interact", {
156
+ method: "POST",
157
+ headers: {
158
+ "Content-Type": "application/json",
159
+ Accept: "application/json"
160
+ },
161
+ body: JSON.stringify({ capability, params })
162
+ });
163
+ if (response.ok) {
164
+ const body = await response.json();
165
+ if (body && typeof body === "object" && "state" in body && isSimpleViewsSnapshot(body.state)) {
166
+ applySimpleViewsSnapshot(
167
+ body.state
168
+ );
169
+ return;
170
+ }
171
+ }
172
+ } catch {
173
+ }
174
+ }
175
+ await interact(capability, params);
176
+ }
177
+ function useSimpleViewsSnapshot() {
178
+ const [snapshot, setSnapshot] = useState(() => simpleViewsSnapshot());
179
+ useEffect(() => {
180
+ const refresh = () => setSnapshot(simpleViewsSnapshot());
181
+ const onStorage = () => refresh();
182
+ const onUpdate = () => refresh();
183
+ const onSharedViewEvent = (event) => {
184
+ const detail = event.detail;
185
+ if (detail?.type === "simple-views:update" || detail?.type === "view:notes:updated" || detail?.type === "view:simple-calendar:updated") {
186
+ refresh();
187
+ }
188
+ };
189
+ window.addEventListener(SIMPLE_VIEWS_EVENT, onUpdate);
190
+ window.addEventListener("storage", onStorage);
191
+ window.addEventListener("elizaos-view-event", onSharedViewEvent);
192
+ void fetchSimpleViewsSnapshot().then((next) => {
193
+ if (next) {
194
+ applySimpleViewsSnapshot(next, { preserveLocalDataOnEmpty: true });
195
+ refresh();
196
+ }
197
+ });
198
+ return () => {
199
+ window.removeEventListener(SIMPLE_VIEWS_EVENT, onUpdate);
200
+ window.removeEventListener("storage", onStorage);
201
+ window.removeEventListener("elizaos-view-event", onSharedViewEvent);
202
+ };
203
+ }, []);
204
+ return snapshot;
205
+ }
206
+ function AgentTextInput({
207
+ id,
208
+ label,
209
+ value,
210
+ onFill,
211
+ group,
212
+ placeholder,
213
+ style
214
+ }) {
215
+ const { ref, agentProps } = useAgentElement({
216
+ id,
217
+ label,
218
+ role: "text-input",
219
+ group,
220
+ fillable: true,
221
+ getValue: () => value,
222
+ onFill
223
+ });
224
+ return /* @__PURE__ */ jsx(
225
+ "input",
226
+ {
227
+ ref,
228
+ "aria-label": label,
229
+ ...agentProps,
230
+ value,
231
+ onChange: (event) => onFill(event.target.value),
232
+ placeholder,
233
+ style: { ...fieldStyle, ...style }
234
+ }
235
+ );
236
+ }
237
+ function AgentTextarea({
238
+ id,
239
+ label,
240
+ value,
241
+ onFill,
242
+ group
243
+ }) {
244
+ const { ref, agentProps } = useAgentElement({
245
+ id,
246
+ label,
247
+ role: "textarea",
248
+ group,
249
+ fillable: true,
250
+ getValue: () => value,
251
+ onFill
252
+ });
253
+ return /* @__PURE__ */ jsx(
254
+ "textarea",
255
+ {
256
+ ref,
257
+ "aria-label": label,
258
+ ...agentProps,
259
+ value,
260
+ onChange: (event) => onFill(event.target.value),
261
+ rows: 5,
262
+ style: { ...fieldStyle, minHeight: 112, resize: "vertical" }
263
+ }
264
+ );
265
+ }
266
+ function openSettings() {
267
+ if (typeof window === "undefined") return;
268
+ window.history.pushState(null, "", "/settings");
269
+ window.dispatchEvent(new PopStateEvent("popstate"));
270
+ }
271
+ function NoteCard({
272
+ note,
273
+ onDelete
274
+ }) {
275
+ const cardElement = useAgentElement({
276
+ id: `note-card-${note.id}`,
277
+ label: `Note: ${note.title}`,
278
+ role: "card",
279
+ group: "notes-list",
280
+ description: note.body,
281
+ status: note.color
282
+ });
283
+ const deleteElement = useAgentElement({
284
+ id: `delete-note-${note.id}`,
285
+ label: `Delete note ${note.title}`,
286
+ role: "button",
287
+ group: "notes-list",
288
+ onActivate: () => onDelete(note.id)
289
+ });
290
+ return /* @__PURE__ */ jsxs(
291
+ "div",
292
+ {
293
+ ref: cardElement.ref,
294
+ ...cardElement.agentProps,
295
+ style: {
296
+ ...NOTE_COLORS[note.color],
297
+ minHeight: 180,
298
+ border: "1px solid",
299
+ borderRadius: 8,
300
+ padding: 16,
301
+ display: "flex",
302
+ flexDirection: "column",
303
+ gap: 12,
304
+ boxShadow: "0 8px 20px rgba(31, 39, 30, 0.08)"
305
+ },
306
+ children: [
307
+ /* @__PURE__ */ jsxs(
308
+ "div",
309
+ {
310
+ style: { display: "flex", justifyContent: "space-between", gap: 10 },
311
+ children: [
312
+ /* @__PURE__ */ jsx(
313
+ "h2",
314
+ {
315
+ style: {
316
+ margin: 0,
317
+ fontSize: 16,
318
+ lineHeight: "22px",
319
+ fontWeight: 700,
320
+ overflowWrap: "anywhere"
321
+ },
322
+ children: note.title
323
+ }
324
+ ),
325
+ /* @__PURE__ */ jsx(
326
+ "button",
327
+ {
328
+ ref: deleteElement.ref,
329
+ type: "button",
330
+ ...deleteElement.agentProps,
331
+ onClick: () => onDelete(note.id),
332
+ style: {
333
+ ...iconButtonStyle,
334
+ width: 30,
335
+ height: 30,
336
+ minHeight: 30,
337
+ background: "rgba(255,255,255,0.68)"
338
+ },
339
+ title: "Delete note",
340
+ children: /* @__PURE__ */ jsx(Trash2, { size: 16, "aria-hidden": true })
341
+ }
342
+ )
343
+ ]
344
+ }
345
+ ),
346
+ /* @__PURE__ */ jsx(
347
+ "p",
348
+ {
349
+ style: {
350
+ margin: 0,
351
+ color: "#253025",
352
+ fontSize: 14,
353
+ lineHeight: "21px",
354
+ whiteSpace: "pre-wrap",
355
+ overflowWrap: "anywhere"
356
+ },
357
+ children: note.body
358
+ }
359
+ )
360
+ ]
361
+ }
362
+ );
363
+ }
364
+ function NotesView() {
365
+ const { notes } = useSimpleViewsSnapshot();
366
+ const [title, setTitle] = useState("");
367
+ const [body, setBody] = useState("");
368
+ const [color, setColor] = useState("yellow");
369
+ const titleRef = useRef(title);
370
+ const bodyRef = useRef(body);
371
+ const setTitleValue = useCallback((value) => {
372
+ titleRef.current = value;
373
+ setTitle(value);
374
+ }, []);
375
+ const setBodyValue = useCallback((value) => {
376
+ bodyRef.current = value;
377
+ setBody(value);
378
+ }, []);
379
+ const addNote = useCallback(async () => {
380
+ const nextTitle = titleRef.current.trim();
381
+ const nextBody = bodyRef.current.trim();
382
+ if (!nextTitle && !nextBody) return;
383
+ await callSimpleViewsCapability("create-note", {
384
+ title: nextTitle || "Untitled",
385
+ body: nextBody || "New note",
386
+ color
387
+ });
388
+ setTitleValue("");
389
+ setBodyValue("");
390
+ }, [color, setBodyValue, setTitleValue]);
391
+ const deleteNote = useCallback((id) => {
392
+ void callSimpleViewsCapability("delete-note", { id });
393
+ }, []);
394
+ const clearNotes = useCallback(() => {
395
+ void callSimpleViewsCapability("clear-notes");
396
+ }, []);
397
+ const handleSubmit = (event) => {
398
+ event.preventDefault();
399
+ void addNote();
400
+ };
401
+ return /* @__PURE__ */ jsxs("main", { "data-testid": "simple-notes-view", style: shellStyle, children: [
402
+ /* @__PURE__ */ jsxs("header", { style: headerStyle, children: [
403
+ /* @__PURE__ */ jsxs("div", { style: titleWrapStyle, children: [
404
+ /* @__PURE__ */ jsx("div", { style: iconBoxStyle, children: /* @__PURE__ */ jsx(StickyNote, { size: 18, "aria-hidden": true }) }),
405
+ /* @__PURE__ */ jsxs("div", { children: [
406
+ /* @__PURE__ */ jsx("h1", { style: h1Style, children: "Notes" }),
407
+ /* @__PURE__ */ jsxs("p", { style: subTextStyle, children: [
408
+ notes.length,
409
+ " sticky notes"
410
+ ] })
411
+ ] })
412
+ ] }),
413
+ /* @__PURE__ */ jsxs("div", { style: toolbarStyle, children: [
414
+ /* @__PURE__ */ jsx(
415
+ AgentButton,
416
+ {
417
+ agentId: "notes-clear",
418
+ agentLabel: "Clear notes",
419
+ onClick: clearNotes,
420
+ style: buttonStyle,
421
+ children: "Clear"
422
+ }
423
+ ),
424
+ /* @__PURE__ */ jsx(
425
+ AgentButton,
426
+ {
427
+ agentId: "notes-open-settings",
428
+ agentLabel: "Open settings",
429
+ onClick: openSettings,
430
+ style: iconButtonStyle,
431
+ title: "Open settings",
432
+ children: /* @__PURE__ */ jsx(Settings, { size: 16, "aria-hidden": true })
433
+ }
434
+ )
435
+ ] })
436
+ ] }),
437
+ /* @__PURE__ */ jsxs(
438
+ "div",
439
+ {
440
+ style: {
441
+ display: "grid",
442
+ gridTemplateColumns: "repeat(auto-fit, minmax(min(100%, 280px), 1fr))",
443
+ gap: 18,
444
+ padding: 20,
445
+ alignItems: "start"
446
+ },
447
+ children: [
448
+ /* @__PURE__ */ jsxs(
449
+ "form",
450
+ {
451
+ onSubmit: handleSubmit,
452
+ style: {
453
+ ...panelStyle,
454
+ display: "flex",
455
+ flexDirection: "column",
456
+ gap: 12,
457
+ padding: 16
458
+ },
459
+ children: [
460
+ /* @__PURE__ */ jsx(
461
+ AgentTextInput,
462
+ {
463
+ id: "note-title",
464
+ label: "Note title",
465
+ value: title,
466
+ onFill: setTitleValue,
467
+ placeholder: "Title",
468
+ group: "note-compose"
469
+ }
470
+ ),
471
+ /* @__PURE__ */ jsx(
472
+ AgentTextarea,
473
+ {
474
+ id: "note-body",
475
+ label: "Note body",
476
+ value: body,
477
+ onFill: setBodyValue,
478
+ group: "note-compose"
479
+ }
480
+ ),
481
+ /* @__PURE__ */ jsx("div", { style: { display: "flex", gap: 8, flexWrap: "wrap" }, children: COLORS.map((option) => /* @__PURE__ */ jsx(
482
+ AgentButton,
483
+ {
484
+ agentId: `note-color-${option}`,
485
+ agentLabel: `Set note color ${option}`,
486
+ agentRole: "toggle",
487
+ agentStatus: color === option ? "active" : void 0,
488
+ onClick: () => setColor(option),
489
+ style: {
490
+ ...buttonStyle,
491
+ background: NOTE_COLORS[option].background,
492
+ borderColor: color === option ? "#15171c" : NOTE_COLORS[option].borderColor,
493
+ minWidth: 72,
494
+ textTransform: "capitalize"
495
+ },
496
+ children: option
497
+ },
498
+ option
499
+ )) }),
500
+ /* @__PURE__ */ jsxs(
501
+ AgentButton,
502
+ {
503
+ agentId: "add-note",
504
+ agentLabel: "Add note",
505
+ onClick: addNote,
506
+ style: primaryButtonStyle,
507
+ children: [
508
+ /* @__PURE__ */ jsx(Plus, { size: 16, "aria-hidden": true }),
509
+ "Add note"
510
+ ]
511
+ }
512
+ )
513
+ ]
514
+ }
515
+ ),
516
+ /* @__PURE__ */ jsx(
517
+ "section",
518
+ {
519
+ style: {
520
+ display: "grid",
521
+ gridTemplateColumns: "repeat(auto-fill, minmax(220px, 1fr))",
522
+ gap: 16
523
+ },
524
+ "aria-label": "Sticky note wall",
525
+ children: notes.map((note) => /* @__PURE__ */ jsx(NoteCard, { note, onDelete: deleteNote }, note.id))
526
+ }
527
+ )
528
+ ]
529
+ }
530
+ )
531
+ ] });
532
+ }
533
+ function dateKey(date) {
534
+ return todayDateKey(date);
535
+ }
536
+ function parseDateKey(value) {
537
+ const normalized = normalizeDateKey(value) ?? todayDateKey();
538
+ const [year, month, day] = normalized.split("-").map((part) => Number(part));
539
+ return new Date(Date.UTC(year, month - 1, day));
540
+ }
541
+ function monthLabel(date) {
542
+ return new Intl.DateTimeFormat("en", {
543
+ month: "long",
544
+ year: "numeric",
545
+ timeZone: "UTC"
546
+ }).format(date);
547
+ }
548
+ function startOfMonth(date) {
549
+ return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), 1));
550
+ }
551
+ function addMonths(date, amount) {
552
+ return new Date(
553
+ Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + amount, 1)
554
+ );
555
+ }
556
+ function buildCalendarDays(cursor) {
557
+ const monthStart = startOfMonth(cursor);
558
+ const startOffset = monthStart.getUTCDay();
559
+ const gridStart = new Date(monthStart);
560
+ gridStart.setUTCDate(monthStart.getUTCDate() - startOffset);
561
+ return Array.from({ length: 42 }, (_, index) => {
562
+ const day = new Date(gridStart);
563
+ day.setUTCDate(gridStart.getUTCDate() + index);
564
+ return day;
565
+ });
566
+ }
567
+ function eventsForDate(events, date) {
568
+ return events.filter((event) => event.date === date).sort(
569
+ (a, b) => `${a.time} ${a.title}`.localeCompare(`${b.time} ${b.title}`)
570
+ );
571
+ }
572
+ function CalendarDayButton({
573
+ day,
574
+ selectedDate,
575
+ cursorMonth,
576
+ events,
577
+ onSelect
578
+ }) {
579
+ const key = dateKey(day);
580
+ const isSelected = key === selectedDate;
581
+ const inMonth = day.getUTCMonth() === cursorMonth;
582
+ const dayEvents = eventsForDate(events, key);
583
+ const dayElement = useAgentElement({
584
+ id: `calendar-day-${key}`,
585
+ label: `Select ${key}`,
586
+ role: "button",
587
+ group: "simple-calendar-grid",
588
+ description: dayEvents.length > 0 ? `${dayEvents.length} event${dayEvents.length === 1 ? "" : "s"}` : "No events",
589
+ status: isSelected ? "selected" : inMonth ? "current-month" : "outside-month",
590
+ onActivate: () => onSelect(key)
591
+ });
592
+ return /* @__PURE__ */ jsxs(
593
+ "button",
594
+ {
595
+ ref: dayElement.ref,
596
+ type: "button",
597
+ ...dayElement.agentProps,
598
+ onClick: () => onSelect(key),
599
+ style: {
600
+ minWidth: 0,
601
+ minHeight: 78,
602
+ border: isSelected ? "2px solid #476b3b" : "1px solid #dde1d8",
603
+ borderRadius: 8,
604
+ padding: 6,
605
+ background: inMonth ? "#ffffff" : "#eef1eb",
606
+ color: inMonth ? "#15171c" : "#687264",
607
+ textAlign: "left",
608
+ cursor: "pointer",
609
+ display: "flex",
610
+ flexDirection: "column",
611
+ gap: 6
612
+ },
613
+ children: [
614
+ /* @__PURE__ */ jsx("span", { style: { fontWeight: 700, fontSize: 13 }, children: day.getUTCDate() }),
615
+ /* @__PURE__ */ jsx("span", { style: { display: "flex", gap: 4, flexWrap: "wrap" }, children: dayEvents.slice(0, 4).map((event) => /* @__PURE__ */ jsx(
616
+ "span",
617
+ {
618
+ style: {
619
+ width: 7,
620
+ height: 7,
621
+ borderRadius: 999,
622
+ background: EVENT_DOTS[event.color]
623
+ }
624
+ },
625
+ event.id
626
+ )) }),
627
+ dayEvents[0] ? /* @__PURE__ */ jsx(
628
+ "span",
629
+ {
630
+ style: {
631
+ fontSize: 11,
632
+ lineHeight: "15px",
633
+ color: "#505b4d",
634
+ overflowWrap: "anywhere"
635
+ },
636
+ children: dayEvents[0].title
637
+ }
638
+ ) : null
639
+ ]
640
+ }
641
+ );
642
+ }
643
+ function EventRow({
644
+ event,
645
+ onDelete
646
+ }) {
647
+ const rowElement = useAgentElement({
648
+ id: `calendar-event-${event.id}`,
649
+ label: `Event: ${event.title}`,
650
+ role: "card",
651
+ group: "simple-calendar-events",
652
+ description: `${event.date} ${event.time}. ${event.notes}`,
653
+ status: event.color
654
+ });
655
+ const deleteElement = useAgentElement({
656
+ id: `delete-calendar-event-${event.id}`,
657
+ label: `Delete calendar event ${event.title}`,
658
+ role: "button",
659
+ group: "simple-calendar-events",
660
+ onActivate: () => onDelete(event.id)
661
+ });
662
+ return /* @__PURE__ */ jsxs(
663
+ "div",
664
+ {
665
+ ref: rowElement.ref,
666
+ ...rowElement.agentProps,
667
+ style: {
668
+ display: "grid",
669
+ gridTemplateColumns: "10px minmax(0, 1fr) 32px",
670
+ gap: 10,
671
+ alignItems: "center",
672
+ padding: "10px 0",
673
+ borderBottom: "1px solid #ecefe9"
674
+ },
675
+ children: [
676
+ /* @__PURE__ */ jsx(
677
+ "span",
678
+ {
679
+ style: {
680
+ width: 8,
681
+ height: 34,
682
+ borderRadius: 999,
683
+ background: EVENT_DOTS[event.color]
684
+ }
685
+ }
686
+ ),
687
+ /* @__PURE__ */ jsxs("div", { style: { minWidth: 0 }, children: [
688
+ /* @__PURE__ */ jsxs(
689
+ "div",
690
+ {
691
+ style: {
692
+ fontWeight: 700,
693
+ fontSize: 14,
694
+ lineHeight: "20px",
695
+ overflowWrap: "anywhere"
696
+ },
697
+ children: [
698
+ event.time,
699
+ " - ",
700
+ event.title
701
+ ]
702
+ }
703
+ ),
704
+ event.notes ? /* @__PURE__ */ jsx(
705
+ "div",
706
+ {
707
+ style: {
708
+ color: "#60695d",
709
+ fontSize: 12,
710
+ lineHeight: "17px",
711
+ overflowWrap: "anywhere"
712
+ },
713
+ children: event.notes
714
+ }
715
+ ) : null
716
+ ] }),
717
+ /* @__PURE__ */ jsx(
718
+ "button",
719
+ {
720
+ ref: deleteElement.ref,
721
+ type: "button",
722
+ ...deleteElement.agentProps,
723
+ onClick: () => onDelete(event.id),
724
+ style: iconButtonStyle,
725
+ title: "Delete event",
726
+ children: /* @__PURE__ */ jsx(Trash2, { size: 16, "aria-hidden": true })
727
+ }
728
+ )
729
+ ]
730
+ }
731
+ );
732
+ }
733
+ function SimpleCalendarView() {
734
+ const snapshot = useSimpleViewsSnapshot();
735
+ const [selectedDate, setSelectedDate] = useState(() => readSelectedDate());
736
+ const [title, setTitle] = useState("");
737
+ const [time, setTime] = useState("09:00");
738
+ const [notes, setNotes] = useState("");
739
+ const [color, setColor] = useState("green");
740
+ const selectedDateRef = useRef(selectedDate);
741
+ const titleRef = useRef(title);
742
+ const timeRef = useRef(time);
743
+ const notesRef = useRef(notes);
744
+ const colorRef = useRef(color);
745
+ useEffect(() => {
746
+ selectedDateRef.current = snapshot.selectedDate;
747
+ setSelectedDate(snapshot.selectedDate);
748
+ }, [snapshot.selectedDate]);
749
+ const setTitleValue = useCallback((value) => {
750
+ titleRef.current = value;
751
+ setTitle(value);
752
+ }, []);
753
+ const setTimeValue = useCallback((value) => {
754
+ timeRef.current = value;
755
+ setTime(value);
756
+ }, []);
757
+ const setNotesValue = useCallback((value) => {
758
+ notesRef.current = value;
759
+ setNotes(value);
760
+ }, []);
761
+ const setColorValue = useCallback((value) => {
762
+ colorRef.current = value;
763
+ setColor(value);
764
+ }, []);
765
+ const cursor = useMemo(
766
+ () => startOfMonth(parseDateKey(selectedDate)),
767
+ [selectedDate]
768
+ );
769
+ const days = useMemo(() => buildCalendarDays(cursor), [cursor]);
770
+ const selectedEvents = useMemo(
771
+ () => eventsForDate(snapshot.events, selectedDate),
772
+ [snapshot.events, selectedDate]
773
+ );
774
+ const selectDate = useCallback((date) => {
775
+ const normalizedDate = normalizeDateKey(date);
776
+ if (!normalizedDate) return;
777
+ selectedDateRef.current = normalizedDate;
778
+ setSelectedDate(normalizedDate);
779
+ void callSimpleViewsCapability("select-calendar-date", {
780
+ date: normalizedDate
781
+ });
782
+ }, []);
783
+ const moveMonth = useCallback(
784
+ (amount) => {
785
+ selectDate(dateKey(addMonths(cursor, amount)));
786
+ },
787
+ [cursor, selectDate]
788
+ );
789
+ const selectToday = useCallback(() => {
790
+ selectDate(dateKey(/* @__PURE__ */ new Date()));
791
+ }, [selectDate]);
792
+ const addEvent = useCallback(async () => {
793
+ const eventTitle = titleRef.current.trim();
794
+ if (!eventTitle) return;
795
+ await callSimpleViewsCapability("create-calendar-event", {
796
+ title: eventTitle,
797
+ date: selectedDateRef.current,
798
+ time: timeRef.current || "09:00",
799
+ notes: notesRef.current.trim(),
800
+ color: colorRef.current
801
+ });
802
+ setTitleValue("");
803
+ setNotesValue("");
804
+ }, [setNotesValue, setTitleValue]);
805
+ const deleteEvent = useCallback((id) => {
806
+ void callSimpleViewsCapability("delete-calendar-event", { id });
807
+ }, []);
808
+ const handleSubmit = (event) => {
809
+ event.preventDefault();
810
+ void addEvent();
811
+ };
812
+ return /* @__PURE__ */ jsxs("main", { "data-testid": "simple-calendar-view", style: shellStyle, children: [
813
+ /* @__PURE__ */ jsxs("header", { style: headerStyle, children: [
814
+ /* @__PURE__ */ jsxs("div", { style: titleWrapStyle, children: [
815
+ /* @__PURE__ */ jsx("div", { style: iconBoxStyle, children: /* @__PURE__ */ jsx(CalendarDays, { size: 18, "aria-hidden": true }) }),
816
+ /* @__PURE__ */ jsxs("div", { children: [
817
+ /* @__PURE__ */ jsx("h1", { style: h1Style, children: "Simple Calendar" }),
818
+ /* @__PURE__ */ jsxs("p", { style: subTextStyle, children: [
819
+ monthLabel(cursor),
820
+ " - ",
821
+ snapshot.events.length,
822
+ " events"
823
+ ] })
824
+ ] })
825
+ ] }),
826
+ /* @__PURE__ */ jsxs("div", { style: toolbarStyle, children: [
827
+ /* @__PURE__ */ jsx(
828
+ AgentButton,
829
+ {
830
+ agentId: "calendar-prev-month",
831
+ agentLabel: "Previous month",
832
+ onClick: () => moveMonth(-1),
833
+ style: iconButtonStyle,
834
+ title: "Previous month",
835
+ children: /* @__PURE__ */ jsx(ChevronLeft, { size: 16, "aria-hidden": true })
836
+ }
837
+ ),
838
+ /* @__PURE__ */ jsx(
839
+ AgentButton,
840
+ {
841
+ agentId: "calendar-today",
842
+ agentLabel: "Go to today",
843
+ onClick: selectToday,
844
+ style: buttonStyle,
845
+ children: "Today"
846
+ }
847
+ ),
848
+ /* @__PURE__ */ jsx(
849
+ AgentButton,
850
+ {
851
+ agentId: "calendar-next-month",
852
+ agentLabel: "Next month",
853
+ onClick: () => moveMonth(1),
854
+ style: iconButtonStyle,
855
+ title: "Next month",
856
+ children: /* @__PURE__ */ jsx(ChevronRight, { size: 16, "aria-hidden": true })
857
+ }
858
+ ),
859
+ /* @__PURE__ */ jsx(
860
+ AgentButton,
861
+ {
862
+ agentId: "calendar-open-settings",
863
+ agentLabel: "Open settings",
864
+ onClick: openSettings,
865
+ style: iconButtonStyle,
866
+ title: "Open settings",
867
+ children: /* @__PURE__ */ jsx(Settings, { size: 16, "aria-hidden": true })
868
+ }
869
+ )
870
+ ] })
871
+ ] }),
872
+ /* @__PURE__ */ jsxs(
873
+ "div",
874
+ {
875
+ style: {
876
+ display: "grid",
877
+ gridTemplateColumns: "repeat(auto-fit, minmax(min(100%, 320px), 1fr))",
878
+ gap: 18,
879
+ padding: 20,
880
+ alignItems: "start"
881
+ },
882
+ children: [
883
+ /* @__PURE__ */ jsxs("section", { style: { ...panelStyle, padding: 14 }, children: [
884
+ /* @__PURE__ */ jsx(
885
+ "div",
886
+ {
887
+ style: {
888
+ display: "grid",
889
+ gridTemplateColumns: "repeat(7, minmax(0, 1fr))",
890
+ gap: 8,
891
+ marginBottom: 8,
892
+ color: "#60695d",
893
+ fontSize: 12,
894
+ fontWeight: 700,
895
+ textAlign: "center"
896
+ },
897
+ children: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"].map((day) => /* @__PURE__ */ jsx("div", { children: day }, day))
898
+ }
899
+ ),
900
+ /* @__PURE__ */ jsx(
901
+ "div",
902
+ {
903
+ style: {
904
+ display: "grid",
905
+ gridTemplateColumns: "repeat(7, minmax(0, 1fr))",
906
+ gap: 8
907
+ },
908
+ children: days.map((day) => /* @__PURE__ */ jsx(
909
+ CalendarDayButton,
910
+ {
911
+ day,
912
+ selectedDate,
913
+ cursorMonth: cursor.getUTCMonth(),
914
+ events: snapshot.events,
915
+ onSelect: selectDate
916
+ },
917
+ dateKey(day)
918
+ ))
919
+ }
920
+ )
921
+ ] }),
922
+ /* @__PURE__ */ jsxs(
923
+ "aside",
924
+ {
925
+ style: {
926
+ ...panelStyle,
927
+ display: "flex",
928
+ flexDirection: "column",
929
+ gap: 16,
930
+ padding: 16
931
+ },
932
+ children: [
933
+ /* @__PURE__ */ jsxs("div", { children: [
934
+ /* @__PURE__ */ jsx("h2", { style: { ...h1Style, fontSize: 17, lineHeight: "23px" }, children: selectedDate }),
935
+ /* @__PURE__ */ jsxs("p", { style: subTextStyle, children: [
936
+ selectedEvents.length,
937
+ " event",
938
+ selectedEvents.length === 1 ? "" : "s"
939
+ ] })
940
+ ] }),
941
+ /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, style: { display: "grid", gap: 10 }, children: [
942
+ /* @__PURE__ */ jsx(
943
+ AgentTextInput,
944
+ {
945
+ id: "calendar-event-title",
946
+ label: "Calendar event title",
947
+ value: title,
948
+ onFill: setTitleValue,
949
+ placeholder: "Event title",
950
+ group: "simple-calendar-compose"
951
+ }
952
+ ),
953
+ /* @__PURE__ */ jsxs(
954
+ "div",
955
+ {
956
+ style: {
957
+ display: "grid",
958
+ gridTemplateColumns: "minmax(0, 1fr) minmax(88px, 112px)",
959
+ gap: 8
960
+ },
961
+ children: [
962
+ /* @__PURE__ */ jsx(
963
+ AgentTextInput,
964
+ {
965
+ id: "calendar-event-date",
966
+ label: "Calendar event date",
967
+ value: selectedDate,
968
+ onFill: selectDate,
969
+ group: "simple-calendar-compose"
970
+ }
971
+ ),
972
+ /* @__PURE__ */ jsx(
973
+ AgentTextInput,
974
+ {
975
+ id: "calendar-event-time",
976
+ label: "Calendar event time",
977
+ value: time,
978
+ onFill: setTimeValue,
979
+ group: "simple-calendar-compose"
980
+ }
981
+ )
982
+ ]
983
+ }
984
+ ),
985
+ /* @__PURE__ */ jsx(
986
+ AgentTextarea,
987
+ {
988
+ id: "calendar-event-notes",
989
+ label: "Calendar event notes",
990
+ value: notes,
991
+ onFill: setNotesValue,
992
+ group: "simple-calendar-compose"
993
+ }
994
+ ),
995
+ /* @__PURE__ */ jsx("div", { style: { display: "flex", gap: 8, flexWrap: "wrap" }, children: COLORS.map((option) => /* @__PURE__ */ jsx(
996
+ AgentButton,
997
+ {
998
+ agentId: `calendar-event-color-${option}`,
999
+ agentLabel: `Set calendar event color ${option}`,
1000
+ agentRole: "toggle",
1001
+ agentStatus: color === option ? "active" : void 0,
1002
+ onClick: () => setColorValue(option),
1003
+ style: {
1004
+ ...buttonStyle,
1005
+ background: NOTE_COLORS[option].background,
1006
+ borderColor: color === option ? "#15171c" : NOTE_COLORS[option].borderColor,
1007
+ minWidth: 72,
1008
+ textTransform: "capitalize"
1009
+ },
1010
+ children: option
1011
+ },
1012
+ option
1013
+ )) }),
1014
+ /* @__PURE__ */ jsxs(
1015
+ AgentButton,
1016
+ {
1017
+ agentId: "add-calendar-event",
1018
+ agentLabel: "Add calendar event",
1019
+ onClick: addEvent,
1020
+ style: primaryButtonStyle,
1021
+ children: [
1022
+ /* @__PURE__ */ jsx(Plus, { size: 16, "aria-hidden": true }),
1023
+ "Add event"
1024
+ ]
1025
+ }
1026
+ )
1027
+ ] }),
1028
+ /* @__PURE__ */ jsx("div", { children: selectedEvents.map((event) => /* @__PURE__ */ jsx(EventRow, { event, onDelete: deleteEvent }, event.id)) })
1029
+ ]
1030
+ }
1031
+ )
1032
+ ]
1033
+ }
1034
+ )
1035
+ ] });
1036
+ }
1037
+ export {
1038
+ NotesView,
1039
+ SimpleCalendarView
1040
+ };
1041
+ //# sourceMappingURL=ui.js.map