Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ ui/
- **焦点环**:全局 `:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }`;输入类控件(`.input` / `.th-filter`)已有边框高亮,`outline: none` 不叠加;
- **全局快捷键**(`document` keydown,输入框内不触发、Cmd/Ctrl/Alt 组合不劫持):
- `/` → 聚焦市场搜索 `#mk-search`;
- 数字 **1-7** → 切换侧边栏视图(`NAV_ORDER` 顺序:仪表盘/市场/共享/钱包/交易/管理/设置;游客模式由 `switchView` 拦截提示登录);
- 数字 **1-8** → 切换侧边栏视图(键位 = `NAV_ORDER` 下标 +1,范围由该数组长度决定:仪表盘/市场/共享/钱包/交易/管理/运营/设置;游客模式由 `switchView` 拦截提示登录);
- Esc → 关闭行内新建 Key(`#ak-new-inline`);
- **导航提示**:nav-item 补 `title`("快捷键 N · 名称")+ 右侧 `.nav-key` 键位角标(管理视图带「管理员」tag 时省略角标)。

Expand Down Expand Up @@ -156,7 +156,7 @@ ui/

## 动态文档标题约定(v1.18,rant 2026-08-17T18:06:09 F)

- `document.title` **跟随视图切换**:`switchView` 内统一设置「`VIEW_TITLE[id]` · AITokenPool」(如「模型市场 Marketplace · AITokenPool」);7 个视图全覆盖,未知视图回退「AITokenPool」;
- `document.title` **跟随视图切换**:`switchView` 内统一设置「`VIEW_TITLE[id]` · AITokenPool」(如「模型市场 Marketplace · AITokenPool」);8 个视图全覆盖,未知视图回退「AITokenPool」;
- **默认「AITokenPool」**:DOMContentLoaded 初始化与登录页/无视图态回默认;HTML `<title>` 即「AITokenPool」;
- 游客受限视图被 `GUEST_VIEWS` 拦截时 `switchView` 提前 return → 标题保持不变。

Expand Down Expand Up @@ -197,14 +197,14 @@ ui/
- 单例 `#toast` 已废弃 → 改为**队列容器** `#toast-wrap`(`index.html` 底部,初始为空):`position:fixed; bottom:28px; left:50%; translateX(-50%)`,`flex-direction:column` 纵向堆叠,`gap:10px`,`pointer-events:none`(不拦截页面点击);
- `toast(msg, type)` **每次创建独立 `.toast` 元素**(`document.createElement` + `appendChild`),不再覆盖旧消息;**上限 `TOAST_MAX = 3`**——超限同步移除最旧一条(`wrap.children[0]`)腾位;
- **独立生命周期**:每条到时(`TOAST_MS=2600`)加 `.out` 触发 `toast-out` 淡出动画(`TOAST_OUT_MS=200`)后 `removeChild`;互不影响、不共享定时器;
- **分级样式保留**:`.toast.success/.error/.info` 边框色 + 文字色与 v1.16 一致,39 个 `toast()` 调用点零改动;`.toast` 自身 `pointer-events:auto`(容器 none),为可交互 toast(如按钮)预留;
- **分级样式保留**:`.toast.success/.error/.info` 边框色 + 文字色与 v1.16 一致,75 个 `toast()` 调用点零改动;`.toast` 自身 `pointer-events:auto`(容器 none),为可交互 toast(如按钮)预留;
- 冒烟测试注意:DOM-stub 的 `classList.add` 只更新 `_classes` 集合、不同步 `className` 字符串——断言淡出态用 `_classes.has("out")`。

## 快捷键帮助面板约定(v1.19,rant 2026-08-17T20:39:30 E)

- **触发**:按 `?`(或 `Shift+/`,浏览器会给出 `e.key === "?"`)开合右上角行内卡片 `#help-panel`;**Esc 或再按 `?` 关闭**;关闭按钮 × 同效;
- **形态**:`position:fixed; top:76px; right:24px` 浮层卡片(非 modal、无遮罩、`z-index:950` 低于 toast),入场 `help-in` 动画;窄屏(≤560px)左右 12px 全宽、`top:68px`;
- **内容**:`renderHelp()` 渲染 4 行快捷键(`/` 搜索、`1–7` 视图、`Esc` 关闭/取消、`?` 帮助)+ 底部上下文行(当前视图 `VIEW_TITLE[activeView]` + 亮/深色主题);
- **内容**:`renderHelp()` 渲染 4 行快捷键(`/` 搜索、`1–8` 视图、`Esc` 关闭/取消、`?` 帮助)+ 底部上下文行(当前视图 `VIEW_TITLE[activeView]` + 亮/深色主题);
- **优先级**:全局 keydown 里帮助打开时 **Esc 先关帮助**(再关行内新建 Key),`?` 在 typing 守卫之后(输入框内不劫持);`toggleHelp(force)` 支持强制开/关(close 按钮用 `toggleHelp(false)`);
- **kbd 键帽**:`.kbd` 样式(等宽、边框、底部 2px 立体),与 `.nav-key` 视觉一致。

Expand Down Expand Up @@ -277,7 +277,7 @@ ui/
- **作用表**(`KBD_TABLE_IDS`):`mk-body` / `share-body` / `api-keys` / `emp-body` / `dept-body` / `ops-body`(tbody)+ `tx-table`(div 包 table);
- **激活**:① 点击表格行(容器 click 委托,`e.target.closest("tr")`,跳过 `.mk-detail`);② 直接按 ↑/↓——`kbdContainerFrom(t)` 沿 `closest("tbody")`(id 匹配)或 `closest("table").parentNode`(#tx-table)解析当前表格,找不到沿用 `kbd.c`;
- **键位**:`ArrowDown/Up` → `kbdMove(dir, c)` 行高亮 `.row-active`(accent 左侧竖条 `inset 3px 0 0` + `--accent-soft` 底),未激活时 ↓ 首行 / ↑ 末行,`scrollIntoView({block:"nearest"})`;`Enter` → `kbdEnter()` 点击行内首个可用 `button.btn:not(.row-expand)`(disabled 不触发);`Esc` → `kbdClear()`(无高亮时落到原逻辑:关帮助/行内表单/引导);
- **守卫**:typing(INPUT/TEXTAREA/SELECT/contentEditable)与 meta/ctrl/alt 组合键不拦截;`?`、1-7 视图切换、Esc 原有优先级(引导 > 帮助 > 行内新建 Key > 表格高亮)均不受影响;
- **守卫**:typing(INPUT/TEXTAREA/SELECT/contentEditable)与 meta/ctrl/alt 组合键不拦截;`?`、数字键视图切换、Esc 原有优先级(引导 > 帮助 > 行内新建 Key > 表格高亮)均不受影响;
- 冒烟测试注意:`qs(sel)` 的 stub id 会带 `#` 前缀——`kbdContainerFrom` 依赖真实无 `#` 的 id(如 `tx-table`),须手动修正;Esc 分支链依赖 `#ak-new-inline`、`#help-panel` hidden 预置 + `atp-tour-done=1`(防 tour 拦截)。

## 品牌与登录页氛围约定(v1.20,rant 2026-08-17T20:46:57 G)
Expand Down Expand Up @@ -322,14 +322,14 @@ ui/

## 界面国际化 i18n 约定(v1.21.1,rant 2026-08-18T20:49:22 + 21:40:10 去中英混排)

- **语言包**:`ui/js/i18n.js` 零依赖 IIFE,`I18N = { zh, en }` 双词典(733 键 ×2,覆盖导航/登录/视图标题/通用/仪表盘/市场/共享/钱包/交易/设置/管理/运营/聊天/游客/相对时间/帮助/tour/主题/错误映射);`window.t(key, vars)` 查当前语言,**缺失回退 zh,再缺回退 key 本身**;`{var}` 占位符插值;
- **语言包**:`ui/js/i18n.js` 零依赖 IIFE,`I18N = { zh, en }` 双词典(785 键 ×2,覆盖导航/登录/视图标题/通用/仪表盘/市场/共享/钱包/交易/设置/管理/运营/聊天/游客/相对时间/帮助/tour/主题/错误映射);`window.t(key, vars)` 查当前语言,**缺失回退 zh,再缺回退 key 本身**;`{var}` 占位符插值;
- **切换机制**:设置页「偏好 → 界面语言」下拉(`#prefs-lang`,zh/en)→ `I18n.setLang()`:写 `localStorage('atp_lang')` + `document.documentElement.lang` 同步(zh→`zh-CN` / en→`en`)+ 派发 `atp:langchange` → app.js 重渲染 `renderNav()` + `renderView(activeView)` + `document.title`(引导中额外 `renderTourStep()`);**首载**:localStorage → `navigator.language` 前缀(`zh*`→zh,否则 en)→ 默认 zh;切换即时生效无需刷新;
- **静态文案**:`index.html` 内静态中文用 `data-i18n` / `data-i18n-ph`(placeholder)/ `data-i18n-title`(title)标记,`applyStatic()` 启动时与每次切换时批量替换;**容器含表单控件的 `<label>` 用 `<label><span data-i18n="KEY">文本</span><input…></label>` 结构**(避免 innerHTML 替换销毁控件);
- **动态文案**:`app.js` 面向用户字符串全部走 `t('key')`;**语言敏感常量存 key 而非文案**(NAV/VIEW_TITLE/TOUR_STEPS/HELP_KEYS 存 key,渲染时 `T()` 解析;SHARE_STATUS/RAISE_STATUS 的 `text` 为函数;TX_COLUMNS 的 `title`/`options` 为函数;`DAY_LABELS` 动态 `T("share.day."+n)`)——保证切换语言后重渲染即时生效;
- **数字/时间本地化**:`I18n.fmtNum`(zh→`zh-CN` / en→`en-US` `toLocaleString`);`I18n.fmtRelTime`(刚刚/N 分钟前/N 小时前/昨天 ↔ just now/N min ago/N hr ago/yesterday);数量单位(人/个/笔/次)用 `cnt.*` 键(zh 带量词,en 纯数字);
- **后端错误映射**:`api.js` 抛错前过 `I18n.mapErr()`——en 模式下已知中文错误(「该模型暂无可用 key」「点数余额不足」「需要管理员权限」等 15 组)映射为英文,未知原样返回;zh 模式原样透传;
- **单语原则(v1.21.1 去混排)**:zh 词典值一律纯中文(仅保留 API/Key/Plan/tokens/CSV 等专有名词、键盘快捷键与占位符),不再内联英文注释;`index.html` 已移除全部 `<span class="en">` 静态小字(55 处);`.en` CSS 样式已删除;en 词典保持纯英文;
- 冒烟测试:node 无 DOM 桩跑 i18n.js(t/setLang/mapErr/fmtNum/fmtRelTime 断言,见开发记录);Key 一致性扫描(app.js+index.html 引用的 496 键全部存在于 ZH/EN)。
- 冒烟测试:node 无 DOM 桩跑 i18n.js(t/setLang/mapErr/fmtNum/fmtRelTime 断言,见开发记录);Key 一致性扫描(`src/i18n_pack.rs` 门禁:app.js 的 `T()` 字面量 430 个、index.html 的 `data-i18n*` 305 个,去重并集 680 键全部存在于 ZH/EN)。

## 仪表盘「我的共享」数据源与降级约定(v1.21.2,rant 2026-08-19T15:48:17 BUG)

Expand Down
2 changes: 1 addition & 1 deletion ui/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -804,7 +804,7 @@ mark {
color: var(--accent-text);
background: var(--accent-soft);
}
/* 快捷键提示(rant 16:57:17 D:数字 1-7 切换视图) */
/* 快捷键提示(rant 16:57:17 D:数字键切换视图) */
.nav-key {
margin-left: auto;
font-size: 10px;
Expand Down
10 changes: 5 additions & 5 deletions ui/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AITokenPool</title>
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cdefs%3E%3ClinearGradient id='g' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop offset='0' stop-color='%234ecdc4'/%3E%3Cstop offset='1' stop-color='%232a9d8f'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='64' height='64' rx='14' fill='url(%23g)'/%3E%3Ctext x='32' y='43' font-family='-apple-system,Segoe UI,Arial,sans-serif' font-size='30' font-weight='800' text-anchor='middle' fill='%23062021'%3EAT%3C/text%3E%3C/svg%3E">
<link rel="stylesheet" href="css/style.css?v=20260912-3">
<link rel="stylesheet" href="css/style.css?v=20260912-4">
</head>
<body>

Expand Down Expand Up @@ -842,9 +842,9 @@ <h3 id="chat-title">使用模型</h3>
</div>
</div>

<script src="js/api.js?v=20260912-3"></script>
<script src="js/data.js?v=20260912-3"></script>
<script src="js/i18n.js?v=20260912-3"></script>
<script src="js/app.js?v=20260912-3"></script>
<script src="js/api.js?v=20260912-4"></script>
<script src="js/data.js?v=20260912-4"></script>
<script src="js/i18n.js?v=20260912-4"></script>
<script src="js/app.js?v=20260912-4"></script>
</body>
</html>
12 changes: 7 additions & 5 deletions ui/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
// 快捷键帮助面板(rant 20:39:30 E:行内卡片非 modal;? / Shift+/ 开合,Esc 或再按 ? 关闭)
const HELP_KEYS = [
["/", "help.k1"],
["1–7", "help.k2"],
["1–8", "help.k2"],
["Esc", "help.k3"],
["?", "help.k4"],
];
Expand Down Expand Up @@ -431,7 +431,7 @@
]},
];

// 侧边栏视图顺序(rant 16:57:17 D:数字 1-7 切换对应视图,title 提示快捷键)
// 侧边栏视图顺序(rant 16:57:17 D:数字键 1..N 切换对应视图,N = 本数组长度,title 提示快捷键)
const NAV_ORDER = NAV.flatMap((g) => g.items);

const VIEW_TITLE = {
Expand Down Expand Up @@ -493,7 +493,7 @@
const b = document.createElement("button");
b.className = "nav-item" + (item.id === activeView ? " active" : "");
b.dataset.view = item.id;
const short = NAV_ORDER.indexOf(item) + 1; // 1-7
const short = NAV_ORDER.indexOf(item) + 1;
b.title = T("nav.shortcut", { n: short, label: T(item.label) });
b.innerHTML = '<span class="ico">' + (ICONS[item.icon] || "") + '</span><span class="label">' + esc(T(item.label)) + "</span>" +
(item.role ? "" : '<span class="nav-key">' + short + "</span>");
Expand Down Expand Up @@ -3814,7 +3814,7 @@
themeSel.addEventListener("change", () => applyTheme(themeSel.value));
}

// 全局快捷键(rant 16:57:17 D):/ 聚焦市场搜索;数字 1-7 切换侧边栏视图;Esc 关闭行内新建 key
// 全局快捷键(rant 16:57:17 D):/ 聚焦市场搜索;数字键切换侧边栏视图(1..NAV_ORDER.length);Esc 关闭行内新建 key
// rant 20:39:30 E:? / Shift+/ 开合快捷键帮助面板(Esc 优先关帮助)
// rant 20:46:57 A:引导中 Esc 优先关引导
document.addEventListener("keydown", (e) => {
Expand Down Expand Up @@ -3842,7 +3842,9 @@
$("#mk-search").focus();
return;
}
if (e.key >= "1" && e.key <= "7") {
// 数字键切换视图:不再写死上限——角标是 NAV_ORDER 下标 +1,这里用同一数组取项,
// 越界数字自然落空,视图增删后两者自动保持一致(原先写死 "7",插入 ops 后 settings 角标成了 8)
if (/^[0-9]$/.test(e.key)) {
const item = NAV_ORDER[Number(e.key) - 1];
if (item && (!item.role || D.USER.role === item.role)) switchView(item.id);
}
Expand Down
Loading