fix(web): keep onboarding and mobile natal board on one aligned overlay
Put the compact board trigger in the chat header, keep the sheet above the composer on iOS, and align onboarding messages with the intake card on the session column. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+49
-1
@@ -4966,7 +4966,7 @@
|
|||||||
- 根因:BUG-328 把窄屏盘面改成工作区第二行(约 46% 高)且未设叠层;输入框 `.composer-wrap` 仍是 `position: relative; z-index: 2`。盘面与输入框在同一工作区网格里重叠时,输入框画在盘面标题之上。
|
- 根因:BUG-328 把窄屏盘面改成工作区第二行(约 46% 高)且未设叠层;输入框 `.composer-wrap` 仍是 `position: relative; z-index: 2`。盘面与输入框在同一工作区网格里重叠时,输入框画在盘面标题之上。
|
||||||
- 修复:仅在视口宽度小于 768px 的移动端,把盘面改成工作区内 `z-index: 20` 的底部 sheet 覆盖层;桌面网页仍是右侧分栏,不出现底部弹出。遮罩点击、Escape、44px 关闭按钮可关;聊天列在打开时 `inert`。
|
- 修复:仅在视口宽度小于 768px 的移动端,把盘面改成工作区内 `z-index: 20` 的底部 sheet 覆盖层;桌面网页仍是右侧分栏,不出现底部弹出。遮罩点击、Escape、44px 关闭按钮可关;聊天列在打开时 `inert`。
|
||||||
- 验证:`frontend/tests/rectification-agentic-entry.test.ts`。
|
- 验证:`frontend/tests/rectification-agentic-entry.test.ts`。
|
||||||
- 防复发:底部 sheet 只允许 `matchMedia(max-width: 767px)` 触发,不得用聊天区 `clientWidth` 把桌面侧栏挤窄误判成移动端;桌面必须保持 `grid-template-columns: minmax(0, 1fr) minmax(18rem, 22.5rem)`。窄屏盘面必须是覆盖层(absolute、高于输入框的 z-index、不透明背景)。
|
- 防复发:底部 sheet 只允许 `matchMedia(max-width: 767px)` 触发;工作区必须 `isolation: isolate` 且 `z-index: 0`,打开时隐藏 composer(含 `backdrop-filter`),覆盖层 z-index 必须高于输入框。不得用聊天区 `clientWidth` 把桌面侧栏挤窄误判成移动端。
|
||||||
- 相关记录:BUG-322、BUG-326、BUG-328
|
- 相关记录:BUG-322、BUG-326、BUG-328
|
||||||
- 复发自:BUG-328(当时为了露出聊天改成下半屏分栏,叠层低于输入框)
|
- 复发自:BUG-328(当时为了露出聊天改成下半屏分栏,叠层低于输入框)
|
||||||
- 修复版本:9670b661
|
- 修复版本:9670b661
|
||||||
@@ -5019,6 +5019,54 @@
|
|||||||
- 复发自:无
|
- 复发自:无
|
||||||
- 修复版本:9c4b64c9
|
- 修复版本:9c4b64c9
|
||||||
|
|
||||||
|
## BUG-334 | 移动端盘面覆盖层仍被输入框 backdrop-filter 盖住
|
||||||
|
|
||||||
|
- 状态:resolved
|
||||||
|
- 首次发现:2026-08-20
|
||||||
|
- 最近更新:2026-08-20
|
||||||
|
- 影响面:生时纠正窄屏盘面、`.composer-wrap`、`.rectification-board-overlay`
|
||||||
|
- 用户现象:打开“当前盘面”后,输入区预览条仍叠在弹层标题上;覆盖层和输入框像同一层,聊天列表也被挡住看不清。
|
||||||
|
- 触发条件:移动端视口打开宫位表。iOS Safari 上更明显。
|
||||||
|
- 根因:`.composer-wrap` 带 `z-index: 2` 和 `backdrop-filter`。iOS 会把该滤镜层合成到覆盖层之上。BUG-330 只把 overlay 设为 `z-index: 20`,工作区没有独立层叠上下文,也没有在打开时关掉输入框滤镜。
|
||||||
|
- 修复:工作区 `isolation: isolate; z-index: 0` 锁住输入框层叠;覆盖层 `z-index: 50`。打开盘面时隐藏 composer(`visibility: hidden`、关掉 `backdrop-filter`),并收起预览条。sheet 高度改为 72%,上方聊天列表可从遮罩后看见。
|
||||||
|
- 验证:`frontend/tests/rectification-agentic-entry.test.ts`。
|
||||||
|
- 防复发:窄屏覆盖层必须高于 composer;打开时 composer 不得再带 `backdrop-filter`。源码合同锁定 `isolation: isolate`、overlay `z-index: 50`、`visibility: hidden`。展开入口不得放在输入框上方,必须挂在页头右上角。
|
||||||
|
- 相关记录:BUG-328、BUG-330、BUG-335
|
||||||
|
- 复发自:BUG-330(覆盖层 z-index 未锁住 iOS backdrop-filter 合成层)
|
||||||
|
- 修复版本:待发布
|
||||||
|
|
||||||
|
## BUG-335 | 移动端当前盘面入口仍在输入框上方
|
||||||
|
|
||||||
|
- 状态:resolved
|
||||||
|
- 首次发现:2026-08-20
|
||||||
|
- 最近更新:2026-08-20
|
||||||
|
- 影响面:生时纠正窄屏盘面入口、`.chat-header-actions`、`.rectification-board-peek`
|
||||||
|
- 用户现象:点开盘面的按钮在输入框上方,占掉打字区;希望放到右上角。
|
||||||
|
- 触发条件:窄屏生时纠正会话。
|
||||||
|
- 根因:`RectificationBoardPeek` 渲染在 `.composer-wrap` 里,输入框上方整条通栏。
|
||||||
|
- 修复:页头 `chat-header-actions` 增加挂载点;窄屏未打开盘面时把预览按钮 portal 到余额按钮左侧。输入框上方不再放展开入口。
|
||||||
|
- 验证:`frontend/tests/rectification-agentic-entry.test.ts`。
|
||||||
|
- 防复发:源码合同锁定 `createPortal`、`data-rectification-header-slot`,并禁止 composer 内出现 `RectificationBoardPeek`。
|
||||||
|
- 相关记录:BUG-330、BUG-334
|
||||||
|
- 复发自:BUG-330(窄屏入口一开始就放在输入区)
|
||||||
|
- 修复版本:待发布
|
||||||
|
|
||||||
|
## BUG-336 | 初始化对话列表与资料卡不在同一栏
|
||||||
|
|
||||||
|
- 状态:resolved
|
||||||
|
- 首次发现:2026-08-20
|
||||||
|
- 最近更新:2026-08-20
|
||||||
|
- 影响面:首页 onboarding `.welcome`、出生资料卡、输入框
|
||||||
|
- 用户现象:新用户初始化时,对话气泡和出生日期卡片左右对不齐;称呼阶段输入框也比上面的对话更窄。
|
||||||
|
- 触发条件:未完成资料的新会话,尤其是填写出生日期与时间的步骤。
|
||||||
|
- 根因:`.welcome` 被后续规则拉到 1040px 且无水平居中;资料卡 `max-width: 680/760px` 靠左。会话正文和输入框已经收成 760px 栏,初始化没有走同一套栏宽。
|
||||||
|
- 修复:未完成资料时给会话加上 `is-onboarding`。欢迎区和资料卡使用 `--session-column-width`,与正式会话、输入框同一栏。
|
||||||
|
- 验证:`frontend/tests/session-conversation-layout.test.ts`。
|
||||||
|
- 防复发:源码合同锁定 `is-onboarding`、欢迎区栏宽和资料卡 `width: 100%; max-width: none`。
|
||||||
|
- 相关记录:无
|
||||||
|
- 复发自:无
|
||||||
|
- 修复版本:待发布
|
||||||
|
|
||||||
## BUG-329 | 生时纠正 Agent 回答在结算后一次性出现,推理中无法停止
|
## BUG-329 | 生时纠正 Agent 回答在结算后一次性出现,推理中无法停止
|
||||||
|
|
||||||
- 状态:resolved
|
- 状态:resolved
|
||||||
|
|||||||
@@ -1763,8 +1763,24 @@ input:not([type="radio"]):not([type="checkbox"]):not([class^="ant-"]):not([class
|
|||||||
.onboarding-card-heading b { font-size: clamp(22px, 2.6vw, 30px); letter-spacing: -.03em; }
|
.onboarding-card-heading b { font-size: clamp(22px, 2.6vw, 30px); letter-spacing: -.03em; }
|
||||||
.onboarding-card-heading small { max-width: 260px; color: var(--color-ink-secondary); line-height: 1.55; font-size: var(--type-caption); text-wrap: pretty; }
|
.onboarding-card-heading small { max-width: 260px; color: var(--color-ink-secondary); line-height: 1.55; font-size: var(--type-caption); text-wrap: pretty; }
|
||||||
.birth-time-intake { gap: var(--space-6); }
|
.birth-time-intake { gap: var(--space-6); }
|
||||||
|
.conversation.is-onboarding .welcome {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: min(calc(var(--session-column-width) + (var(--session-column-gutter) * 2)), 100%);
|
||||||
|
margin-inline: auto;
|
||||||
|
}
|
||||||
|
.conversation.is-onboarding.is-empty .welcome {
|
||||||
|
width: min(var(--session-column-width), 100%);
|
||||||
|
}
|
||||||
|
.conversation.is-onboarding .onboarding-card,
|
||||||
|
.conversation.is-onboarding .onboarding-card.birth-time-transition-card {
|
||||||
|
width: 100%;
|
||||||
|
max-width: none;
|
||||||
|
margin-inline: 0;
|
||||||
|
}
|
||||||
.conversation.is-onboarding-form { align-content: start; padding-bottom: var(--space-8); }
|
.conversation.is-onboarding-form { align-content: start; padding-bottom: var(--space-8); }
|
||||||
.conversation.is-onboarding-form .welcome { padding-top: var(--space-6); padding-bottom: var(--space-8); }
|
.conversation.is-onboarding-form .welcome {
|
||||||
|
padding: var(--space-6) var(--session-column-gutter) var(--space-8);
|
||||||
|
}
|
||||||
.conversation.is-onboarding-form .welcome > .onboarding-message:first-child { padding-bottom: var(--space-3); }
|
.conversation.is-onboarding-form .welcome > .onboarding-message:first-child { padding-bottom: var(--space-3); }
|
||||||
.conversation.is-onboarding-form .welcome > .onboarding-message:first-child .message-bubble p {
|
.conversation.is-onboarding-form .welcome > .onboarding-message:first-child .message-bubble p {
|
||||||
max-width: 680px;
|
max-width: 680px;
|
||||||
@@ -1876,6 +1892,8 @@ input:not([type="radio"]):not([type="checkbox"]):not([class^="ant-"]):not([class
|
|||||||
|
|
||||||
.rectification-workspace {
|
.rectification-workspace {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
z-index: 0;
|
||||||
|
isolation: isolate;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@@ -1908,6 +1926,12 @@ input:not([type="radio"]):not([type="checkbox"]):not([class^="ant-"]):not([class
|
|||||||
position: relative;
|
position: relative;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
.rectification-workspace.is-compact.is-board-open .rectification-workspace__chat .composer-wrap {
|
||||||
|
z-index: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
pointer-events: none;
|
||||||
|
backdrop-filter: none;
|
||||||
|
}
|
||||||
.rectification-board {
|
.rectification-board {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
@@ -1920,10 +1944,9 @@ input:not([type="radio"]):not([type="checkbox"]):not([class^="ant-"]):not([class
|
|||||||
.rectification-workspace.is-compact .rectification-board-overlay {
|
.rectification-workspace.is-compact .rectification-board-overlay {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
z-index: 20;
|
z-index: 50;
|
||||||
display: grid;
|
display: grid;
|
||||||
align-items: end;
|
align-items: end;
|
||||||
isolation: isolate;
|
|
||||||
}
|
}
|
||||||
.rectification-board-scrim {
|
.rectification-board-scrim {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -1937,7 +1960,7 @@ input:not([type="radio"]):not([type="checkbox"]):not([class^="ant-"]):not([class
|
|||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 92%;
|
height: 72%;
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -2102,6 +2125,33 @@ input:not([type="radio"]):not([type="checkbox"]):not([class^="ant-"]):not([class
|
|||||||
color: var(--color-ink);
|
color: var(--color-ink);
|
||||||
font-size: var(--type-caption);
|
font-size: var(--type-caption);
|
||||||
}
|
}
|
||||||
|
.chat-header-rectification:empty {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.chat-header-rectification .rectification-board-peek {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
width: auto;
|
||||||
|
max-width: min(11.5rem, 42vw);
|
||||||
|
min-height: 44px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 var(--space-3);
|
||||||
|
gap: var(--space-2);
|
||||||
|
}
|
||||||
|
.chat-header-rectification .rectification-board-peek strong,
|
||||||
|
.chat-header-rectification .rectification-board-peek span {
|
||||||
|
display: inline;
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.chat-header-rectification .rectification-board-peek span {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
.rectification-board-peek {
|
.rectification-board-peek {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -3559,6 +3559,7 @@ export default function Home() {
|
|||||||
<strong>{activeSession?.title || "新对话"}</strong>
|
<strong>{activeSession?.title || "新对话"}</strong>
|
||||||
</div>
|
</div>
|
||||||
<div className="chat-header-actions">
|
<div className="chat-header-actions">
|
||||||
|
<div className="chat-header-rectification" data-rectification-header-slot="" />
|
||||||
<button className="credit-button" type="button" onClick={() => router.push(membershipHref("credits"))} aria-label={account ? `余额 ${account.credits} 点,会员与充值` : accountError || "读取余额中"}>
|
<button className="credit-button" type="button" onClick={() => router.push(membershipHref("credits"))} aria-label={account ? `余额 ${account.credits} 点,会员与充值` : accountError || "读取余额中"}>
|
||||||
<Sparkles className="credit-icon" aria-hidden="true" />
|
<Sparkles className="credit-icon" aria-hidden="true" />
|
||||||
<span>{account ? account.credits : "—"}</span>
|
<span>{account ? account.credits : "—"}</span>
|
||||||
@@ -3567,7 +3568,7 @@ export default function Home() {
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
{!rectificationSurfaceOpen && (
|
{!rectificationSurfaceOpen && (
|
||||||
<div ref={conversation} className={`conversation ${!activeSession?.messages.length && !onboardingFormActive ? "is-empty" : ""} ${onboardingFormActive ? "is-onboarding-form" : ""}`}>
|
<div ref={conversation} className={`conversation${!activeSession?.messages.length && !onboardingFormActive ? " is-empty" : ""}${!profileComplete ? " is-onboarding" : ""}${onboardingFormActive ? " is-onboarding-form" : ""}`}>
|
||||||
{!activeSession?.messages.length ? (
|
{!activeSession?.messages.length ? (
|
||||||
<div className="welcome">
|
<div className="welcome">
|
||||||
{!profileComplete ? (
|
{!profileComplete ? (
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import { ArrowUp, Square } from "lucide-react";
|
import { ArrowUp, Square } from "lucide-react";
|
||||||
import { useCallback, useEffect, useId, useLayoutEffect, useRef, useState } from "react";
|
import { useCallback, useEffect, useId, useLayoutEffect, useRef, useState } from "react";
|
||||||
|
import { createPortal } from "react-dom";
|
||||||
import { parseAgentReply } from "@/lib/agent-reply";
|
import { parseAgentReply } from "@/lib/agent-reply";
|
||||||
import type { ChatMessage, ChatMessageView } from "@/lib/chat-message-view";
|
import type { ChatMessage, ChatMessageView } from "@/lib/chat-message-view";
|
||||||
import {
|
import {
|
||||||
@@ -262,9 +263,14 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
|
|||||||
const [compactBoard, setCompactBoard] = useState(false);
|
const [compactBoard, setCompactBoard] = useState(false);
|
||||||
const [boardOpen, setBoardOpen] = useState(false);
|
const [boardOpen, setBoardOpen] = useState(false);
|
||||||
const [boardDiff, setBoardDiff] = useState(() => diffRectificationBoard(null, null));
|
const [boardDiff, setBoardDiff] = useState(() => diffRectificationBoard(null, null));
|
||||||
|
const [headerSlot, setHeaderSlot] = useState<HTMLElement | null>(null);
|
||||||
const boardId = useId();
|
const boardId = useId();
|
||||||
const boardTitleId = useId();
|
const boardTitleId = useId();
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
setHeaderSlot(document.querySelector("[data-rectification-header-slot]"));
|
||||||
|
}, []);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
const query = window.matchMedia(`(max-width: ${RECTIFICATION_BOARD_SPLIT_MIN_PX - 1}px)`);
|
const query = window.matchMedia(`(max-width: ${RECTIFICATION_BOARD_SPLIT_MIN_PX - 1}px)`);
|
||||||
const update = () => {
|
const update = () => {
|
||||||
@@ -682,11 +688,21 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
|
|||||||
: undefined;
|
: undefined;
|
||||||
const canSend = !busy && !readonly && !regeneratingMessageKey;
|
const canSend = !busy && !readonly && !regeneratingMessageKey;
|
||||||
|
|
||||||
|
const boardPeek = compactBoard && !boardOpen ? (
|
||||||
|
<RectificationBoardPeek
|
||||||
|
result={candidateResult}
|
||||||
|
expanded={boardOpen}
|
||||||
|
boardId={boardId}
|
||||||
|
onOpen={toggleBoard}
|
||||||
|
/>
|
||||||
|
) : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={workspace}
|
ref={workspace}
|
||||||
className={`rectification-workspace${compactBoard ? " is-compact" : ""}${boardOpen ? " is-board-open" : ""}`}
|
className={`rectification-workspace${compactBoard ? " is-compact" : ""}${boardOpen ? " is-board-open" : ""}`}
|
||||||
>
|
>
|
||||||
|
{headerSlot && boardPeek ? createPortal(boardPeek, headerSlot) : null}
|
||||||
<div className="rectification-workspace__chat" inert={compactBoard && boardOpen ? true : undefined}>
|
<div className="rectification-workspace__chat" inert={compactBoard && boardOpen ? true : undefined}>
|
||||||
<section ref={conversation} className="conversation is-rectification" aria-label="生时校正对话" aria-busy={busy || regeneratingMessageKey !== null}>
|
<section ref={conversation} className="conversation is-rectification" aria-label="生时校正对话" aria-busy={busy || regeneratingMessageKey !== null}>
|
||||||
<div className="message-list">
|
<div className="message-list">
|
||||||
@@ -777,14 +793,6 @@ export function RectificationAgenticChat(props: RectificationAgenticChatProps) {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<div className="composer-wrap">
|
<div className="composer-wrap">
|
||||||
{compactBoard && (
|
|
||||||
<RectificationBoardPeek
|
|
||||||
result={candidateResult}
|
|
||||||
expanded={boardOpen}
|
|
||||||
boardId={boardId}
|
|
||||||
onOpen={toggleBoard}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<form className="composer" onSubmit={submit}>
|
<form className="composer" onSubmit={submit}>
|
||||||
<Textarea
|
<Textarea
|
||||||
ref={composer}
|
ref={composer}
|
||||||
|
|||||||
@@ -271,6 +271,7 @@ export function RectificationBoardPeek({
|
|||||||
<button
|
<button
|
||||||
className="rectification-board-peek"
|
className="rectification-board-peek"
|
||||||
type="button"
|
type="button"
|
||||||
|
aria-label={peek.detail ? `${peek.title},${peek.detail}` : peek.title}
|
||||||
aria-expanded={expanded}
|
aria-expanded={expanded}
|
||||||
aria-controls={boardId}
|
aria-controls={boardId}
|
||||||
onClick={onOpen}
|
onClick={onOpen}
|
||||||
|
|||||||
@@ -392,11 +392,17 @@ test("the natal house table is a live board beside the chat, not a message", ()
|
|||||||
|
|
||||||
test("compact board overlays chat as a bottom sheet above the composer", () => {
|
test("compact board overlays chat as a bottom sheet above the composer", () => {
|
||||||
assert.doesNotMatch(styles, /\.rectification-workspace\.is-compact\.is-board-open \{[\s\S]*grid-template-rows: minmax\(8rem, 1fr\) minmax\(13rem, 46%\)/);
|
assert.doesNotMatch(styles, /\.rectification-workspace\.is-compact\.is-board-open \{[\s\S]*grid-template-rows: minmax\(8rem, 1fr\) minmax\(13rem, 46%\)/);
|
||||||
|
assert.match(styles, /\.rectification-workspace \{[\s\S]*isolation: isolate/);
|
||||||
|
assert.match(styles, /\.rectification-workspace \{[\s\S]*z-index: 0/);
|
||||||
assert.match(styles, /\.rectification-workspace\.is-compact \.rectification-board-overlay \{[\s\S]*position: absolute/);
|
assert.match(styles, /\.rectification-workspace\.is-compact \.rectification-board-overlay \{[\s\S]*position: absolute/);
|
||||||
assert.match(styles, /\.rectification-workspace\.is-compact \.rectification-board-overlay \{[\s\S]*inset: 0/);
|
assert.match(styles, /\.rectification-workspace\.is-compact \.rectification-board-overlay \{[\s\S]*inset: 0/);
|
||||||
assert.match(styles, /\.rectification-workspace\.is-compact \.rectification-board-overlay \{[\s\S]*z-index: 20/);
|
assert.match(styles, /\.rectification-workspace\.is-compact \.rectification-board-overlay \{[^}]*z-index: 50/);
|
||||||
|
assert.doesNotMatch(styles, /\.rectification-workspace\.is-compact \.rectification-board-overlay \{[^}]*z-index: 20/);
|
||||||
|
assert.match(styles, /\.rectification-workspace\.is-compact\.is-board-open \.rectification-workspace__chat \.composer-wrap \{[\s\S]*visibility: hidden/);
|
||||||
|
assert.match(styles, /\.rectification-workspace\.is-compact\.is-board-open \.rectification-workspace__chat \.composer-wrap \{[\s\S]*backdrop-filter: none/);
|
||||||
assert.match(styles, /\.rectification-workspace__chat \.composer-wrap \{[\s\S]*z-index: 2/);
|
assert.match(styles, /\.rectification-workspace__chat \.composer-wrap \{[\s\S]*z-index: 2/);
|
||||||
assert.match(styles, /\.rectification-board\.is-sheet \{[\s\S]*height: 92%/);
|
assert.match(styles, /\.rectification-board\.is-sheet \{[^}]*height: 72%/);
|
||||||
|
assert.doesNotMatch(styles, /\.rectification-board\.is-sheet \{[^}]*height: 92%/);
|
||||||
assert.match(styles, /\.rectification-board\.is-sheet \{[\s\S]*background: var\(--color-canvas\)/);
|
assert.match(styles, /\.rectification-board\.is-sheet \{[\s\S]*background: var\(--color-canvas\)/);
|
||||||
assert.match(styles, /\.rectification-board\.is-sheet \{[\s\S]*border-block-start: 1px solid var\(--color-border\)/);
|
assert.match(styles, /\.rectification-board\.is-sheet \{[\s\S]*border-block-start: 1px solid var\(--color-border\)/);
|
||||||
assert.match(styles, /@keyframes rectification-sheet-enter \{[\s\S]*translateY\(18%\)/);
|
assert.match(styles, /@keyframes rectification-sheet-enter \{[\s\S]*translateY\(18%\)/);
|
||||||
@@ -404,6 +410,14 @@ test("compact board overlays chat as a bottom sheet above the composer", () => {
|
|||||||
assert.match(styles, /\.rectification-board__close \{[\s\S]*width: 44px/);
|
assert.match(styles, /\.rectification-board__close \{[\s\S]*width: 44px/);
|
||||||
assert.match(styles, /\.rectification-board__close \{[\s\S]*min-height: 44px/);
|
assert.match(styles, /\.rectification-board__close \{[\s\S]*min-height: 44px/);
|
||||||
assert.match(board, /<X aria-hidden="true"/);
|
assert.match(board, /<X aria-hidden="true"/);
|
||||||
|
assert.match(chat, /compactBoard && !boardOpen \? \(/);
|
||||||
|
assert.match(chat, /createPortal\(/);
|
||||||
|
assert.match(chat, /\[data-rectification-header-slot\]/);
|
||||||
|
assert.match(page, /className="chat-header-rectification"/);
|
||||||
|
assert.match(page, /data-rectification-header-slot=""/);
|
||||||
|
assert.match(styles, /\.chat-header-rectification \.rectification-board-peek \{[\s\S]*width: auto/);
|
||||||
|
assert.doesNotMatch(chat, /rectification-workspace__board-trigger/);
|
||||||
|
assert.doesNotMatch(chat.slice(chat.indexOf("className=\"composer-wrap\""), chat.indexOf("<form className=\"composer\"")), /RectificationBoardPeek/);
|
||||||
assert.match(chat, /className=\{`rectification-workspace\$\{compactBoard \? " is-compact" : ""\}\$\{boardOpen \? " is-board-open" : ""\}`\}/);
|
assert.match(chat, /className=\{`rectification-workspace\$\{compactBoard \? " is-compact" : ""\}\$\{boardOpen \? " is-board-open" : ""\}`\}/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,15 @@ test("aligns the session transcript and composer to one readable column", () =>
|
|||||||
assert.match(globalStyles, /\.conversation:not\(\.is-empty\):not\(\.is-rectification\) \+ \.composer-wrap \.composer-footer[^\{]*\{[^}]*width:\s*min\(var\(--session-column-width\),\s*100%\)/);
|
assert.match(globalStyles, /\.conversation:not\(\.is-empty\):not\(\.is-rectification\) \+ \.composer-wrap \.composer-footer[^\{]*\{[^}]*width:\s*min\(var\(--session-column-width\),\s*100%\)/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("keeps onboarding transcript and intake card on the same session column", () => {
|
||||||
|
const pageSource = readProjectFile("src/app/page.tsx");
|
||||||
|
assert.match(pageSource, /\$\{!profileComplete \? " is-onboarding" : ""\}/);
|
||||||
|
assert.match(globalStyles, /\.conversation\.is-onboarding \.welcome \{[\s\S]*width:\s*min\(calc\(var\(--session-column-width\) \+ \(var\(--session-column-gutter\) \* 2\)\), 100%\)/);
|
||||||
|
assert.match(globalStyles, /\.conversation\.is-onboarding\.is-empty \.welcome \{[\s\S]*width:\s*min\(var\(--session-column-width\), 100%\)/);
|
||||||
|
assert.match(globalStyles, /\.conversation\.is-onboarding \.onboarding-card,\s*\n\.conversation\.is-onboarding \.onboarding-card\.birth-time-transition-card \{[\s\S]*width:\s*100%;[\s\S]*max-width:\s*none/);
|
||||||
|
assert.match(globalStyles, /\.conversation\.is-onboarding-form \.welcome \{[\s\S]*padding:\s*var\(--space-6\) var\(--session-column-gutter\) var\(--space-8\)/);
|
||||||
|
});
|
||||||
|
|
||||||
test("keeps message motion restrained and honors reduced-motion preferences", () => {
|
test("keeps message motion restrained and honors reduced-motion preferences", () => {
|
||||||
assert.match(messageRowSource, /gsap\.matchMedia\(\)/);
|
assert.match(messageRowSource, /gsap\.matchMedia\(\)/);
|
||||||
assert.match(messageRowSource, /prefers-reduced-motion:\s*no-preference/);
|
assert.match(messageRowSource, /prefers-reduced-motion:\s*no-preference/);
|
||||||
|
|||||||
Reference in New Issue
Block a user