fix(08): 用主题色替换留存的 white/black 工具颜色,
This commit is contained in:
parent
161e5fad3c
commit
54ef439226
|
|
@ -37,6 +37,13 @@
|
||||||
- [ ] **ATREF-03**: 引用文件复用 `additional_kwargs.files` 提交,含来源元信息;失效引用软剔除并不阻断消息发送
|
- [ ] **ATREF-03**: 引用文件复用 `additional_kwargs.files` 提交,含来源元信息;失效引用软剔除并不阻断消息发送
|
||||||
- [ ] **ATREF-04**: 引用能力具备自动化回归验证(单测 + E2E)及按 style/logic/tests/docs 的提交分组计划
|
- [ ] **ATREF-04**: 引用能力具备自动化回归验证(单测 + E2E)及按 style/logic/tests/docs 的提交分组计划
|
||||||
|
|
||||||
|
### Theme Tokenization and Color Guard (Phase 8)
|
||||||
|
|
||||||
|
- [ ] **P8-01**: Workspace 核心页面与组件(thread page、input box、artifact detail/list、workspace layout/header)中的 `bg-[#...]`/`text-[#...]`/`stroke="#..."` 等硬编码颜色迁移为 light/dark 主题 token
|
||||||
|
- [ ] **P8-02**: 建立颜色 token 注册表并满足“每个 distinct 颜色值对应一个 distinct token 名称”的唯一性约束(禁止多个不同颜色值映射到同名 token)
|
||||||
|
- [ ] **P8-03**: 增加自动化扫描守卫,阻止新增 `#hex` 与 `bg-[#...]`/`text-[#...]`(含同类 arbitrary color)回归
|
||||||
|
- [ ] **P8-04**: 覆盖 workspace 关键页面与组件的 light/dark 回归验证(静态扫描 + 自动化用例 + 可复现命令)
|
||||||
|
|
||||||
## v2 Requirements
|
## v2 Requirements
|
||||||
|
|
||||||
### Tooling Improvements
|
### Tooling Improvements
|
||||||
|
|
@ -73,10 +80,14 @@
|
||||||
| ATREF-02 | Phase 6 | Pending |
|
| ATREF-02 | Phase 6 | Pending |
|
||||||
| ATREF-03 | Phase 6 | Pending |
|
| ATREF-03 | Phase 6 | Pending |
|
||||||
| ATREF-04 | Phase 6 | Pending |
|
| ATREF-04 | Phase 6 | Pending |
|
||||||
|
| P8-01 | Phase 8 | Pending |
|
||||||
|
| P8-02 | Phase 8 | Pending |
|
||||||
|
| P8-03 | Phase 8 | Pending |
|
||||||
|
| P8-04 | Phase 8 | Pending |
|
||||||
|
|
||||||
**Coverage:**
|
**Coverage:**
|
||||||
- v1 requirements: 17 total
|
- v1 requirements: 21 total
|
||||||
- Mapped to phases: 17
|
- Mapped to phases: 21
|
||||||
- Unmapped: 0
|
- Unmapped: 0
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,19 @@ Plans:
|
||||||
- [x] 07-01-PLAN.md — 提交态增强文本组装 + 三入口统一透传 + 显示态/提交态分离回归
|
- [x] 07-01-PLAN.md — 提交态增强文本组装 + 三入口统一透传 + 显示态/提交态分离回归
|
||||||
- [x] 07-02-PLAN.md — gap closure:修复 ContextMenu 自动引用、提示前缀唯一化、Skill 使用 id 拼接
|
- [x] 07-02-PLAN.md — gap closure:修复 ContextMenu 自动引用、提示前缀唯一化、Skill 使用 id 拼接
|
||||||
|
|
||||||
|
### Phase 8: 现在系统中有非常多写死的颜色值比如bg-[#00000],text-[#000000],我想把这些颜色值都提升到浅色模式和深色模式里面
|
||||||
|
|
||||||
|
**Goal:** 将 workspace 核心页面/组件中的硬编码颜色迁移为 light/dark 主题 token,并建立防回归扫描守卫。
|
||||||
|
**Requirements**: P8-01, P8-02, P8-03, P8-04
|
||||||
|
**Depends on:** Phase 7
|
||||||
|
**Plans:** 4 plans
|
||||||
|
|
||||||
|
Plans:
|
||||||
|
- [ ] 08-01-PLAN.md — 建立颜色 token 注册表与扫描守卫基础能力
|
||||||
|
- [ ] 08-02-PLAN.md — 迁移 chat/input/workspace 关键页面组件的硬编码颜色
|
||||||
|
- [ ] 08-03-PLAN.md — 迁移 artifact 关键组件的硬编码颜色与局部样式变量
|
||||||
|
- [ ] 08-04-PLAN.md — 建立回归验证闭环并固化防回归检查
|
||||||
|
|
||||||
---
|
---
|
||||||
*Milestone status:* `complete`
|
*Milestone status:* `complete`
|
||||||
*Next command:* `/gsd-new-milestone`
|
*Next command:* `/gsd-new-milestone`
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,15 @@
|
||||||
gsd_state_version: 1.0
|
gsd_state_version: 1.0
|
||||||
milestone: v1.0
|
milestone: v1.0
|
||||||
milestone_name: milestone
|
milestone_name: milestone
|
||||||
status: v1.0 milestone complete
|
status: Executing Phase 8
|
||||||
last_updated: "2026-04-22T02:08:30.000Z"
|
last_updated: "2026-04-23T01:22:12.681Z"
|
||||||
last_activity: 2026-04-22
|
last_activity: 2026-04-23
|
||||||
progress:
|
progress:
|
||||||
total_phases: 8
|
total_phases: 8
|
||||||
completed_phases: 7
|
completed_phases: 7
|
||||||
total_plans: 13
|
total_plans: 17
|
||||||
completed_plans: 16
|
completed_plans: 16
|
||||||
percent: 100
|
percent: 94
|
||||||
---
|
---
|
||||||
|
|
||||||
# STATE.md
|
# STATE.md
|
||||||
|
|
@ -20,7 +20,7 @@ progress:
|
||||||
See: .planning/PROJECT.md (updated 2026-04-07)
|
See: .planning/PROJECT.md (updated 2026-04-07)
|
||||||
|
|
||||||
**Core value:** Keep the frontend visually familiar while preserving and hardening new-system behavior end to end.
|
**Core value:** Keep the frontend visually familiar while preserving and hardening new-system behavior end to end.
|
||||||
**Current focus:** Milestone v1.0 completed
|
**Current focus:** Phase 8 — 现在系统中有非常多写死的颜色值比如bg-[#00000],text-[#000000],我想把这些颜色值都提升到浅色模式和深色模式里面
|
||||||
|
|
||||||
## Workflow State
|
## Workflow State
|
||||||
|
|
||||||
|
|
@ -46,6 +46,7 @@ See: .planning/PROJECT.md (updated 2026-04-07)
|
||||||
|
|
||||||
- Phase 6 added: 在输入框输入@时,可引用已生成文件和已上传附件
|
- Phase 6 added: 在输入框输入@时,可引用已生成文件和已上传附件
|
||||||
- Phase 7 added: 发送时拼接附件与Skill优先提示词并在消息区过滤
|
- Phase 7 added: 发送时拼接附件与Skill优先提示词并在消息区过滤
|
||||||
|
- Phase 8 added: 现在系统中有非常多写死的颜色值比如bg-[#00000],text-[#000000],我想把这些颜色值都提升到浅色模式和深色模式里面
|
||||||
|
|
||||||
### Quick Tasks Completed
|
### Quick Tasks Completed
|
||||||
|
|
||||||
|
|
@ -55,4 +56,4 @@ See: .planning/PROJECT.md (updated 2026-04-07)
|
||||||
| 260416-koe | 归档 Phase 06 明确指代(“这张图”)语义修复到 GSD 流程(已验收,通过人工确认,免验证) | 2026-04-16 | pending | [260416-koe-phase-06](./quick/260416-koe-phase-06/) |
|
| 260416-koe | 归档 Phase 06 明确指代(“这张图”)语义修复到 GSD 流程(已验收,通过人工确认,免验证) | 2026-04-16 | pending | [260416-koe-phase-06](./quick/260416-koe-phase-06/) |
|
||||||
| 260422-e2i | 后端为会话历史消息增加时间戳字段(前端不显示) | 2026-04-22 | pending | [260422-e2i-message-timestamp](./quick/260422-e2i-message-timestamp/) |
|
| 260422-e2i | 后端为会话历史消息增加时间戳字段(前端不显示) | 2026-04-22 | pending | [260422-e2i-message-timestamp](./quick/260422-e2i-message-timestamp/) |
|
||||||
|
|
||||||
Last activity: 2026-04-22
|
Last activity: 2026-04-23
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,8 @@ const TOKENS_PATH = path.join(SRC_ROOT, "styles", "workspace-color-tokens.ts");
|
||||||
const HEX_RE = /#[0-9a-fA-F]{3,8}\b/g;
|
const HEX_RE = /#[0-9a-fA-F]{3,8}\b/g;
|
||||||
const ARBITRARY_COLOR_RE =
|
const ARBITRARY_COLOR_RE =
|
||||||
/\b(?:bg|text|border|ring|from|to|via|fill|stroke)-\[[^\]]+\]/g;
|
/\b(?:bg|text|border|ring|from|to|via|fill|stroke)-\[[^\]]+\]/g;
|
||||||
|
const NAMED_COLOR_RE =
|
||||||
|
/\b(?:bg|text|border|ring|from|to|via|fill|stroke)-(?:white|black)(?:\/\d+)?\b/g;
|
||||||
const EXCLUDED_HEX_FILES = new Set([GLOBALS_PATH, TOKENS_PATH]);
|
const EXCLUDED_HEX_FILES = new Set([GLOBALS_PATH, TOKENS_PATH]);
|
||||||
const MODE = process.argv.includes("--mode=guard") ? "guard" : "audit";
|
const MODE = process.argv.includes("--mode=guard") ? "guard" : "audit";
|
||||||
|
|
||||||
|
|
@ -53,6 +55,7 @@ function scanFullSource() {
|
||||||
const report = {
|
const report = {
|
||||||
hex: [],
|
hex: [],
|
||||||
arbitrary: [],
|
arbitrary: [],
|
||||||
|
named: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
|
|
@ -74,6 +77,10 @@ function scanFullSource() {
|
||||||
for (const finding of arbitraryFindings) {
|
for (const finding of arbitraryFindings) {
|
||||||
report.arbitrary.push({ file, ...finding });
|
report.arbitrary.push({ file, ...finding });
|
||||||
}
|
}
|
||||||
|
const namedFindings = collectMatchesInContent(content, NAMED_COLOR_RE, () => true);
|
||||||
|
for (const finding of namedFindings) {
|
||||||
|
report.named.push({ file, ...finding });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return report;
|
return report;
|
||||||
|
|
@ -151,6 +158,7 @@ function scanAddedViolations() {
|
||||||
const report = {
|
const report = {
|
||||||
hex: [],
|
hex: [],
|
||||||
arbitrary: [],
|
arbitrary: [],
|
||||||
|
named: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const [file, lines] of addedLines.entries()) {
|
for (const [file, lines] of addedLines.entries()) {
|
||||||
|
|
@ -165,6 +173,10 @@ function scanAddedViolations() {
|
||||||
for (const match of content.matchAll(ARBITRARY_COLOR_RE)) {
|
for (const match of content.matchAll(ARBITRARY_COLOR_RE)) {
|
||||||
report.arbitrary.push({ file, line, match: match[0] });
|
report.arbitrary.push({ file, line, match: match[0] });
|
||||||
}
|
}
|
||||||
|
NAMED_COLOR_RE.lastIndex = 0;
|
||||||
|
for (const match of content.matchAll(NAMED_COLOR_RE)) {
|
||||||
|
report.named.push({ file, line, match: match[0] });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -276,10 +288,10 @@ async function main() {
|
||||||
|
|
||||||
console.log(`[color-guard] mode=${MODE}`);
|
console.log(`[color-guard] mode=${MODE}`);
|
||||||
console.log(
|
console.log(
|
||||||
`[summary] full-scan hex=${fullScan.hex.length} arbitrary=${fullScan.arbitrary.length}`,
|
`[summary] full-scan hex=${fullScan.hex.length} arbitrary=${fullScan.arbitrary.length} named=${fullScan.named.length}`,
|
||||||
);
|
);
|
||||||
console.log(
|
console.log(
|
||||||
`[summary] added-violations hex=${addedViolations.hex.length} arbitrary=${addedViolations.arbitrary.length}`,
|
`[summary] added-violations hex=${addedViolations.hex.length} arbitrary=${addedViolations.arbitrary.length} named=${addedViolations.named.length}`,
|
||||||
);
|
);
|
||||||
console.log(
|
console.log(
|
||||||
`[summary] ws-vars root=${globalsValidation.rootCount} dark=${globalsValidation.darkCount} inline=${globalsValidation.inlineCount}`,
|
`[summary] ws-vars root=${globalsValidation.rootCount} dark=${globalsValidation.darkCount} inline=${globalsValidation.inlineCount}`,
|
||||||
|
|
@ -287,6 +299,7 @@ async function main() {
|
||||||
|
|
||||||
printFindings("[added] hex violations", addedViolations.hex);
|
printFindings("[added] hex violations", addedViolations.hex);
|
||||||
printFindings("[added] arbitrary color violations", addedViolations.arbitrary);
|
printFindings("[added] arbitrary color violations", addedViolations.arbitrary);
|
||||||
|
printFindings("[added] named color violations", addedViolations.named);
|
||||||
|
|
||||||
const semanticErrors = [...tokenValidation.errors, ...globalsValidation.errors];
|
const semanticErrors = [...tokenValidation.errors, ...globalsValidation.errors];
|
||||||
if (semanticErrors.length > 0) {
|
if (semanticErrors.length > 0) {
|
||||||
|
|
@ -299,6 +312,7 @@ async function main() {
|
||||||
const hasViolations =
|
const hasViolations =
|
||||||
addedViolations.hex.length > 0 ||
|
addedViolations.hex.length > 0 ||
|
||||||
addedViolations.arbitrary.length > 0 ||
|
addedViolations.arbitrary.length > 0 ||
|
||||||
|
addedViolations.named.length > 0 ||
|
||||||
semanticErrors.length > 0;
|
semanticErrors.length > 0;
|
||||||
|
|
||||||
if (MODE === "guard" && hasViolations) {
|
if (MODE === "guard" && hasViolations) {
|
||||||
|
|
|
||||||
|
|
@ -400,7 +400,7 @@ export default function ChatPage() {
|
||||||
<div className="flex items-center justify-end gap-2 overflow-hidden">
|
<div className="flex items-center justify-end gap-2 overflow-hidden">
|
||||||
{/* 取消TodoList */}
|
{/* 取消TodoList */}
|
||||||
{/* <DevTodoList
|
{/* <DevTodoList
|
||||||
className="bg-white"
|
className="bg-ws-ffffff"
|
||||||
todos={thread.values.todos ?? []}
|
todos={thread.values.todos ?? []}
|
||||||
hidden={
|
hidden={
|
||||||
!thread.values.todos || thread.values.todos.length === 0
|
!thread.values.todos || thread.values.todos.length === 0
|
||||||
|
|
@ -438,7 +438,7 @@ export default function ChatPage() {
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex min-h-0 max-w-full grow flex-col",
|
"flex min-h-0 max-w-full grow flex-col",
|
||||||
showWelcomeStyle && !hasSubmitted
|
showWelcomeStyle && !hasSubmitted
|
||||||
? "bg-white"
|
? "bg-ws-ffffff"
|
||||||
: "bg-background",
|
: "bg-background",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|
@ -609,14 +609,14 @@ export default function ChatPage() {
|
||||||
</p>
|
</p>
|
||||||
<DevDialogFooter>
|
<DevDialogFooter>
|
||||||
<Button
|
<Button
|
||||||
className="w-full bg-ws-f9f8fa hover:bg-ws-8e47f0 hover:text-white"
|
className="w-full bg-ws-f9f8fa hover:bg-ws-8e47f0 hover:text-primary-foreground"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
onClick={() => setShowExitDialog(false)}
|
onClick={() => setShowExitDialog(false)}
|
||||||
>
|
>
|
||||||
{t.common.cancel}
|
{t.common.cancel}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
className="w-full bg-ws-f9f8fa hover:bg-ws-8e47f0 hover:text-white"
|
className="w-full bg-ws-f9f8fa hover:bg-ws-8e47f0 hover:text-primary-foreground"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
// 如果正在生成,先终止再退出
|
// 如果正在生成,先终止再退出
|
||||||
|
|
@ -665,7 +665,7 @@ export default function ChatPage() {
|
||||||
</p>
|
</p>
|
||||||
<DevDialogFooter singleColumn>
|
<DevDialogFooter singleColumn>
|
||||||
<Button
|
<Button
|
||||||
className="w-full bg-ws-f9f8fa hover:bg-ws-8e47f0 hover:text-white"
|
className="w-full bg-ws-f9f8fa hover:bg-ws-8e47f0 hover:text-primary-foreground"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
onClick={clearSelectedSkillError}
|
onClick={clearSelectedSkillError}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -138,12 +138,12 @@ export default function WorkspaceLayout({
|
||||||
/* 单行布局,内容水平居中 */
|
/* 单行布局,内容水平居中 */
|
||||||
"flex items-center justify-center gap-0",
|
"flex items-center justify-center gap-0",
|
||||||
/* 整体文字样式 */
|
/* 整体文字样式 */
|
||||||
"text-white text-sm font-normal font-sans",
|
"text-primary-foreground text-sm font-normal font-sans",
|
||||||
/* 去掉 icon 区域间距 */
|
/* 去掉 icon 区域间距 */
|
||||||
"[&>[data-icon]]:hidden",
|
"[&>[data-icon]]:hidden",
|
||||||
].join(" "),
|
].join(" "),
|
||||||
title:
|
title:
|
||||||
"text-white! text-sm font-normal text-center w-full leading-snug",
|
"text-primary-foreground! text-sm font-normal text-center w-full leading-snug",
|
||||||
description: "hidden",
|
description: "hidden",
|
||||||
icon: "hidden",
|
icon: "hidden",
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ export const Message = ({
|
||||||
"group flex w-full flex-col gap-2",
|
"group flex w-full flex-col gap-2",
|
||||||
from === "user"
|
from === "user"
|
||||||
? cn("is-user ml-auto justify-end", !isFirstInSession && "mt-6")
|
? cn("is-user ml-auto justify-end", !isFirstInSession && "mt-6")
|
||||||
: "is-assistant rounded-[10px] bg-white p-4",
|
: "is-assistant rounded-[10px] bg-ws-ffffff p-4",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
|
||||||
|
|
@ -352,7 +352,7 @@ export function PromptInputAttachment({
|
||||||
{/* 删除按钮 - 右上角 */}
|
{/* 删除按钮 - 右上角 */}
|
||||||
<button
|
<button
|
||||||
aria-label={t.common.removeAttachment}
|
aria-label={t.common.removeAttachment}
|
||||||
className="absolute top-1.5 right-1.5 z-10 flex size-4 cursor-pointer items-center justify-center rounded-sm transition-colors hover:bg-white/20"
|
className="absolute top-1.5 right-1.5 z-10 flex size-4 cursor-pointer items-center justify-center rounded-sm transition-colors hover:bg-ws-ffffff/20"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if (onRemove) {
|
if (onRemove) {
|
||||||
|
|
@ -397,7 +397,7 @@ export function PromptInputAttachment({
|
||||||
{/* 关闭按钮 - 右上角 */}
|
{/* 关闭按钮 - 右上角 */}
|
||||||
<button
|
<button
|
||||||
aria-label={t.common.removeAttachment}
|
aria-label={t.common.removeAttachment}
|
||||||
className="absolute top-1 right-1 z-10 flex size-5 cursor-pointer items-center justify-center rounded bg-white/90 opacity-0 transition-opacity group-hover:opacity-100 hover:bg-white dark:bg-gray-800/90 dark:hover:bg-gray-800"
|
className="absolute top-1 right-1 z-10 flex size-5 cursor-pointer items-center justify-center rounded bg-ws-ffffff/90 opacity-0 transition-opacity group-hover:opacity-100 hover:bg-ws-ffffff dark:bg-gray-800/90 dark:hover:bg-gray-800"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if (onRemove) {
|
if (onRemove) {
|
||||||
|
|
|
||||||
|
|
@ -430,7 +430,7 @@ export function ArtifactFileDetail({
|
||||||
type="single"
|
type="single"
|
||||||
variant={null}
|
variant={null}
|
||||||
size="default"
|
size="default"
|
||||||
className="h-[28px] bg-white"
|
className="h-[28px] bg-ws-ffffff"
|
||||||
value={viewMode}
|
value={viewMode}
|
||||||
onValueChange={(value) => {
|
onValueChange={(value) => {
|
||||||
if (value) {
|
if (value) {
|
||||||
|
|
@ -721,7 +721,7 @@ export function ArtifactFileDetail({
|
||||||
</ArtifactHeader>
|
</ArtifactHeader>
|
||||||
<ArtifactContent>
|
<ArtifactContent>
|
||||||
{/* 遮挡多余的滚动顶部 */}
|
{/* 遮挡多余的滚动顶部 */}
|
||||||
{/* <div className="absolute w-[calc(100%-40px)] bg-white z-20 h-5 rounded-t-[10px] top-[57px]"></div> */}
|
{/* <div className="absolute w-[calc(100%-40px)] bg-ws-ffffff z-20 h-5 rounded-t-[10px] top-[57px]"></div> */}
|
||||||
{previewable &&
|
{previewable &&
|
||||||
viewMode === "preview" &&
|
viewMode === "preview" &&
|
||||||
(language === "markdown" || language === "html") && (
|
(language === "markdown" || language === "html") && (
|
||||||
|
|
@ -734,7 +734,7 @@ export function ArtifactFileDetail({
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{isCodeFile && viewMode === "code" && (
|
{isCodeFile && viewMode === "code" && (
|
||||||
<div className="mb-0 mb-[207px] min-h-full rounded-b-[10px] bg-white p-0">
|
<div className="mb-0 mb-[207px] min-h-full rounded-b-[10px] bg-ws-ffffff p-0">
|
||||||
<CodeEditor
|
<CodeEditor
|
||||||
className="size-full resize-none rounded-none border-none py-[20px]"
|
className="size-full resize-none rounded-none border-none py-[20px]"
|
||||||
value={displayContent ?? ""}
|
value={displayContent ?? ""}
|
||||||
|
|
@ -917,7 +917,7 @@ export function ArtifactFilePreview({
|
||||||
if (language === "markdown") {
|
if (language === "markdown") {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn("mb-[207px] w-full bg-white p-[20px]")}
|
className={cn("mb-[207px] w-full bg-ws-ffffff p-[20px]")}
|
||||||
style={{ "--zoom-scale": zoomScale } as CSSProperties}
|
style={{ "--zoom-scale": zoomScale } as CSSProperties}
|
||||||
>
|
>
|
||||||
<Streamdown
|
<Streamdown
|
||||||
|
|
@ -974,7 +974,7 @@ function PreviewIframe({
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
{isLoading && (
|
{isLoading && (
|
||||||
<div className="absolute inset-0 z-10 flex items-center justify-center bg-white/85">
|
<div className="absolute inset-0 z-10 flex items-center justify-center bg-ws-ffffff/85">
|
||||||
<LoaderIcon className="text-muted-foreground size-5 animate-spin" />
|
<LoaderIcon className="text-muted-foreground size-5 animate-spin" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -1046,7 +1046,7 @@ function ArtifactPdfPreview({
|
||||||
|
|
||||||
const pageWrapper = document.createElement("div");
|
const pageWrapper = document.createElement("div");
|
||||||
pageWrapper.className =
|
pageWrapper.className =
|
||||||
"mx-auto mb-4 w-fit rounded-md border border-ws-e4e7ec bg-white p-2 shadow-sm";
|
"mx-auto mb-4 w-fit rounded-md border border-ws-e4e7ec bg-ws-ffffff p-2 shadow-sm";
|
||||||
|
|
||||||
const canvas = document.createElement("canvas");
|
const canvas = document.createElement("canvas");
|
||||||
canvas.style.width = `${viewport.width}px`;
|
canvas.style.width = `${viewport.width}px`;
|
||||||
|
|
@ -1090,7 +1090,7 @@ function ArtifactPdfPreview({
|
||||||
if (error) {
|
if (error) {
|
||||||
return (
|
return (
|
||||||
<div className={cn("relative overflow-auto bg-ws-f9f8fa p-4", className)}>
|
<div className={cn("relative overflow-auto bg-ws-f9f8fa p-4", className)}>
|
||||||
<div className="mx-auto grid max-w-xl gap-3 rounded-md border border-ws-e4e7ec bg-white p-5 text-center">
|
<div className="mx-auto grid max-w-xl gap-3 rounded-md border border-ws-e4e7ec bg-ws-ffffff p-5 text-center">
|
||||||
<p className="text-sm font-medium break-all">{fileName}</p>
|
<p className="text-sm font-medium break-all">{fileName}</p>
|
||||||
<p className="text-muted-foreground text-sm">{error}</p>
|
<p className="text-muted-foreground text-sm">{error}</p>
|
||||||
<a
|
<a
|
||||||
|
|
@ -1115,7 +1115,7 @@ function ArtifactPdfPreview({
|
||||||
</div>
|
</div>
|
||||||
<div ref={containerRef} />
|
<div ref={containerRef} />
|
||||||
{isLoading && (
|
{isLoading && (
|
||||||
<div className="absolute inset-0 z-10 flex items-center justify-center bg-white/70">
|
<div className="absolute inset-0 z-10 flex items-center justify-center bg-ws-ffffff/70">
|
||||||
<LoaderIcon className="text-muted-foreground size-5 animate-spin" />
|
<LoaderIcon className="text-muted-foreground size-5 animate-spin" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -1313,7 +1313,7 @@ function ArtifactOfficePreview({
|
||||||
}, [canRenderPptx, t.artifactPreview.pptxDownloadHint]);
|
}, [canRenderPptx, t.artifactPreview.pptxDownloadHint]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn("relative h-full overflow-hidden bg-white", className)}>
|
<div className={cn("relative h-full overflow-hidden bg-ws-ffffff", className)}>
|
||||||
{canRenderXlsx && sheetNames.length > 0 && (
|
{canRenderXlsx && sheetNames.length > 0 && (
|
||||||
<div className="border-border flex items-center gap-1 overflow-x-auto border-b p-2">
|
<div className="border-border flex items-center gap-1 overflow-x-auto border-b p-2">
|
||||||
{sheetNames.map((sheetName) => (
|
{sheetNames.map((sheetName) => (
|
||||||
|
|
@ -1357,7 +1357,7 @@ function ArtifactOfficePreview({
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{isLoading && (
|
{isLoading && (
|
||||||
<div className="absolute inset-0 z-10 flex items-center justify-center bg-white/85">
|
<div className="absolute inset-0 z-10 flex items-center justify-center bg-ws-ffffff/85">
|
||||||
<LoaderIcon className="text-muted-foreground size-5 animate-spin" />
|
<LoaderIcon className="text-muted-foreground size-5 animate-spin" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -1376,7 +1376,7 @@ function ArtifactPreviewFallback({
|
||||||
}) {
|
}) {
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
return (
|
return (
|
||||||
<div className="absolute inset-0 z-20 grid place-content-center bg-white p-6 text-center">
|
<div className="absolute inset-0 z-20 grid place-content-center bg-ws-ffffff p-6 text-center">
|
||||||
<p className="text-foreground mb-2 text-sm font-medium">{fileName}</p>
|
<p className="text-foreground mb-2 text-sm font-medium">{fileName}</p>
|
||||||
<p className="text-muted-foreground mb-3 text-xs">{message}</p>
|
<p className="text-muted-foreground mb-3 text-xs">{message}</p>
|
||||||
<a
|
<a
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ export function DevTodoList({
|
||||||
<DropdownMenuTrigger asChild>{trigger}</DropdownMenuTrigger>
|
<DropdownMenuTrigger asChild>{trigger}</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent
|
<DropdownMenuContent
|
||||||
className={cn(
|
className={cn(
|
||||||
"z-[100] rounded-[20px] bg-white p-5 shadow-[0_0_20px_0_rgba(0,0,0,0.20)]",
|
"z-[100] rounded-[20px] bg-ws-ffffff p-5 shadow-[0_0_20px_0_rgba(0,0,0,0.20)]",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
align="start"
|
align="start"
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,7 @@ export function IframeTestPanel() {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
className={cn(
|
className={cn(
|
||||||
"fixed z-[9999] rounded-full bg-violet-500 px-3 py-1 text-xs font-bold text-white shadow-lg hover:bg-violet-600",
|
"fixed z-[9999] rounded-full bg-violet-500 px-3 py-1 text-xs font-bold text-primary-foreground shadow-lg hover:bg-violet-600",
|
||||||
position ? "top-0 left-0" : "bottom-24 left-3",
|
position ? "top-0 left-0" : "bottom-24 left-3",
|
||||||
)}
|
)}
|
||||||
style={position ? { left: position.x, top: position.y } : undefined}
|
style={position ? { left: position.x, top: position.y } : undefined}
|
||||||
|
|
@ -157,7 +157,7 @@ export function IframeTestPanel() {
|
||||||
<div
|
<div
|
||||||
ref={panelRef}
|
ref={panelRef}
|
||||||
className={cn(
|
className={cn(
|
||||||
"fixed z-[9999] w-72 rounded-xl border border-violet-200 bg-white/95 shadow-2xl backdrop-blur-sm",
|
"fixed z-[9999] w-72 rounded-xl border border-violet-200 bg-ws-ffffff/95 shadow-2xl backdrop-blur-sm",
|
||||||
position ? "top-0 left-0" : "bottom-24 left-3",
|
position ? "top-0 left-0" : "bottom-24 left-3",
|
||||||
)}
|
)}
|
||||||
style={position ? { left: position.x, top: position.y } : undefined}
|
style={position ? { left: position.x, top: position.y } : undefined}
|
||||||
|
|
@ -170,17 +170,17 @@ export function IframeTestPanel() {
|
||||||
)}
|
)}
|
||||||
onPointerDown={handlePointerDown}
|
onPointerDown={handlePointerDown}
|
||||||
>
|
>
|
||||||
<span className="text-xs font-bold text-white">🧪 iframe 通信测试</span>
|
<span className="text-xs font-bold text-primary-foreground">🧪 iframe 通信测试</span>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<button
|
<button
|
||||||
className="text-white/70 hover:text-white"
|
className="text-primary-foreground/70 hover:text-primary-foreground"
|
||||||
onPointerDown={(event) => event.stopPropagation()}
|
onPointerDown={(event) => event.stopPropagation()}
|
||||||
onClick={() => setCollapsed((prev) => !prev)}
|
onClick={() => setCollapsed((prev) => !prev)}
|
||||||
>
|
>
|
||||||
{collapsed ? "▢" : "—"}
|
{collapsed ? "▢" : "—"}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="text-white/70 hover:text-white"
|
className="text-primary-foreground/70 hover:text-primary-foreground"
|
||||||
onPointerDown={(event) => event.stopPropagation()}
|
onPointerDown={(event) => event.stopPropagation()}
|
||||||
onClick={() => setOpen(false)}
|
onClick={() => setOpen(false)}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,7 @@ export function MessageGroup({
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<ChainOfThought
|
<ChainOfThought
|
||||||
className={cn("w-full gap-2 rounded-lg bg-white", className)}
|
className={cn("w-full gap-2 rounded-lg bg-ws-ffffff", className)}
|
||||||
open={true}
|
open={true}
|
||||||
>
|
>
|
||||||
{aboveLastToolCallSteps.length > 0 && (
|
{aboveLastToolCallSteps.length > 0 && (
|
||||||
|
|
|
||||||
|
|
@ -225,7 +225,7 @@ export function MessageList({
|
||||||
{showScrollToBottomButton && (
|
{showScrollToBottomButton && (
|
||||||
<ConversationScrollButton
|
<ConversationScrollButton
|
||||||
className={cn(
|
className={cn(
|
||||||
"z-20 rounded-full border bg-white/90 shadow-sm backdrop-blur-sm",
|
"z-20 rounded-full border bg-ws-ffffff/90 shadow-sm backdrop-blur-sm",
|
||||||
scrollButtonClassName,
|
scrollButtonClassName,
|
||||||
)}
|
)}
|
||||||
title={t.chats.scrollToBottom}
|
title={t.chats.scrollToBottom}
|
||||||
|
|
|
||||||
|
|
@ -157,7 +157,7 @@ function ThemePreviewCard({
|
||||||
"relative overflow-hidden rounded-md border text-xs transition-colors",
|
"relative overflow-hidden rounded-md border text-xs transition-colors",
|
||||||
previewMode === "dark"
|
previewMode === "dark"
|
||||||
? "border-neutral-800 bg-neutral-900 text-neutral-200"
|
? "border-neutral-800 bg-neutral-900 text-neutral-200"
|
||||||
: "border-slate-200 bg-white text-slate-900",
|
: "border-slate-200 bg-ws-ffffff text-slate-900",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="border-border/50 flex items-center gap-2 border-b px-3 py-2">
|
<div className="border-border/50 flex items-center gap-2 border-b px-3 py-2">
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ export function TodoList({
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex h-fit w-full origin-bottom translate-y-4 flex-col overflow-hidden rounded-t-xl border border-b-0 bg-white backdrop-blur-sm transition-all duration-200 ease-out",
|
"flex h-fit w-full origin-bottom translate-y-4 flex-col overflow-hidden rounded-t-xl border border-b-0 bg-ws-ffffff backdrop-blur-sm transition-all duration-200 ease-out",
|
||||||
hidden ? "pointer-events-none translate-y-8 opacity-0" : "",
|
hidden ? "pointer-events-none translate-y-8 opacity-0" : "",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ export function WorkspaceSidebar({
|
||||||
<WorkspaceNavChatList />
|
<WorkspaceNavChatList />
|
||||||
{isSidebarOpen && <RecentChatList />}
|
{isSidebarOpen && <RecentChatList />}
|
||||||
</SidebarContent>
|
</SidebarContent>
|
||||||
<SidebarFooter>{/* <WorkspaceNavMenu /> */}</SidebarFooter>
|
<SidebarFooter><WorkspaceNavMenu /></SidebarFooter>
|
||||||
<SidebarRail />
|
<SidebarRail />
|
||||||
</Sidebar>
|
</Sidebar>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -226,7 +226,7 @@ test.describe("安全 / 思考块与敏感信息泄露", () => {
|
||||||
|
|
||||||
// 不限制在单条 assistant 消息内:以 Chain-of-thought 容器出现 “steps” 作为信号。
|
// 不限制在单条 assistant 消息内:以 Chain-of-thought 容器出现 “steps” 作为信号。
|
||||||
const stepsSignal = page
|
const stepsSignal = page
|
||||||
.locator(".not-prose.w-full.gap-2.rounded-lg.bg-white")
|
.locator(".not-prose.w-full.gap-2.rounded-lg.bg-background")
|
||||||
.locator("text=/steps/i");
|
.locator("text=/steps/i");
|
||||||
|
|
||||||
const hasStepsSignal = await waitForConditionWithLeakCheck({
|
const hasStepsSignal = await waitForConditionWithLeakCheck({
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue