@agent-native/core 0.159.2 → 0.159.3
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/clips/.agents/skills/meetings/SKILL.md +11 -6
- package/corpus/templates/clips/AGENTS.md +1 -0
- package/corpus/templates/clips/actions/lib/meeting-content.ts +15 -4
- package/corpus/templates/clips/actions/list-meetings.ts +179 -42
- package/corpus/templates/clips/actions/search-meetings.ts +277 -0
- package/corpus/templates/clips/app/components/meetings/agenda-card.tsx +273 -0
- package/corpus/templates/clips/app/components/meetings/day-grouped-card.tsx +113 -0
- package/corpus/templates/clips/app/components/meetings/meeting-history-row.tsx +117 -0
- package/corpus/templates/clips/app/hooks/use-navigation-state.ts +12 -2
- package/corpus/templates/clips/app/i18n/en-US.ts +7 -2
- package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +263 -357
- package/corpus/templates/clips/changelog/2026-08-14-meetings-history-is-searchable-again.md +6 -0
- package/corpus/templates/clips/desktop/design-refs/granola-ux.md +17 -0
- package/corpus/templates/content/actions/_database-source-utils.ts +29 -2
- package/corpus/templates/content/app/components/editor/SlashCommandMenu.tsx +11 -11
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +11 -50
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +57 -20
- package/corpus/templates/content/app/components/editor/database-sources/BuilderSourceReviewDialog.tsx +30 -0
- package/corpus/templates/content/app/components/editor/extensions/NotionExtensions.tsx +204 -51
- package/corpus/templates/content/app/global.css +4 -1
- package/corpus/templates/content/app/i18n-data.ts +30 -0
- package/corpus/templates/content/changelog/2026-08-12-toggle-blocks-now-follow-notion-style-enter-and-shift-tab-be.md +6 -0
- package/corpus/templates/content/docs/solutions/2026-08-12-toggle-summary-focus-persistence-shape.md +733 -0
- package/corpus/templates/content/shared/builder-mdx.ts +44 -4
- package/corpus/templates/slides/actions/get-layout-overflows.ts +65 -2
- package/dist/deploy/build.d.ts +6 -4
- package/dist/deploy/build.js +27 -11
- package/dist/mcp/screen-memory-stdio.d.ts +7 -7
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +2 -2
- package/dist/secrets/routes.d.ts +6 -6
- package/package.json +2 -2
- package/corpus/templates/clips/app/components/meetings/meeting-card.tsx +0 -333
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <MeetingHistoryRow /> — one past meeting, one line.
|
|
3
|
+
*
|
|
4
|
+
* Granola renders history as Apple-Notes-style rows: attendee avatars, title,
|
|
5
|
+
* who was on the call, time. No card border, no summary preview, no
|
|
6
|
+
* status pills — see `desktop/design-refs/granola-ux.md` §2. Status badges on
|
|
7
|
+
* every row read as noise at list scale; the meeting detail page is where
|
|
8
|
+
* transcript/notes state belongs.
|
|
9
|
+
*
|
|
10
|
+
* `snippet` replaces the attendee subtitle in search results, where the reason
|
|
11
|
+
* a row matched is the only thing worth reading.
|
|
12
|
+
*/
|
|
13
|
+
import { useSession } from "@agent-native/core/client/hooks";
|
|
14
|
+
import { useT } from "@agent-native/core/client/i18n";
|
|
15
|
+
import { IconFileText } from "@tabler/icons-react";
|
|
16
|
+
import { NavLink } from "react-router";
|
|
17
|
+
|
|
18
|
+
import { AttendeeStack, type AttendeeStackParticipant } from "./attendee-stack";
|
|
19
|
+
|
|
20
|
+
export interface MeetingHistoryItem {
|
|
21
|
+
id: string;
|
|
22
|
+
title: string;
|
|
23
|
+
scheduledStart?: string | null;
|
|
24
|
+
scheduledEnd?: string | null;
|
|
25
|
+
actualStart?: string | null;
|
|
26
|
+
actualEnd?: string | null;
|
|
27
|
+
createdAt?: string | null;
|
|
28
|
+
participants?: AttendeeStackParticipant[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function formatTime(iso?: string | null): string {
|
|
32
|
+
if (!iso) return "";
|
|
33
|
+
const d = new Date(iso);
|
|
34
|
+
if (Number.isNaN(d.getTime())) return "";
|
|
35
|
+
return d.toLocaleTimeString([], { hour: "numeric", minute: "2-digit" });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* "Elaine" · "Lisa, Cody & 2 others" — Granola's attendee subtitle.
|
|
40
|
+
*
|
|
41
|
+
* The viewer is dropped: the subtitle answers "who was I with", and repeating
|
|
42
|
+
* the reader's own name down every row of their own history says nothing. A
|
|
43
|
+
* solo note keeps an empty subtitle rather than rendering just the viewer.
|
|
44
|
+
*/
|
|
45
|
+
export function formatParticipantNames(
|
|
46
|
+
participants: AttendeeStackParticipant[],
|
|
47
|
+
viewerEmail?: string | null,
|
|
48
|
+
): string {
|
|
49
|
+
const viewer = viewerEmail?.trim().toLowerCase();
|
|
50
|
+
const names = participants
|
|
51
|
+
.filter((p) => !viewer || p.email?.trim().toLowerCase() !== viewer)
|
|
52
|
+
.map((p) => p.name?.trim() || p.email?.trim().replace(/@.*$/, "") || "")
|
|
53
|
+
.filter(Boolean);
|
|
54
|
+
if (names.length === 0) return "";
|
|
55
|
+
if (names.length <= 2) return names.join(", ");
|
|
56
|
+
return `${names.slice(0, 2).join(", ")} & ${names.length - 2} others`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function MeetingHistoryRow({
|
|
60
|
+
meeting,
|
|
61
|
+
snippet,
|
|
62
|
+
}: {
|
|
63
|
+
meeting: MeetingHistoryItem;
|
|
64
|
+
snippet?: string | null;
|
|
65
|
+
}) {
|
|
66
|
+
const t = useT();
|
|
67
|
+
const { session } = useSession();
|
|
68
|
+
const participants = meeting.participants ?? [];
|
|
69
|
+
const subtitle =
|
|
70
|
+
snippet?.trim() || formatParticipantNames(participants, session?.email);
|
|
71
|
+
const time = formatTime(
|
|
72
|
+
meeting.actualStart ?? meeting.scheduledStart ?? meeting.createdAt,
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<NavLink
|
|
77
|
+
to={`/meetings/${meeting.id}`}
|
|
78
|
+
className="group flex items-center gap-3 rounded-md px-2 py-2 transition-colors hover:bg-accent/40 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
79
|
+
>
|
|
80
|
+
{participants.length > 0 ? (
|
|
81
|
+
<AttendeeStack participants={participants} size="md" max={2} />
|
|
82
|
+
) : (
|
|
83
|
+
<span className="flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-muted text-muted-foreground">
|
|
84
|
+
<IconFileText className="h-3.5 w-3.5" />
|
|
85
|
+
</span>
|
|
86
|
+
)}
|
|
87
|
+
<div className="min-w-0 flex-1">
|
|
88
|
+
<div className="truncate text-sm text-foreground">
|
|
89
|
+
{meeting.title || t("meetingDetail.untitledMeeting")}
|
|
90
|
+
</div>
|
|
91
|
+
{subtitle ? (
|
|
92
|
+
<div className="truncate text-xs text-muted-foreground">
|
|
93
|
+
{subtitle}
|
|
94
|
+
</div>
|
|
95
|
+
) : null}
|
|
96
|
+
</div>
|
|
97
|
+
{time ? (
|
|
98
|
+
<span className="shrink-0 text-xs tabular-nums text-muted-foreground">
|
|
99
|
+
{time}
|
|
100
|
+
</span>
|
|
101
|
+
) : null}
|
|
102
|
+
</NavLink>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function MeetingHistoryRowSkeleton() {
|
|
107
|
+
return (
|
|
108
|
+
<div className="flex items-center gap-3 px-2 py-2">
|
|
109
|
+
<div className="h-7 w-7 shrink-0 animate-pulse rounded-md bg-muted" />
|
|
110
|
+
<div className="min-w-0 flex-1 space-y-1.5">
|
|
111
|
+
<div className="h-3.5 w-2/5 animate-pulse rounded bg-muted" />
|
|
112
|
+
<div className="h-3 w-24 animate-pulse rounded bg-muted/70" />
|
|
113
|
+
</div>
|
|
114
|
+
<div className="h-3 w-12 animate-pulse rounded bg-muted/70" />
|
|
115
|
+
</div>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
@@ -30,6 +30,7 @@ export interface NavigationState {
|
|
|
30
30
|
search?: string;
|
|
31
31
|
path?: string;
|
|
32
32
|
meetingId?: string;
|
|
33
|
+
meetingsTab?: "agenda" | "past";
|
|
33
34
|
dictationId?: string;
|
|
34
35
|
}
|
|
35
36
|
|
|
@@ -60,6 +61,9 @@ interface NavigateCommand extends Partial<NavigationState> {
|
|
|
60
61
|
* /embed/:shareId -> embed
|
|
61
62
|
* /notifications -> notifications
|
|
62
63
|
* /settings[/*] -> settings
|
|
64
|
+
* /meetings -> meetings (meetingsTab: agenda)
|
|
65
|
+
* /meetings?tab=past -> meetings (meetingsTab: past)
|
|
66
|
+
* /meetings/:meetingId -> meeting
|
|
63
67
|
*/
|
|
64
68
|
export function stateFromLocation(
|
|
65
69
|
pathname: string,
|
|
@@ -110,7 +114,13 @@ export function stateFromLocation(
|
|
|
110
114
|
if (meetingMatch[1]) {
|
|
111
115
|
return { view: "meeting", meetingId: meetingMatch[1] };
|
|
112
116
|
}
|
|
113
|
-
|
|
117
|
+
// ?tab= is absent on the default Agenda tab, so report it explicitly
|
|
118
|
+
// rather than leaving the agent to infer which list the user is looking at.
|
|
119
|
+
return {
|
|
120
|
+
view: "meetings",
|
|
121
|
+
meetingsTab: params.get("tab") === "past" ? "past" : "agenda",
|
|
122
|
+
...(searchTerm ? { search: searchTerm } : {}),
|
|
123
|
+
};
|
|
114
124
|
}
|
|
115
125
|
|
|
116
126
|
// /dictate (optionally /dictate/:dictationId in the future)
|
|
@@ -185,7 +195,7 @@ export function pathFromCommand(cmd: NavigateCommand): string {
|
|
|
185
195
|
case "settings":
|
|
186
196
|
return "/settings";
|
|
187
197
|
case "meetings":
|
|
188
|
-
return "/meetings";
|
|
198
|
+
return cmd.meetingsTab === "past" ? "/meetings?tab=past" : "/meetings";
|
|
189
199
|
case "meeting":
|
|
190
200
|
return cmd.meetingId ? `/meetings/${cmd.meetingId}` : "/meetings";
|
|
191
201
|
case "dictate":
|
|
@@ -1580,7 +1580,6 @@ All notable user-facing changes to Clips are documented here. Open it any time f
|
|
|
1580
1580
|
transcriptPending: "Transcript pending",
|
|
1581
1581
|
notesPending: "Notes pending",
|
|
1582
1582
|
pastRecordings: "Past recordings",
|
|
1583
|
-
loadOlder: "Load older",
|
|
1584
1583
|
calendarNeedsReconnect:
|
|
1585
1584
|
"Google Calendar needs to be reconnected to keep showing your upcoming meetings.",
|
|
1586
1585
|
connectGoogleCalendar: "Connect Google Calendar",
|
|
@@ -1607,7 +1606,13 @@ All notable user-facing changes to Clips are documented here. Open it any time f
|
|
|
1607
1606
|
title: "Meetings",
|
|
1608
1607
|
intro:
|
|
1609
1608
|
"Upcoming calendar meetings and your recorded notes. Start live notes from Clips Desktop at meeting time.",
|
|
1610
|
-
|
|
1609
|
+
agendaTab: "Agenda",
|
|
1610
|
+
pastTab: "Past",
|
|
1611
|
+
now: "Now",
|
|
1612
|
+
noPastMeetings: "No past meetings yet",
|
|
1613
|
+
loadOlder: "Load older",
|
|
1614
|
+
searchFailed: "Couldn't search meetings. Try again in a moment.",
|
|
1615
|
+
searchPlaceholder: "Search meetings, attendees, and transcripts...",
|
|
1611
1616
|
clearSearch: "Clear search",
|
|
1612
1617
|
noMeetingsYet: "No meetings yet",
|
|
1613
1618
|
noMeetingsDescription:
|