@agent-native/core 0.157.5 → 0.157.6

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 CHANGED
@@ -31,4 +31,4 @@ rg -n "defineAction|useActionQuery" node_modules/@agent-native/core/corpus
31
31
 
32
32
  ## Generated Counts
33
33
 
34
- - template files: 8249
34
+ - template files: 8250
@@ -9,6 +9,7 @@
9
9
 
10
10
  import { defineAction } from "@agent-native/core";
11
11
  import { writeAppState } from "@agent-native/core/application-state";
12
+ import { getRequestUserName } from "@agent-native/core/server/request-context";
12
13
  import { resolveAccess } from "@agent-native/core/sharing";
13
14
  import { and, eq, isNull } from "drizzle-orm";
14
15
  import { z } from "zod";
@@ -93,7 +94,7 @@ export default defineAction({
93
94
  email: string;
94
95
  name?: string;
95
96
  isOrganizer?: boolean;
96
- }> = args.participants ?? [];
97
+ }> = [...(args.participants ?? [])];
97
98
  let calendarEventIdLink: string | null = null;
98
99
  let source: "calendar" | "adhoc" | "manual" =
99
100
  args.source ?? (args.title ? "manual" : "adhoc");
@@ -190,6 +191,23 @@ export default defineAction({
190
191
  }
191
192
  }
192
193
 
194
+ // Backfill a missing display name onto the owner's own attendee row —
195
+ // but only when they're already a genuine calendar attendee. Inserting
196
+ // a synthetic row for a non-attendee owner would put their email in
197
+ // meeting_participants indistinguishably from a real attendee, and that
198
+ // table is what the public share payload returns; a meeting made public
199
+ // later would leak an email the owner never actually disclosed. When the
200
+ // owner isn't an attendee, speaker resolution already has a safe
201
+ // fallback (the generic "Me" label) — no snapshot needed for that case.
202
+ const ownerName = getRequestUserName()?.trim() || undefined;
203
+ const ownerParticipant = participantsToInsert.find(
204
+ (participant) =>
205
+ participant.email.trim().toLowerCase() === ownerEmail.toLowerCase(),
206
+ );
207
+ if (ownerParticipant && !ownerParticipant.name?.trim() && ownerName) {
208
+ ownerParticipant.name = ownerName;
209
+ }
210
+
193
211
  const visibility =
194
212
  args.visibility ??
195
213
  (await getDefaultRecordingVisibility(
@@ -1,4 +1,4 @@
1
- import { IconSearch } from "@tabler/icons-react";
1
+ import { useT } from "@agent-native/core/client/i18n";
2
2
  import { useMemo } from "react";
3
3
 
4
4
  import {
@@ -9,6 +9,7 @@ import {
9
9
  } from "@/components/ui/tooltip";
10
10
  import { cn } from "@/lib/utils";
11
11
 
12
+ import { formatTranscriptTimestamp } from "../transcript/transcript-segment-row";
12
13
  import type { TranscriptSegment } from "./transcript-bubbles";
13
14
 
14
15
  const STOPWORDS = new Set<string>([
@@ -129,39 +130,61 @@ export function BulletLink({
129
130
  onJumpTo,
130
131
  children,
131
132
  }: BulletLinkProps) {
133
+ const t = useT();
132
134
  const matchIndex = useMemo(
133
135
  () => findBestSegmentMatch(bullet, segments),
134
136
  [bullet, segments],
135
137
  );
136
138
  const hasMatch = matchIndex >= 0;
139
+ const timestamp = hasMatch
140
+ ? formatTranscriptTimestamp(segments[matchIndex]!.startMs)
141
+ : null;
137
142
 
138
143
  return (
139
144
  <TooltipProvider delayDuration={200}>
140
145
  <div className="group flex items-start gap-1.5">
141
- <div className="flex-1 min-w-0">{children}</div>
142
- <Tooltip>
143
- <TooltipTrigger asChild>
144
- <button
145
- type="button"
146
- disabled={!hasMatch}
147
- onClick={() => hasMatch && onJumpTo(matchIndex)}
148
- aria-label={
149
- hasMatch ? "Jump to transcript moment" : "No matching moment"
150
- }
151
- className={cn(
152
- "shrink-0 mt-0.5 inline-flex h-5 w-5 items-center justify-center rounded transition-opacity",
153
- hasMatch
154
- ? "opacity-0 group-hover:opacity-50 hover:!opacity-100 hover:bg-accent text-muted-foreground hover:text-foreground cursor-pointer"
155
- : "opacity-0 group-hover:opacity-30 text-muted-foreground/50 cursor-default",
156
- )}
157
- >
158
- <IconSearch className="h-3.5 w-3.5" />
159
- </button>
160
- </TooltipTrigger>
161
- <TooltipContent side="left">
162
- {hasMatch ? "Jump to transcript" : "No matching moment found"}
163
- </TooltipContent>
164
- </Tooltip>
146
+ <div className="flex min-w-0 flex-1">{children}</div>
147
+ {timestamp ? (
148
+ <Tooltip>
149
+ <TooltipTrigger asChild>
150
+ <button
151
+ type="button"
152
+ onClick={() => onJumpTo(matchIndex)}
153
+ aria-label={t("bulletLink.jumpToTranscript", {
154
+ time: timestamp,
155
+ })}
156
+ className={cn(
157
+ "mt-0.5 inline-flex h-5 shrink-0 items-center rounded px-1 font-mono text-[10px] tabular-nums text-muted-foreground/80 opacity-0 transition-opacity",
158
+ "group-hover:opacity-100 [@media(hover:none)]:opacity-100",
159
+ "cursor-pointer hover:bg-accent hover:text-foreground focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50",
160
+ )}
161
+ >
162
+ {timestamp}
163
+ </button>
164
+ </TooltipTrigger>
165
+ <TooltipContent side="top">
166
+ {t("bulletLink.jumpToTranscript", { time: timestamp })}
167
+ </TooltipContent>
168
+ </Tooltip>
169
+ ) : (
170
+ <Tooltip>
171
+ <TooltipTrigger asChild>
172
+ {/* A span, not a button — there's no action to disable, just
173
+ a status to announce, and tabIndex keeps it keyboard- and
174
+ screen-reader-reachable (a disabled button wouldn't be). */}
175
+ <span
176
+ tabIndex={0}
177
+ aria-label={t("bulletLink.noMatchingMoment")}
178
+ className="mt-0.5 inline-flex h-5 shrink-0 cursor-default items-center rounded px-1 font-mono text-[10px] tabular-nums text-muted-foreground/40 opacity-0 transition-opacity group-hover:opacity-100 focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 [@media(hover:none)]:opacity-100"
179
+ >
180
+ --:--
181
+ </span>
182
+ </TooltipTrigger>
183
+ <TooltipContent side="top">
184
+ {t("bulletLink.noMatchingMoment")}
185
+ </TooltipContent>
186
+ </Tooltip>
187
+ )}
165
188
  </div>
166
189
  </TooltipProvider>
167
190
  );
@@ -29,9 +29,10 @@ interface CanvasEditorProps {
29
29
  userNotesMd?: string;
30
30
  /** Save user notes. Called on blur after edit. */
31
31
  onUserNotesChange?: (next: string) => void;
32
- /** AI-generated summary (renders muted-gray). For the "ai" view. */
32
+ /** AI-generated summary — primary content, so it renders full-strength
33
+ * foreground like user notes. For the "ai" view. */
33
34
  summaryMd?: string;
34
- /** AI-generated bullets (renders muted-gray). For the "ai" view. */
35
+ /** AI-generated bullets — same weight as the summary. For the "ai" view. */
35
36
  bullets?: string[];
36
37
  /** Save AI summary when the user edits the summary section. */
37
38
  onSummaryChange?: (next: string) => void;
@@ -76,7 +77,7 @@ export function CanvasEditor({
76
77
  />
77
78
  )}
78
79
 
79
- {/* AI bullets — muted gray, with optional BulletLink wrappers */}
80
+ {/* AI bullets — with optional BulletLink wrappers */}
80
81
  {showAi && bullets.length > 0 && (
81
82
  <AiBulletsBlock bullets={bullets} renderBullet={renderBullet} />
82
83
  )}
@@ -181,7 +182,7 @@ function AiSummaryBlock({
181
182
  return (
182
183
  <div className="space-y-1.5">
183
184
  <AiTabIndicator />
184
- <p className="text-sm leading-relaxed whitespace-pre-wrap text-muted-foreground">
185
+ <p className="text-sm leading-relaxed whitespace-pre-wrap text-foreground">
185
186
  {value}
186
187
  </p>
187
188
  </div>
@@ -234,7 +235,7 @@ function AiSummaryBlock({
234
235
  >
235
236
  <p
236
237
  className={cn(
237
- "text-sm leading-relaxed whitespace-pre-wrap text-muted-foreground rounded -mx-1 px-1 group-hover:bg-accent/30",
238
+ "text-sm leading-relaxed whitespace-pre-wrap text-foreground rounded -mx-1 px-1 group-hover:bg-accent/30",
238
239
  )}
239
240
  >
240
241
  {value}
@@ -263,7 +264,7 @@ function AiBulletsBlock({
263
264
  <ul className="space-y-1.5">
264
265
  {bullets.map((b, i) => {
265
266
  const content = (
266
- <div className="flex gap-2 text-sm leading-relaxed text-muted-foreground">
267
+ <div className="flex gap-2 text-sm leading-relaxed text-foreground">
267
268
  <span>•</span>
268
269
  <span className="flex-1">{b}</span>
269
270
  </div>