From b3897bba3e6a2564654fb0459136214e3aece0f9 Mon Sep 17 00:00:00 2001 From: Rajat Date: Fri, 30 Oct 2026 11:22:54 +0530 Subject: [PATCH 1/2] fix(web): show why a docs sync failed and keep the GitHub dialog current - "What went wrong" lists the failed sync's diagnostics (file, location, message) instead of the bare error code. - The GitHub dialog polls every 5 seconds while a sync runs, so it settles on "In sync" or the failure without clicking Refresh. A retry after a failure shows as syncing. - File counts use the singular for one file. Co-Authored-By: Claude Opus 5.5 --- .../content/documentation-proposals.tsx | 3 +- .../content/documentation-source-manage.tsx | 37 ++++++++++++------- .../content/documentation-source.tsx | 25 ++++++++++--- 3 files changed, 46 insertions(+), 19 deletions(-) diff --git a/apps/web/components/content/documentation-proposals.tsx b/apps/web/components/content/documentation-proposals.tsx index f163d8a..c84a3c5 100644 --- a/apps/web/components/content/documentation-proposals.tsx +++ b/apps/web/components/content/documentation-proposals.tsx @@ -607,7 +607,8 @@ export function DocumentationProposals({ {proposal.title} - {proposal.captured.length} files ·{" "} + {proposal.captured.length}{" "} + {proposal.captured.length === 1 ? "file" : "files"} ·{" "} {new Date(proposal.createdAt).toLocaleString()} diff --git a/apps/web/components/content/documentation-source-manage.tsx b/apps/web/components/content/documentation-source-manage.tsx index 652f190..9fdc373 100644 --- a/apps/web/components/content/documentation-source-manage.tsx +++ b/apps/web/components/content/documentation-source-manage.tsx @@ -21,7 +21,10 @@ import { SummaryRow, } from "@/components/content/documentation-connect-parts"; import type { DocumentationGitSource } from "@/lib/api"; -import { PagesSourceSection } from "@/components/content/pages-source-section"; +import { + DiagnosticList, + PagesSourceSection, +} from "@/components/content/pages-source-section"; import { ApiReferenceSourceSection } from "@/components/content/api-reference-source-section"; import { cn } from "@/lib/utils"; @@ -84,6 +87,11 @@ type Health = { body: string; }; +/** A sync is underway ("Syncing"); the dialog polls until it settles. */ +export function syncInProgress(source: DocumentationGitSource) { + return Boolean(source.binding) && health(source, source.binding!).tone === "info"; +} + function health(source: DocumentationGitSource, binding: Binding): Health { if (source.status === "access_lost") return { @@ -103,22 +111,21 @@ function health(source: DocumentationGitSource, binding: Binding): Health { title: "Sync paused", body: "New commits aren't being picked up. Your site keeps showing the last good version.", }; + const syncing: Health = { + tone: "info", + title: "Syncing", + body: `Checking the newest commit on ${binding.branch}. Your site still shows the previous version.`, + }; + // A new run goes first: retrying after a failure must show (and poll) as syncing. + if (source.sync?.status === "queued" || source.sync?.status === "running") + return syncing; if (source.sync?.status === "failed" || binding.lastDiagnostic) return { tone: "warning", title: "Latest sync didn't go live", body: "Your site keeps showing the last good version. Fix the problem in GitHub and sync again.", }; - if ( - source.sync?.status === "queued" || - source.sync?.status === "running" || - binding.observedSha !== binding.liveSha - ) - return { - tone: "info", - title: "Syncing", - body: `Checking the newest commit on ${binding.branch}. Your site still shows the previous version.`, - }; + if (binding.observedSha !== binding.liveSha) return syncing; return { tone: "success", title: "In sync", @@ -324,9 +331,13 @@ export function ManageSource({ onRefresh={onRefresh} busy={busy} /> - {(binding.lastDiagnostic || source.sync?.diagnostic) && ( + {state.tone !== "info" && (binding.lastDiagnostic || source.sync?.diagnostic) && ( - {binding.lastDiagnostic ?? source.sync?.diagnostic} + {source.sync?.diagnostics.length ? ( + + ) : ( + (binding.lastDiagnostic ?? source.sync?.diagnostic) + )} )} {binding.lastPrReconcileError && ( diff --git a/apps/web/components/content/documentation-source.tsx b/apps/web/components/content/documentation-source.tsx index 3374e8d..6e23a0d 100644 --- a/apps/web/components/content/documentation-source.tsx +++ b/apps/web/components/content/documentation-source.tsx @@ -6,6 +6,7 @@ import { Check, ExternalLink, Github, GitPullRequest, Loader2 } from "lucide-rea import { ManageShell, ManageSource, + syncInProgress, } from "@/components/content/documentation-source-manage"; import { Badge } from "@/components/ui/codelit/badge"; import { Button } from "@/components/ui/codelit/button"; @@ -555,8 +556,21 @@ export function DocumentationSource({ verificationAttempt.current = attempt; verifyMergedSetup(); }, [migration?.planId, migration?.status, migration?.pullRequest?.status, setupId]); + const polling = open && !wizardMode && source != null && syncInProgress(source); + useEffect(() => { + if (!polling) return; + const timer = setInterval( + () => + void getDocumentationGitSource() + .then(setSource) + .catch(() => {}), + 5000, + ); + return () => clearInterval(timer); + }, [polling]); const repoLabel = migration?.repositoryName ?? repositoryName ?? "your repository"; const fileCount = migration?.total ?? 0; + const filesLabel = `${fileCount} ${fileCount === 1 ? "file" : "files"}`; const isEmptyRepo = Boolean(migration?.initializeEmptyRepositoryRequired); const draftCount = migration?.unpublishedDraftCount ?? 0; const noGitHub = !setupId; @@ -601,7 +615,7 @@ export function DocumentationSource({ title: "FrontLit checks the files and connects", note: "Usually takes a few seconds.", state: at[2], - waiting: `Checking ${fileCount} files`, + waiting: `Checking ${filesLabel}`, }, ]; }; @@ -1124,7 +1138,7 @@ export function DocumentationSource({ disabled={busy} > {isEmptyRepo - ? `I approve adding ${migration.selection.directory}/.gitkeep to ${migration.selection.branch} and opening a pull request with these ${fileCount} files.` + ? `I approve adding ${migration.selection.directory}/.gitkeep to ${migration.selection.branch} and opening a pull request with ${fileCount === 1 ? "this file" : `these ${filesLabel}`}.` : fileCount ? `I approve opening a pull request with ${fileCount === 1 ? "this file" : `these ${fileCount} files`}.` : `I approve opening a pull request that creates the empty ${migration.selection.directory}/ folder.`} @@ -1195,7 +1209,7 @@ export function DocumentationSource({ Pull request opened
@@ -1208,7 +1222,7 @@ export function DocumentationSource({ {migration.pullRequest?.pullRequestNumber ? ` · #${migration.pullRequest.pullRequestNumber}` : ""}{" "} - · {fileCount} files + · {filesLabel} {migration.pullRequest?.pullRequestUrl && ( @@ -1417,7 +1431,8 @@ export function DocumentationSource({ {preflight.directory}/ - {preflight.publishedCount} public of {preflight.fileCount} files + {preflight.publishedCount} public of {preflight.fileCount}{" "} + {preflight.fileCount === 1 ? "file" : "files"} ) : ( From 09e447ba28231a48b1d021038803f671c6caeb2b Mon Sep 17 00:00:00 2001 From: Rajat Date: Fri, 30 Oct 2026 22:49:32 +0530 Subject: [PATCH 2/2] fix(web): notice syncs that start while the GitHub dialog is open The dialog only polled once a sync was already running, so a sync started by a push while it showed "In sync" went unnoticed until Refresh. It now polls the whole time the connected dialog is open: every 5 s during a sync, every 30 s otherwise. Co-Authored-By: Claude Opus 5.5 --- apps/web/components/content/documentation-source.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/apps/web/components/content/documentation-source.tsx b/apps/web/components/content/documentation-source.tsx index 6e23a0d..8faf2b4 100644 --- a/apps/web/components/content/documentation-source.tsx +++ b/apps/web/components/content/documentation-source.tsx @@ -556,18 +556,21 @@ export function DocumentationSource({ verificationAttempt.current = attempt; verifyMergedSetup(); }, [migration?.planId, migration?.status, migration?.pullRequest?.status, setupId]); - const polling = open && !wizardMode && source != null && syncInProgress(source); + // While the connected dialog is open, poll: quickly during a sync, and + // slowly otherwise so a sync started by a push is noticed too. + const watching = open && !wizardMode && source?.binding != null; + const pollMs = source && syncInProgress(source) ? 5000 : 30000; useEffect(() => { - if (!polling) return; + if (!watching) return; const timer = setInterval( () => void getDocumentationGitSource() .then(setSource) .catch(() => {}), - 5000, + pollMs, ); return () => clearInterval(timer); - }, [polling]); + }, [watching, pollMs]); const repoLabel = migration?.repositoryName ?? repositoryName ?? "your repository"; const fileCount = migration?.total ?? 0; const filesLabel = `${fileCount} ${fileCount === 1 ? "file" : "files"}`;