@agent-native/core-corpus 0.210.2 → 0.211.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 (192) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/actions/references/action-fields.md +5 -3
  3. package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +41 -2
  4. package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +4 -2
  5. package/corpus/templates/analytics/CHANGELOG.md +18 -0
  6. package/corpus/templates/analytics/actions/build-data-index.ts +11 -2
  7. package/corpus/templates/analytics/actions/configure-content-collection.ts +29 -4
  8. package/corpus/templates/analytics/actions/configure-first-party-events-model.ts +9 -7
  9. package/corpus/templates/analytics/actions/create-analytics-public-key.ts +4 -0
  10. package/corpus/templates/analytics/actions/estimate-content-signups-cost.ts +72 -0
  11. package/corpus/templates/analytics/actions/estimate-query-cost.ts +16 -134
  12. package/corpus/templates/analytics/actions/list-sigma-workbooks.ts +123 -42
  13. package/corpus/templates/analytics/actions/migrate-sigma-workbook.ts +20 -17
  14. package/corpus/templates/analytics/actions/revoke-analytics-public-key.ts +4 -0
  15. package/corpus/templates/analytics/app/components/dashboard/SigmaMigrationDialog.tsx +246 -680
  16. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +1 -1
  17. package/corpus/templates/analytics/app/i18n/en-US.ts +54 -55
  18. package/corpus/templates/analytics/app/pages/DashboardOverview.tsx +0 -1
  19. package/corpus/templates/analytics/app/pages/DataSources.tsx +56 -0
  20. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/filter-vars.ts +4 -3
  21. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +26 -8
  22. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/interpolate.ts +3 -1
  23. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/panel-cost-notice.ts +172 -0
  24. package/corpus/templates/analytics/app/pages/data-sources/SearchConsoleDetail.tsx +441 -0
  25. package/corpus/templates/analytics/changelog/2026-10-15-a-content-dashboard-lists-every-blog-article-with-its-author.md +6 -0
  26. package/corpus/templates/analytics/changelog/2026-10-15-data-sources-has-a-google-search-console-card-that-shows-the.md +6 -0
  27. package/corpus/templates/analytics/changelog/2026-10-16-the-content-dashboard-can-show-signups-per-blog-article.md +6 -0
  28. package/corpus/templates/analytics/changelog/2026-10-19-creating-or-revoking-a-public-analytics-key-now-asks-you-to-.md +6 -0
  29. package/corpus/templates/analytics/changelog/2026-10-19-search-sigma-workbooks-by-name-and-open-ones-a-teammate-alre.md +6 -0
  30. package/corpus/templates/analytics/changelog/2026-10-20-blog-signup-attribution-excludes-signups-before-the-first-vi.md +6 -0
  31. package/corpus/templates/analytics/changelog/2026-10-20-collapsed-analytics-navigation-starts-at-the-top.md +6 -0
  32. package/corpus/templates/analytics/changelog/2026-10-20-content-keeps-successful-coverage-dates-and-missing-day-gaps.md +6 -0
  33. package/corpus/templates/analytics/changelog/2026-10-20-private-github-repositories-can-be-indexed-with-your-connect.md +6 -0
  34. package/corpus/templates/analytics/changelog/2026-10-20-search-console-preserves-backfill-and-paused-collection.md +6 -0
  35. package/corpus/templates/analytics/changelog/2026-10-20-search-console-refreshes-historical-coverage-when-properties.md +6 -0
  36. package/corpus/templates/analytics/changelog/2026-10-20-search-console-setup-keeps-saved-properties-readable-and-sup.md +6 -0
  37. package/corpus/templates/analytics/seeds/dashboards/content-view.json +182 -0
  38. package/corpus/templates/analytics/server/db/schema.ts +1 -0
  39. package/corpus/templates/analytics/server/lib/bi-migration/external-dashboards-store.ts +56 -18
  40. package/corpus/templates/analytics/server/lib/bi-migration/unfinished-import.ts +64 -0
  41. package/corpus/templates/analytics/server/lib/builtin-dashboard-pass.ts +17 -2
  42. package/corpus/templates/analytics/server/lib/content-metrics/collect.ts +2 -0
  43. package/corpus/templates/analytics/server/lib/content-metrics/config.ts +92 -3
  44. package/corpus/templates/analytics/server/lib/content-metrics/legacy-content-view.json +172 -0
  45. package/corpus/templates/analytics/server/lib/content-metrics/migration.ts +2 -0
  46. package/corpus/templates/analytics/server/lib/content-metrics/steps/daily-metrics.ts +8 -3
  47. package/corpus/templates/analytics/server/lib/content-metrics/steps/search-console.ts +16 -3
  48. package/corpus/templates/analytics/server/lib/content-metrics/steps/signups.ts +240 -0
  49. package/corpus/templates/analytics/server/lib/content-metrics/store.ts +5 -0
  50. package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +21 -0
  51. package/corpus/templates/analytics/server/lib/dashboard-panel-verification.ts +300 -5
  52. package/corpus/templates/analytics/server/lib/dashboard-seeds.ts +2 -0
  53. package/corpus/templates/analytics/server/lib/dashboards-store.ts +28 -0
  54. package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +128 -24
  55. package/corpus/templates/analytics/server/lib/google-analytics.ts +56 -16
  56. package/corpus/templates/analytics/server/lib/index-build-github.ts +5 -0
  57. package/corpus/templates/analytics/server/lib/provider-credentials.ts +47 -2
  58. package/corpus/templates/analytics/server/lib/query-cost-estimate.ts +184 -5
  59. package/corpus/templates/analytics/server/plugins/db.ts +9 -1
  60. package/corpus/templates/analytics/shared/builtin-dashboard-ids.ts +2 -0
  61. package/corpus/templates/analytics/shared/search-console-property.ts +78 -0
  62. package/corpus/templates/assets/.agents/skills/actions/references/action-fields.md +5 -3
  63. package/corpus/templates/brain/.agents/skills/actions/references/action-fields.md +5 -3
  64. package/corpus/templates/calendar/.agents/skills/actions/references/action-fields.md +5 -3
  65. package/corpus/templates/calendar/actions/delete-event.ts +1 -3
  66. package/corpus/templates/calendar/actions/delete-events.ts +1 -2
  67. package/corpus/templates/calendar/actions/event-action-helpers.ts +0 -4
  68. package/corpus/templates/calendar/actions/update-event.ts +2 -6
  69. package/corpus/templates/chat/.agents/skills/actions/references/action-fields.md +5 -3
  70. package/corpus/templates/clips/.agents/skills/actions/references/action-fields.md +5 -3
  71. package/corpus/templates/clips/CHANGELOG.md +11 -24
  72. package/corpus/templates/clips/actions/create-recording-upload.ts +5 -1
  73. package/corpus/templates/clips/app/components/player/chapter-list.tsx +7 -6
  74. package/corpus/templates/clips/app/components/player/comment-composer.tsx +5 -1
  75. package/corpus/templates/clips/app/components/player/player-controls.tsx +1 -0
  76. package/corpus/templates/clips/app/components/player/scrubber.tsx +4 -2
  77. package/corpus/templates/clips/app/i18n/en-US.ts +3 -3
  78. package/corpus/templates/clips/app/lib/extension-auth.ts +70 -0
  79. package/corpus/templates/clips/app/root.tsx +22 -10
  80. package/corpus/templates/clips/app/routes/_app.notifications.tsx +3 -0
  81. package/corpus/templates/clips/app/routes/import.tsx +115 -110
  82. package/corpus/templates/clips/changelog/2026-10-16-agent-uploads-can-use-free-clips-storage.md +6 -0
  83. package/corpus/templates/clips/changelog/2026-10-19-agents-see-a-clip-s-frames-as-soon-as-it-finishes-processing.md +6 -0
  84. package/corpus/templates/clips/changelog/2026-10-19-chapter-list-times-now-match-the-player-s-clock-and-each-cha.md +6 -0
  85. package/corpus/templates/clips/changelog/2026-10-19-editing-a-comment-puts-the-cursor-after-its-text-and-reply-o.md +6 -0
  86. package/corpus/templates/clips/changelog/2026-10-19-youtube-import-followups.md +6 -0
  87. package/corpus/templates/clips/changelog/2026-10-20-locally-saved-desktop-recordings-now-appear-in-the-recovery-.md +6 -0
  88. package/corpus/templates/clips/desktop/src/app.tsx +29 -186
  89. package/corpus/templates/clips/desktop/src/components/RecordingRecovery.tsx +171 -16
  90. package/corpus/templates/clips/desktop/src/lib/record-first.ts +9 -32
  91. package/corpus/templates/clips/desktop/src/lib/recording-recovery.ts +30 -1
  92. package/corpus/templates/clips/server/lib/agent-frame-request.ts +5 -2
  93. package/corpus/templates/clips/server/lib/recording-upload-ticket.ts +8 -0
  94. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +2 -0
  95. package/corpus/templates/content/.agents/skills/actions/references/action-fields.md +5 -3
  96. package/corpus/templates/content/CHANGELOG.md +12 -0
  97. package/corpus/templates/content/actions/_content-database-personal-view.ts +5 -15
  98. package/corpus/templates/content/actions/_database-navigation.ts +3 -3
  99. package/corpus/templates/content/actions/_database-utils.ts +10 -22
  100. package/corpus/templates/content/actions/_database-view-setup.ts +16 -76
  101. package/corpus/templates/content/actions/create-comment-ai-suggestion.ts +93 -44
  102. package/corpus/templates/content/actions/export-document.ts +2 -14
  103. package/corpus/templates/content/actions/suggest-document-edit.ts +5 -7
  104. package/corpus/templates/content/actions/update-content-database-view.ts +2 -14
  105. package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +86 -30
  106. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +64 -28
  107. package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +6 -46
  108. package/corpus/templates/content/app/components/editor/PageDraftRecovery.tsx +72 -7
  109. package/corpus/templates/content/app/components/editor/TableHoverControls.tsx +292 -103
  110. package/corpus/templates/content/app/components/editor/ThreadSuggestion.tsx +147 -40
  111. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +108 -11
  112. package/corpus/templates/content/app/components/editor/database/ContentTable.tsx +2 -1
  113. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +150 -166
  114. package/corpus/templates/content/app/components/editor/database/database-cell-comments.tsx +515 -0
  115. package/corpus/templates/content/app/components/editor/database/database-item-comments.tsx +5 -43
  116. package/corpus/templates/content/app/components/editor/database/filter-sort.ts +8 -783
  117. package/corpus/templates/content/app/components/editor/live-editor-session.ts +5 -1
  118. package/corpus/templates/content/app/components/editor/page-draft-journal.ts +61 -1
  119. package/corpus/templates/content/app/components/editor/property-comments.tsx +35 -4
  120. package/corpus/templates/content/app/components/editor/property-date-format.ts +45 -0
  121. package/corpus/templates/content/app/components/editor/thread-suggestions.ts +30 -0
  122. package/corpus/templates/content/app/components/editor/useDocumentReconcileRecovery.ts +23 -4
  123. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +36 -10
  124. package/corpus/templates/content/app/global.css +23 -0
  125. package/corpus/templates/content/app/i18n-data.ts +1 -0
  126. package/corpus/templates/content/changelog/2026-10-16-opening-a-page-in-a-new-tab-while-another-tab-is-still-savin.md +6 -0
  127. package/corpus/templates/content/changelog/2026-10-16-typing-steadily-in-one-tab-no-longer-makes-another-open-tab-.md +6 -0
  128. package/corpus/templates/content/changelog/2026-10-19-database-views-can-filter-the-same-property-more-than-once-n.md +6 -0
  129. package/corpus/templates/content/changelog/2026-10-19-edits-made-from-a-block-s-menu-or-a-table-s-row-and-column-m.md +6 -0
  130. package/corpus/templates/content/changelog/2026-10-19-starting-a-comment-puts-the-cursor-in-the-comment-box-ready-.md +6 -0
  131. package/corpus/templates/content/changelog/2026-10-19-table-row-and-column-options-now-appear-for-the-row-or-colum.md +6 -0
  132. package/corpus/templates/content/docs/product/capabilities/content.comment.page-owned.md +5 -1
  133. package/corpus/templates/content/docs/product/capabilities/content.revision.suggestions.md +3 -1
  134. package/corpus/templates/content/e2e/helpers.ts +10 -0
  135. package/corpus/templates/content/shared/api.ts +18 -12
  136. package/corpus/templates/content/shared/database-query.ts +107 -13
  137. package/corpus/templates/content/shared/suggestion-diff.ts +35 -0
  138. package/corpus/templates/crm/.agents/skills/actions/references/action-fields.md +5 -3
  139. package/corpus/templates/design/.agents/skills/actions/references/action-fields.md +5 -3
  140. package/corpus/templates/design/CHANGELOG.md +2 -0
  141. package/corpus/templates/design/actions/delete-file.ts +1 -4
  142. package/corpus/templates/design/app/components/design/LayersPanel.tsx +10 -1
  143. package/corpus/templates/design/app/components/visual-editor/ReviewCanvasPins.tsx +24 -4
  144. package/corpus/templates/design/app/pages/design-editor/commands/mode-change.ts +14 -9
  145. package/corpus/templates/design/app/pages/design-editor/domains/use-editor-modes.ts +23 -2
  146. package/corpus/templates/design/app/pages/design-editor/tool-state.ts +29 -0
  147. package/corpus/templates/design/changelog/2026-10-19-reply-and-edit-on-a-comment-in-the-sidebar-or-on-a-canvas-pi.md +6 -0
  148. package/corpus/templates/design/changelog/2026-10-20-comments-return-to-the-mode-they-came-from-and-layers-search.md +6 -0
  149. package/corpus/templates/design/e2e/global-setup.ts +185 -85
  150. package/corpus/templates/dispatch/.agents/skills/actions/references/action-fields.md +5 -3
  151. package/corpus/templates/dispatch/CHANGELOG.md +4 -0
  152. package/corpus/templates/dispatch/actions/apply-workspace-connection-setup.ts +4 -0
  153. package/corpus/templates/dispatch/actions/delete-workspace-connection.ts +4 -0
  154. package/corpus/templates/dispatch/actions/set-workspace-connection-grant.ts +4 -0
  155. package/corpus/templates/dispatch/actions/upsert-workspace-connection.ts +4 -0
  156. package/corpus/templates/dispatch/changelog/2026-10-19-vault-secrets-grants-link-tokens-app-access-and-workspace-co.md +6 -0
  157. package/corpus/templates/factory/.agents/skills/actions/references/action-fields.md +5 -3
  158. package/corpus/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +105 -33
  159. package/corpus/templates/factory/.agents/skills/review-latest-feedback/references/channel-inventory.md +128 -74
  160. package/corpus/templates/factory/.agents/skills/review-latest-feedback/references/slack-reaction-replies.md +39 -11
  161. package/corpus/templates/forms/.agents/skills/actions/references/action-fields.md +5 -3
  162. package/corpus/templates/mail/.agents/skills/actions/references/action-fields.md +5 -3
  163. package/corpus/templates/mail/.agents/skills/contacts-and-crm/SKILL.md +22 -17
  164. package/corpus/templates/mail/AGENTS.md +1 -1
  165. package/corpus/templates/mail/CHANGELOG.md +14 -0
  166. package/corpus/templates/mail/actions/provider-api-request.ts +3 -3
  167. package/corpus/templates/mail/app/components/email/IntegrationsSidebar.tsx +211 -54
  168. package/corpus/templates/mail/app/hooks/use-inbox-threads.ts +26 -2
  169. package/corpus/templates/mail/app/i18n/en-US.ts +5 -1
  170. package/corpus/templates/mail/app/lib/gong-connection.ts +27 -0
  171. package/corpus/templates/mail/app/lib/thread-cache.ts +110 -9
  172. package/corpus/templates/mail/app/root.tsx +10 -2
  173. package/corpus/templates/mail/changelog/2026-10-20-connected-gong-settings-now-open-the-shared-integration-mana.md +6 -0
  174. package/corpus/templates/mail/changelog/2026-10-20-gong-connection-details-now-match-the-call-data-available-in.md +6 -0
  175. package/corpus/templates/mail/changelog/2026-10-20-improve-gong-setup-with-a-click-to-connect-flow-and-permissi.md +6 -0
  176. package/corpus/templates/mail/changelog/2026-10-20-mail-lets-you-retry-when-it-cannot-check-your-gong-access.md +6 -0
  177. package/corpus/templates/mail/changelog/2026-10-20-mail-thread-cache-stays-isolated-when-mailbox-scope-changes.md +6 -0
  178. package/corpus/templates/mail/changelog/2026-10-20-the-inbox-and-recently-opened-threads-now-open-instantly-fro.md +6 -0
  179. package/corpus/templates/mail/server/handlers/emails.ts +9 -0
  180. package/corpus/templates/plan/.agents/skills/actions/references/action-fields.md +5 -3
  181. package/corpus/templates/plan/CHANGELOG.md +4 -0
  182. package/corpus/templates/plan/app/pages/PlansPage.tsx +173 -33
  183. package/corpus/templates/plan/changelog/2026-10-19-opening-a-comment-thread-puts-the-cursor-in-the-reply-box-an.md +6 -0
  184. package/corpus/templates/slides/.agents/skills/actions/references/action-fields.md +5 -3
  185. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +6 -42
  186. package/corpus/templates/slides/app/components/editor/FloatingToolbar.tsx +3 -7
  187. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +5 -14
  188. package/corpus/templates/slides/app/i18n/en-US.ts +0 -2
  189. package/corpus/templates/slides/app/pages/DeckEditor.tsx +27 -33
  190. package/corpus/templates/slides/scripts/edit-fidelity/run.ts +1 -3
  191. package/corpus/templates/tasks/.agents/skills/actions/references/action-fields.md +5 -3
  192. package/package.json +1 -1
@@ -0,0 +1,441 @@
1
+ import {
2
+ useActionMutation,
3
+ useActionQuery,
4
+ } from "@agent-native/core/client/hooks";
5
+ import { useFormatters, useT } from "@agent-native/core/client/i18n";
6
+ import { useOrgRole } from "@agent-native/core/client/org";
7
+ import {
8
+ normalizeSearchConsoleProperty,
9
+ searchConsolePropertyProblem,
10
+ searchConsolePathPrefixProblem,
11
+ type SearchConsolePropertyProblem,
12
+ } from "@shared/search-console-property";
13
+ import { IconCheck, IconCopy, IconPlus, IconTrash } from "@tabler/icons-react";
14
+ import { useQueryClient } from "@tanstack/react-query";
15
+ import { useEffect, useState } from "react";
16
+ import { Link } from "react-router";
17
+
18
+ import { Button } from "@/components/ui/button";
19
+ import { Input } from "@/components/ui/input";
20
+ import { Skeleton } from "@/components/ui/skeleton";
21
+ import { Switch } from "@/components/ui/switch";
22
+
23
+ type ServiceAccount =
24
+ | { status: "missing" }
25
+ | { status: "invalid" }
26
+ | { status: "ok"; email: string };
27
+
28
+ type SearchConsoleSite = { siteUrl: string; pathPrefix?: string };
29
+
30
+ type StepRun = {
31
+ status: string;
32
+ startedAt: string;
33
+ errorCode: string | null;
34
+ errorMessage: string | null;
35
+ };
36
+
37
+ export type ContentCollectionStatus = {
38
+ configured: boolean;
39
+ enabled: boolean;
40
+ config: {
41
+ searchConsole: { enabled: boolean; sites: SearchConsoleSite[] };
42
+ } | null;
43
+ serviceAccount: ServiceAccount;
44
+ steps: Array<{ step: string; enabled: boolean; lastRun: StepRun | null }>;
45
+ };
46
+
47
+ export type SearchConsoleState =
48
+ | "collecting"
49
+ | "needs_access"
50
+ | "failed"
51
+ | "unavailable"
52
+ | "off";
53
+
54
+ export function searchConsoleUpdate(
55
+ status: ContentCollectionStatus,
56
+ enabled: boolean,
57
+ sites: SearchConsoleSite[],
58
+ toggle = false,
59
+ ) {
60
+ return {
61
+ enabled: status.enabled || (toggle && enabled),
62
+ searchConsole: enabled,
63
+ ...(toggle && !enabled ? {} : { searchConsoleSites: sites }),
64
+ };
65
+ }
66
+
67
+ export function searchConsoleState(
68
+ status: ContentCollectionStatus | undefined,
69
+ error?: unknown,
70
+ ): SearchConsoleState {
71
+ if (error || !status) return "unavailable";
72
+ const step = status?.steps.find((entry) => entry.step === "search_console");
73
+ if (!status?.enabled || !step?.enabled) return "off";
74
+ switch (step.lastRun?.status) {
75
+ case "succeeded":
76
+ case "running":
77
+ case undefined:
78
+ return "collecting";
79
+ case "not_configured":
80
+ return step.lastRun?.errorCode === "no_search_console_sites"
81
+ ? "off"
82
+ : "needs_access";
83
+ default:
84
+ return "failed";
85
+ }
86
+ }
87
+
88
+ export function useContentCollectionStatus() {
89
+ return useActionQuery("get-content-collection", undefined, {
90
+ staleTime: 10_000,
91
+ retry: false,
92
+ }) as {
93
+ data: ContentCollectionStatus | undefined;
94
+ isLoading: boolean;
95
+ error: unknown;
96
+ };
97
+ }
98
+
99
+ function ServiceAccountRow({ account }: { account: ServiceAccount }) {
100
+ const t = useT();
101
+ const [copied, setCopied] = useState(false);
102
+
103
+ if (account.status === "missing") {
104
+ return (
105
+ <div className="flex items-center justify-between gap-3 text-xs text-muted-foreground">
106
+ <span>{t("dataSources.searchConsole.noServiceAccount")}</span>
107
+ <Button asChild size="sm" variant="outline" className="text-xs">
108
+ <Link to="/data-sources?source=gcloud">
109
+ {t("dataSources.searchConsole.openGoogleCloud")}
110
+ </Link>
111
+ </Button>
112
+ </div>
113
+ );
114
+ }
115
+ if (account.status === "invalid") {
116
+ return (
117
+ <p className="text-xs text-destructive">
118
+ {t("dataSources.searchConsole.invalidServiceAccount")}
119
+ </p>
120
+ );
121
+ }
122
+
123
+ const copy = async () => {
124
+ await navigator.clipboard?.writeText(account.email);
125
+ setCopied(true);
126
+ };
127
+ return (
128
+ <div className="space-y-1.5">
129
+ <p className="text-xs font-medium text-foreground">
130
+ {t("dataSources.searchConsole.serviceAccount")}
131
+ </p>
132
+ <div className="flex items-center gap-2">
133
+ <code className="min-w-0 flex-1 truncate rounded-md bg-muted/40 px-2 py-1.5 font-mono text-xs">
134
+ {account.email}
135
+ </code>
136
+ <Button
137
+ size="sm"
138
+ variant="outline"
139
+ onClick={copy}
140
+ aria-label={t("dataSources.searchConsole.copyEmail")}
141
+ className="h-8 w-8 p-0"
142
+ >
143
+ {copied ? (
144
+ <IconCheck className="h-3.5 w-3.5" />
145
+ ) : (
146
+ <IconCopy className="h-3.5 w-3.5" />
147
+ )}
148
+ </Button>
149
+ </div>
150
+ <p className="text-xs text-muted-foreground">
151
+ {t("dataSources.searchConsole.grantHint")}
152
+ </p>
153
+ </div>
154
+ );
155
+ }
156
+
157
+ function LastRun({ run }: { run: StepRun }) {
158
+ const t = useT();
159
+ const formatters = useFormatters();
160
+ const when = formatters.formatDate(run.startedAt, {
161
+ dateStyle: "medium",
162
+ timeStyle: "short",
163
+ });
164
+ const tone =
165
+ run.status === "succeeded"
166
+ ? "text-emerald-600 dark:text-emerald-400"
167
+ : run.status === "not_configured"
168
+ ? "text-amber-600 dark:text-amber-400"
169
+ : run.status === "running"
170
+ ? "text-muted-foreground"
171
+ : "text-destructive";
172
+ const label =
173
+ run.status === "succeeded"
174
+ ? t("dataSources.searchConsole.runSucceeded", { when })
175
+ : run.status === "not_configured"
176
+ ? run.errorCode === "no_search_console_sites"
177
+ ? t("dataSources.notConfigured")
178
+ : t("dataSources.searchConsole.runNeedsAccess", { when })
179
+ : run.status === "running"
180
+ ? t("dataSources.searchConsole.runRunning", { when })
181
+ : t("dataSources.searchConsole.runFailed", { when });
182
+ return (
183
+ <div className="space-y-1 text-xs">
184
+ <p className={tone}>{label}</p>
185
+ {run.errorCode === "google_credentials_unreadable" ? (
186
+ <p className="break-words text-muted-foreground">
187
+ {t("dataSources.searchConsole.keyUnreadable")}
188
+ </p>
189
+ ) : (
190
+ run.errorMessage &&
191
+ run.status !== "succeeded" && (
192
+ <p className="break-words text-muted-foreground">
193
+ {run.errorMessage}
194
+ </p>
195
+ )
196
+ )}
197
+ </div>
198
+ );
199
+ }
200
+
201
+ function PropertyProblem({
202
+ problem,
203
+ }: {
204
+ problem: SearchConsolePropertyProblem;
205
+ }) {
206
+ const t = useT();
207
+ const text =
208
+ problem.kind === "bare_domain"
209
+ ? t("dataSources.searchConsole.propertyBareDomain", {
210
+ suggestion: problem.suggestion,
211
+ })
212
+ : problem.kind === "missing_trailing_slash"
213
+ ? t("dataSources.searchConsole.propertyMissingSlash", {
214
+ suggestion: problem.suggestion,
215
+ })
216
+ : problem.kind === "invalid_domain" && problem.suggestion
217
+ ? t("dataSources.searchConsole.propertyInvalidDomain", {
218
+ suggestion: problem.suggestion,
219
+ })
220
+ : t("dataSources.searchConsole.propertyUnrecognized");
221
+ return <p className="text-xs text-destructive">{text}</p>;
222
+ }
223
+
224
+ export function SearchConsoleDetail() {
225
+ const t = useT();
226
+ const queryClient = useQueryClient();
227
+ const { canManageOrg } = useOrgRole();
228
+ const { data: status, isLoading, error } = useContentCollectionStatus();
229
+ const savedSites = status?.config?.searchConsole.sites;
230
+ const savedEnabled = status?.config?.searchConsole.enabled ?? false;
231
+ const [sites, setSites] = useState<SearchConsoleSite[]>([]);
232
+ const [touched, setTouched] = useState<ReadonlySet<number>>(new Set());
233
+
234
+ const savedSitesKey = JSON.stringify(savedSites ?? []);
235
+ useEffect(() => {
236
+ const saved = JSON.parse(savedSitesKey) as SearchConsoleSite[];
237
+ setSites(saved.length ? saved : [{ siteUrl: "" }]);
238
+ setTouched(new Set());
239
+ }, [savedSitesKey]);
240
+
241
+ const configure = useActionMutation("configure-content-collection", {
242
+ onSuccess: () => {
243
+ void queryClient.invalidateQueries({
244
+ queryKey: ["action", "get-content-collection"],
245
+ });
246
+ },
247
+ });
248
+
249
+ if (isLoading) {
250
+ return (
251
+ <div className="space-y-3">
252
+ <Skeleton className="h-4 w-28" />
253
+ <Skeleton className="h-8 w-full" />
254
+ <Skeleton className="h-8 w-full" />
255
+ </div>
256
+ );
257
+ }
258
+ if (error || !status) {
259
+ return (
260
+ <p className="text-xs text-destructive">
261
+ {t("dataSources.searchConsole.unavailable", {
262
+ message: error instanceof Error ? error.message : String(error),
263
+ })}
264
+ </p>
265
+ );
266
+ }
267
+
268
+ const cleanSites = sites
269
+ .map((site) => ({
270
+ siteUrl: normalizeSearchConsoleProperty(site.siteUrl),
271
+ ...(site.pathPrefix?.trim()
272
+ ? { pathPrefix: site.pathPrefix.trim() }
273
+ : {}),
274
+ }))
275
+ .filter((site) => site.siteUrl);
276
+ const dirty = JSON.stringify(cleanSites) !== savedSitesKey;
277
+ const problems = sites.map((site) =>
278
+ site.siteUrl.trim() ? searchConsolePropertyProblem(site.siteUrl) : null,
279
+ );
280
+ const pathProblems = sites.map((site) =>
281
+ searchConsolePathPrefixProblem(site.pathPrefix),
282
+ );
283
+ const hasProblem = problems.some(Boolean) || pathProblems.some(Boolean);
284
+ const checked = configure.isPending
285
+ ? Boolean(
286
+ (configure.variables as { searchConsole?: boolean } | undefined)
287
+ ?.searchConsole,
288
+ )
289
+ : savedEnabled;
290
+ const lastRun = status.steps.find(
291
+ (step) => step.step === "search_console",
292
+ )?.lastRun;
293
+
294
+ const save = (enabled: boolean, toggle = false) => {
295
+ configure.mutate(searchConsoleUpdate(status, enabled, cleanSites, toggle));
296
+ };
297
+
298
+ const updateSite = (index: number, patch: Partial<SearchConsoleSite>) => {
299
+ setSites((current) =>
300
+ current.map((site, i) => (i === index ? { ...site, ...patch } : site)),
301
+ );
302
+ };
303
+
304
+ return (
305
+ <div className="space-y-5">
306
+ <ServiceAccountRow account={status.serviceAccount} />
307
+
308
+ <div className="space-y-2">
309
+ <p className="text-xs font-medium text-foreground">
310
+ {t("dataSources.searchConsole.properties")}
311
+ </p>
312
+ {sites.map((site, index) => (
313
+ <div key={index} className="space-y-1">
314
+ <div className="flex items-center gap-2">
315
+ <Input
316
+ value={site.siteUrl}
317
+ onChange={(event) =>
318
+ updateSite(index, { siteUrl: event.target.value })
319
+ }
320
+ onBlur={() =>
321
+ setTouched((current) => new Set(current).add(index))
322
+ }
323
+ placeholder="sc-domain:example.com"
324
+ aria-label={t("dataSources.searchConsole.siteLabel")}
325
+ aria-invalid={touched.has(index) && Boolean(problems[index])}
326
+ disabled={!canManageOrg}
327
+ className="h-8 flex-1 font-mono text-xs"
328
+ />
329
+ <Input
330
+ value={site.pathPrefix ?? ""}
331
+ onChange={(event) =>
332
+ updateSite(index, { pathPrefix: event.target.value })
333
+ }
334
+ onBlur={() =>
335
+ setTouched((current) => new Set(current).add(index))
336
+ }
337
+ aria-invalid={touched.has(index) && pathProblems[index]}
338
+ placeholder="/blog/"
339
+ aria-label={t("dataSources.searchConsole.pathLabel")}
340
+ disabled={!canManageOrg}
341
+ className="h-8 w-28 font-mono text-xs"
342
+ />
343
+ {canManageOrg && (
344
+ <Button
345
+ size="sm"
346
+ variant="ghost"
347
+ onClick={() => {
348
+ setSites((current) =>
349
+ current.filter((_, i) => i !== index),
350
+ );
351
+ setTouched(
352
+ (current) =>
353
+ new Set(
354
+ [...current].flatMap((i) =>
355
+ i === index ? [] : [i < index ? i : i - 1],
356
+ ),
357
+ ),
358
+ );
359
+ }}
360
+ aria-label={t("dataSources.searchConsole.removeProperty")}
361
+ className="h-8 w-8 p-0"
362
+ >
363
+ <IconTrash className="h-3.5 w-3.5" />
364
+ </Button>
365
+ )}
366
+ </div>
367
+ {touched.has(index) && pathProblems[index] && (
368
+ <p className="text-xs text-destructive">
369
+ {t("dataSources.searchConsole.pathInvalid")}
370
+ </p>
371
+ )}
372
+ {touched.has(index) && problems[index] && (
373
+ <PropertyProblem problem={problems[index]} />
374
+ )}
375
+ </div>
376
+ ))}
377
+ {canManageOrg && (
378
+ <div className="flex items-center justify-between gap-2">
379
+ <Button
380
+ size="sm"
381
+ variant="ghost"
382
+ onClick={() =>
383
+ setSites((current) => [...current, { siteUrl: "" }])
384
+ }
385
+ className="text-xs"
386
+ >
387
+ <IconPlus className="mr-1 h-3.5 w-3.5" />
388
+ {t("dataSources.searchConsole.addProperty")}
389
+ </Button>
390
+ {dirty && (
391
+ <Button
392
+ size="sm"
393
+ onClick={() => save(savedEnabled)}
394
+ disabled={
395
+ configure.isPending ||
396
+ hasProblem ||
397
+ (savedEnabled && cleanSites.length === 0)
398
+ }
399
+ className="text-xs"
400
+ >
401
+ {t("dataSources.searchConsole.save")}
402
+ </Button>
403
+ )}
404
+ </div>
405
+ )}
406
+ </div>
407
+
408
+ <div className="flex items-center justify-between gap-3">
409
+ <label
410
+ htmlFor="search-console-collect"
411
+ className="text-xs font-medium text-foreground"
412
+ >
413
+ {t("dataSources.searchConsole.collectDaily")}
414
+ </label>
415
+ <Switch
416
+ id="search-console-collect"
417
+ checked={checked}
418
+ disabled={
419
+ !canManageOrg ||
420
+ configure.isPending ||
421
+ (!checked && (cleanSites.length === 0 || hasProblem))
422
+ }
423
+ onCheckedChange={(checked) => save(checked, true)}
424
+ />
425
+ </div>
426
+ {!canManageOrg && (
427
+ <p className="text-xs text-muted-foreground">
428
+ {t("dataSources.searchConsole.adminOnly")}
429
+ </p>
430
+ )}
431
+ {configure.error && (
432
+ <p className="text-xs text-destructive">
433
+ {configure.error instanceof Error
434
+ ? configure.error.message
435
+ : String(configure.error)}
436
+ </p>
437
+ )}
438
+ {lastRun && savedEnabled && <LastRun run={lastRun} />}
439
+ </div>
440
+ );
441
+ }
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: added
3
+ date: 2026-10-15
4
+ ---
5
+
6
+ A Content dashboard lists every blog article with its author, pageviews, and search clicks, and says when a source failed or isn't set up instead of showing zero
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: added
3
+ date: 2026-10-15
4
+ ---
5
+
6
+ Data sources has a Google Search Console card that shows the service account to grant and turns on daily collection
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: added
3
+ date: 2026-10-16
4
+ ---
5
+
6
+ The Content dashboard can show signups per blog article, collected once a day behind a BigQuery cost cap that a dry run measures first
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: security
3
+ date: 2026-10-19
4
+ ---
5
+
6
+ Creating or revoking a public analytics key now asks you to approve it every time the agent or another app tries it.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: added
3
+ date: 2026-10-19
4
+ ---
5
+
6
+ Import from Sigma lists every workbook you can see in Sigma, with search. Workbooks already migrated show greyed out with their owner and open the dashboard in a new tab, so nobody migrates the same workbook twice.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-10-20
4
+ ---
5
+
6
+ Blog signup attribution excludes signups before the first visit and URLs on unrelated hosts
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-10-20
4
+ ---
5
+
6
+ Collapsed Analytics navigation starts at the top with more space between icons.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-10-20
4
+ ---
5
+
6
+ Content keeps successful coverage dates and missing-day gaps, and Search Console setup validates paths before saving
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-10-20
4
+ ---
5
+
6
+ Private GitHub repositories can be indexed with an organization-scoped GitHub token
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-10-20
4
+ ---
5
+
6
+ Search Console preserves historical backfill after empty results and keeps daily collection paused when editing properties.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-10-20
4
+ ---
5
+
6
+ Search Console refreshes historical coverage when properties change and advances normally after empty results
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-10-20
4
+ ---
5
+
6
+ Search Console setup keeps saved properties readable and supports disabling collection with invalid drafts.