Commit 9fd7024f authored by edy's avatar edy

fix(ui): align task automation settings workbench ui

parent af633e6e
...@@ -360,12 +360,12 @@ export function SettingsPanels({ ...@@ -360,12 +360,12 @@ export function SettingsPanels({
tabIndex={value ? 0 : -1} tabIndex={value ? 0 : -1}
aria-disabled={!value} aria-disabled={!value}
aria-label={value ? `${label} ${copied ? "已复制" : "复制"}` : label} aria-label={value ? `${label} ${copied ? "已复制" : "复制"}` : label}
title={copied ? "已复制" : value || undefined} title={copied ? "已复制" : value || undefined}
onClick={() => copyReadonlyConfigValue(value)} onClick={() => copyReadonlyConfigValue(value)}
onKeyDown={(event) => handleReadonlyConfigKeyDown(event, value)} onKeyDown={(event) => handleReadonlyConfigKeyDown(event, value)}
> >
<span className="settings-readonly-config-text">{value || "-"}</span> <span className="settings-readonly-config-text">{value || "-"}</span>
{copied ? <span className="settings-readonly-config-copy-feedback">已复制</span> : null} {copied ? <span className="settings-readonly-config-copy-feedback">已复制</span> : null}
</div> </div>
</div> </div>
) )
......
...@@ -110,6 +110,15 @@ function TaskPanelOutputIcon({ artifact }: { artifact: TaskPanelArtifact }) { ...@@ -110,6 +110,15 @@ function TaskPanelOutputIcon({ artifact }: { artifact: TaskPanelArtifact }) {
) )
} }
function TaskPanelPackageIcon() {
return (
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">
<path d="M4.75 8.15 12 4.2l7.25 3.95v7.7L12 19.8l-7.25-3.95v-7.7Z" fill="none" stroke="currentColor" strokeLinejoin="round" strokeWidth="1.7" />
<path d="m4.95 8.35 7.05 3.9 7.05-3.9M12 12.25v7.25M8.35 6.2l7.25 4" fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.55" />
</svg>
)
}
function TaskPanelStatCards({ items }: { items: TaskPanelItem[] }) { function TaskPanelStatCards({ items }: { items: TaskPanelItem[] }) {
const summary = useMemo(() => summarizeTaskPanelItems(items), [items]) const summary = useMemo(() => summarizeTaskPanelItems(items), [items])
const stats = [ const stats = [
...@@ -194,7 +203,7 @@ function TaskPanelOutputList({ outputs }: { outputs: TaskPanelOutputItem[] }) { ...@@ -194,7 +203,7 @@ function TaskPanelOutputList({ outputs }: { outputs: TaskPanelOutputItem[] }) {
<div className="task-panel-output-header"> <div className="task-panel-output-header">
<div className="task-panel-output-heading"> <div className="task-panel-output-heading">
<span className="task-panel-output-heading-icon" aria-hidden="true"> <span className="task-panel-output-heading-icon" aria-hidden="true">
📦 <TaskPanelPackageIcon />
</span> </span>
<h2>内容产出</h2> <h2>内容产出</h2>
</div> </div>
...@@ -225,7 +234,7 @@ function TaskPanelOutputList({ outputs }: { outputs: TaskPanelOutputItem[] }) { ...@@ -225,7 +234,7 @@ function TaskPanelOutputList({ outputs }: { outputs: TaskPanelOutputItem[] }) {
{artifactPath} {artifactPath}
</button> </button>
{copiedOutputRowKey === outputRowKey ? ( {copiedOutputRowKey === outputRowKey ? (
<span className="task-panel-artifact-copied" aria-live="polite">已复制</span> <span className="task-panel-artifact-copied" aria-live="polite">已复制</span>
) : null} ) : null}
</div> </div>
) : null} ) : null}
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
gap: 14px; gap: 14px;
min-height: 0; min-height: 0;
padding: 0; padding: 0;
overflow: hidden;
} }
.automation-header, .automation-header,
...@@ -49,6 +50,7 @@ ...@@ -49,6 +50,7 @@
justify-content: center; justify-content: center;
gap: 6px; gap: 6px;
white-space: nowrap; white-space: nowrap;
min-width: 0;
} }
.automation-action-button svg, .automation-action-button svg,
...@@ -104,6 +106,13 @@ ...@@ -104,6 +106,13 @@
color: #1d4ed8; color: #1d4ed8;
} }
.automation-action-button:disabled,
.automation-delete-button:disabled,
.automation-filter-row button:disabled {
opacity: 0.55;
cursor: not-allowed;
}
.automation-form-actions .automation-form-button-secondary { .automation-form-actions .automation-form-button-secondary {
border: 1px solid rgba(203, 213, 225, 0.92); border: 1px solid rgba(203, 213, 225, 0.92);
background: #ffffff; background: #ffffff;
...@@ -183,9 +192,11 @@ ...@@ -183,9 +192,11 @@
.automation-layout { .automation-layout {
display: grid; display: grid;
grid-template-columns: minmax(260px, 0.8fr) minmax(0, 1.6fr); grid-template-columns: minmax(260px, 0.72fr) minmax(0, 1.7fr);
gap: 14px; gap: 14px;
height: 100%;
min-height: 0; min-height: 0;
overflow: hidden;
} }
.automation-list-pane, .automation-list-pane,
...@@ -201,7 +212,7 @@ ...@@ -201,7 +212,7 @@
.automation-list-pane { .automation-list-pane {
display: grid; display: grid;
grid-template-rows: auto minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr);
gap: 10px; gap: 8px;
padding: 12px; padding: 12px;
} }
...@@ -226,6 +237,7 @@ ...@@ -226,6 +237,7 @@
border-color: #93c5fd; border-color: #93c5fd;
background: #eff6ff; background: #eff6ff;
color: #1d4ed8; color: #1d4ed8;
box-shadow: inset 0 0 0 1px rgba(37, 99, 235, 0.12);
} }
.automation-list-scroll { .automation-list-scroll {
...@@ -243,13 +255,13 @@ ...@@ -243,13 +255,13 @@
.automation-task-list, .automation-task-list,
.automation-run-list { .automation-run-list {
display: grid; display: grid;
gap: 8px; gap: 7px;
} }
.automation-task-row { .automation-task-row {
height: 84px; height: 78px;
overflow: hidden; overflow: hidden;
padding: 12px; padding: 10px;
border: 1px solid rgba(226, 232, 240, 0.9); border: 1px solid rgba(226, 232, 240, 0.9);
border-radius: 12px; border-radius: 12px;
background: #ffffff; background: #ffffff;
...@@ -260,6 +272,7 @@ ...@@ -260,6 +272,7 @@
.automation-task-row.active { .automation-task-row.active {
border-color: #60a5fa; border-color: #60a5fa;
background: linear-gradient(180deg, #eff6ff, #ffffff); background: linear-gradient(180deg, #eff6ff, #ffffff);
box-shadow: inset 3px 0 0 #2563eb;
} }
.automation-task-select { .automation-task-select {
...@@ -303,8 +316,8 @@ ...@@ -303,8 +316,8 @@
.automation-detail-pane { .automation-detail-pane {
display: grid; display: grid;
grid-template-rows: auto auto minmax(0, 1fr); grid-template-rows: auto auto minmax(0, 1fr);
gap: 12px; gap: 10px;
padding: 16px; padding: 14px;
overflow: hidden; overflow: hidden;
} }
...@@ -327,7 +340,8 @@ ...@@ -327,7 +340,8 @@
max-width: 100%; max-width: 100%;
overflow: hidden; overflow: hidden;
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
-webkit-line-clamp: 3; -webkit-line-clamp: 2;
overflow-wrap: anywhere;
} }
.automation-kicker { .automation-kicker {
...@@ -391,7 +405,7 @@ ...@@ -391,7 +405,7 @@
} }
.automation-run-item { .automation-run-item {
padding: 12px; padding: 10px 12px;
border: 1px solid rgba(226, 232, 240, 0.9); border: 1px solid rgba(226, 232, 240, 0.9);
border-radius: 12px; border-radius: 12px;
background: #ffffff; background: #ffffff;
...@@ -442,8 +456,9 @@ ...@@ -442,8 +456,9 @@
.automation-form { .automation-form {
display: grid; display: grid;
grid-template-rows: minmax(0, 1fr) auto; grid-template-rows: minmax(0, 1fr) auto;
gap: 12px; gap: 10px;
padding: 14px; padding: 12px;
overflow: hidden;
} }
.automation-form-grid { .automation-form-grid {
...@@ -489,7 +504,7 @@ ...@@ -489,7 +504,7 @@
.automation-form-grid textarea { .automation-form-grid textarea {
resize: vertical; resize: vertical;
min-height: 120px; min-height: 104px;
padding: 10px; padding: 10px;
line-height: 1.5; line-height: 1.5;
} }
......
...@@ -456,6 +456,11 @@ ...@@ -456,6 +456,11 @@
line-height: 1; line-height: 1;
} }
.task-panel-output-heading-icon svg {
width: 22px;
height: 22px;
}
.task-panel-output-header h2 { .task-panel-output-header h2 {
margin: 0; margin: 0;
color: #17253d; color: #17253d;
......
...@@ -138,7 +138,8 @@ test("read-only config values expose full values by title and copy on click or k ...@@ -138,7 +138,8 @@ test("read-only config values expose full values by title and copy on click or k
assert.match(settingsPanelsSource, /const \[copiedConfigValue, setCopiedConfigValue\] = useState<string \| null>\(null\)/) assert.match(settingsPanelsSource, /const \[copiedConfigValue, setCopiedConfigValue\] = useState<string \| null>\(null\)/)
assert.match(settingsPanelsSource, /setCopiedConfigValue\(value\)/) assert.match(settingsPanelsSource, /setCopiedConfigValue\(value\)/)
assert.match(settingsPanelsSource, /settings-readonly-config-copy-feedback/) assert.match(settingsPanelsSource, /settings-readonly-config-copy-feedback/)
assert.match(settingsPanelsSource, />✅已复制</) assert.match(settingsPanelsSource, />已复制</)
assert.doesNotMatch(settingsPanelsSource, /✅已复制/)
assert.match(settingsPanelsSource, /navigator\.clipboard\.writeText\(value\)/) assert.match(settingsPanelsSource, /navigator\.clipboard\.writeText\(value\)/)
assert.match(settingsPanelsSource, /const handleReadonlyConfigKeyDown = \(event: KeyboardEvent<HTMLDivElement>, value\?: string\) =>/) assert.match(settingsPanelsSource, /const handleReadonlyConfigKeyDown = \(event: KeyboardEvent<HTMLDivElement>, value\?: string\) =>/)
assert.match(settingsPanelsSource, /event\.key !== "Enter" && event\.key !== " "/) assert.match(settingsPanelsSource, /event\.key !== "Enter" && event\.key !== " "/)
......
...@@ -352,7 +352,7 @@ ...@@ -352,7 +352,7 @@
- Modify: `apps/ui/src/features/settings/SettingsPanels.tsx` - Modify: `apps/ui/src/features/settings/SettingsPanels.tsx`
- Modify: matching CSS files under `apps/ui/src/styles/` - Modify: matching CSS files under `apps/ui/src/styles/`
- [ ] **Step 1: Chat composer** - [x] **Step 1: Chat composer**
Requirements: Requirements:
...@@ -368,7 +368,11 @@ ...@@ -368,7 +368,11 @@
- Attachment chip text truncates cleanly. - Attachment chip text truncates cleanly.
- Submit button remains icon-first with accessible label. - Submit button remains icon-first with accessible label.
- [ ] **Step 2: Message list** Progress note:
- Completed on 2026-05-25: added a single blue focus ring on the composer surface and stabilized attachment chip truncation.
- [x] **Step 2: Message list**
Requirements: Requirements:
...@@ -382,7 +386,11 @@ ...@@ -382,7 +386,11 @@
- Long Chinese, English, URLs, and code blocks do not break layout. - Long Chinese, English, URLs, and code blocks do not break layout.
- Message actions do not shift message height when appearing. - Message actions do not shift message height when appearing.
- [ ] **Step 3: Task panel icon cleanup** Progress note:
- Completed on 2026-05-25: added long-line wrapping guards for message text, assistant markdown, and code blocks while preserving existing message alignment and `aria-live` trace behavior.
- [x] **Step 3: Task panel icon cleanup**
Replace visible emoji used as UI icons with existing icon components or local SVG components. Replace visible emoji used as UI icons with existing icon components or local SVG components.
...@@ -395,7 +403,11 @@ ...@@ -395,7 +403,11 @@
- No visible emoji icons remain in primary tool controls or headings. - No visible emoji icons remain in primary tool controls or headings.
- Existing Chinese text remains unchanged except for necessary label updates. - Existing Chinese text remains unchanged except for necessary label updates.
- [ ] **Step 4: Automation tasks density** Progress note:
- Completed on 2026-05-25: replaced the content-output heading emoji with a local SVG icon and changed copy feedback to text-only `已复制`.
- [x] **Step 4: Automation tasks density**
Requirements: Requirements:
...@@ -408,7 +420,11 @@ ...@@ -408,7 +420,11 @@
- The page reads as an operational tool, not a card-heavy marketing layout. - The page reads as an operational tool, not a card-heavy marketing layout.
- Long task prompts truncate or wrap predictably. - Long task prompts truncate or wrap predictably.
- [ ] **Step 5: Settings hierarchy** Progress note:
- Completed on 2026-05-25: tightened list/detail/form/run-history spacing, preserved `role="alert"` errors, and added clearer active/disabled states.
- [x] **Step 5: Settings hierarchy**
Requirements: Requirements:
...@@ -422,7 +438,11 @@ ...@@ -422,7 +438,11 @@
- Users can scan required vs optional config groups quickly. - Users can scan required vs optional config groups quickly.
- Editing one section does not visually imply other sections are editing. - Editing one section does not visually imply other sections are editing.
- [ ] **Step 6: Validate key workflows** Progress note:
- Completed on 2026-05-25: preserved source toggle, secret reveal/copy, save/reset/edit, and Tabs semantics; copy feedback is now text-only without emoji.
- [x] **Step 6: Validate key workflows**
Run: Run:
...@@ -444,6 +464,11 @@ ...@@ -444,6 +464,11 @@
- Typecheck and build pass. - Typecheck and build pass.
- Relevant source tests pass. - Relevant source tests pass.
Progress note:
- Passed on 2026-05-25: `corepack pnpm --filter @qjclaw/ui typecheck`; `corepack pnpm build`.
- Passed on 2026-05-25: `node --test apps/ui/test/taskPanelViewSource.test.ts`; `node --test apps/ui/test/settingsPanelsSource.test.ts`; `node --test apps/ui/test/chatCancelSource.test.ts`.
## 7. Phase 5 - 可访问性和响应式补齐 ## 7. Phase 5 - 可访问性和响应式补齐
**目标:** 解决 UI-ux-pro-max 指出的高优先级问题:键盘导航、skip link、错误提示、图片 alt、响应式边界。 **目标:** 解决 UI-ux-pro-max 指出的高优先级问题:键盘导航、skip link、错误提示、图片 alt、响应式边界。
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment