@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
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { Spinner } from "@agent-native/toolkit/design-system";
|
|
2
|
+
import { useId, type ReactNode } from "react";
|
|
3
|
+
|
|
4
|
+
import { Skeleton } from "@/components/ui/skeleton";
|
|
5
|
+
|
|
6
|
+
import type { DesignLibraryTemplate } from "./DesignTemplateLibrary";
|
|
7
|
+
import { TemplatePreview } from "./TemplatePreview";
|
|
8
|
+
|
|
9
|
+
const GRID_CLASS =
|
|
10
|
+
"grid gap-x-4 gap-y-3 [grid-template-columns:repeat(auto-fill,minmax(min(100%,15rem),1fr))]";
|
|
11
|
+
|
|
12
|
+
export const TEMPLATE_GRID_SKELETON_COUNT = 8;
|
|
13
|
+
|
|
14
|
+
export function DesignTemplateGrid({
|
|
15
|
+
templates,
|
|
16
|
+
loading,
|
|
17
|
+
pendingId,
|
|
18
|
+
pendingLabel,
|
|
19
|
+
disabled,
|
|
20
|
+
selectedId,
|
|
21
|
+
loadingLabel,
|
|
22
|
+
emptyLabel,
|
|
23
|
+
onSelect,
|
|
24
|
+
renderActions,
|
|
25
|
+
}: {
|
|
26
|
+
templates: readonly DesignLibraryTemplate[];
|
|
27
|
+
loading: boolean;
|
|
28
|
+
pendingId: string | null;
|
|
29
|
+
pendingLabel: string;
|
|
30
|
+
disabled: boolean;
|
|
31
|
+
selectedId?: string;
|
|
32
|
+
loadingLabel: string;
|
|
33
|
+
emptyLabel: string;
|
|
34
|
+
onSelect: (template: DesignLibraryTemplate, element: HTMLElement) => void;
|
|
35
|
+
renderActions?: (template: DesignLibraryTemplate) => ReactNode;
|
|
36
|
+
}) {
|
|
37
|
+
if (templates.length === 0) {
|
|
38
|
+
return loading ? (
|
|
39
|
+
<div
|
|
40
|
+
className={GRID_CLASS}
|
|
41
|
+
role="status"
|
|
42
|
+
aria-label={loadingLabel}
|
|
43
|
+
aria-busy="true"
|
|
44
|
+
>
|
|
45
|
+
{Array.from({ length: TEMPLATE_GRID_SKELETON_COUNT }, (_, index) => (
|
|
46
|
+
<div
|
|
47
|
+
key={index}
|
|
48
|
+
className="overflow-hidden rounded-[10px] border border-border"
|
|
49
|
+
>
|
|
50
|
+
<Skeleton className="aspect-[2/1] w-full rounded-none" />
|
|
51
|
+
<div className="grid gap-1 px-3.5 py-[13px]">
|
|
52
|
+
<Skeleton className="h-4 w-3/5" />
|
|
53
|
+
<Skeleton className="h-3.5 w-4/5" />
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
))}
|
|
57
|
+
</div>
|
|
58
|
+
) : (
|
|
59
|
+
<div
|
|
60
|
+
role="status"
|
|
61
|
+
className="py-8 text-center text-sm text-muted-foreground"
|
|
62
|
+
>
|
|
63
|
+
{emptyLabel}
|
|
64
|
+
</div>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
return (
|
|
68
|
+
<ul className={GRID_CLASS} aria-busy={loading || undefined}>
|
|
69
|
+
{templates.map((template) => (
|
|
70
|
+
<DesignTemplateCard
|
|
71
|
+
key={template.id}
|
|
72
|
+
template={template}
|
|
73
|
+
pending={template.id === pendingId}
|
|
74
|
+
pendingLabel={pendingLabel}
|
|
75
|
+
disabled={disabled}
|
|
76
|
+
selected={template.id === selectedId}
|
|
77
|
+
onSelect={onSelect}
|
|
78
|
+
actions={renderActions?.(template)}
|
|
79
|
+
/>
|
|
80
|
+
))}
|
|
81
|
+
</ul>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function DesignTemplateCard({
|
|
86
|
+
template,
|
|
87
|
+
pending,
|
|
88
|
+
pendingLabel,
|
|
89
|
+
disabled,
|
|
90
|
+
selected,
|
|
91
|
+
onSelect,
|
|
92
|
+
actions,
|
|
93
|
+
}: {
|
|
94
|
+
template: DesignLibraryTemplate;
|
|
95
|
+
pending: boolean;
|
|
96
|
+
pendingLabel: string;
|
|
97
|
+
disabled: boolean;
|
|
98
|
+
selected: boolean;
|
|
99
|
+
onSelect: (template: DesignLibraryTemplate, element: HTMLElement) => void;
|
|
100
|
+
actions?: ReactNode;
|
|
101
|
+
}) {
|
|
102
|
+
const titleId = useId();
|
|
103
|
+
const descriptionId = useId();
|
|
104
|
+
return (
|
|
105
|
+
<li
|
|
106
|
+
className="group/card relative min-w-0"
|
|
107
|
+
aria-busy={pending || undefined}
|
|
108
|
+
>
|
|
109
|
+
<button
|
|
110
|
+
type="button"
|
|
111
|
+
aria-labelledby={titleId}
|
|
112
|
+
aria-describedby={template.description ? descriptionId : undefined}
|
|
113
|
+
aria-pressed={selected}
|
|
114
|
+
aria-disabled={disabled || undefined}
|
|
115
|
+
onClick={(event) => {
|
|
116
|
+
if (!disabled) onSelect(template, event.currentTarget);
|
|
117
|
+
}}
|
|
118
|
+
className="block w-full overflow-hidden rounded-[10px] border border-border bg-background text-left transition-colors outline-none hover:border-foreground/30 focus-visible:ring-2 focus-visible:ring-ring aria-pressed:border-foreground/40 aria-disabled:cursor-default"
|
|
119
|
+
>
|
|
120
|
+
<span className="relative block aspect-[2/1] overflow-hidden bg-muted">
|
|
121
|
+
<TemplatePreview
|
|
122
|
+
html={template.previewHtml}
|
|
123
|
+
title={template.title}
|
|
124
|
+
width={template.width}
|
|
125
|
+
height={template.height}
|
|
126
|
+
fit="cover"
|
|
127
|
+
className="absolute inset-0 h-full w-full"
|
|
128
|
+
/>
|
|
129
|
+
</span>
|
|
130
|
+
<span className="block px-3.5 py-[13px]">
|
|
131
|
+
<span
|
|
132
|
+
id={titleId}
|
|
133
|
+
className="block truncate text-[13px] leading-4 font-semibold text-foreground"
|
|
134
|
+
>
|
|
135
|
+
{template.title}
|
|
136
|
+
</span>
|
|
137
|
+
{template.description ? (
|
|
138
|
+
<span
|
|
139
|
+
id={descriptionId}
|
|
140
|
+
className="mt-1 block truncate text-[11px] leading-3.5 text-muted-foreground"
|
|
141
|
+
>
|
|
142
|
+
{template.description}
|
|
143
|
+
</span>
|
|
144
|
+
) : null}
|
|
145
|
+
</span>
|
|
146
|
+
</button>
|
|
147
|
+
{actions ? (
|
|
148
|
+
<div className="absolute top-2 right-2 z-10 [@media(hover:hover)]:opacity-0 [@media(hover:hover)]:group-focus-within/card:opacity-100 [@media(hover:hover)]:group-hover/card:opacity-100">
|
|
149
|
+
{actions}
|
|
150
|
+
</div>
|
|
151
|
+
) : null}
|
|
152
|
+
{pending ? (
|
|
153
|
+
<div
|
|
154
|
+
role="status"
|
|
155
|
+
className="absolute inset-0 z-20 flex items-center justify-center gap-2 rounded-[10px] bg-background/80 p-4 text-sm font-medium text-foreground backdrop-blur-sm"
|
|
156
|
+
>
|
|
157
|
+
<Spinner className="size-4" />
|
|
158
|
+
{pendingLabel}
|
|
159
|
+
</div>
|
|
160
|
+
) : null}
|
|
161
|
+
</li>
|
|
162
|
+
);
|
|
163
|
+
}
|
|
@@ -4,16 +4,13 @@ import {
|
|
|
4
4
|
useActionQuery,
|
|
5
5
|
} from "@agent-native/core/client/hooks";
|
|
6
6
|
import { useT } from "@agent-native/core/client/i18n";
|
|
7
|
-
import {
|
|
8
|
-
TemplateLibraryGrid,
|
|
9
|
-
TemplatePreviewDialog,
|
|
10
|
-
} from "@agent-native/toolkit/app-shell";
|
|
7
|
+
import { TemplatePreviewDialog } from "@agent-native/toolkit/app-shell";
|
|
11
8
|
import { ShareButton } from "@agent-native/toolkit/app/sharing";
|
|
12
9
|
import { designTemplateRetryKey } from "@shared/design-template-retry";
|
|
13
10
|
import { IconDots } from "@tabler/icons-react";
|
|
14
11
|
import { useQueryClient } from "@tanstack/react-query";
|
|
15
12
|
import { nanoid } from "nanoid";
|
|
16
|
-
import { useRef, useState, type RefObject } from "react";
|
|
13
|
+
import { useEffect, useMemo, useRef, useState, type RefObject } from "react";
|
|
17
14
|
import { useNavigate, useSearchParams } from "react-router";
|
|
18
15
|
import { toast } from "sonner";
|
|
19
16
|
|
|
@@ -36,6 +33,7 @@ import {
|
|
|
36
33
|
DropdownMenuTrigger,
|
|
37
34
|
} from "@/components/ui/dropdown-menu";
|
|
38
35
|
|
|
36
|
+
import { DesignTemplateGrid } from "./DesignTemplateGrid";
|
|
39
37
|
import { TemplatePreview } from "./TemplatePreview";
|
|
40
38
|
|
|
41
39
|
export interface DesignLibraryTemplate {
|
|
@@ -78,9 +76,11 @@ function previewFilenameForHref(
|
|
|
78
76
|
export function DesignTemplateLibrary({
|
|
79
77
|
templates,
|
|
80
78
|
loading = false,
|
|
79
|
+
previewParam = "templateId",
|
|
81
80
|
}: {
|
|
82
81
|
templates: readonly DesignLibraryTemplate[];
|
|
83
82
|
loading?: boolean;
|
|
83
|
+
previewParam?: string;
|
|
84
84
|
}) {
|
|
85
85
|
const t = useT();
|
|
86
86
|
const navigate = useNavigate();
|
|
@@ -92,7 +92,7 @@ export function DesignTemplateLibrary({
|
|
|
92
92
|
const pending = useRef(false);
|
|
93
93
|
const retryIds = useRef(new Map<string, { key: string; id: string }>());
|
|
94
94
|
const preview = templates.find(
|
|
95
|
-
(template) => template.id === params.get(
|
|
95
|
+
(template) => template.id === params.get(previewParam),
|
|
96
96
|
);
|
|
97
97
|
const [deleting, setDeleting] = useState<DesignLibraryTemplate | null>(null);
|
|
98
98
|
const restoreFocusRef = useRef<HTMLElement | null>(null);
|
|
@@ -100,8 +100,8 @@ export function DesignTemplateLibrary({
|
|
|
100
100
|
setParams(
|
|
101
101
|
(current) => {
|
|
102
102
|
const next = new URLSearchParams(current);
|
|
103
|
-
if (id) next.set(
|
|
104
|
-
else next.delete(
|
|
103
|
+
if (id) next.set(previewParam, id);
|
|
104
|
+
else next.delete(previewParam);
|
|
105
105
|
return next;
|
|
106
106
|
},
|
|
107
107
|
{ replace: true },
|
|
@@ -158,41 +158,28 @@ export function DesignTemplateLibrary({
|
|
|
158
158
|
};
|
|
159
159
|
return (
|
|
160
160
|
<>
|
|
161
|
-
<
|
|
162
|
-
|
|
161
|
+
<DesignTemplateGrid
|
|
162
|
+
templates={templates}
|
|
163
163
|
loading={loading}
|
|
164
164
|
pendingId={pendingId}
|
|
165
165
|
pendingLabel={t("templatesPage.opening")}
|
|
166
166
|
disabled={Boolean(pendingId)}
|
|
167
167
|
selectedId={preview?.id}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
168
|
+
loadingLabel={t("templatesPage.loading")}
|
|
169
|
+
emptyLabel={t("promptDialog.noTemplatesFound")}
|
|
170
|
+
onSelect={(template, element) => {
|
|
171
|
+
restoreFocusRef.current = element;
|
|
172
|
+
selectPreview(template.id);
|
|
173
173
|
}}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
<
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
height={template.height}
|
|
181
|
-
className="h-full w-full"
|
|
174
|
+
renderActions={(template) =>
|
|
175
|
+
!template.isBuiltIn && template.isOwner ? (
|
|
176
|
+
<TemplateMenu
|
|
177
|
+
template={template}
|
|
178
|
+
disabled={pendingId !== null}
|
|
179
|
+
onDelete={() => setDeleting(template)}
|
|
182
180
|
/>
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
renderActions={(template) => (
|
|
186
|
-
<TemplateMenu
|
|
187
|
-
template={template}
|
|
188
|
-
disabled={pendingId !== null}
|
|
189
|
-
onDelete={() => setDeleting(template)}
|
|
190
|
-
onPreview={(trigger) => {
|
|
191
|
-
restoreFocusRef.current = trigger;
|
|
192
|
-
selectPreview(template.id);
|
|
193
|
-
}}
|
|
194
|
-
/>
|
|
195
|
-
)}
|
|
181
|
+
) : null
|
|
182
|
+
}
|
|
196
183
|
/>
|
|
197
184
|
{preview ? (
|
|
198
185
|
<DesignTemplatePreviewDialog
|
|
@@ -235,24 +222,19 @@ export function DesignTemplateLibrary({
|
|
|
235
222
|
function TemplateMenu({
|
|
236
223
|
template,
|
|
237
224
|
disabled,
|
|
238
|
-
onPreview,
|
|
239
225
|
onDelete,
|
|
240
226
|
}: {
|
|
241
227
|
template: DesignLibraryTemplate;
|
|
242
228
|
disabled: boolean;
|
|
243
|
-
onPreview: (trigger: HTMLElement | null) => void;
|
|
244
229
|
onDelete: () => void;
|
|
245
230
|
}) {
|
|
246
231
|
const t = useT();
|
|
247
|
-
const trigger = useRef<HTMLButtonElement>(null);
|
|
248
|
-
const openingPreview = useRef(false);
|
|
249
232
|
return (
|
|
250
233
|
<DropdownMenu>
|
|
251
234
|
<DropdownMenuTrigger asChild disabled={disabled}>
|
|
252
235
|
<Button
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
size="icon"
|
|
236
|
+
variant="secondary"
|
|
237
|
+
size="icon-sm"
|
|
256
238
|
disabled={disabled}
|
|
257
239
|
aria-label={t("templatesPage.templateActions", {
|
|
258
240
|
title: template.title,
|
|
@@ -261,39 +243,18 @@ function TemplateMenu({
|
|
|
261
243
|
<IconDots />
|
|
262
244
|
</Button>
|
|
263
245
|
</DropdownMenuTrigger>
|
|
264
|
-
<DropdownMenuContent
|
|
265
|
-
align="end"
|
|
266
|
-
onCloseAutoFocus={(event) => {
|
|
267
|
-
if (openingPreview.current) {
|
|
268
|
-
event.preventDefault();
|
|
269
|
-
openingPreview.current = false;
|
|
270
|
-
}
|
|
271
|
-
}}
|
|
272
|
-
>
|
|
246
|
+
<DropdownMenuContent align="end">
|
|
273
247
|
<DropdownMenuGroup>
|
|
274
|
-
<
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
248
|
+
<ShareButton
|
|
249
|
+
resourceType="design-template"
|
|
250
|
+
resourceId={template.id}
|
|
251
|
+
allowedRoles={["viewer", "editor", "admin"]}
|
|
252
|
+
resourceTitle={template.title}
|
|
253
|
+
trigger="label"
|
|
254
|
+
/>
|
|
255
|
+
<DropdownMenuItem disabled={disabled} onSelect={onDelete}>
|
|
256
|
+
{t("home.delete")}
|
|
282
257
|
</DropdownMenuItem>
|
|
283
|
-
{!template.isBuiltIn && template.isOwner ? (
|
|
284
|
-
<>
|
|
285
|
-
<ShareButton
|
|
286
|
-
resourceType="design-template"
|
|
287
|
-
resourceId={template.id}
|
|
288
|
-
allowedRoles={["viewer", "editor", "admin"]}
|
|
289
|
-
resourceTitle={template.title}
|
|
290
|
-
trigger="label"
|
|
291
|
-
/>
|
|
292
|
-
<DropdownMenuItem disabled={disabled} onSelect={onDelete}>
|
|
293
|
-
{t("home.delete")}
|
|
294
|
-
</DropdownMenuItem>
|
|
295
|
-
</>
|
|
296
|
-
) : null}
|
|
297
258
|
</DropdownMenuGroup>
|
|
298
259
|
</DropdownMenuContent>
|
|
299
260
|
</DropdownMenu>
|
|
@@ -321,12 +282,38 @@ function DesignTemplatePreviewDialog({
|
|
|
321
282
|
{ templateId: template.id },
|
|
322
283
|
);
|
|
323
284
|
const [selectedId, setSelectedId] = useState<string | null>(null);
|
|
324
|
-
const files =
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
285
|
+
const files = useMemo(
|
|
286
|
+
() =>
|
|
287
|
+
data && "files" in data
|
|
288
|
+
? data.files.filter((file) => file.fileType === "html")
|
|
289
|
+
: [],
|
|
290
|
+
[data],
|
|
291
|
+
);
|
|
328
292
|
const active =
|
|
329
293
|
files.find((file) => file.templateFileId === selectedId) ?? files[0];
|
|
294
|
+
const activeId = active?.templateFileId;
|
|
295
|
+
useEffect(() => {
|
|
296
|
+
if (files.length < 2) return;
|
|
297
|
+
const step = (event: KeyboardEvent) => {
|
|
298
|
+
if (
|
|
299
|
+
event.defaultPrevented ||
|
|
300
|
+
event.altKey ||
|
|
301
|
+
event.metaKey ||
|
|
302
|
+
event.ctrlKey
|
|
303
|
+
)
|
|
304
|
+
return;
|
|
305
|
+
const offset =
|
|
306
|
+
event.key === "ArrowRight" ? 1 : event.key === "ArrowLeft" ? -1 : 0;
|
|
307
|
+
if (!offset) return;
|
|
308
|
+
const index = files.findIndex((file) => file.templateFileId === activeId);
|
|
309
|
+
const next = files[(index + offset + files.length) % files.length];
|
|
310
|
+
if (!next) return;
|
|
311
|
+
event.preventDefault();
|
|
312
|
+
setSelectedId(next.templateFileId);
|
|
313
|
+
};
|
|
314
|
+
window.addEventListener("keydown", step);
|
|
315
|
+
return () => window.removeEventListener("keydown", step);
|
|
316
|
+
}, [files, activeId]);
|
|
330
317
|
return (
|
|
331
318
|
<TemplatePreviewDialog
|
|
332
319
|
open
|
|
@@ -33,6 +33,7 @@ function TemplatePreviewContent({
|
|
|
33
33
|
width,
|
|
34
34
|
height,
|
|
35
35
|
className,
|
|
36
|
+
fit = "contain",
|
|
36
37
|
interactive = false,
|
|
37
38
|
recordSessionReplay = false,
|
|
38
39
|
onNavigate,
|
|
@@ -44,6 +45,7 @@ function TemplatePreviewContent({
|
|
|
44
45
|
width?: number | null;
|
|
45
46
|
height?: number | null;
|
|
46
47
|
className?: string;
|
|
48
|
+
fit?: "contain" | "cover";
|
|
47
49
|
interactive?: boolean;
|
|
48
50
|
recordSessionReplay?: boolean;
|
|
49
51
|
onNavigate?: (href: string) => void;
|
|
@@ -140,8 +142,9 @@ function TemplatePreviewContent({
|
|
|
140
142
|
const availableWidth = element.clientWidth;
|
|
141
143
|
const availableHeight = element.clientHeight;
|
|
142
144
|
if (availableWidth > 0 && availableHeight > 0) {
|
|
145
|
+
const fitScale = fit === "cover" ? Math.max : Math.min;
|
|
143
146
|
setScale(
|
|
144
|
-
|
|
147
|
+
fitScale(
|
|
145
148
|
availableWidth / naturalWidth,
|
|
146
149
|
availableHeight / naturalHeight,
|
|
147
150
|
),
|
|
@@ -152,7 +155,7 @@ function TemplatePreviewContent({
|
|
|
152
155
|
const observer = new ResizeObserver(update);
|
|
153
156
|
observer.observe(element);
|
|
154
157
|
return () => observer.disconnect();
|
|
155
|
-
}, [naturalHeight, naturalWidth]);
|
|
158
|
+
}, [fit, naturalHeight, naturalWidth]);
|
|
156
159
|
|
|
157
160
|
useEffect(() => {
|
|
158
161
|
const frame = frameRef.current;
|
|
@@ -289,6 +292,7 @@ function TemplatePreviewContent({
|
|
|
289
292
|
designId,
|
|
290
293
|
);
|
|
291
294
|
}}
|
|
295
|
+
data-fit={fit}
|
|
292
296
|
tabIndex={interactive ? 0 : -1}
|
|
293
297
|
aria-hidden={!interactive || undefined}
|
|
294
298
|
className={cn(
|
|
@@ -22,6 +22,12 @@
|
|
|
22
22
|
backface-visibility: hidden;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
.design-template-preview-frame[data-fit="cover"] {
|
|
26
|
+
top: 0;
|
|
27
|
+
transform: translateX(-50%) scale(var(--design-template-scale));
|
|
28
|
+
transform-origin: top center;
|
|
29
|
+
}
|
|
30
|
+
|
|
25
31
|
.design-library-card-preview {
|
|
26
32
|
position: absolute;
|
|
27
33
|
inset: 0;
|
|
@@ -164,6 +164,23 @@ export default {
|
|
|
164
164
|
expandSidebar: "Expand sidebar",
|
|
165
165
|
collapseSidebar: "Collapse sidebar",
|
|
166
166
|
},
|
|
167
|
+
gettingStarted: {
|
|
168
|
+
title: "Getting started",
|
|
169
|
+
progress: "{{completeCount}} of {{totalCount}} complete",
|
|
170
|
+
collapse: "Collapse",
|
|
171
|
+
expand: "Expand",
|
|
172
|
+
completed: "Completed",
|
|
173
|
+
loadError: "Couldn't load your checklist",
|
|
174
|
+
retry: "Retry",
|
|
175
|
+
steps: {
|
|
176
|
+
createDesign: "Create your first design",
|
|
177
|
+
addBrand: "Add your brand",
|
|
178
|
+
useRealData: "Use your real data",
|
|
179
|
+
shareFeedback: "Share for feedback",
|
|
180
|
+
inviteTeammate: "Invite a teammate",
|
|
181
|
+
saveTemplate: "Save a design as a template",
|
|
182
|
+
},
|
|
183
|
+
},
|
|
167
184
|
settings: {
|
|
168
185
|
agentTitle: "Manage agent",
|
|
169
186
|
agentDescription:
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef } from "react";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
clearCanvasOutcomeDesign,
|
|
5
|
+
recordCanvasOutcome,
|
|
6
|
+
type CanvasLoadReadyBucket,
|
|
7
|
+
} from "./canvas-outcome-tracking";
|
|
8
|
+
|
|
9
|
+
/** A canvas that is not ready by then counts as stalled. */
|
|
10
|
+
export const CANVAS_LOAD_STALL_MS = 10_000;
|
|
11
|
+
|
|
12
|
+
export function canvasLoadReadyBucket(
|
|
13
|
+
elapsedMs: number,
|
|
14
|
+
): CanvasLoadReadyBucket {
|
|
15
|
+
if (elapsedMs < 1_000) return "lt_1s";
|
|
16
|
+
if (elapsedMs <= 4_000) return "1_to_4s";
|
|
17
|
+
return "gt_4s";
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface CanvasLoad {
|
|
21
|
+
designId: string;
|
|
22
|
+
startedAt: number;
|
|
23
|
+
settled: boolean;
|
|
24
|
+
hidden: boolean;
|
|
25
|
+
stallTimer: ReturnType<typeof setTimeout>;
|
|
26
|
+
onVisibilityChange: () => void;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// One load per design opened: a board mounts a canvas per screen, and screens
|
|
30
|
+
// remount, so counting per canvas would inflate loads and stalls.
|
|
31
|
+
let currentLoad: CanvasLoad | null = null;
|
|
32
|
+
|
|
33
|
+
function startLoad(designId: string): CanvasLoad {
|
|
34
|
+
const load: CanvasLoad = {
|
|
35
|
+
designId,
|
|
36
|
+
startedAt: Date.now(),
|
|
37
|
+
settled: false,
|
|
38
|
+
hidden: document.visibilityState === "hidden",
|
|
39
|
+
stallTimer: setTimeout(
|
|
40
|
+
() => settleLoad(load, "stalled"),
|
|
41
|
+
CANVAS_LOAD_STALL_MS,
|
|
42
|
+
),
|
|
43
|
+
// Hidden tabs throttle timers and frames, so their load time means nothing.
|
|
44
|
+
onVisibilityChange: () => {
|
|
45
|
+
if (document.visibilityState === "hidden") load.hidden = true;
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
document.addEventListener("visibilitychange", load.onVisibilityChange);
|
|
49
|
+
currentLoad = load;
|
|
50
|
+
return load;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function endLoad(load: CanvasLoad): void {
|
|
54
|
+
load.settled = true;
|
|
55
|
+
clearTimeout(load.stallTimer);
|
|
56
|
+
document.removeEventListener("visibilitychange", load.onVisibilityChange);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function settleLoad(load: CanvasLoad, outcome: "ready" | "stalled"): void {
|
|
60
|
+
if (load.settled) return;
|
|
61
|
+
endLoad(load);
|
|
62
|
+
const { designId } = load;
|
|
63
|
+
if (load.hidden) {
|
|
64
|
+
recordCanvasOutcome("canvas_load", "skipped", "hidden_during_load", {
|
|
65
|
+
designId,
|
|
66
|
+
});
|
|
67
|
+
} else if (outcome === "stalled") {
|
|
68
|
+
recordCanvasOutcome("canvas_load", "failed", "stalled", { designId });
|
|
69
|
+
} else {
|
|
70
|
+
recordCanvasOutcome("canvas_load", "succeeded", "none", {
|
|
71
|
+
designId,
|
|
72
|
+
readyBucket: canvasLoadReadyBucket(Date.now() - load.startedAt),
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// A load still running when its design closes is not counted.
|
|
78
|
+
function abandonLoad(): void {
|
|
79
|
+
const load = currentLoad;
|
|
80
|
+
currentLoad = null;
|
|
81
|
+
if (load && !load.settled) endLoad(load);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Counts one `canvas_load` per design opened: the first measured canvas of a
|
|
86
|
+
* design starts it and the first to report ready ends it. Pass
|
|
87
|
+
* `measure: false` for a canvas that may never report ready (a snapshot, a
|
|
88
|
+
* localhost or Fusion preview), or a missing bridge would count as a stall.
|
|
89
|
+
* Call the returned function when the iframe bridge reports ready.
|
|
90
|
+
*/
|
|
91
|
+
export function useCanvasLoadOutcome({
|
|
92
|
+
designId,
|
|
93
|
+
measure,
|
|
94
|
+
}: {
|
|
95
|
+
designId: string | undefined;
|
|
96
|
+
measure: boolean;
|
|
97
|
+
}): () => void {
|
|
98
|
+
const latest = useRef({ designId, measure });
|
|
99
|
+
latest.current = { designId, measure };
|
|
100
|
+
const startedLoad = useRef<CanvasLoad | null>(null);
|
|
101
|
+
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
try {
|
|
104
|
+
if (!measure || !designId) {
|
|
105
|
+
// A canvas that stops being measured ends the load it started, or that
|
|
106
|
+
// load would only ever end as a stall.
|
|
107
|
+
if (startedLoad.current && startedLoad.current === currentLoad) {
|
|
108
|
+
abandonLoad();
|
|
109
|
+
}
|
|
110
|
+
startedLoad.current = null;
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (currentLoad?.designId === designId) return;
|
|
114
|
+
abandonLoad();
|
|
115
|
+
startedLoad.current = startLoad(designId);
|
|
116
|
+
} catch {
|
|
117
|
+
// coercion-ok: load tracking must never break canvas mounting.
|
|
118
|
+
}
|
|
119
|
+
}, [designId, measure]);
|
|
120
|
+
|
|
121
|
+
// Stable, and reads the latest props: the bridge handlers that call it are
|
|
122
|
+
// registered once and keep whichever callback they first saw.
|
|
123
|
+
return useCallback(() => {
|
|
124
|
+
try {
|
|
125
|
+
const { designId: canvasDesign, measure: measuring } = latest.current;
|
|
126
|
+
if (measuring && canvasDesign && currentLoad?.designId === canvasDesign) {
|
|
127
|
+
settleLoad(currentLoad, "ready");
|
|
128
|
+
}
|
|
129
|
+
} catch {
|
|
130
|
+
// coercion-ok: load tracking must never break the bridge-ready path.
|
|
131
|
+
}
|
|
132
|
+
}, []);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** The editor closed its design: end its load and send pending counts. */
|
|
136
|
+
export function leaveCanvasOutcomeDesign(): void {
|
|
137
|
+
try {
|
|
138
|
+
abandonLoad();
|
|
139
|
+
clearCanvasOutcomeDesign();
|
|
140
|
+
} catch {
|
|
141
|
+
// coercion-ok: tracking teardown must never break navigation.
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function __resetCanvasLoadOutcomeForTests(): void {
|
|
146
|
+
abandonLoad();
|
|
147
|
+
}
|