diff --git a/deploy/docker-compose.server.yml b/deploy/docker-compose.server.yml index 614d2091..9c1521a0 100644 --- a/deploy/docker-compose.server.yml +++ b/deploy/docker-compose.server.yml @@ -17,6 +17,8 @@ services: interval: 30s timeout: 5s retries: 5 + start_period: 30s + start_interval: 1s web: image: ${WEB_IMAGE:-jyotisha-web:local} diff --git a/docs/research/pre_work_error_ledger.md b/docs/research/pre_work_error_ledger.md index d7b96c7e..91e9da69 100644 --- a/docs/research/pre_work_error_ledger.md +++ b/docs/research/pre_work_error_ledger.md @@ -122,8 +122,18 @@ The replayable public-case manifest currently contains 10 events: 5 career and 5 Prevention: add a new domain only after its public cases satisfy the same source/replay contract; never substitute loosely sourced celebrity notes or absence of a match for a negative conclusion. +## ERR-083 | Duplicate Supabase migration versions skipped required schema | resolved 2026-07-19 +Four migration pairs reused the same timestamp prefix, while Supabase records that prefix as the migration identity. The remote ledger therefore recorded only one file from each pair: `chart_profiles`, `synastry_reports`, and part of the profile recovery contract were absent even though their SQL files existed locally. Dynamic birth-time migrations were also still pending after the application branch was merged. + +Prevention: `tests/test_supabase_migration_versions.py` requires every migration prefix to be unique. Preserve already-recorded versions, move skipped SQL into later uniquely numbered repair migrations, run `supabase db push --linked --dry-run`, and verify the remote migration ledger plus live schema before deploying dependent application code. + ## Fragment Sweep Command Set +## ERR-086 | Steve Jobs jyotishganit artifacts used non-San-Francisco coordinates | mitigated 2026-07-21 +The jyotishganit Shadbala and field-comparison artifacts labelled the public case as San Francisco while using `37.3382, -122.0383`, unlike the canonical parity runner input `37.7749, -122.4194`. The affected artifacts are invalidated and production tuning remains blocked. Probe defaults now use the canonical coordinates; regeneration is still blocked in the active project virtualenv because `skyfield` is unavailable. + +Prevention: hash only normalized calculation-bearing input, require the location label and canonical coordinates to share one input contract, and reject an oracle artifact when its canonical input hash differs from the comparison packet. + ## ERR-084 | Pre-work fragment test assumes zero candidates despite current audited candidates | active 2026-07-19 `scripts/pre_work_check.py` reported `fragment_audit.candidate_count=2` on 2026-07-19 and reports `3` in the 2026-07-20 run, while `tests/test_preflight_fragment_scan.py` requires exactly zero. The pre-work command therefore cannot be reported green until the candidates are classified or the test is updated to validate the reviewed state rather than a hard-coded count. diff --git a/docs/research/user_reported_birth_time_flow_issues_2026_07_20.md b/docs/research/user_reported_birth_time_flow_issues_2026_07_20.md index 873d5288..f2fa11d1 100644 --- a/docs/research/user_reported_birth_time_flow_issues_2026_07_20.md +++ b/docs/research/user_reported_birth_time_flow_issues_2026_07_20.md @@ -19,6 +19,33 @@ the tested deployment SHA. | ISSUE-BT-004 | Choosing `都不符合` surfaced the same raw English pattern error. | verified-local | The actual orchestrator treats `都不符合` as a normal direction change; Task 12 E2E advances the durable turn and preserves the single fee; client maps terminal 502/non-JSON failures to stable Chinese copy. | Authenticated production `都不符合` action followed by reload/resume, tied to the deployed SHA. | | ISSUE-BT-005 | Initialization used generic broad-year choices and lost the rich card/chat rectification analysis. | verified-local | Task 9 onboarding soft gate; v3 narrative grounding rejects broad-year questionnaires; Task 12 asserts candidate boundary, D1/D9/D10 layers, three domain rationales, free text, and year/month event request; PG14 proves future background persists without scoring and the legacy suite imports old unfinished work once with `migration_waived`. | Authenticated synthetic first-turn snapshot and one legacy import smoke tied to the deployed SHA. | +## Initial production diagnosis (preserved) + +The original issue record captured these causes before implementation: + +- Chat deletion lacked both an authenticated `DELETE` grant and an owner-only + RLS policy. +- `startNewChat()` created only a chat session; the birth profile and + rectification case were account-scoped, so a new chat could not start an + explicit re-rectification while preserving the active chart. +- `dynamic-choice-v2` emitted `request_candidate_confirmation`, but the client + sent the legacy `confirm_guided_candidate` mutation, which rejected dynamic + cases and left them in `confirming` without applying the candidate. +- The reported `都不符合` failure coincided with a deployment replacement in + which Caddy returned HTTP 502 while Docker could not resolve the temporarily + unavailable `web` service. +- WebKit can represent a non-JSON parse failure as a `DOMException` named + `SyntaxError`; the old transport recognized JavaScript `SyntaxError` only and + could expose `The string did not match the expected pattern.` instead of + stable Chinese product copy. + +The original closure checklist required owner-only deletion, explicit +account-level re-rectification, atomic idempotent candidate confirmation, +deployment availability or bounded retry behavior, stable localized WebKit +errors, and authenticated end-to-end coverage for `都不符合`, confirmation, +re-rectification, and deletion. The table above maps each requirement to the +implemented local evidence and the remaining production proof. + ## Release decision All five issues remain `verified-local` until the production closure artifacts diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 27b51bf3..5fc738e0 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -25,6 +25,7 @@ "react-dom": "19.2.4", "react-markdown": "^10.1.0", "remark-gfm": "^4.0.1", + "sonner": "^2.0.7", "tailwind-merge": "^3.6.0", "tailwindcss": "^4.3.2", "tw-animate-css": "^1.4.0", @@ -10112,6 +10113,16 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/sonner": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.7.tgz", + "integrity": "sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==", + "license": "MIT", + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index fd4f53ed..868d0f07 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -33,6 +33,7 @@ "react-dom": "19.2.4", "react-markdown": "^10.1.0", "remark-gfm": "^4.0.1", + "sonner": "^2.0.7", "tailwind-merge": "^3.6.0", "tailwindcss": "^4.3.2", "tw-animate-css": "^1.4.0", diff --git a/frontend/src/app/api/birth-time-conversation/route.ts b/frontend/src/app/api/birth-time-conversation/route.ts index 6949651e..f0ee4fac 100644 --- a/frontend/src/app/api/birth-time-conversation/route.ts +++ b/frontend/src/app/api/birth-time-conversation/route.ts @@ -429,8 +429,13 @@ function layerMetadata(scan: RectificationQuestionnaire, calculationVersion: str ["D4", "d4Sign"], ["D9", "d9Sign"], ["D10", "d10Sign"], + ["D2", "d2Sign"], + ["D11", "d11Sign"], ["D24", "d24Sign"], ["D30", "d30Sign"], + ["A7", "a7Sign"], + ["UL", "ulSign"], + ["A10", "a10Sign"], ] as const; const availableLayers = layers .filter(([, key]) => scan.samples.some((sample) => typeof sample[key] === "string" && sample[key]?.trim())) @@ -504,6 +509,7 @@ export async function buildProductionConversationalRectificationPacket( lon: place.longitude, tz: place.timezoneOffset, evidence: [], + events, dismissedOpportunityIds: [], questionFingerprints: [], partitionFingerprints: [], diff --git a/frontend/src/app/api/onboarding/route.ts b/frontend/src/app/api/onboarding/route.ts index 568dde57..ab15475b 100644 --- a/frontend/src/app/api/onboarding/route.ts +++ b/frontend/src/app/api/onboarding/route.ts @@ -17,7 +17,7 @@ function createProfileRepository( async loadProfile(userId) { return admin .from("profiles") - .select("id,name,birth_date,birth_time,active_birth_time,birth_time_status,country_code,province_code,city_code,onboarding_payload,onboarding_version,onboarding_generated_at") + .select("id,name,birth_date,birth_time,reported_birth_time,active_birth_time,birth_time_source,birth_time_period,birth_time_clue,uncertainty_before_minutes,uncertainty_after_minutes,birth_time_status,country_code,province_code,city_code,onboarding_payload,onboarding_version,onboarding_generated_at") .eq("id", userId) .maybeSingle(); }, @@ -64,7 +64,7 @@ export const POST = createOnboardingPost({ repository: createProfileRepository(admin), }; }, - generateText: async (name) => { + generateText: async (name, signal) => { const model = defaultLanguageModel(); if (!model) return null; const result = await getOnboardingAgent(model).generate([ @@ -75,7 +75,7 @@ export const POST = createOnboardingPost({ "请生成首次欢迎语和三个入门问题。欢迎语直接邀请用户提问,不要提到出生资料、资料准备或系统处理过程。", ].join("\n"), }, - ]); + ], { abortSignal: signal }); return result.text; }, now: () => new Date(), diff --git a/frontend/src/app/globals.css b/frontend/src/app/globals.css index 5d26b307..cb60a1c7 100644 --- a/frontend/src/app/globals.css +++ b/frontend/src/app/globals.css @@ -132,10 +132,9 @@ button:disabled { cursor: default; opacity: .45; } .onboarding-card-actions { display: flex; justify-content: flex-end; padding-top: 2px; } .onboarding-card-actions .button-primary { min-width: 84px; } .birth-time-transition-card { position: relative; overflow: hidden; } -.unverified-birth-time-choice-scrim { position: fixed; inset: 0; z-index: 90; display: grid; place-items: center; padding: var(--space-4); overflow-y: auto; background: var(--color-scrim); } -.unverified-birth-time-choice { width: min(100%, 620px); margin: 0; overflow: visible; } -.unverified-birth-time-choice .onboarding-card-actions { flex-wrap: wrap; gap: var(--space-2); } -.unverified-birth-time-choice button { min-height: 44px; } +[data-sonner-toaster] [data-sonner-toast] { border: 1px solid var(--color-border) !important; border-radius: var(--radius-lg) !important; background: var(--color-canvas) !important; color: var(--color-ink) !important; box-shadow: var(--shadow-elevated) !important; } +[data-sonner-toast] [data-title] { font-size: var(--type-body-sm); font-weight: 600; } +[data-sonner-toast] [data-description] { color: var(--color-ink-secondary) !important; font-size: var(--type-caption); line-height: 1.5; } .birth-time-transition-fields { min-width: 0; display: grid; gap: 14px; margin: 0; padding: 0; border: 0; } .birth-time-assessment-overlay { position: absolute; z-index: 2; inset: 0; display: grid; place-items: center; padding: var(--space-6); border-radius: inherit; background: color-mix(in srgb, var(--color-canvas-soft) 94%, transparent); backdrop-filter: blur(2px); } .birth-time-assessment-progress { width: min(360px, 100%); display: grid; justify-items: center; gap: var(--space-2); color: var(--color-ink-secondary); text-align: center; } @@ -269,7 +268,7 @@ button:disabled { cursor: default; opacity: .45; } .group\/sidebar-provider[data-viewport] { height: 100dvh; min-height: 0; overflow: hidden; } .chat-app { width: 100%; height: 100%; min-height: 0; overflow: hidden; display: grid; background: var(--color-canvas); grid-template-columns: minmax(0, 1fr); } -.sidebar { position: relative; width: 100%; height: 100%; min-width: 0; min-height: 0; overflow: hidden; display: flex; flex-direction: column; border-right: 1px solid var(--sidebar-border); color: var(--sidebar-foreground); padding: var(--space-5) var(--space-3) var(--space-3); background: var(--sidebar-background); backdrop-filter: saturate(130%) blur(20px); } +.sidebar { position: relative; width: 100%; height: 100%; min-width: 0; min-height: 0; overflow: hidden; display: flex; flex-direction: column; border-right: 1px solid var(--sidebar-border); color: var(--sidebar-foreground); padding: var(--space-5) var(--space-3) 0; background: var(--sidebar-background); backdrop-filter: saturate(130%) blur(20px); } [data-sidebar="header"] { flex: 0 0 auto; } [data-sidebar="content"] { min-height: 0; flex: 1 1 auto; overflow-x: hidden; overflow-y: auto; overscroll-behavior: contain; } [data-sidebar="footer"] { flex: 0 0 auto; } @@ -299,11 +298,11 @@ button:disabled { cursor: default; opacity: .45; } .session-actions button > svg { width: 16px; height: 16px; } .session-actions button:hover { background: var(--color-canvas-muted); color: var(--color-ink); } .session-actions .session-action-danger { color: var(--color-danger); } -.sidebar-footer { position: relative; margin-top: var(--space-3); padding-top: 10px; border-top: 1px solid var(--sidebar-border); } +.sidebar-footer { position: relative; margin-top: 0; padding-top: 10px; border-top: 1px solid var(--sidebar-border); } .profile-trigger { width: 100%; display: grid; grid-template-columns: 34px minmax(0, 1fr) 18px; align-items: center; gap: 9px; padding: 5px 7px; border: 0; background: transparent; color: var(--sidebar-foreground); cursor: pointer; text-align: left; transition: background-color 120ms ease-out, transform 120ms ease-out; min-height: 56px; border-radius: var(--radius-md); } .profile-initial { width: 32px; height: 32px; display: grid; place-items: center; border-radius: 50%; font-size: 12px; text-transform: uppercase; border: 0; background: var(--color-action); color: var(--color-on-dark); font-weight: 500; } .profile-trigger b { display: block; overflow: hidden; line-height: 1.4; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; font-weight: 500; } -[data-sidebar="trigger"] { width: 44px; height: 44px; display: grid; flex: 0 0 auto; place-items: center; padding: 0; border: 0; border-radius: 50%; background: transparent; color: var(--sidebar-foreground); cursor: pointer; transition: background-color 120ms ease-out, color 120ms ease-out, transform 120ms ease-out; } +[data-sidebar="trigger"] { width: 44px; height: 44px; display: grid; flex: 0 0 auto; place-items: center; padding: 0; border: 0; border-radius: 50%; background: transparent; color: var(--sidebar-foreground); cursor: pointer; } [data-sidebar="trigger"] svg { width: 18px; height: 18px; } [data-sidebar="trigger"]:hover { background: var(--sidebar-accent); color: var(--sidebar-accent-foreground); } [data-sidebar="trigger"]:active { transform: translateY(1px); } @@ -443,24 +442,34 @@ button:disabled { cursor: default; opacity: .45; } .birth-time-success-note { padding: var(--space-4); border-left: 2px solid var(--color-success); background: var(--color-success-muted); color: var(--color-success); } .birth-time-candidate-terminal { justify-items: start; } .birth-time-candidate-result, .birth-time-candidate-result > *, .birth-time-confirmation-panel > * { min-width: 0; max-width: 100%; } +.rectification-session-surface { width: min(720px, 100%); min-width: 0; margin: 0 auto; } +.rectification-loading-state { min-height: min(52dvh, 420px); display: grid; align-content: center; justify-items: center; gap: var(--space-2); padding: var(--space-8); color: var(--color-ink-secondary); text-align: center; } +.rectification-loading-state strong { margin-top: var(--space-2); color: var(--color-ink); font-family: var(--font-display); font-size: var(--type-title-lg); font-weight: 400; } +.rectification-loading-state > span { font-size: var(--type-body-sm); } .conversational-rectification { width: 100%; min-width: 0; max-width: 720px; display: grid; gap: var(--space-5); overflow-wrap: anywhere; color: var(--color-ink); } .conversational-rectification > *, .conversational-rectification form, .conversational-rectification fieldset { min-width: 0; max-width: 100%; } .conversational-rectification button { min-height: 44px; max-width: 100%; overflow-wrap: anywhere; } -.conversational-rectification :where(button, textarea, summary, .conversational-status):focus-visible { outline: 3px solid color-mix(in srgb, var(--color-focus) 56%, transparent); outline-offset: 3px; } -.conversational-narrative { padding: var(--space-5); border-left: 3px solid var(--color-action); border-radius: 0 var(--radius-lg) var(--radius-lg) 0; background: var(--color-action-soft); } -.conversational-narrative .message-markdown { overflow-wrap: anywhere; } +.conversational-rectification :where(button, textarea):focus-visible { outline: 3px solid color-mix(in srgb, var(--color-focus) 56%, transparent); outline-offset: 3px; } +.conversational-narrative { overflow: hidden; border: 1px solid var(--color-border); border-radius: var(--radius-lg); background: var(--color-canvas); box-shadow: var(--shadow-soft); } +.conversational-narrative-body { padding: var(--space-4) var(--space-5); } +.conversational-narrative .message-markdown { overflow-wrap: anywhere; color: var(--color-ink-secondary); font-size: var(--type-body-sm); line-height: 1.8; } +.conversational-narrative .message-markdown > :first-child { margin-top: 0; } +.conversational-narrative .message-markdown > :last-child { margin-bottom: 0; } +.conversational-narrative .message-markdown h2, .conversational-narrative .message-markdown h3 { margin: var(--space-4) 0 var(--space-2); font-family: var(--font-sans); font-size: var(--type-body-md); font-weight: 600; letter-spacing: 0; } .conversational-domain-picker { display: grid; gap: var(--space-3); margin: 0; padding: 0; border: 0; } -.conversational-domain-picker legend { margin-bottom: var(--space-2); color: var(--color-ink-secondary); font-size: var(--type-caption); font-weight: 600; } +.conversational-domain-picker .conversational-domain-question { margin-bottom: var(--space-2); color: var(--color-ink); font-size: var(--type-body-md); font-weight: 600; line-height: 1.5; } .conversational-domain-picker > div { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: var(--space-2); } .conversational-domain-picker button { padding: var(--space-2) var(--space-3); border: 1px solid var(--color-border); border-radius: var(--radius-md); background: var(--color-canvas); color: var(--color-ink); cursor: pointer; text-align: left; } .conversational-domain-picker button[aria-pressed="true"] { border-color: var(--color-action); background: var(--color-action-soft); color: var(--color-action); } .conversational-composer { display: grid; gap: var(--space-3); padding: var(--space-4); border: 1px solid var(--color-border); border-radius: var(--radius-lg); background: var(--color-canvas); } .conversational-composer label { display: grid; gap: var(--space-1); font-size: var(--type-body-sm); font-weight: 600; } .conversational-composer label span { color: var(--color-ink-secondary); font-size: var(--type-caption); font-weight: 400; line-height: 1.5; } +.conversational-event-date { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-3); } +.conversational-event-date label { gap: var(--space-2); color: var(--color-ink-secondary); font-size: var(--type-caption); font-weight: 500; } .conversational-composer textarea { width: 100%; min-width: 0; min-height: 112px; resize: vertical; padding: var(--space-3); border: 1px solid var(--color-border-strong); border-radius: var(--radius-md); background: var(--color-canvas); color: var(--color-ink); line-height: 1.55; } .conversational-composer-footer { min-width: 0; display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); } .conversational-composer-footer small { color: var(--color-ink-tertiary); font-size: var(--type-overline); } -.conversational-evidence-recap, .conversational-candidate, .conversational-confirmation, .conversational-original-question, .conversational-abandon-confirmation, .conversational-empty-state { min-width: 0; display: grid; gap: var(--space-3); padding: var(--space-4); border: 1px solid var(--color-border); border-radius: var(--radius-lg); background: var(--color-canvas); } +.conversational-evidence-recap, .conversational-candidate, .conversational-confirmation, .conversational-original-question, .conversational-empty-state { min-width: 0; display: grid; gap: var(--space-3); padding: var(--space-4); border: 1px solid var(--color-border); border-radius: var(--radius-lg); background: var(--color-canvas); } .conversational-evidence-recap > header, .conversational-candidate > header { min-width: 0; display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); } .conversational-evidence-recap h3, .conversational-candidate h3, .conversational-confirmation h3 { margin: 0; font-family: var(--font-display); font-size: var(--type-title-md); font-weight: 400; } .conversational-evidence-recap > header span, .conversational-candidate > header span { color: var(--color-ink-secondary); font-size: var(--type-caption); } @@ -470,26 +479,16 @@ button:disabled { cursor: default; opacity: .45; } .conversational-evidence-recap li { min-width: 0; display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: var(--space-3); padding: var(--space-3); border-radius: var(--radius-md); background: var(--color-canvas-soft); } .conversational-evidence-recap li > div { min-width: 0; } .conversational-evidence-recap time { color: var(--color-ink-tertiary); font-size: var(--type-overline); } -.conversational-evidence-recap p, .conversational-candidate p, .conversational-confirmation p, .conversational-original-question p, .conversational-abandon-confirmation p, .conversational-empty-state p, .conversational-status p { margin: 0; color: var(--color-ink-secondary); font-size: var(--type-body-sm); line-height: 1.6; } +.conversational-evidence-recap p, .conversational-candidate p, .conversational-confirmation p, .conversational-original-question p, .conversational-empty-state p, .conversational-status p { margin: 0; color: var(--color-ink-secondary); font-size: var(--type-body-sm); line-height: 1.6; } .conversational-evidence-recap li button { min-width: 56px; padding: 0 var(--space-3); border: 0; background: transparent; color: var(--color-action); cursor: pointer; } -.conversational-candidate dl, .conversational-technical-receipt dl { min-width: 0; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1px; margin: 0; overflow: hidden; border: 1px solid var(--color-border); border-radius: var(--radius-md); background: var(--color-border); } -.conversational-candidate dl > div, .conversational-technical-receipt dl > div { min-width: 0; display: grid; gap: var(--space-1); padding: var(--space-3); background: var(--color-canvas-soft); } -.conversational-candidate dt, .conversational-technical-receipt dt { color: var(--color-ink-tertiary); font-size: var(--type-overline); } -.conversational-candidate dd, .conversational-technical-receipt dd { min-width: 0; margin: 0; color: var(--color-ink); font-size: var(--type-body-sm); overflow-wrap: anywhere; } +.conversational-candidate dl { min-width: 0; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1px; margin: 0; overflow: hidden; border: 1px solid var(--color-border); border-radius: var(--radius-md); background: var(--color-border); } +.conversational-candidate dl > div { min-width: 0; display: grid; gap: var(--space-1); padding: var(--space-3); background: var(--color-canvas-soft); } +.conversational-candidate dt { color: var(--color-ink-tertiary); font-size: var(--type-overline); } +.conversational-candidate dd { min-width: 0; margin: 0; color: var(--color-ink); font-size: var(--type-body-sm); overflow-wrap: anywhere; } .conversational-candidate time { font-family: var(--font-mono); font-variant-numeric: tabular-nums; } -.conversational-technical-receipt { min-width: 0; padding: var(--space-3) var(--space-4); border: 1px solid var(--color-border); border-radius: var(--radius-lg); } -.conversational-technical-receipt summary { min-height: 44px; display: flex; align-items: center; color: var(--color-action); cursor: pointer; font-size: var(--type-body-sm); font-weight: 600; } -.conversational-technical-receipt code { font-family: var(--font-mono); font-size: var(--type-overline); } .conversational-confirmation { border-color: color-mix(in srgb, var(--color-action) 44%, var(--color-border)); background: var(--color-action-soft); } .conversational-status { min-width: 0; min-height: 1px; } .conversational-original-question { background: var(--color-success-muted); } -.conversational-session-actions { min-width: 0; display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--space-2); } -.conversational-abandon { min-height: 44px; padding: 0 var(--space-3); border: 1px solid transparent; border-radius: var(--radius-md); background: transparent; color: var(--color-danger); cursor: pointer; } -.conversational-abandon.is-confirm { border-color: var(--color-danger); background: var(--color-danger); color: var(--color-on-dark); } -.conversational-abandon-scrim { position: fixed; inset: 0; z-index: 80; display: grid; align-items: center; justify-items: center; padding: var(--space-4); overflow-y: auto; background: var(--color-scrim); } -.conversational-abandon-confirmation { width: min(480px, 100%); border-color: color-mix(in srgb, var(--color-danger) 50%, var(--color-border)); background: var(--color-danger-muted); box-shadow: var(--shadow-elevated); } -.conversational-abandon-confirmation h3 { margin: 0; font-family: var(--font-display); font-size: var(--type-title-md); font-weight: 400; } -.conversational-abandon-confirmation > div { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--space-2); } .phrase-nowrap { white-space: nowrap; } .starter-list { border-top: 1px solid var(--color-border); display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-3); border: 0; } @@ -762,10 +761,10 @@ input:disabled, select:disabled { color: var(--color-ink-tertiary); background: .session-delete-confirmation p { margin: 0; color: var(--color-ink-secondary); } @media (max-width: 430px) { + [data-sonner-toaster] { right: var(--space-4) !important; left: var(--space-4) !important; width: auto !important; } .conversational-rectification { width: 100%; max-width: 100%; gap: var(--space-4); overflow-x: clip; } - .conversational-narrative, .conversational-composer, .conversational-evidence-recap, .conversational-candidate, .conversational-confirmation, .conversational-original-question, .conversational-abandon-confirmation, .conversational-empty-state { padding: var(--space-3); } - .conversational-domain-picker > div, .conversational-candidate dl, .conversational-technical-receipt dl { grid-template-columns: minmax(0, 1fr); } + .conversational-narrative-body, .conversational-composer, .conversational-evidence-recap, .conversational-candidate, .conversational-confirmation, .conversational-original-question, .conversational-empty-state { padding: var(--space-3); } + .conversational-domain-picker > div, .conversational-candidate dl { grid-template-columns: minmax(0, 1fr); } .conversational-composer-footer { align-items: stretch; flex-direction: column; } - .conversational-composer-footer button, .conversational-confirmation button, .conversational-original-question button, .conversational-session-actions > button, .conversational-abandon-confirmation button { width: 100%; } - .conversational-session-actions, .conversational-abandon-confirmation > div { flex-direction: column; } + .conversational-composer-footer button, .conversational-confirmation button, .conversational-original-question button { width: 100%; } } diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx index fae44c11..4ff46db7 100644 --- a/frontend/src/app/layout.tsx +++ b/frontend/src/app/layout.tsx @@ -1,5 +1,6 @@ import type { Metadata } from "next"; import Script from "next/script"; +import { Toaster } from "@/components/ui/sonner"; import "./globals.css"; import "./birth-time-choice.css"; @@ -30,7 +31,10 @@ export default function RootLayout({ children }: Readonly<{ children: React.Reac > )} -
{children} + + {children} +