@agent-native/core-corpus 0.216.0 → 0.218.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.
- package/corpus/README.md +1 -1
- package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +61 -1
- package/corpus/templates/analytics/AGENTS.md +1 -1
- package/corpus/templates/analytics/CHANGELOG.md +3 -0
- package/corpus/templates/analytics/actions/get-session-replay-summary.ts +9 -65
- package/corpus/templates/analytics/actions/list-event-catalog.ts +1 -1
- package/corpus/templates/analytics/actions/list-session-outputs.ts +40 -0
- package/corpus/templates/analytics/actions/list-session-recordings.ts +17 -4
- package/corpus/templates/analytics/actions/view-screen.ts +29 -3
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +38 -167
- package/corpus/templates/analytics/app/i18n/en-US.ts +27 -0
- package/corpus/templates/analytics/app/pages/sessions/EventCatalogPage.tsx +18 -1
- package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +3 -1
- package/corpus/templates/analytics/app/pages/sessions/SessionOutputs.tsx +197 -0
- package/corpus/templates/analytics/app/pages/sessions/SessionsTriagePage.tsx +114 -4
- package/corpus/templates/analytics/changelog/2026-10-15-sessions-can-find-people-who-made-a-slides-deck-but-didn-t-p.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-16-owned-and-shared-chats-appear-in-separate-history-sections.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-16-the-activation-funnel-counts-visitors-who-decline-analytics-.md +6 -0
- package/corpus/templates/analytics/server/db/schema.ts +67 -0
- package/corpus/templates/analytics/server/lib/canonical-first-party-dashboard-repair.ts +9 -0
- package/corpus/templates/analytics/server/lib/first-party-analytics.ts +28 -22
- package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +36 -1
- package/corpus/templates/analytics/server/lib/output-outcomes.ts +712 -0
- package/corpus/templates/analytics/server/lib/replay-origins.ts +33 -0
- package/corpus/templates/analytics/server/lib/session-event-index.ts +20 -0
- package/corpus/templates/analytics/server/lib/session-performance.ts +6 -40
- package/corpus/templates/analytics/server/lib/session-recording-associations.ts +30 -1
- package/corpus/templates/analytics/server/lib/session-replay-triage.ts +99 -0
- package/corpus/templates/analytics/server/lib/session-replay.ts +45 -31
- package/corpus/templates/analytics/server/lib/sessions-triage-lab.ts +8 -2
- package/corpus/templates/analytics/server/plugins/db.ts +46 -0
- package/corpus/templates/analytics/shared/output-use.ts +205 -0
- package/corpus/templates/analytics/shared/session-events.ts +6 -0
- package/corpus/templates/assets/CHANGELOG.md +6 -0
- package/corpus/templates/assets/app/components/layout/Sidebar.tsx +51 -117
- package/corpus/templates/assets/changelog/2026-10-16-owned-and-shared-chats-appear-in-separate-history-sections.md +6 -0
- package/corpus/templates/brain/app/components/layout/Sidebar.tsx +34 -101
- package/corpus/templates/brain/app/routes/sources.tsx +1 -1
- package/corpus/templates/brain/vitest.config.ts +7 -0
- package/corpus/templates/calendar/CHANGELOG.md +12 -0
- package/corpus/templates/calendar/actions/create-event.ts +2 -4
- package/corpus/templates/calendar/actions/update-event.ts +35 -6
- package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +1 -1
- package/corpus/templates/calendar/changelog/2026-10-16-booking-links-show-localized-visibility-badges.md +6 -0
- package/corpus/templates/calendar/changelog/2026-10-21-solo-events-avoid-notification-approval.md +6 -0
- package/corpus/templates/chat/app/components/layout/Sidebar.tsx +62 -101
- package/corpus/templates/clips/CHANGELOG.md +7 -20
- package/corpus/templates/clips/app/components/meetings/share-meeting-dialog.tsx +20 -77
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +63 -90
- package/corpus/templates/clips/app/components/sharing/share-test-support.tsx +180 -0
- package/corpus/templates/clips/app/components/sharing/share-ui.tsx +5 -584
- package/corpus/templates/clips/changelog/2026-10-16-share-dialogs-make-it-easier-to-see-who-has-access-and-manag.md +6 -0
- package/corpus/templates/clips/changelog/2026-10-21-share-panels-describe-general-access-in-a-sentence.md +6 -0
- package/corpus/templates/community/qr/package.json +1 -1
- package/corpus/templates/content/.agents/skills/document-editing/references/databases.md +6 -3
- package/corpus/templates/content/CHANGELOG.md +10 -0
- package/corpus/templates/content/actions/_browser-document-save-attempt.ts +23 -1
- package/corpus/templates/content/actions/_builder-cms-blob-custody.ts +467 -0
- package/corpus/templates/content/actions/_builder-cms-execution-lease.ts +17 -0
- package/corpus/templates/content/actions/_builder-cms-intent-lookup.ts +5 -0
- package/corpus/templates/content/actions/_builder-cms-read-client.ts +881 -21
- package/corpus/templates/content/actions/_builder-cms-write-adapter.ts +28 -2
- package/corpus/templates/content/actions/_builder-cms-write-client.ts +109 -11
- package/corpus/templates/content/actions/_builder-docs-client.ts +1 -0
- package/corpus/templates/content/actions/_builder-private-blob.test-fixture.ts +90 -0
- package/corpus/templates/content/actions/_builder-source-execution-guard.ts +48 -0
- package/corpus/templates/content/actions/_content-save-outcomes.ts +5 -0
- package/corpus/templates/content/actions/_database-source-utils.ts +290 -8
- package/corpus/templates/content/actions/_database-utils.ts +93 -7
- package/corpus/templates/content/actions/_database-view-setup.ts +21 -2
- package/corpus/templates/content/actions/_delete-content-space.ts +8 -1
- package/corpus/templates/content/actions/_github-source-freshness.ts +30 -4
- package/corpus/templates/content/actions/add-content-database-source-field-property.ts +23 -6
- package/corpus/templates/content/actions/cancel-prepared-builder-source-update.ts +110 -26
- package/corpus/templates/content/actions/change-content-database-source-role.ts +51 -7
- package/corpus/templates/content/actions/delete-document.ts +41 -3
- package/corpus/templates/content/actions/disconnect-content-database-source.ts +68 -2
- package/corpus/templates/content/actions/disconnect-local-folder-source.ts +33 -2
- package/corpus/templates/content/actions/execute-builder-source-execution.ts +900 -140
- package/corpus/templates/content/actions/get-document.ts +2 -0
- package/corpus/templates/content/actions/permanently-delete-document.ts +6 -0
- package/corpus/templates/content/actions/prepare-builder-source-execution.ts +155 -13
- package/corpus/templates/content/actions/prepare-builder-source-review.ts +299 -73
- package/corpus/templates/content/actions/set-live-body-switch.ts +45 -0
- package/corpus/templates/content/actions/update-document.ts +139 -20
- package/corpus/templates/content/actions/validate-builder-source-execution.ts +135 -26
- package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +119 -30
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +56 -13
- package/corpus/templates/content/app/components/editor/SuggestionText.tsx +18 -3
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +89 -22
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +23 -0
- package/corpus/templates/content/app/components/editor/database/GitHubSourceStatusLine.tsx +27 -2
- package/corpus/templates/content/app/components/editor/database/ListItemLayout.tsx +32 -8
- package/corpus/templates/content/app/components/editor/database/ListItemRow.tsx +27 -2
- package/corpus/templates/content/app/components/editor/database-sources/BuilderSourceReviewDialog.tsx +1 -0
- package/corpus/templates/content/app/components/editor/extensions/SuggestionHighlight.ts +383 -34
- package/corpus/templates/content/app/components/editor/suggestions/draft-session.ts +31 -0
- package/corpus/templates/content/app/components/editor/suggestions/presentation-rebase.ts +82 -4
- package/corpus/templates/content/app/global.css +31 -0
- package/corpus/templates/content/app/hooks/use-content-database.ts +40 -2
- package/corpus/templates/content/app/hooks/use-documents.ts +14 -0
- package/corpus/templates/content/app/i18n-data.ts +1 -0
- package/corpus/templates/content/changelog/2026-10-16-accepting-a-suggestion-on-a-page-that-changed-since-it-was-s.md +6 -0
- package/corpus/templates/content/changelog/2026-10-17-builder-content-avoids-retrying-a-write-when-builder-accepte.md +6 -0
- package/corpus/templates/content/changelog/2026-10-18-link-a-new-collection-to-a-github-folder-from-its-sources.md +1 -1
- package/corpus/templates/content/changelog/2026-10-20-builder-content-sync-keeps-read-and-write-status-accurate-wh.md +6 -0
- package/corpus/templates/content/changelog/2026-10-20-suggested-edits-that-add-remove-split-or-merge-paragraphs-li.md +6 -0
- package/corpus/templates/content/changelog/2026-10-21-list-rows-can-show-each-pages-first-lines.md +6 -0
- package/corpus/templates/content/changelog/2026-10-22-a-collection-linked-to-a-big-github-folder-fills-faster-and-p.md +6 -0
- package/corpus/templates/content/docs/product/capabilities/content.source.adapters.md +29 -0
- package/corpus/templates/content/parity/matrix.md +42 -42
- package/corpus/templates/content/parity/matrix.ts +3 -1
- package/corpus/templates/content/server/db/schema.ts +20 -0
- package/corpus/templates/content/server/lib/content-trash-purge.ts +15 -1
- package/corpus/templates/content/server/lib/live-body-writer.ts +138 -26
- package/corpus/templates/content/server/live-copy/binding.ts +31 -0
- package/corpus/templates/content/server/live-copy/bounded-task.d.ts +6 -0
- package/corpus/templates/content/server/live-copy/exec-db.ts +1 -0
- package/corpus/templates/content/server/live-copy/holds.ts +59 -0
- package/corpus/templates/content/server/live-copy/persist.ts +656 -0
- package/corpus/templates/content/server/live-copy/switch.ts +136 -0
- package/corpus/templates/content/server/live-copy/task-names.ts +4 -0
- package/corpus/templates/content/server/live-copy/tasks.ts +183 -0
- package/corpus/templates/content/server/live-copy/write-cause.ts +85 -0
- package/corpus/templates/content/server/live-copy/write.ts +85 -0
- package/corpus/templates/content/server/plugins/builder-private-payload-cleanup.ts +22 -0
- package/corpus/templates/content/server/plugins/collab.ts +3 -0
- package/corpus/templates/content/server/plugins/db.ts +28 -0
- package/corpus/templates/content/shared/api.ts +30 -1
- package/corpus/templates/content/shared/database-item-layout.ts +54 -22
- package/corpus/templates/content/shared/document-intent-merge.ts +16 -9
- package/corpus/templates/content/shared/og-description.ts +258 -36
- package/corpus/templates/content/shared/suggestion-diff.ts +23 -6
- package/corpus/templates/content/shared/suggestion-formatting.ts +44 -14
- package/corpus/templates/content/shared/suggestion-text.ts +20 -5
- package/corpus/templates/design/.agents/skills/design-templates/SKILL.md +5 -1
- package/corpus/templates/design/.generated/bridge/shader-runtime.generated.ts +25 -4
- package/corpus/templates/design/CHANGELOG.md +5 -0
- package/corpus/templates/design/actions/get-getting-started.ts +53 -0
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +10 -0
- package/corpus/templates/design/app/components/design/bridge/native-shader-runtime.bridge.ts +132 -79
- package/corpus/templates/design/app/components/design/bridge/native-source-provider.ts +268 -130
- package/corpus/templates/design/app/components/design/bridge/native-worker-scene-host.ts +122 -42
- package/corpus/templates/design/app/components/design/bridge/shader-runtime.bridge.ts +41 -4
- package/corpus/templates/design/app/components/design/design-canvas/text-content-change-result.ts +46 -12
- package/corpus/templates/design/app/components/home/DesignHomeLibrary.tsx +15 -8
- package/corpus/templates/design/app/components/layout/GettingStarted.tsx +50 -0
- package/corpus/templates/design/app/components/layout/Sidebar.tsx +3 -0
- package/corpus/templates/design/app/components/templates/DesignTemplateGrid.tsx +163 -0
- package/corpus/templates/design/app/components/templates/DesignTemplateLibrary.tsx +65 -78
- package/corpus/templates/design/app/components/templates/TemplatePreview.tsx +6 -2
- package/corpus/templates/design/app/global.css +6 -0
- package/corpus/templates/design/app/i18n/en-US.ts +17 -0
- package/corpus/templates/design/app/lib/canvas-load-outcome.ts +147 -0
- package/corpus/templates/design/app/lib/canvas-outcome-tracking.ts +531 -0
- package/corpus/templates/design/app/lib/canvas-style-kind.ts +50 -0
- package/corpus/templates/design/app/pages/DesignSystems.tsx +1 -1
- package/corpus/templates/design/app/pages/Index.tsx +28 -67
- package/corpus/templates/design/app/pages/design-editor/commands/commit-visual-styles.ts +86 -29
- package/corpus/templates/design/app/pages/design-editor/commands/save-file-content.ts +38 -3
- package/corpus/templates/design/app/pages/design-editor/domains/use-editor-core.ts +9 -0
- package/corpus/templates/design/app/pages/design-editor/domains/use-editor-files-and-saving.ts +2 -0
- package/corpus/templates/design/changelog/2026-10-16-design-pages-use-localized-visibility-badges-and-a-shared-ow.md +6 -0
- package/corpus/templates/design/changelog/2026-10-21-a-getting-started-checklist-at-the-bottom-of-the-sidebar-tra.md +6 -0
- package/corpus/templates/design/changelog/2026-10-21-templates-now-open-a-read-only-preview-from-the-card-and-the.md +6 -0
- package/corpus/templates/design/changelog/2026-10-22-collapsed-shader-layers-no-longer-interrupt-other-shaders-on.md +6 -0
- package/corpus/templates/design/changelog/2026-10-22-shader-fills-keep-rendering-when-other-screen-content-cannot.md +6 -0
- package/corpus/templates/design/scripts/design-export-validation/export-baseline.json +24 -0
- package/corpus/templates/design/server/lib/getting-started.ts +267 -0
- package/corpus/templates/design/server/plugins/getting-started.ts +5 -0
- package/corpus/templates/design/shared/design-template-presets/analytics-dashboard.ts +127 -0
- package/corpus/templates/design/shared/design-template-presets/design-studio.ts +98 -0
- package/corpus/templates/design/shared/design-template-presets/messaging-app.ts +101 -0
- package/corpus/templates/design/shared/design-template-presets/project-tasks.ts +120 -0
- package/corpus/templates/design/shared/design-template-presets.ts +8 -0
- package/corpus/templates/dispatch/CHANGELOG.md +4 -0
- package/corpus/templates/dispatch/app/i18n-data.ts +75 -0
- package/corpus/templates/dispatch/changelog/2026-10-21-owned-and-shared-chats-appear-in-separate-history-sections.md +6 -0
- package/corpus/templates/factory/app/components/layout/Sidebar.tsx +34 -100
- package/corpus/templates/forms/app/pages/FormsListPage.tsx +1 -1
- package/corpus/templates/forms/server/plugins/agent-chat.ts +1 -1
- package/corpus/templates/plan/CHANGELOG.md +6 -0
- package/corpus/templates/plan/app/components/layout/Sidebar.tsx +34 -109
- package/corpus/templates/plan/changelog/2026-10-16-owned-and-shared-chats-appear-in-separate-history-sections.md +6 -0
- package/corpus/templates/slides/CHANGELOG.md +7 -0
- package/corpus/templates/slides/actions/delete-deck.ts +7 -0
- package/corpus/templates/slides/app/components/deck/DeckCard.tsx +1 -1
- package/corpus/templates/slides/app/components/design-system/DesignSystemCard.tsx +1 -1
- package/corpus/templates/slides/app/pages/Index.tsx +7 -2
- package/corpus/templates/slides/changelog/2026-10-16-deck-lists-use-the-shared-ownership-filter-and-visibility-ba.md +6 -0
- package/corpus/templates/slides/changelog/2026-10-21-connected-deck-deletion-requires-approval.md +6 -0
- package/corpus/templates/slides/scripts/edit-fidelity/client-action.ts +61 -0
- package/corpus/templates/slides/scripts/edit-fidelity/run.ts +10 -0
- package/package.json +1 -1
- package/corpus/templates/chat/app/lib/sidebar-thread-state.ts +0 -29
- 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 {
|
|
3
|
+
import type { ShareButtonVisibility } from "@agent-native/core/client/sharing/useShareButtonController";
|
|
8
4
|
import { writeClipboardText } from "@agent-native/toolkit/clipboard";
|
|
9
|
-
import {
|
|
10
|
-
|
|
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 =
|
|
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
|
-
}
|
|
@@ -244,9 +244,12 @@ supports searching property types by label or machine name.
|
|
|
244
244
|
List views can save an item layout (`itemLayout`) that decides what each row
|
|
245
245
|
shows: a start slot (page icon, a checkbox property, a status property, or
|
|
246
246
|
nothing), the title weight, line 2 (a text, URL, or non-body Blocks property,
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
247
|
+
or with `source: "page"` the first lines of each row's page, clamped to 1-3
|
|
248
|
+
lines), and ordered property IDs for line 3 and the end of the row; end values
|
|
249
|
+
move under the title on narrow screens and empty values take no space. While
|
|
250
|
+
any list view shows page text, collection reads give each item an `excerpt`
|
|
251
|
+
with the start of its page as plain text; other collections leave page bodies
|
|
252
|
+
out of row reads. Ticking a row's checkbox writes the property through
|
|
250
253
|
`set-document-property` and strikes the title through without moving the row.
|
|
251
254
|
A view without `itemLayout` keeps the default rows. Agents update it with an
|
|
252
255
|
`update-content-database-view` `update` patch on the list view: pass
|