@agent-native/core-corpus 0.202.0 → 0.202.1-nightly-20261007011022

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 (71) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/actions/list-sql-dashboards.ts +2 -0
  3. package/corpus/templates/analytics/actions/query-dashboard-panel.ts +12 -3
  4. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +49 -5
  5. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +38 -8
  6. package/corpus/templates/analytics/app/i18n/en-US.ts +1 -1
  7. package/corpus/templates/analytics/app/lib/dashboard-visibility.ts +17 -2
  8. package/corpus/templates/analytics/app/lib/sql-query.ts +60 -8
  9. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +8 -18
  10. package/corpus/templates/analytics/changelog/2026-10-06-dashboard-filter-mine-now-lists-every-dashboard-you-own-incl.md +6 -0
  11. package/corpus/templates/analytics/changelog/2026-10-06-refreshing-a-bigquery-dashboard-panel-fetches-current-data.md +6 -0
  12. package/corpus/templates/analytics/server/db/schema.ts +5 -0
  13. package/corpus/templates/analytics/server/lib/bigquery.ts +500 -133
  14. package/corpus/templates/analytics/server/lib/dashboard-panel-query.ts +7 -2
  15. package/corpus/templates/analytics/server/lib/dashboard-panel-source-resolver.ts +7 -0
  16. package/corpus/templates/analytics/server/plugins/db.ts +17 -0
  17. package/corpus/templates/brain/.agents/skills/ingestion-and-connectors/SKILL.md +7 -2
  18. package/corpus/templates/brain/changelog/2026-10-06-zoom-transcripts-now-download.md +6 -0
  19. package/corpus/templates/brain/server/lib/connectors.ts +66 -4
  20. package/corpus/templates/brain/server/lib/zoom.ts +37 -4
  21. package/corpus/templates/brain/server/register-secrets.ts +3 -3
  22. package/corpus/templates/calendar/changelog/2026-10-06-builder-io-setup-details-render-immediately-and-model-settin.md +6 -0
  23. package/corpus/templates/clips/app/components/player/create-account-dialog.tsx +123 -10
  24. package/corpus/templates/clips/app/components/player/sign-in-prompt-dialog.tsx +7 -2
  25. package/corpus/templates/clips/app/components/player/signed-out-share-actions.tsx +11 -2
  26. package/corpus/templates/clips/app/i18n/en-US.ts +8 -0
  27. package/corpus/templates/clips/app/routes/access-request.approve.tsx +4 -1
  28. package/corpus/templates/clips/app/routes/invite.$token.tsx +2 -1
  29. package/corpus/templates/clips/app/routes/share.$shareId.tsx +12 -8
  30. package/corpus/templates/clips/changelog/2026-10-06-fixed-returning-to-shared-clips-after-email-verification.md +6 -0
  31. package/corpus/templates/content/changelog/2026-10-06-generated-documents-from-chatgpt-open-in-the-full-content-ed.md +6 -0
  32. package/corpus/templates/content/server/lib/chatgpt-directory-tools.ts +0 -2
  33. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +12 -6
  34. package/corpus/templates/design/actions/generate-design.ts +188 -45
  35. package/corpus/templates/design/actions/present-design-variants.ts +113 -39
  36. package/corpus/templates/design/app/components/home/DesignHomeLibrary.tsx +5 -11
  37. package/corpus/templates/design/app/hooks/use-question-flow.ts +1 -1
  38. package/corpus/templates/design/app/pages/Index.tsx +57 -13
  39. package/corpus/templates/design/app/pages/design-editor/derive/overview-screens.ts +10 -2
  40. package/corpus/templates/design/app/pages/design-editor/geometry-persistence.ts +5 -4
  41. package/corpus/templates/design/changelog/2026-10-05-design-keeps-exact-size-screens-at-their-requested-dimension.md +6 -0
  42. package/corpus/templates/design/changelog/2026-10-06-the-recent-tab-stays-visible-while-designs-load-and-opens-im.md +6 -0
  43. package/corpus/templates/design/server/plugins/agent-chat.ts +1 -1
  44. package/corpus/templates/design/shared/canvas-dimensions.ts +204 -0
  45. package/corpus/templates/design/shared/responsive-frame-layout.ts +1 -0
  46. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +17 -5
  47. package/corpus/templates/mail/app/root.tsx +16 -1
  48. package/corpus/templates/mail/changelog/2026-10-02-pressing-r-on-a-message-in-the-list-opens-the-reply-window-e.md +6 -0
  49. package/corpus/templates/plan/app/components/layout/Sidebar.tsx +5 -2
  50. package/corpus/templates/plan/app/i18n/en-US.ts +6 -1
  51. package/corpus/templates/plan/app/pages/PlansPage.tsx +113 -9
  52. package/corpus/templates/slides/actions/add-slide.ts +3 -3
  53. package/corpus/templates/slides/actions/get-deck-reference-context.ts +101 -21
  54. package/corpus/templates/slides/actions/get-design-system.ts +51 -11
  55. package/corpus/templates/slides/actions/read-composer-source.ts +5 -1
  56. package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +4 -1
  57. package/corpus/templates/slides/app/i18n/en-US.ts +1 -0
  58. package/corpus/templates/slides/app/lib/composer-context.ts +1 -1
  59. package/corpus/templates/slides/app/lib/create-deck-generation.ts +133 -34
  60. package/corpus/templates/slides/app/pages/DeckEditor.tsx +3 -3
  61. package/corpus/templates/slides/app/pages/Index.tsx +138 -41
  62. package/corpus/templates/slides/app/routes/access-request.approve.tsx +4 -1
  63. package/corpus/templates/slides/changelog/2026-10-06-fixed-returning-to-shared-decks-after-completing-an-account-.md +6 -0
  64. package/corpus/templates/slides/changelog/2026-10-06-generated-decks-from-chatgpt-open-in-the-full-slides-editor.md +6 -0
  65. package/corpus/templates/slides/changelog/2026-10-06-google-slides-exports-open-the-created-copy-directly.md +6 -0
  66. package/corpus/templates/slides/changelog/2026-10-06-slides-applies-linked-design-systems-to-deck-style-reference.md +6 -0
  67. package/corpus/templates/slides/changelog/2026-10-06-slides-stop-cleanly-when-generation-reaches-the-requested-sl.md +6 -0
  68. package/corpus/templates/slides/changelog/2026-10-06-slides-uses-a-reference-deck-s-linked-system-only-when-avail.md +6 -0
  69. package/corpus/templates/slides/changelog/2026-10-06-the-recent-tab-restores-immediately-from-the-last-known-libr.md +6 -0
  70. package/corpus/templates/slides/server/lib/chatgpt-directory-tools.ts +0 -2
  71. package/package.json +1 -1
package/corpus/README.md CHANGED
@@ -32,4 +32,4 @@ rg -n "defineAction|useActionQuery" node_modules/@agent-native/core-corpus/corpu
32
32
 
33
33
  ## Generated Counts
34
34
 
35
- - template files: 11246
35
+ - template files: 11263
@@ -70,12 +70,14 @@ export default defineAction({
70
70
  kind: "sql",
71
71
  archived,
72
72
  hidden,
73
+ includeCatalogMetadata: true,
73
74
  });
74
75
  return rows.map((d) => ({
75
76
  id: d.id,
76
77
  name: d.name,
77
78
  ...(d.parentId ? { parentId: d.parentId } : {}),
78
79
  ...(d.folderId ? { folderId: d.folderId } : {}),
80
+ ...(d.demoId ? { demo: true } : {}),
79
81
  ownerEmail: d.ownerEmail,
80
82
  orgId: d.orgId,
81
83
  visibility: d.visibility,
@@ -1,4 +1,4 @@
1
- import { defineAction } from "@agent-native/core/action";
1
+ import { defineAction, type ActionRunContext } from "@agent-native/core/action";
2
2
  import { getCredentialContext } from "@agent-native/core/server/request-context";
3
3
  import { z } from "zod";
4
4
 
@@ -14,18 +14,27 @@ export default defineAction({
14
14
  schema: z.object({
15
15
  source: z.enum(DASHBOARD_PANEL_SOURCES),
16
16
  query: z.unknown(),
17
+ forceRefresh: z.boolean().optional(),
17
18
  }),
18
19
  http: { method: "POST" },
19
20
  readOnly: true,
20
21
  agentTool: false,
21
22
  grounding: true,
22
- run: async (args) => {
23
+ run: async (args, actionContext?: ActionRunContext) => {
23
24
  const context = getCredentialContext();
24
25
  if (!context) {
25
26
  throw new Error("No authenticated context for query-dashboard-panel.");
26
27
  }
27
28
 
28
29
  const query = normalizeDashboardPanelQuery(args.source, args.query);
29
- return resolveAnalyticsPanelSource({ source: args.source, query }, context);
30
+ return resolveAnalyticsPanelSource(
31
+ {
32
+ source: args.source,
33
+ query,
34
+ ...(args.forceRefresh ? { forceRefresh: true } : {}),
35
+ ...(actionContext?.signal ? { signal: actionContext.signal } : {}),
36
+ },
37
+ context,
38
+ );
30
39
  },
31
40
  });
@@ -1253,6 +1253,8 @@ interface SqlChartProps {
1253
1253
  showLoadingWhenDisabled?: boolean;
1254
1254
  timeRange?: number;
1255
1255
  reportScreenshot?: boolean;
1256
+ refreshToken?: number;
1257
+ onRefreshRequested?: () => void;
1256
1258
  onExportCsvChange?: (handler: (() => void) | null) => void;
1257
1259
  onCopyTableChange?: (handler: (() => Promise<void>) | null) => void;
1258
1260
  dashboardId?: string;
@@ -1267,6 +1269,8 @@ export function SqlChart({
1267
1269
  showLoadingWhenDisabled = true,
1268
1270
  timeRange,
1269
1271
  reportScreenshot = false,
1272
+ refreshToken = 0,
1273
+ onRefreshRequested,
1270
1274
  onExportCsvChange,
1271
1275
  onCopyTableChange,
1272
1276
  dashboardId,
@@ -1278,17 +1282,21 @@ export function SqlChart({
1278
1282
  const isExtension = panel.chartType === "extension";
1279
1283
  const shouldQuery = !isSection && !isExtension && loadData && !resultOverride;
1280
1284
  const sql = serializePanelSql(resolvedSql ?? panel.sql);
1285
+ const [localRefreshToken, setLocalRefreshToken] = useState(0);
1281
1286
  const {
1282
1287
  data: queryResult,
1283
1288
  isLoading: queryIsLoading,
1284
1289
  isFetching: queryIsFetching,
1285
1290
  error: queryError,
1286
- refetch,
1287
1291
  } = useSqlQuery(
1288
1292
  ["sql-chart", dashboardId || panel.id, sql, panel.source],
1289
1293
  sql,
1290
1294
  panel.source,
1291
- { enabled: shouldQuery, reportScreenshot },
1295
+ {
1296
+ enabled: shouldQuery,
1297
+ reportScreenshot,
1298
+ refreshToken: refreshToken + localRefreshToken,
1299
+ },
1292
1300
  );
1293
1301
 
1294
1302
  const result = resultOverride ?? queryResult;
@@ -1300,6 +1308,14 @@ export function SqlChart({
1300
1308
  ? (result?.error ??
1301
1309
  (queryError ? formatSqlChartError(queryError) : undefined))
1302
1310
  : undefined;
1311
+ const refreshError =
1312
+ shouldQuery && rawRows.length > 0 && queryError
1313
+ ? formatSqlChartError(queryError)
1314
+ : undefined;
1315
+ const requestRefresh = () => {
1316
+ if (onRefreshRequested) onRefreshRequested();
1317
+ else setLocalRefreshToken((token) => token + 1);
1318
+ };
1303
1319
 
1304
1320
  const { rows: queryRows, forcedYKeys } = useMemo(() => {
1305
1321
  if (panel.config?.pivot && rawRows.length) {
@@ -1402,7 +1418,7 @@ export function SqlChart({
1402
1418
  type="button"
1403
1419
  variant="secondary"
1404
1420
  size="sm"
1405
- onClick={() => void refetch()}
1421
+ onClick={requestRefresh}
1406
1422
  >
1407
1423
  <IconRefresh className="mr-2 h-3.5 w-3.5" />
1408
1424
  {t("sqlDashboard.refresh")}
@@ -1425,8 +1441,35 @@ export function SqlChart({
1425
1441
  }
1426
1442
 
1427
1443
  const missingConfigKeys = configuredKeysMissingFromRows(rows, panel);
1428
- const withConfigWarning = (node: ReactNode) =>
1429
- missingConfigKeys.length > 0 ? (
1444
+ const withConfigWarning = (node: ReactNode) => {
1445
+ if (refreshError) {
1446
+ return (
1447
+ <div className="flex h-full min-h-0 flex-col gap-2">
1448
+ <div
1449
+ className="flex items-center justify-between gap-3 rounded-md border border-destructive/30 bg-destructive/5 px-3 py-2"
1450
+ role="alert"
1451
+ >
1452
+ <p className="min-w-0 break-words text-xs text-destructive">
1453
+ {refreshError}
1454
+ </p>
1455
+ <Button
1456
+ type="button"
1457
+ variant="secondary"
1458
+ size="sm"
1459
+ onClick={requestRefresh}
1460
+ >
1461
+ <IconRefresh className="mr-2 h-3.5 w-3.5" />
1462
+ {t("sqlDashboard.refresh")}
1463
+ </Button>
1464
+ </div>
1465
+ {missingConfigKeys.length > 0 && (
1466
+ <ConfigWarning keys={missingConfigKeys} />
1467
+ )}
1468
+ <div className="min-h-0 flex-1">{node}</div>
1469
+ </div>
1470
+ );
1471
+ }
1472
+ return missingConfigKeys.length > 0 ? (
1430
1473
  <div className="space-y-2">
1431
1474
  <ConfigWarning keys={missingConfigKeys} />
1432
1475
  {node}
@@ -1434,6 +1477,7 @@ export function SqlChart({
1434
1477
  ) : (
1435
1478
  node
1436
1479
  );
1480
+ };
1437
1481
 
1438
1482
  if (chartType === "metric") {
1439
1483
  return withConfigWarning(<MetricRenderer rows={rows} panel={panel} />);
@@ -71,6 +71,9 @@ type SidebarDashboard = {
71
71
  resourceId?: string;
72
72
  visibility?: Visibility;
73
73
  ownerEmail?: string | null;
74
+ demo?: boolean;
75
+ /** Owner label, set only when another dashboard shares this title. */
76
+ ownerHint?: string;
74
77
  parentId?: string;
75
78
  };
76
79
 
@@ -486,6 +489,7 @@ function SortableRow({
486
489
  id,
487
490
  favoriteKey,
488
491
  name,
492
+ hint,
489
493
  href,
490
494
  isActive,
491
495
  favoriteIds,
@@ -504,6 +508,7 @@ function SortableRow({
504
508
  id: string;
505
509
  favoriteKey: string;
506
510
  name: string;
511
+ hint?: string;
507
512
  href: string;
508
513
  isActive: boolean;
509
514
  favoriteIds: Set<string>;
@@ -725,10 +730,17 @@ function SortableRow({
725
730
  onTouchStart={onPrefetch}
726
731
  className="min-w-0 flex-1 px-2 py-1.5 pe-12 text-xs transition-[padding] md:pe-2 md:group-hover/item:pe-12 md:group-focus-within/item:pe-12"
727
732
  >
728
- <span className="block truncate">{name}</span>
733
+ <span className="block truncate">
734
+ {name}
735
+ {hint ? (
736
+ <span className="text-muted-foreground"> · {hint}</span>
737
+ ) : null}
738
+ </span>
729
739
  </Link>
730
740
  </TooltipTrigger>
731
- <TooltipContent side="right">{name}</TooltipContent>
741
+ <TooltipContent side="right">
742
+ {hint ? `${name} · ${hint}` : name}
743
+ </TooltipContent>
732
744
  </Tooltip>
733
745
  )}
734
746
  <div className="pointer-events-none absolute end-1 top-1/2 flex -translate-y-1/2 items-center gap-0.5 opacity-100 transition-opacity md:opacity-0 md:group-hover/item:opacity-100 md:group-focus-within/item:opacity-100">
@@ -981,6 +993,7 @@ function SortableDashboardItem({
981
993
  id={d.id}
982
994
  favoriteKey={favoriteKey}
983
995
  name={d.name}
996
+ hint={d.ownerHint}
984
997
  href={href}
985
998
  isActive={isActive}
986
999
  favoriteIds={favoriteIds}
@@ -1182,6 +1195,7 @@ type SqlDashboardListItem = {
1182
1195
  name: string;
1183
1196
  visibility?: Visibility;
1184
1197
  ownerEmail?: string | null;
1198
+ demo?: boolean;
1185
1199
  parentId?: string;
1186
1200
  };
1187
1201
 
@@ -1211,6 +1225,7 @@ async function fetchSqlDashboards(
1211
1225
  ? d.name
1212
1226
  : t("sidebar.untitledDashboard"),
1213
1227
  visibility: d.visibility as Visibility,
1228
+ ...(d.demo === true ? { demo: true } : {}),
1214
1229
  ...(ownerEmail ? { ownerEmail } : {}),
1215
1230
  parentId:
1216
1231
  typeof d.parentId === "string" && d.parentId.trim().length > 0
@@ -1225,6 +1240,7 @@ async function fetchSidebarAnalyses(t: (key: string) => string): Promise<
1225
1240
  id: string;
1226
1241
  name: string;
1227
1242
  visibility: Visibility;
1243
+ ownerEmail?: string;
1228
1244
  hiddenAt: string | null;
1229
1245
  }[]
1230
1246
  > {
@@ -1241,6 +1257,9 @@ async function fetchSidebarAnalyses(t: (key: string) => string): Promise<
1241
1257
  a.visibility === "org" || a.visibility === "public"
1242
1258
  ? a.visibility
1243
1259
  : ("private" as Visibility),
1260
+ ...(typeof a.ownerEmail === "string" && a.ownerEmail
1261
+ ? { ownerEmail: a.ownerEmail }
1262
+ : {}),
1244
1263
  hiddenAt: typeof a.hiddenAt === "string" ? a.hiddenAt : null,
1245
1264
  }));
1246
1265
  }
@@ -1801,6 +1820,7 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
1801
1820
  source: "sql",
1802
1821
  visibility: d.visibility,
1803
1822
  ownerEmail: d.ownerEmail,
1823
+ demo: d.demo,
1804
1824
  parentId: d.parentId,
1805
1825
  }));
1806
1826
  const analysisItems: SidebarDashboard[] = analysesList.map((a) => ({
@@ -1809,6 +1829,7 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
1809
1829
  name: a.name,
1810
1830
  source: "analysis",
1811
1831
  visibility: a.visibility,
1832
+ ...(a.ownerEmail ? { ownerEmail: a.ownerEmail } : {}),
1812
1833
  }));
1813
1834
  const all = [...staticItems, ...sqlItems, ...analysisItems];
1814
1835
  if (dashboardSortMode === "alphabetical") {
@@ -1851,13 +1872,22 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
1851
1872
  popularity,
1852
1873
  ]);
1853
1874
 
1854
- const filteredDashboards = useMemo(
1855
- () =>
1856
- visibleDashboards.filter((dashboard) =>
1875
+ const filteredDashboards = useMemo(() => {
1876
+ const titleCounts = new Map<string, number>();
1877
+ for (const d of visibleDashboards) {
1878
+ const key = d.name.trim().toLowerCase();
1879
+ titleCounts.set(key, (titleCounts.get(key) ?? 0) + 1);
1880
+ }
1881
+ return visibleDashboards
1882
+ .filter((dashboard) =>
1857
1883
  matchesVisibilityFilter(dashboard, dashFilter, auth?.email),
1858
- ),
1859
- [auth?.email, visibleDashboards, dashFilter],
1860
- );
1884
+ )
1885
+ .map((d) =>
1886
+ d.ownerEmail && (titleCounts.get(d.name.trim().toLowerCase()) ?? 0) > 1
1887
+ ? { ...d, ownerHint: d.ownerEmail.split("@")[0] }
1888
+ : d,
1889
+ );
1890
+ }, [auth?.email, visibleDashboards, dashFilter]);
1861
1891
 
1862
1892
  const dashboardChildren = useMemo<Map<string, SidebarDashboard[]>>(() => {
1863
1893
  const byId = new Map(filteredDashboards.map((d) => [d.id, d]));
@@ -254,7 +254,7 @@ export default {
254
254
  visibilityPrivateOnly: "Mine",
255
255
  visibilitySharedOnly: "Shared",
256
256
  visibilityAllDescription: "Show all items",
257
- visibilityPrivateOnlyDescription: "Show items visible only to you",
257
+ visibilityPrivateOnlyDescription: "Show items you created",
258
258
  visibilitySharedOnlyDescription: "Show org-shared and public items",
259
259
  hiddenAnalyses: "Hidden analyses",
260
260
  shareWithOrg: "Share with org",
@@ -4,6 +4,8 @@ export type DashboardVisibilityFilter = "all" | "private" | "shared";
4
4
  export type DashboardVisibilityItem = {
5
5
  visibility?: DashboardVisibility;
6
6
  ownerEmail?: string | null;
7
+ /** Sample content installed for the user; never theirs, never someone else's. */
8
+ demo?: boolean;
7
9
  };
8
10
 
9
11
  function normalizeEmail(email: string | null | undefined): string | null {
@@ -28,13 +30,26 @@ export function isDashboardMine(
28
30
  return true;
29
31
  }
30
32
 
33
+ // Sidebar "Mine" means "I own it", whether or not I have shared it. The
34
+ // Overview's personal/shared scope keeps `isDashboardMine`: it picks folders.
35
+ function isOwnedByViewer(
36
+ item: DashboardVisibilityItem,
37
+ currentUserEmail?: string | null,
38
+ ): boolean {
39
+ if (!("ownerEmail" in item)) return item.visibility === "private";
40
+ const ownerEmail = normalizeEmail(item.ownerEmail);
41
+ const viewerEmail = normalizeEmail(currentUserEmail);
42
+ return Boolean(ownerEmail && viewerEmail && ownerEmail === viewerEmail);
43
+ }
44
+
31
45
  export function matchesDashboardVisibilityFilter(
32
46
  item: DashboardVisibilityItem,
33
47
  filter: DashboardVisibilityFilter,
34
48
  currentUserEmail?: string | null,
35
49
  ): boolean {
36
50
  if (filter === "all") return true;
51
+ if (item.demo) return false;
37
52
  return filter === "private"
38
- ? isDashboardMine(item, currentUserEmail)
39
- : !isDashboardMine(item, currentUserEmail);
53
+ ? isOwnedByViewer(item, currentUserEmail)
54
+ : !isOwnedByViewer(item, currentUserEmail);
40
55
  }
@@ -4,7 +4,8 @@ import {
4
4
  MAX_CONCURRENT_FIRST_PARTY_SQL_QUERIES,
5
5
  MAX_CONCURRENT_SQL_QUERIES,
6
6
  } from "@shared/sql-query-limits";
7
- import { useQuery } from "@tanstack/react-query";
7
+ import { useQuery, useQueryClient } from "@tanstack/react-query";
8
+ import { useEffect, useRef } from "react";
8
9
 
9
10
  import type { DataSourceType } from "@/pages/adhoc/sql-dashboard/types";
10
11
 
@@ -138,7 +139,7 @@ export async function executeSqlQuery(
138
139
  sql: string,
139
140
  source: DataSourceType,
140
141
  signal?: AbortSignal,
141
- options?: { reportScreenshot?: boolean },
142
+ options?: { reportScreenshot?: boolean; forceRefresh?: boolean },
142
143
  ): Promise<SqlQueryResult> {
143
144
  const deadline = createDeadlineSignal(
144
145
  signal,
@@ -150,7 +151,13 @@ export async function executeSqlQuery(
150
151
  release = await acquireSqlQuerySlot(source, deadline.signal);
151
152
  data = await callAction<DashboardPanelQueryResponse>(
152
153
  "query-dashboard-panel",
153
- { query: sql, source },
154
+ {
155
+ query: sql,
156
+ source,
157
+ ...(options?.forceRefresh && source === "bigquery"
158
+ ? { forceRefresh: true }
159
+ : {}),
160
+ },
154
161
  {
155
162
  signal: deadline.signal,
156
163
  timeoutMs: DASHBOARD_REPORT_ACTION_TIMEOUT_MS,
@@ -191,16 +198,36 @@ export function useSqlQuery(
191
198
  refetchOnWindowFocus?: boolean | "always";
192
199
  retry?: boolean | number;
193
200
  reportScreenshot?: boolean;
201
+ refreshToken?: number;
194
202
  staleTime?: number;
195
203
  },
196
204
  ) {
197
- return useQuery<SqlQueryResult>({
205
+ const successfulRefreshToken = useRef(0);
206
+ const refreshToken = options?.refreshToken ?? 0;
207
+ const enabled = options?.enabled ?? true;
208
+ const latestRefreshToken = useRef(refreshToken);
209
+ const latestStartedRefreshToken = useRef(refreshToken);
210
+ latestRefreshToken.current = refreshToken;
211
+ const previousRefreshToken = useRef(refreshToken);
212
+ const previousEnabled = useRef(enabled);
213
+ const queryClient = useQueryClient();
214
+ const query = useQuery<SqlQueryResult>({
198
215
  queryKey,
199
- queryFn: ({ signal }) =>
200
- executeSqlQuery(sql, source, signal, {
216
+ queryFn: async ({ signal }) => {
217
+ const currentRefreshToken = latestRefreshToken.current;
218
+ latestStartedRefreshToken.current = currentRefreshToken;
219
+ const forceRefresh = currentRefreshToken > successfulRefreshToken.current;
220
+ const result = await executeSqlQuery(sql, source, signal, {
201
221
  reportScreenshot: options?.reportScreenshot,
202
- }),
203
- enabled: options?.enabled ?? true,
222
+ forceRefresh,
223
+ });
224
+ successfulRefreshToken.current = Math.max(
225
+ successfulRefreshToken.current,
226
+ currentRefreshToken,
227
+ );
228
+ return result;
229
+ },
230
+ enabled,
204
231
  refetchInterval: options?.refetchInterval,
205
232
  refetchOnMount: options?.refetchOnMount ?? false,
206
233
  refetchOnReconnect: options?.refetchOnReconnect ?? false,
@@ -208,4 +235,29 @@ export function useSqlQuery(
208
235
  retry: options?.retry ?? false,
209
236
  staleTime: options?.staleTime ?? 5 * 60 * 1000,
210
237
  });
238
+ const refetch = query.refetch;
239
+
240
+ useEffect(() => {
241
+ const wasEnabled = previousEnabled.current;
242
+ previousEnabled.current = enabled;
243
+ const refreshTokenChanged = refreshToken > previousRefreshToken.current;
244
+ previousRefreshToken.current = refreshToken;
245
+ if (!enabled) return;
246
+ const enablingPendingRefresh =
247
+ !wasEnabled && refreshToken > successfulRefreshToken.current;
248
+ if (
249
+ (!refreshTokenChanged && !enablingPendingRefresh) ||
250
+ refreshToken <= successfulRefreshToken.current
251
+ ) {
252
+ return;
253
+ }
254
+ if (query.isFetching && latestStartedRefreshToken.current >= refreshToken) {
255
+ return;
256
+ }
257
+ void queryClient
258
+ .cancelQueries({ queryKey, exact: true })
259
+ .then(() => refetch());
260
+ }, [enabled, query.isFetching, queryClient, queryKey, refetch, refreshToken]);
261
+
262
+ return query;
211
263
  }
@@ -16,8 +16,7 @@ import {
16
16
  IconBrandGoogle,
17
17
  IconArrowUpRight,
18
18
  } from "@tabler/icons-react";
19
- import { useQueryClient } from "@tanstack/react-query";
20
- import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
19
+ import { memo, useCallback, useEffect, useRef, useState } from "react";
21
20
  import { toast } from "sonner";
22
21
 
23
22
  import { ChartFillHeight, SqlChart } from "@/components/dashboard/SqlChart";
@@ -62,7 +61,6 @@ import type { SelectDashboardPanelOptions } from "@/hooks/use-dashboard-chat-con
62
61
  import { buildCustomBlockPromotionRequest } from "@/lib/custom-block-promotion";
63
62
  import { cn } from "@/lib/utils";
64
63
 
65
- import { serializePanelSql } from "./panel-sql";
66
64
  import { timeRangeDays } from "./pivot";
67
65
  import type { DashboardFilter, SqlPanel } from "./types";
68
66
  import { ViewSqlPopover } from "./ViewSqlPopover";
@@ -152,7 +150,6 @@ export function SqlChartCard({
152
150
  }: SqlChartCardProps) {
153
151
  const t = useT();
154
152
  const timeRange = timeRangeDays(filters?.timeRange);
155
- const queryClient = useQueryClient();
156
153
  const exportToGoogleSheets = useActionMutation(
157
154
  "export-dashboard-panel-to-google-sheet",
158
155
  );
@@ -172,17 +169,8 @@ export function SqlChartCard({
172
169
  panel.chartType === "section" ||
173
170
  panel.chartType === "extension",
174
171
  );
172
+ const [refreshToken, setRefreshToken] = useState(0);
175
173
  const cardRef = useRef<HTMLDivElement | null>(null);
176
- const chartQueryKey = useMemo(
177
- () =>
178
- [
179
- "sql-chart",
180
- dashboardId || panel.id,
181
- serializePanelSql(resolvedSql ?? panel.sql),
182
- panel.source,
183
- ] as const,
184
- [dashboardId, panel.id, panel.source, panel.sql, resolvedSql],
185
- );
186
174
  const setCardNodeRef = useCallback((node: HTMLDivElement | null) => {
187
175
  cardRef.current = node;
188
176
  }, []);
@@ -210,10 +198,8 @@ export function SqlChartCard({
210
198
 
211
199
  const handleRefresh = useCallback(() => {
212
200
  setShouldLoadData(true);
213
- void queryClient.invalidateQueries({
214
- queryKey: chartQueryKey,
215
- });
216
- }, [chartQueryKey, queryClient]);
201
+ setRefreshToken((token) => token + 1);
202
+ }, []);
217
203
 
218
204
  const handleExportToGoogleSheets = useCallback(async () => {
219
205
  if (!dashboardId || panel.chartType !== "table") return;
@@ -784,6 +770,8 @@ export function SqlChartCard({
784
770
  loadData={shouldLoadData}
785
771
  timeRange={timeRange}
786
772
  reportScreenshot={reportScreenshot}
773
+ refreshToken={refreshToken}
774
+ onRefreshRequested={handleRefresh}
787
775
  dashboardId={dashboardId}
788
776
  onExportCsvChange={handleExportCsvChange}
789
777
  onCopyTableChange={handleCopyTableChange}
@@ -805,6 +793,8 @@ export function SqlChartCard({
805
793
  loadData
806
794
  timeRange={timeRange}
807
795
  reportScreenshot={reportScreenshot}
796
+ refreshToken={refreshToken}
797
+ onRefreshRequested={handleRefresh}
808
798
  dashboardId={dashboardId}
809
799
  extensionContext={extensionContext}
810
800
  />
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-10-06
4
+ ---
5
+
6
+ Dashboard filter "Mine" now lists every dashboard you own (including ones you've shared) and no longer includes installed demos or items shared with you
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-10-06
4
+ ---
5
+
6
+ Refreshing a BigQuery dashboard panel now fetches current data.
@@ -182,6 +182,11 @@ export const bigqueryCache = table("bigquery_cache", {
182
182
  bytesProcessed: integer("bytes_processed").notNull().default(0),
183
183
  createdAt: text("created_at").notNull(),
184
184
  expiresAt: text("expires_at").notNull(),
185
+ generation: integer("generation").notNull().default(0),
186
+ fenceToken: text("fence_token"),
187
+ refreshInProgress: boolean("refresh_in_progress").notNull().default(false),
188
+ refreshForced: boolean("refresh_forced").notNull().default(false),
189
+ refreshStartedAt: text("refresh_started_at"),
185
190
  });
186
191
 
187
192
  export const firstPartyAnalyticsCache = table("first_party_analytics_cache", {