@agent-native/core-corpus 0.217.0 → 0.219.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.
Files changed (81) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/hubspot/SKILL.md +5 -3
  3. package/corpus/templates/analytics/CHANGELOG.md +2 -0
  4. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +38 -167
  5. package/corpus/templates/analytics/changelog/2026-10-16-owned-and-shared-chats-appear-in-separate-history-sections.md +6 -0
  6. package/corpus/templates/assets/CHANGELOG.md +6 -0
  7. package/corpus/templates/assets/app/components/layout/Sidebar.tsx +51 -117
  8. package/corpus/templates/assets/changelog/2026-10-16-owned-and-shared-chats-appear-in-separate-history-sections.md +6 -0
  9. package/corpus/templates/brain/app/components/layout/Sidebar.tsx +34 -101
  10. package/corpus/templates/brain/app/routes/sources.tsx +1 -1
  11. package/corpus/templates/brain/vitest.config.ts +7 -0
  12. package/corpus/templates/calendar/CHANGELOG.md +6 -0
  13. package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +1 -1
  14. package/corpus/templates/calendar/changelog/2026-10-16-booking-links-show-localized-visibility-badges.md +6 -0
  15. package/corpus/templates/chat/app/components/layout/Sidebar.tsx +62 -101
  16. package/corpus/templates/clips/CHANGELOG.md +7 -20
  17. package/corpus/templates/clips/app/components/meetings/share-meeting-dialog.tsx +20 -77
  18. package/corpus/templates/clips/app/components/player/share-dialog.tsx +63 -90
  19. package/corpus/templates/clips/app/components/sharing/share-test-support.tsx +180 -0
  20. package/corpus/templates/clips/app/components/sharing/share-ui.tsx +5 -584
  21. package/corpus/templates/clips/changelog/2026-10-16-share-dialogs-make-it-easier-to-see-who-has-access-and-manag.md +6 -0
  22. package/corpus/templates/clips/changelog/2026-10-21-share-panels-describe-general-access-in-a-sentence.md +6 -0
  23. package/corpus/templates/community/qr/package.json +1 -1
  24. package/corpus/templates/content/CHANGELOG.md +7 -0
  25. package/corpus/templates/content/app/components/editor/CommentStack.tsx +301 -0
  26. package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +770 -179
  27. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +75 -27
  28. package/corpus/templates/content/app/components/editor/SuggestionText.tsx +18 -3
  29. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +89 -22
  30. package/corpus/templates/content/app/components/editor/block-field-comment-threads.ts +15 -0
  31. package/corpus/templates/content/app/components/editor/block-field-comments.tsx +5 -9
  32. package/corpus/templates/content/app/components/editor/comment-stacks.ts +143 -0
  33. package/corpus/templates/content/app/components/editor/extensions/SuggestionHighlight.ts +383 -34
  34. package/corpus/templates/content/app/components/editor/suggestions/draft-session.ts +31 -0
  35. package/corpus/templates/content/app/components/editor/suggestions/presentation-rebase.ts +82 -4
  36. package/corpus/templates/content/app/global.css +31 -0
  37. package/corpus/templates/content/app/i18n-data.ts +8 -0
  38. package/corpus/templates/content/changelog/2026-10-16-accepting-a-suggestion-on-a-page-that-changed-since-it-was-s.md +6 -0
  39. package/corpus/templates/content/changelog/2026-10-20-suggested-edits-that-add-remove-split-or-merge-paragraphs-li.md +6 -0
  40. package/corpus/templates/content/changelog/2026-10-22-blocks-with-several-comment-threads-fold-into-one-stack-in-t.md +6 -0
  41. package/corpus/templates/content/changelog/2026-10-22-opening-a-page-no-longer-saves-an-edit-again-after-another-o.md +6 -0
  42. package/corpus/templates/content/docs/product/capabilities/content.comment.page-owned.md +8 -1
  43. package/corpus/templates/content/shared/suggestion-diff.ts +23 -6
  44. package/corpus/templates/content/shared/suggestion-formatting.ts +44 -14
  45. package/corpus/templates/content/shared/suggestion-text.ts +20 -5
  46. package/corpus/templates/design/.generated/bridge/interact-links.generated.ts +8 -8
  47. package/corpus/templates/design/CHANGELOG.md +8 -0
  48. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +18 -1
  49. package/corpus/templates/design/app/components/design/bridge/interact-links.bridge.ts +8 -8
  50. package/corpus/templates/design/app/components/editor/PromptDialog.tsx +15 -13
  51. package/corpus/templates/design/app/i18n/en-US.ts +1 -1
  52. package/corpus/templates/design/app/pages/DesignSystems.tsx +1 -1
  53. package/corpus/templates/design/app/pages/Index.tsx +18 -64
  54. package/corpus/templates/design/app/pages/design-editor/domains/use-editor-modes.ts +15 -6
  55. package/corpus/templates/design/app/pages/design-editor/domains/use-editor-screen-rendering.tsx +29 -5
  56. package/corpus/templates/design/app/pages/design-editor/prototype-navigation-transition.ts +423 -0
  57. package/corpus/templates/design/app/pages/design-editor/view/single-screen-canvas.tsx +35 -9
  58. package/corpus/templates/design/changelog/2026-10-16-design-pages-use-localized-visibility-badges-and-a-shared-ow.md +6 -0
  59. package/corpus/templates/design/changelog/2026-10-20-the-design-full-app-toggle-on-the-home-page-no-longer-hides-.md +6 -0
  60. package/corpus/templates/design/changelog/2026-10-22-prototype-links-can-play-code-authored-screen-transitions.md +6 -0
  61. package/corpus/templates/design/shared/motion-authored-css-import.ts +938 -7
  62. package/corpus/templates/design/shared/motion-html-source.ts +1689 -135
  63. package/corpus/templates/design/shared/motion-prototype-transition.ts +329 -0
  64. package/corpus/templates/design/shared/motion-react-source.ts +2154 -126
  65. package/corpus/templates/design/shared/motion-text-runtime-v1.fixture.txt +221 -0
  66. package/corpus/templates/dispatch/CHANGELOG.md +4 -0
  67. package/corpus/templates/dispatch/app/i18n-data.ts +75 -0
  68. package/corpus/templates/dispatch/changelog/2026-10-21-owned-and-shared-chats-appear-in-separate-history-sections.md +6 -0
  69. package/corpus/templates/factory/app/components/layout/Sidebar.tsx +34 -100
  70. package/corpus/templates/forms/app/pages/FormsListPage.tsx +1 -1
  71. package/corpus/templates/plan/CHANGELOG.md +6 -0
  72. package/corpus/templates/plan/app/components/layout/Sidebar.tsx +34 -109
  73. package/corpus/templates/plan/changelog/2026-10-16-owned-and-shared-chats-appear-in-separate-history-sections.md +6 -0
  74. package/corpus/templates/slides/CHANGELOG.md +1 -0
  75. package/corpus/templates/slides/app/components/deck/DeckCard.tsx +1 -1
  76. package/corpus/templates/slides/app/components/design-system/DesignSystemCard.tsx +1 -1
  77. package/corpus/templates/slides/app/pages/Index.tsx +7 -2
  78. package/corpus/templates/slides/changelog/2026-10-16-deck-lists-use-the-shared-ownership-filter-and-visibility-ba.md +6 -0
  79. package/package.json +1 -1
  80. package/corpus/templates/chat/app/lib/sidebar-thread-state.ts +0 -29
  81. package/corpus/templates/slides/app/components/deck/DeckFilterMenu.tsx +0 -83
@@ -1,91 +1,14 @@
1
1
  import { trackEvent } from "@agent-native/core/client/analytics";
2
- import {
3
- useActionMutation,
4
- useActionQuery,
5
- } from "@agent-native/core/client/hooks";
6
2
  import { useT } from "@agent-native/core/client/i18n";
7
- import { useOrg } from "@agent-native/core/client/org";
3
+ import type { ShareButtonVisibility } from "@agent-native/core/client/sharing/useShareButtonController";
8
4
  import { writeClipboardText } from "@agent-native/toolkit/clipboard";
9
- import {
10
- IconCheck,
11
- IconChevronDown,
12
- IconLink,
13
- IconLock,
14
- IconSend2,
15
- IconTrash,
16
- IconUsersGroup,
17
- IconWorld,
18
- } from "@tabler/icons-react";
19
- import { useQueryClient } from "@tanstack/react-query";
20
- import { useEffect, useMemo, useRef, useState, type ReactNode } from "react";
5
+ import { IconCheck, IconLink } from "@tabler/icons-react";
6
+ import { useEffect, useRef, useState, type ReactNode } from "react";
21
7
 
22
- import {
23
- Avatar as UserAvatar,
24
- AvatarFallback as UserAvatarFallback,
25
- AvatarImage as UserAvatarImage,
26
- } from "@/components/ui/avatar";
27
8
  import { Button } from "@/components/ui/button";
28
- import { Checkbox } from "@/components/ui/checkbox";
29
- import {
30
- Collapsible,
31
- CollapsibleContent,
32
- CollapsibleTrigger,
33
- } from "@/components/ui/collapsible";
34
- import { Input } from "@/components/ui/input";
35
- import {
36
- Select,
37
- SelectContent,
38
- SelectItem,
39
- SelectTrigger,
40
- SelectValue,
41
- } from "@/components/ui/select";
42
- import { useVisibleAvatarUrl } from "@/lib/use-visible-avatar-url";
43
9
  import { cn } from "@/lib/utils";
44
10
 
45
- export type Visibility = "private" | "org" | "public";
46
- export type Role = "viewer" | "commenter" | "editor" | "admin";
47
- export type ShareSettingsView = "people" | null;
48
-
49
- export interface RoleCopy {
50
- label: string;
51
- description: string;
52
- }
53
-
54
- export interface Share {
55
- id: string;
56
- principalType: "user" | "org";
57
- principalId: string;
58
- role: Role;
59
- }
60
-
61
- export interface SharesResponse {
62
- ownerEmail: string | null;
63
- orgId: string | null;
64
- visibility: Visibility | null;
65
- role?: "owner" | Role;
66
- shares: Share[];
67
- }
68
-
69
- export type SharesQuery = ReturnType<typeof useActionQuery<SharesResponse>>;
70
-
71
- export const VIS_META: Record<Visibility, { Icon: typeof IconLock }> = {
72
- private: {
73
- Icon: IconLock,
74
- },
75
- org: {
76
- Icon: IconUsersGroup,
77
- },
78
- public: {
79
- Icon: IconWorld,
80
- },
81
- };
82
-
83
- export const ROLE_OPTIONS: Array<{ value: Role; label: string }> = [
84
- { value: "viewer", label: "Viewer" },
85
- { value: "commenter", label: "Commenter" },
86
- { value: "editor", label: "Editor" },
87
- { value: "admin", label: "Admin" },
88
- ];
11
+ export type Visibility = ShareButtonVisibility;
89
12
 
90
13
  export function copyToClipboard(value: string): Promise<boolean> {
91
14
  return writeClipboardText(value);
@@ -98,6 +21,7 @@ const NESTED_LAYER_SELECTOR = [
98
21
  "[role='menu']",
99
22
  "[role='listbox']",
100
23
  "[data-sonner-toaster]",
24
+ "[data-agent-native-share-overlay]",
101
25
  ].join(",");
102
26
 
103
27
  const OPEN_NESTED_LAYER_SELECTOR = [
@@ -135,51 +59,6 @@ export function nestedLayerDismissGuards(): {
135
59
  };
136
60
  }
137
61
 
138
- export function useResourceVisibilityMutation(
139
- resourceType: string,
140
- resourceId: string,
141
- sharesQuery: SharesQuery,
142
- ) {
143
- const queryClient = useQueryClient();
144
- const setVisibility = useActionMutation("set-resource-visibility");
145
- const shareQueryKey = useMemo(
146
- () =>
147
- ["action", "list-resource-shares", { resourceType, resourceId }] as const,
148
- [resourceType, resourceId],
149
- );
150
-
151
- const setResourceVisibility = (
152
- next: Visibility,
153
- options?: { onSuccess?: () => void },
154
- ) => {
155
- const previous = queryClient.getQueryData<SharesResponse>(shareQueryKey);
156
- queryClient.setQueryData<SharesResponse>(shareQueryKey, (current) =>
157
- current ? { ...current, visibility: next } : current,
158
- );
159
- setVisibility.mutate(
160
- {
161
- resourceType,
162
- resourceId,
163
- visibility: next,
164
- } as any,
165
- {
166
- onSuccess: () => {
167
- void sharesQuery.refetch().finally(() => options?.onSuccess?.());
168
- },
169
- onError: () => {
170
- if (previous) {
171
- queryClient.setQueryData(shareQueryKey, previous);
172
- } else {
173
- void queryClient.invalidateQueries({ queryKey: shareQueryKey });
174
- }
175
- },
176
- },
177
- );
178
- };
179
-
180
- return { setResourceVisibility, isPending: setVisibility.isPending };
181
- }
182
-
183
62
  export function ShareSectionLabel({
184
63
  children,
185
64
  className,
@@ -268,461 +147,3 @@ export function CopyButton({
268
147
  </Button>
269
148
  );
270
149
  }
271
-
272
- export function Avatar({ label, org }: { label: string; org?: boolean }) {
273
- const { avatarRef, avatarUrl } = useVisibleAvatarUrl(org ? null : label);
274
-
275
- if (org) {
276
- return (
277
- <span
278
- aria-hidden
279
- className="inline-flex size-6 shrink-0 items-center justify-center rounded-full bg-muted text-[10px] font-semibold text-muted-foreground dark:bg-background"
280
- >
281
- <IconUsersGroup size={12} strokeWidth={1.75} />
282
- </span>
283
- );
284
- }
285
-
286
- return (
287
- <UserAvatar ref={avatarRef} className="size-6 shrink-0">
288
- {avatarUrl ? <UserAvatarImage src={avatarUrl} alt={label} /> : null}
289
- <UserAvatarFallback className="bg-muted text-[10px] font-semibold text-muted-foreground dark:bg-background">
290
- {(label.split("@")[0]?.[0] ?? label[0] ?? "?").toUpperCase()}
291
- </UserAvatarFallback>
292
- </UserAvatar>
293
- );
294
- }
295
-
296
- export function AccessAccordionRow({
297
- icon,
298
- label,
299
- meta,
300
- disabled,
301
- open,
302
- onOpenChange,
303
- children,
304
- }: {
305
- icon: ReactNode;
306
- label: ReactNode;
307
- meta?: ReactNode;
308
- disabled?: boolean;
309
- open: boolean;
310
- onOpenChange: (open: boolean) => void;
311
- children?: ReactNode;
312
- }) {
313
- const summary = (
314
- <>
315
- {icon}
316
- <span className="min-w-0 flex-1 truncate text-sm">{label}</span>
317
- {meta ? (
318
- <span className="shrink-0 truncate text-xs text-muted-foreground">
319
- {meta}
320
- </span>
321
- ) : null}
322
- </>
323
- );
324
-
325
- if (!children) {
326
- return (
327
- <div className="flex min-h-8 w-full items-center gap-2 px-1.5 py-1">
328
- {summary}
329
- </div>
330
- );
331
- }
332
-
333
- return (
334
- <Collapsible open={open} onOpenChange={onOpenChange}>
335
- <CollapsibleTrigger
336
- disabled={disabled}
337
- className="flex min-h-8 w-full cursor-pointer items-center gap-2 rounded-md px-1.5 py-1 text-start transition-colors hover:bg-muted/50 disabled:pointer-events-none disabled:opacity-50"
338
- >
339
- {summary}
340
- {/* Sized like the row-level remove button so both trailing icons
341
- share the same optical center. */}
342
- <span className="flex size-6 shrink-0 items-center justify-center">
343
- <IconChevronDown
344
- aria-hidden
345
- className={cn(
346
- "size-3.5 opacity-50 transition-transform",
347
- open && "rotate-180",
348
- )}
349
- />
350
- </span>
351
- </CollapsibleTrigger>
352
- <CollapsibleContent className="clips-collapsible-content">
353
- <div className="pt-1">{children}</div>
354
- </CollapsibleContent>
355
- </Collapsible>
356
- );
357
- }
358
-
359
- const ACCESS_ORDER: Visibility[] = ["public", "org", "private"];
360
-
361
- export function GeneralAccessSelect({
362
- visibility,
363
- canManage,
364
- isPending,
365
- onChange,
366
- }: {
367
- visibility: Visibility;
368
- canManage: boolean;
369
- isPending: boolean;
370
- onChange: (next: Visibility) => void;
371
- }) {
372
- const t = useT();
373
- const { data: org } = useOrg();
374
- const orgName = org?.orgName;
375
- const meta = VIS_META[visibility];
376
-
377
- const optionLabel = (value: Visibility) => {
378
- if (value !== "org") return t(`shareUi.accessOptions.${value}`);
379
- return orgName
380
- ? t("shareUi.accessOptions.org", { orgName })
381
- : t("shareUi.accessOptions.orgFallback");
382
- };
383
-
384
- return (
385
- <Select
386
- value={visibility}
387
- onValueChange={(v) => onChange(v as Visibility)}
388
- disabled={!canManage || isPending}
389
- >
390
- {/* The trigger spans the whole row so the hover affordance matches the
391
- hit area. The icon is a bare svg, not a wrapped span, because the
392
- shared trigger applies line-clamp to every direct span child. */}
393
- <SelectTrigger
394
- size="sm"
395
- aria-label={t("shareUi.selectAccess")}
396
- className="w-full cursor-pointer justify-start gap-2 rounded-md border-0 bg-transparent px-1.5 py-1 text-sm shadow-none transition-colors hover:bg-muted/50 focus:ring-0 focus:ring-offset-0 [&>span]:flex-1 [&>span]:text-start [&>svg:last-child]:me-1"
397
- >
398
- <meta.Icon
399
- aria-hidden
400
- strokeWidth={1.75}
401
- className="size-6 shrink-0 rounded-full bg-muted p-1.5 text-muted-foreground"
402
- />
403
- <SelectValue>{optionLabel(visibility)}</SelectValue>
404
- </SelectTrigger>
405
- <SelectContent align="start">
406
- {ACCESS_ORDER.map((value) => {
407
- const OptionIcon = VIS_META[value].Icon;
408
- return (
409
- <SelectItem
410
- key={value}
411
- value={value}
412
- className="ps-2 pe-8 [&>span:first-child]:start-auto [&>span:first-child]:end-2"
413
- >
414
- <span className="flex items-center gap-2">
415
- <OptionIcon
416
- aria-hidden
417
- size={16}
418
- strokeWidth={1.75}
419
- className="shrink-0 text-muted-foreground"
420
- />
421
- {optionLabel(value)}
422
- </span>
423
- </SelectItem>
424
- );
425
- })}
426
- </SelectContent>
427
- </Select>
428
- );
429
- }
430
-
431
- export function InvitePeopleField({
432
- resourceType,
433
- resourceId,
434
- resourceUrl,
435
- sharesQuery,
436
- onError,
437
- }: {
438
- resourceType: string;
439
- resourceId: string;
440
- resourceUrl?: string;
441
- sharesQuery: SharesQuery;
442
- onError?: (err: unknown) => void;
443
- }) {
444
- const t = useT();
445
- const share = useActionMutation("share-resource");
446
- const [email, setEmail] = useState("");
447
- const [role, setRole] = useState<Role>("viewer");
448
- const [notifyPeople, setNotifyPeople] = useState(true);
449
- const hasInviteEmail = email.trim().length > 0;
450
-
451
- const handleAdd = () => {
452
- const trimmed = email.trim().toLowerCase();
453
- if (!trimmed) return;
454
- share.mutate(
455
- {
456
- resourceType,
457
- resourceId,
458
- principalType: "user",
459
- principalId: trimmed,
460
- role,
461
- notify: notifyPeople,
462
- ...(resourceUrl ? { resourceUrl } : {}),
463
- } as any,
464
- {
465
- onSuccess: () => {
466
- setEmail("");
467
- void sharesQuery.refetch();
468
- },
469
- onError: (err: unknown) => onError?.(err),
470
- },
471
- );
472
- };
473
-
474
- return (
475
- <div className="space-y-2">
476
- <div className="flex items-stretch gap-1.5">
477
- <div className="flex h-8 min-w-0 flex-1 items-center overflow-hidden rounded-md border border-input bg-background focus-within:ring-1 focus-within:ring-ring">
478
- <Input
479
- type="email"
480
- placeholder={t("shareUi.addPeopleByEmail")}
481
- value={email}
482
- onChange={(e) => setEmail(e.target.value)}
483
- onKeyDown={(e) => {
484
- if (e.key === "Enter") handleAdd();
485
- }}
486
- autoComplete="off"
487
- className="h-full min-w-0 flex-1 rounded-none border-0 bg-transparent text-sm shadow-none focus-visible:ring-0 focus-visible:ring-offset-0"
488
- />
489
- <Select value={role} onValueChange={(v) => setRole(v as Role)}>
490
- <SelectTrigger className="h-full w-auto shrink-0 gap-1 rounded-none border-0 bg-transparent px-2 text-xs text-muted-foreground shadow-none focus:ring-0 focus:ring-offset-0">
491
- <SelectValue>{t(`shareUi.roles.${role}`)}</SelectValue>
492
- </SelectTrigger>
493
- <SelectContent align="end">
494
- {ROLE_OPTIONS.map((opt) => (
495
- <SelectItem key={opt.value} value={opt.value}>
496
- {t(`shareUi.roles.${opt.value}`)}
497
- </SelectItem>
498
- ))}
499
- </SelectContent>
500
- </Select>
501
- </div>
502
- <Button
503
- type="button"
504
- size="icon-sm"
505
- onClick={handleAdd}
506
- disabled={!hasInviteEmail || share.isPending}
507
- aria-label={t("shareUi.invite")}
508
- title={t("shareUi.invite")}
509
- className="shrink-0"
510
- >
511
- <IconSend2 size={16} />
512
- </Button>
513
- </div>
514
- {hasInviteEmail ? (
515
- <label className="flex items-center gap-2 text-xs text-muted-foreground">
516
- <Checkbox
517
- checked={notifyPeople}
518
- onCheckedChange={(checked) => setNotifyPeople(checked === true)}
519
- />
520
- {t("shareUi.notifyPeople")}
521
- </label>
522
- ) : null}
523
- </div>
524
- );
525
- }
526
-
527
- export function PeopleAccessSection({
528
- resourceType,
529
- resourceId,
530
- sharesQuery,
531
- canManage,
532
- roleCopy,
533
- onError,
534
- }: {
535
- resourceType: string;
536
- resourceId: string;
537
- sharesQuery: SharesQuery;
538
- canManage: boolean;
539
- roleCopy?: Partial<Record<Role, RoleCopy>>;
540
- onError?: (err: unknown, action: "permission" | "remove") => void;
541
- }) {
542
- const t = useT();
543
- const data = sharesQuery.data;
544
- const people = useMemo(
545
- () => [
546
- ...(data?.ownerEmail ? [data.ownerEmail] : []),
547
- ...(data?.shares.map((s) => s.principalId) ?? []),
548
- ],
549
- [data],
550
- );
551
- const [first, ...rest] = people;
552
- const [open, setOpen] = useState(false);
553
-
554
- const firstShare = data?.shares.find((s) => s.principalId === first);
555
- const firstRole = firstShare
556
- ? (roleCopy?.[firstShare.role]?.label ??
557
- t(`shareUi.roles.${firstShare.role}`))
558
- : t("shareUi.ownerRole");
559
-
560
- return (
561
- <AccessAccordionRow
562
- icon={
563
- first ? (
564
- <Avatar label={first} />
565
- ) : (
566
- <span className="inline-block size-6 shrink-0 rounded-full bg-muted" />
567
- )
568
- }
569
- label={
570
- !first
571
- ? t("shareUi.onlyYou")
572
- : open || rest.length === 0
573
- ? first
574
- : t("shareUi.othersCount", { count: rest.length, email: first })
575
- }
576
- meta={
577
- first && (open || rest.length === 0)
578
- ? firstRole
579
- : t("shareUi.canAccess")
580
- }
581
- disabled={sharesQuery.isLoading}
582
- open={open}
583
- onOpenChange={setOpen}
584
- >
585
- {rest.length > 0 ? (
586
- <PeopleAccessSettingsBody
587
- resourceType={resourceType}
588
- resourceId={resourceId}
589
- sharesQuery={sharesQuery}
590
- canManage={canManage}
591
- roleCopy={roleCopy}
592
- onError={onError}
593
- excludePrincipalId={first}
594
- />
595
- ) : null}
596
- </AccessAccordionRow>
597
- );
598
- }
599
-
600
- export function PeopleAccessSettingsBody({
601
- resourceType,
602
- resourceId,
603
- sharesQuery,
604
- canManage,
605
- roleCopy,
606
- onError,
607
- excludePrincipalId,
608
- }: {
609
- resourceType: string;
610
- resourceId: string;
611
- sharesQuery: SharesQuery;
612
- canManage: boolean;
613
- roleCopy?: Partial<Record<Role, RoleCopy>>;
614
- onError?: (err: unknown, action: "permission" | "remove") => void;
615
- excludePrincipalId?: string;
616
- }) {
617
- const t = useT();
618
- const share = useActionMutation("share-resource");
619
- const unshare = useActionMutation("unshare-resource");
620
- const data = sharesQuery.data;
621
- const ownerEmail = data?.ownerEmail ?? null;
622
- const showOwner = ownerEmail !== null && ownerEmail !== excludePrincipalId;
623
- const shares = (data?.shares ?? []).filter(
624
- (s) => s.principalId !== excludePrincipalId,
625
- );
626
- const getRoleLabel = (value: Role) =>
627
- roleCopy?.[value]?.label ?? t(`shareUi.roles.${value}`);
628
-
629
- const handleChangeRole = (s: Share, nextRole: Role) => {
630
- if (nextRole === s.role) return;
631
- share.mutate(
632
- {
633
- resourceType,
634
- resourceId,
635
- principalType: s.principalType,
636
- principalId: s.principalId,
637
- role: nextRole,
638
- notify: false,
639
- } as any,
640
- {
641
- onSuccess: () => sharesQuery.refetch(),
642
- onError: (err: unknown) => onError?.(err, "permission"),
643
- },
644
- );
645
- };
646
-
647
- const handleRemove = (s: Share) => {
648
- unshare.mutate(
649
- {
650
- resourceType,
651
- resourceId,
652
- principalType: s.principalType,
653
- principalId: s.principalId,
654
- } as any,
655
- {
656
- onSuccess: () => sharesQuery.refetch(),
657
- onError: (err: unknown) => onError?.(err, "remove"),
658
- },
659
- );
660
- };
661
-
662
- return (
663
- <ul className="flex max-h-72 flex-col gap-1 overflow-y-auto p-0 m-0">
664
- {showOwner ? (
665
- <li className="flex min-h-8 items-center gap-2 px-1.5 py-1 text-sm">
666
- <Avatar label={ownerEmail} />
667
- <span className="min-w-0 flex-1 truncate">{ownerEmail}</span>
668
- <span className="text-xs text-muted-foreground">
669
- {t("shareUi.ownerRole")}
670
- </span>
671
- </li>
672
- ) : null}
673
-
674
- {shares.map((s) => (
675
- <li
676
- key={`${s.principalType}:${s.principalId}`}
677
- className="flex min-h-8 items-center gap-2 px-1.5 py-1 text-sm"
678
- >
679
- <Avatar label={s.principalId} org={s.principalType === "org"} />
680
- <span className="min-w-0 flex-1 truncate">{s.principalId}</span>
681
- {canManage ? (
682
- <Select
683
- value={s.role}
684
- onValueChange={(value) => handleChangeRole(s, value as Role)}
685
- disabled={share.isPending}
686
- >
687
- <SelectTrigger
688
- size="sm"
689
- className="w-auto shrink-0 gap-1 border-0 bg-transparent px-2 text-xs text-muted-foreground shadow-none focus:ring-0"
690
- >
691
- <SelectValue>{getRoleLabel(s.role)}</SelectValue>
692
- </SelectTrigger>
693
- <SelectContent align="end">
694
- {ROLE_OPTIONS.map((opt) => (
695
- <SelectItem key={opt.value} value={opt.value}>
696
- {getRoleLabel(opt.value)}
697
- </SelectItem>
698
- ))}
699
- </SelectContent>
700
- </Select>
701
- ) : (
702
- <span className="shrink-0 text-xs text-muted-foreground">
703
- {getRoleLabel(s.role)}
704
- </span>
705
- )}
706
- {canManage ? (
707
- <Button
708
- type="button"
709
- variant="ghost"
710
- size="icon"
711
- aria-label={t("shareUi.remove")}
712
- onClick={() => handleRemove(s)}
713
- className="h-7 w-7 shrink-0 text-muted-foreground"
714
- >
715
- <IconTrash size={14} />
716
- </Button>
717
- ) : null}
718
- </li>
719
- ))}
720
-
721
- {!shares.length && !showOwner ? (
722
- <li className="px-1 py-1.5 text-sm text-muted-foreground">
723
- {t("shareUi.noAccessYet")}
724
- </li>
725
- ) : null}
726
- </ul>
727
- );
728
- }
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-10-16
4
+ ---
5
+
6
+ Share dialogs make it easier to see who has access and manage common sharing changes.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-10-21
4
+ ---
5
+
6
+ Share panels describe general access in a sentence and no longer open an empty people list when you click into them.
@@ -11,7 +11,7 @@
11
11
  "typecheck": "tsc --noEmit"
12
12
  },
13
13
  "dependencies": {
14
- "@agent-native/core": "^0.217.0",
14
+ "@agent-native/core": "^0.219.0",
15
15
  "postgres": "^3.4.9",
16
16
  "qrcode": "^1.5.4",
17
17
  "zod": "^4.6.7"
@@ -5,6 +5,10 @@ time from the command menu (Cmd+K → "What's new").
5
5
 
6
6
  ## 2026-10-22
7
7
 
8
+ ### Added
9
+
10
+ - Blocks with several comment threads fold into one stack in the margin, which opens in place when you click it or a highlight
11
+
8
12
  ### Improved
9
13
 
10
14
  - A collection linked to a big GitHub folder fills faster, and pushes to other linked collections aren't held up while it does
@@ -13,6 +17,7 @@ time from the command menu (Cmd+K → "What's new").
13
17
 
14
18
  ### Fixed
15
19
 
20
+ - Opening a page no longer saves an edit again after another open tab's save already stored it.
16
21
  - Changes made from a menu or popover, such as an image's alt text, are always saved.
17
22
  - Agents can read saved page titles and text after an unrelated icon change fails.
18
23
  - Text an agent writes inside a callout, column or synced block without indenting it now shows on the page and survives your edits.
@@ -42,6 +47,7 @@ time from the command menu (Cmd+K → "What's new").
42
47
 
43
48
  ### Fixed
44
49
 
50
+ - Suggested edits that add, remove, split, or merge paragraphs, line breaks, and list items now show where the break goes and save as one clear suggestion
45
51
  - Builder content sync keeps read and write status accurate when API responses fail.
46
52
  - HTML table cells preserve a trailing backslash when opening Markdown.
47
53
  - Preserve empty paragraph colors and refuse unsupported inline image and oversized math formatting before content is stored.
@@ -109,6 +115,7 @@ time from the command menu (Cmd+K → "What's new").
109
115
 
110
116
  ### Fixed
111
117
 
118
+ - Accepting a suggestion on a Page that changed since it was suggested no longer shows "Something went wrong"
112
119
  - A page open in a tab that shows it's in the trash reopens within seconds when someone restores it.
113
120
  - A page restored from Trash returns to its place in its collection, and edits to a page in Trash are refused
114
121
  - Agent-written Markdown keeps nested lists indented with spaces, underscore bold and italics, and escaped punctuation