diff --git a/docs/design-system/COMPONENTS.md b/docs/design-system/COMPONENTS.md index d32e94fa73..b64d1c5a90 100644 --- a/docs/design-system/COMPONENTS.md +++ b/docs/design-system/COMPONENTS.md @@ -988,7 +988,7 @@ This generated snapshot is a local source-derived inventory. It does not assert | `DisclosureGroup` | layout | yes | yes | inherited-global-root | yes | no | 1 | | `DoseLine` | answer | yes | yes | no | yes | no | 0 | | `DownloadLink` | controls | yes | yes | no | yes | no | 0 | -| `EmptyState` | feedback | yes | yes | inherited-global-root | yes | no | 13 | +| `EmptyState` | feedback | yes | yes | inherited-global-root | yes | no | 14 | | `ErrorState` | feedback | yes | yes | no | yes | no | 0 | | `ErrorSummary` | feedback | yes | yes | no | yes | no | 0 | | `ExternalTextLink` | controls | yes | yes | no | yes | no | 0 | diff --git a/docs/design-system/adoption-manifest.json b/docs/design-system/adoption-manifest.json index 4005a3d674..68b8f09e5f 100644 --- a/docs/design-system/adoption-manifest.json +++ b/docs/design-system/adoption-manifest.json @@ -748,6 +748,7 @@ "src/components/ClinicalDashboard.tsx", "src/components/applications-launcher-page.tsx", "src/components/clinical-dashboard/DocumentManagerPanel.tsx", + "src/components/clinical-dashboard/answer-cancelled-notice.tsx", "src/components/clinical-dashboard/clinical-output-helpers.tsx", "src/components/clinical-dashboard/document-admin.tsx", "src/components/clinical-dashboard/evidence-panels.tsx", @@ -763,6 +764,7 @@ "src/components/ClinicalDashboard.tsx", "src/components/applications-launcher-page.tsx", "src/components/clinical-dashboard/DocumentManagerPanel.tsx", + "src/components/clinical-dashboard/answer-cancelled-notice.tsx", "src/components/clinical-dashboard/clinical-output-helpers.tsx", "src/components/clinical-dashboard/document-admin.tsx", "src/components/clinical-dashboard/evidence-panels.tsx", diff --git a/src/components/ClinicalDashboard.tsx b/src/components/ClinicalDashboard.tsx index 81c2e4acb4..8548543137 100644 --- a/src/components/ClinicalDashboard.tsx +++ b/src/components/ClinicalDashboard.tsx @@ -15,7 +15,6 @@ import { RefreshCw, Search, ShieldAlert, - Square, Activity, Wrench, } from "lucide-react"; @@ -82,6 +81,7 @@ import { LazyGuideDialog, loadGuideDialog } from "@/components/clinical-dashboar import { SystemNotice, DegradedNoticeFrame } from "@/components/clinical-dashboard/dashboard-notices"; import { resolveModeHomeCanvasClass } from "@/components/clinical-dashboard/mode-home-canvas"; import { sanitizeAnswerDisplayText, sanitizeDisplayText } from "@/components/clinical-dashboard/display-text"; +import { AnswerCancelledNotice } from "@/components/clinical-dashboard/answer-cancelled-notice"; import { isPreformattedGroundedAnswer } from "@/components/clinical-dashboard/answer-content"; import { AnswerProgressStepper, @@ -3007,8 +3007,19 @@ function ClinicalDashboardContent({ useEffect(() => { if (showSharedHome) document.title = sharedHomeDocumentTitle(searchMode); }, [searchMode, showSharedHome]); + // A stopped generation reports on the last action rather than describing the + // page, so the notice renders at the top of the content column while this same + // condition still short-circuits the mode-home empty-state chain below. + const showAnswerCancelledNotice = answerLifecycle.status === "cancelled" && activeModeResultKind === "answer"; + // `submittedAnswerSearchActive` stays true after the reader presses Stop, and a + // cancel is not an `error`, so without the cancelled guard the pending branch + // held its skeleton on screen indefinitely — a shimmering placeholder promising + // an answer that was already abandoned, directly beneath the notice saying so. const showAnswerPending = - activeModeResultKind === "answer" && !answer && (loading || (submittedAnswerSearchActive && !error)); + activeModeResultKind === "answer" && + !answer && + !showAnswerCancelledNotice && + (loading || (submittedAnswerSearchActive && !error)); const answerProgressCompleted = answerProgressEvents.at(-1)?.stage === "complete"; const showAnswerProgress = activeModeResultKind === "answer" && @@ -3511,9 +3522,13 @@ function ClinicalDashboardContent({ : // The
reserve already clears the fixed composer dock on // phones, so the old large mobile bottom padding only floated a // long answer's last line high above the dock (and padded a short - // answer's empty space further). Keep it small here; sm+/desktop + // answer's empty space further). This stays far below that, but + // `pb-4` was the smallest tail in the app and left the last card + // sitting almost on the bottom edge once the dock scroll-hides + // and its reserve releases to zero. `pb-10` matches the + // `sm:pb-10` every other mode wrapper already uses. sm+/desktop // keep the original generous padding. - "pb-4 sm:pb-36 lg:pb-40" + "pb-10 sm:pb-36 lg:pb-40" : hasMobileBottomSearch ? compactMobileModeHome ? "sm:pb-10 lg:pb-12" @@ -3522,6 +3537,9 @@ function ClinicalDashboardContent({ )} > + {showAnswerCancelledNotice ? ( + void ask(answerLifecycle.query ?? query)} /> + ) : null} {actionNotice && ( setActionNotice(null)} animated> {actionNotice.message} @@ -3547,25 +3565,14 @@ function ClinicalDashboardContent({

{activeModeSearch.resultHeading}

- {answerLifecycle.status === "cancelled" && activeModeResultKind === "answer" ? ( - void ask(answerLifecycle.query ?? query)} - > -