refine workbench page usability

This commit is contained in:
kris
2026-03-28 06:32:47 +08:00
parent 17809605da
commit 7bf93e610e
6 changed files with 978 additions and 325 deletions

View File

@@ -0,0 +1,85 @@
# Workbench Pages Usability Cleanup Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Reduce cognitive overload across all non-dashboard workbench pages while preserving the current StoryForge UI style and business logic.
**Architecture:** Keep the existing single-page shell and page render functions, but add lightweight page-level tab state, restore the missing settings route, and move admin-grade sections out of user pages. Prefer small render helpers and targeted page restructuring over a full rewrite.
**Tech Stack:** Vanilla JavaScript SPA, static HTML, CSS, Node built-in test runner, Playwright for live verification.
---
### Task 1: Add regression coverage for page routing and scope boundaries
**Files:**
- Create: `/Users/kris/code/StoryForge-gitea/web/storyforge-web-v4/tests/workbench-pages.test.mjs`
- [ ] Write source-level regression tests for:
- settings nav route exists
- settings screen exists
- `renderAll()` renders settings
- automation page no longer renders quota / registry / admin ops
- Agent page no longer renders quota / registry
- discovery / production / admin pages expose page-tab interactions
### Task 2: Restore settings as a real page
**Files:**
- Modify: `/Users/kris/code/StoryForge-gitea/web/storyforge-web-v4/index.html`
- Modify: `/Users/kris/code/StoryForge-gitea/web/storyforge-web-v4/assets/app.js`
- [ ] Add `data-screen-target="settings"` to the sidebar button.
- [ ] Add a real `data-screen="settings"` section.
- [ ] Implement `renderSettingsScreen()` and include it in `renderAll()`.
### Task 3: Add shared page-tab state and event handling
**Files:**
- Modify: `/Users/kris/code/StoryForge-gitea/web/storyforge-web-v4/assets/app.js`
- Modify: `/Users/kris/code/StoryForge-gitea/web/storyforge-web-v4/assets/styles.css`
- [ ] Add page-level tab state to `appState`.
- [ ] Add a small helper to render page tabs consistently.
- [ ] Add click handling for `select-page-tab`.
### Task 4: Simplify the heaviest user pages
**Files:**
- Modify: `/Users/kris/code/StoryForge-gitea/web/storyforge-web-v4/assets/app.js`
- Modify: `/Users/kris/code/StoryForge-gitea/web/storyforge-web-v4/assets/styles.css`
- [ ] Refactor `renderDiscoveryScreen()` to use tabs for overview / snapshots / similar.
- [ ] Refactor `renderProductionScreen()` to use tabs for queue / recovery / recorder / outputs.
- [ ] Refactor `renderAutomationScreen()` to use tabs and remove admin-grade sections.
- [ ] Refactor `renderPlaybookScreen()` to use tabs and remove quota / registry from the user page.
### Task 5: Strengthen thin pages and compress medium pages
**Files:**
- Modify: `/Users/kris/code/StoryForge-gitea/web/storyforge-web-v4/assets/app.js`
- Modify: `/Users/kris/code/StoryForge-gitea/web/storyforge-web-v4/assets/styles.css`
- [ ] Expand `renderOwnedScreen()` into a usable account workbench.
- [ ] Expand `renderCreditsScreen()` into a readable quota page.
- [ ] Lightly tune `renderProjectsScreen()` and `renderReviewScreen()` to reduce raw data feel and repetition.
### Task 6: Convert admin workbench into a tabbed control surface
**Files:**
- Modify: `/Users/kris/code/StoryForge-gitea/web/storyforge-web-v4/assets/app.js`
- Modify: `/Users/kris/code/StoryForge-gitea/web/storyforge-web-v4/assets/styles.css`
- [ ] Refactor `renderAdminWorkbenchScreen()` to use tabs for integrations / storage / agents / ops.
- [ ] Keep all system governance sections here instead of user pages.
### Task 7: Verify locally and on the NAS page
**Files:**
- None
- [ ] Run `node --test /Users/kris/code/StoryForge-gitea/web/storyforge-web-v4/tests/dashboard-home.test.mjs /Users/kris/code/StoryForge-gitea/web/storyforge-web-v4/tests/workbench-pages.test.mjs`
- [ ] Run `node --check /Users/kris/code/StoryForge-gitea/web/storyforge-web-v4/assets/app.js`
- [ ] Run `bash /Users/kris/code/StoryForge-gitea/scripts/check_repo_baseline.sh`
- [ ] Redeploy the NAS frontend if needed.
- [ ] Re-check the key pages in a real browser and confirm no new console errors.

View File

@@ -0,0 +1,134 @@
# 非首页工作台页面可用性整改设计
## 目标
在不改变当前 StoryForge Web V4 整体视觉风格的前提下,整改首页之外的工作台页面,让页面更符合人类使用逻辑:
- 信息更容易扫读
- 同页只处理一类任务
- 普通用户更容易上手
- 管理员页保留专业度,但不再是长页面堆叠
## 范围
本轮只处理首页之外的页面:
- 我的项目
- 找对标
- 跟踪账号
- 我的账号
- Agent
- 生产中心
- 发布与复盘
- 自动流程
- 额度
- 管理员配置台
- 设置
首页不在本轮范围内。
## 设计原则
1. 不换皮,只做信息减法和层级重排。
2. 一个页面只允许一个主任务视角,深层信息通过页内 tab 切换。
3. 系统治理内容从普通用户页面移走,优先归入管理员配置台。
4. 太空的页面要补到能独立成立,太重的页面要压缩成单主区。
## 页面级方案
### 设置
- 修复现有切页异常,点击后必须真正进入设置页。
- 新增轻量设置页,展示:
- 当前连接状态
- 当前工作区 / 当前项目
- 自动连接说明
- 页面使用偏好与帮助入口
- 超级管理员在这里看到“去管理员配置台”的入口提示,但不把管理员配置内容塞进设置页。
### 自动流程
- 只保留普通用户真正需要理解的两层:
- 依赖健康
- 动作防呆
- 租户额度、OneLiner 动作注册表、运维审计从本页移走。
- 页面结构改成页内 tab一次只看一个区块。
### Agent
- 普通用户 Agent 页只保留:
- OneLiner 主 Agent 使用区
- 当前 Agent
- 平台 Agent 协作状态
- 模型/学习/最近生成
- 租户额度和 OneLiner 动作注册表移出本页。
- 页面改成页内 tab降低一次性信息密度。
### 找对标
- 保留当前业务逻辑,不删能力。
- 但把深层信息改成页内 tab一次只展示一种视角
- 账号概览
- 快照 / 字段 / 报告
- 相似对标 / 已绑关系
- 账号列表和当前选中账号仍保留在主结构里。
### 生产中心
- 页面改成页内 tab
- 生产队列
- 失败恢复
- 录制维护
- 作品与产物
- 当前页顶部保留总览和主动作,不再把四大块内容同时展开。
### 管理员配置台
- 保留系统级治理边界:
- 依赖健康
- 存储状态
- 平台 Agent / OneLiner 系统级管理
- 运维与审计
- 页面改成页内 tab避免长页面堆叠。
### 我的账号
- 从“单张摘要卡”补成可成立的工作页面:
- 当前身份
- 当前负责项目与 Agent
- 最近工作摘要
- 常用快捷动作
### 额度
- 从“几个孤立数字”补成更接近产品化额度页:
- 当前额度摘要
- 已用 / 剩余 / 预估
- 套餐化解释
- 风险提示
### 我的项目 / 跟踪账号 / 发布与复盘
- 保持现有方向,不大改视觉骨架。
- 只做轻量压缩:
- 我的项目:弱化原始导入队列感,强调下一步动作
- 跟踪账号:增强空状态可理解性
- 发布与复盘:减少重复项,强调“待补复盘”
## 信息边界
普通用户页面不再承接这些系统级内容:
- OneLiner 动作注册表
- 租户额度与审计全量治理面板
- 运维与审计 Agent 全量事件墙
这些内容统一收口到管理员配置台。
## 验收标准
- 设置页可以真实切换并渲染独立内容。
- 自动流程、Agent、找对标、生产中心、管理员配置台都改成“一次只展开一类信息”的结构。
- 自动流程与 Agent 页面不再混入管理员治理内容。
- 我的账号和额度页面不再显得像半成品空页。
- 控制台无新增报错,现有 NAS 页面可正常浏览。

View File

@@ -39,7 +39,9 @@ for file in web/storyforge-web-v4/assets/app.js web/storyforge-web-v4/assets/sto
done
node --check scripts/douyin-browser-capture/control_panel.mjs
echo "[5/5] validate homepage dashboard tests"
node --test web/storyforge-web-v4/tests/dashboard-home.test.mjs
echo "[5/5] validate homepage and workbench tests"
node --test \
web/storyforge-web-v4/tests/dashboard-home.test.mjs \
web/storyforge-web-v4/tests/workbench-pages.test.mjs
echo "baseline checks passed"

File diff suppressed because it is too large Load Diff

View File

@@ -68,7 +68,7 @@
<span class="icon"></span>
<span>管理员配置台</span>
</button>
<button class="nav-item">
<button class="nav-item" data-screen-target="settings">
<span class="icon"></span>
<span>设置</span>
</button>
@@ -1915,6 +1915,7 @@
</section>
<section class="screen" data-screen="admin-workbench"></section>
<section class="screen" data-screen="settings"></section>
</main>
</div>

View File

@@ -0,0 +1,49 @@
import test from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs";
import path from "node:path";
const ROOT = path.resolve(process.cwd(), "web/storyforge-web-v4");
const HTML = fs.readFileSync(path.join(ROOT, "index.html"), "utf8");
const APP = fs.readFileSync(path.join(ROOT, "assets/app.js"), "utf8");
function extractBetween(source, startToken, endToken) {
const start = source.indexOf(startToken);
assert.notEqual(start, -1, `Missing token: ${startToken}`);
const end = source.indexOf(endToken, start);
assert.notEqual(end, -1, `Missing token: ${endToken}`);
return source.slice(start, end);
}
test("settings navigation and screen are real routes", () => {
assert.match(HTML, /data-screen-target="settings"/);
assert.match(HTML, /data-screen="settings"/);
assert.match(APP, /function renderSettingsScreen\(/);
assert.match(APP, /screenMap\.settings\.innerHTML = renderSettingsScreen\(\);/);
assert.match(APP, /window\.addEventListener\("hashchange"/);
});
test("automation screen stays user-facing and excludes admin-only panels", () => {
const source = extractBetween(APP, "function renderAutomationScreen()", "function renderOwnedScreen()");
assert.doesNotMatch(source, /renderTenantQuotaPanel\(/);
assert.doesNotMatch(source, /renderOneLinerActionRegistryPanel\(/);
assert.doesNotMatch(source, /renderAdminOpsPanel\(/);
assert.match(source, /renderDetailTabs\("automationDetailTab"/);
});
test("agent screen excludes quota and registry panels and uses page tabs", () => {
const source = extractBetween(APP, "function renderPlaybookScreen()", "function renderProductionScreen()");
assert.doesNotMatch(source, /renderTenantQuotaPanel\(/);
assert.doesNotMatch(source, /renderOneLinerActionRegistryPanel\(/);
assert.match(source, /renderDetailTabs\("playbookDetailTab"/);
});
test("discovery, production, and admin screens use page tabs for heavy content", () => {
const discovery = extractBetween(APP, "function renderDiscoveryScreen()", "function renderTrackingScreen()");
const production = extractBetween(APP, "function renderProductionScreen()", "function renderReviewScreen()");
const admin = extractBetween(APP, "function renderAdminWorkbenchScreen()", "function renderDashboardScreen()");
assert.match(discovery, /renderDetailTabs\("discoveryDetailTab"/);
assert.match(production, /renderDetailTabs\("productionDetailTab"/);
assert.match(admin, /renderDetailTabs\("adminWorkbenchTab"/);
});