From 6746fe4a7825db57708d0424fd33a4b2520fd0a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=91=A3=E5=AE=8F=E5=87=AF?= Date: Wed, 17 Jun 2026 19:13:48 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AF=BC=E8=88=AA/=E4=BE=A7=E8=BE=B9?= =?UTF-8?q?=E6=A0=8F=E8=8F=9C=E5=8D=95=E8=84=B1=E6=95=8F=20+=20=E5=9C=86?= =?UTF-8?q?=E6=A1=8C=E4=BC=9A=E5=95=86=E5=90=84=E6=AD=A5=E8=BE=93=E5=85=A5?= =?UTF-8?q?=E6=A1=86=E8=87=AA=E9=80=82=E5=BA=94=E9=AB=98=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 菜单标签展示层脱敏:useSidebarMenuItems 直接用 /api/sensitive-words 结果构造脱敏器,覆盖导航栏/三套侧边栏皮肤/三级子导航;菜单管理页用原始树不受影响 - 新增可复用 AutoGrowTextarea(自适应高度多行框),替换圆桌 Step1/2/3 输入框: 多行时自动扩展、到上限框内滚动;Step2 两个单行 input 改多行;统一 Enter 发送/Shift+Enter 换行 - 各步输入框最大展开高度翻倍(Step1 320 / Step2 280 / Step3 224) Co-Authored-By: Claude Opus 4.8 (1M context) --- CLAUDE.md | 2 +- .../src/components/ui/auto-grow-textarea.tsx | 59 +++++++++++++++++++ .../src/core/page-layout/use-sidebar-menu.ts | 34 +++++++++-- frontend-web/src/lib/desensitize.ts | 16 +++++ .../components/Step1Panel.tsx | 10 ++-- .../components/Step2Panel.tsx | 31 ++++++---- .../components/Step3DashboardMode.tsx | 6 +- .../components/Step3ReportMode.tsx | 6 +- .../components/Step3SummaryMode.tsx | 6 +- 9 files changed, 141 insertions(+), 29 deletions(-) create mode 100644 frontend-web/src/components/ui/auto-grow-textarea.tsx diff --git a/CLAUDE.md b/CLAUDE.md index d7d49990..49d5b2ee 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -81,7 +81,7 @@ The frontend (`frontend-web/`) is a React 19 + Vite SPA: - **Pages**: `ChatPage` (thread chat), `AgentChatPage` (custom agent chat), `AgentsPage`/`NewAgentPage` (agent management), `ScheduledTasksPage`/`ScheduledTaskRunDetailPage` (scheduled tasks), `LightAppManagementPage`/`LightAppListPage`/`LightAppIframeView` (轻应用中心 — register/list/embed mini-apps) - **轻应用中心 (Light App Center)**: a sibling of 任务管理 in the sidebar with two pages — 应用管理 (admin registers `window_open` or `iframe` mini-apps with a route-param builder: login params resolved from `localStorage.userInfo`, theme params literal) and 应用列表 (card grid of `window_open` apps, `window.open` launch). `iframe` apps are mounted under a chosen parent menu (XK/PG/TY/综合管理) and **dynamically injected into the sidebar** by `core/page-layout/use-sidebar-menu.ts` (`useSidebarMenuItems`), opening in the `LightAppIframeView` host. Client + React Query hooks in `strategy-components/api/light-apps.ts`; URL builder/constants in `strategy-components/lib/light-app.ts`. Backend: `/api/light-apps` (global/shared, admin-gated writes). - **菜单管理 (Menu Management, admin-only)**: an **overlay** over the static sidebar menu — `MenuManagementPage` (`pages/MenuManagementPage.tsx`, route `/page/strategy/admin/menu-management`, opened from the admin-only 「菜单管理」 item in the **bottom-left settings dropdown** of `components/page-sidebar.tsx`). Admins rename / 停用·启用 / reorder (↑↓ or @dnd-kit drag) / re-parent (horizontal drag **or** the edit dialog's 「移动到」parent selector) any node across 一级/二级/三级, **including injected iframe light-app nodes** (`light-app-`). The static tree (`core/page-layout/sidebar-menu.ts`) stays the source of icons/routes; only per-node deltas (`label`/`disabled`/`parentId`/`sortOrder`) persist to the backend. Merge logic in `core/page-layout/menu-overrides.ts` (`applyMenuOverrides`, applied in `use-sidebar-menu.ts` after light-app injection, before role filter; disabling a node hides its whole subtree) — strict **3-level cap** (`wouldExceedDepth`). Client `strategy-components/api/menu-overrides.ts`. Backend: `/api/menu-overrides` (global/shared; open read, admin atomic replace-all). -- **敏感词管理 (Sensitive Word Management)**: configures the **展示层脱敏映射** (原始词 → 替换词). **Gated on the username `lqq`** (not the admin role) — `getAccountDisplayName().toLowerCase() === "lqq"` (username = email `@` prefix, else `user_id`, same as the profile page); the dropdown entry, the route guard, and the page-level guard all use this check. `src/lib/desensitize.ts` ships a frozen seed map `SEED_DESENSITIZE_MAP` (the deployment seed) plus a live mutable `DESENSITIZE_MAP` (initialized from the seed so `desensitize()` works before any fetch). `DesensitizeWordsLoader` (mounted in `App.tsx` `AppProviders`) fetches `/api/sensitive-words` on startup and, if non-empty, calls `replaceDesensitizeMap(...)` to make the backend config the source of truth (empty/failed → keep seed). Page `SensitiveWordsPage.tsx` (route `/page/strategy/admin/sensitive-words`, opened from the 「敏感词管理」 item in the **bottom-left settings dropdown** of `components/page-sidebar.tsx`, present in **both** full + collapsed sidebar modes) lists/adds/edits/启停/deletes words and saves the **whole set atomically** (replace-all); a 「导入种子词」 button merges any missing `SEED_DESENSITIZE_MAP` entries into the editor. Client + hooks in `strategy-components/api/sensitive-words.ts` (`wordsToMap` folds enabled rows into the runtime map). Backend: `/api/sensitive-words` (global/shared; open read, write allowed for admin **or** the `lqq` account), mirroring 菜单管理's store shape. +- **敏感词管理 (Sensitive Word Management)**: configures the **展示层脱敏映射** (原始词 → 替换词). **Gated on the username `lqq`** (not the admin role) — `getAccountDisplayName().toLowerCase() === "lqq"` (username = email `@` prefix, else `user_id`, same as the profile page); the dropdown entry, the route guard, and the page-level guard all use this check. `src/lib/desensitize.ts` ships a frozen seed map `SEED_DESENSITIZE_MAP` (the deployment seed) plus a live mutable `DESENSITIZE_MAP` (initialized from the seed so `desensitize()` works before any fetch). `DesensitizeWordsLoader` (mounted in `App.tsx` `AppProviders`) fetches `/api/sensitive-words` on startup and, if non-empty, calls `replaceDesensitizeMap(...)` to make the backend config the source of truth (empty/failed → keep seed). Page `SensitiveWordsPage.tsx` (route `/page/strategy/admin/sensitive-words`, opened from the 「敏感词管理」 item in the **bottom-left settings dropdown** of `components/page-sidebar.tsx`, present in **both** full + collapsed sidebar modes) lists/adds/edits/启停/deletes words and saves the **whole set atomically** (replace-all); a 「导入种子词」 button merges any missing `SEED_DESENSITIZE_MAP` entries into the editor. Client + hooks in `strategy-components/api/sensitive-words.ts` (`wordsToMap` folds enabled rows into the runtime map). Backend: `/api/sensitive-words` (global/shared; open read, write allowed for admin **or** the `lqq` account), mirroring 菜单管理's store shape. **侧边栏 + 导航栏 menu labels are desensitized** at the data source: `useSidebarMenuItems`/`useActiveThirdLevel` (`core/page-layout/use-sidebar-menu.ts`) map every node's `label` through a desensitizer built **directly from the `/api/sensitive-words` query** (`makeDesensitizer(wordsToMap(words))`, falling back to the global seed `desensitize` while empty) — this sidesteps the `replaceDesensitizeMap` effect-timing race. Only `label` is rewritten; `path`/`id`/`icon` are untouched so routing/highlight are unaffected. All nav surfaces (`page-sidebar.tsx`, `page-sidebar-dark.tsx`, `page-sidebar-v1.tsx`, `strategy-components/Header.tsx`, `PageRoutes.tsx`) consume this hook and are covered; `MenuManagementPage` uses the raw tree so admins still edit real labels. - **State**: TanStack Query for server state, context providers (`SubtasksProvider`, `ArtifactsProvider`, `PromptInputProvider`) scoped to `WorkspaceRoutes` - **LLM streaming**: `@langchain/langgraph-sdk` streams messages from the backend via SSE - **UI**: Radix UI primitives + TailwindCSS v4 + shadcn-style components in `src/components/ui/` diff --git a/frontend-web/src/components/ui/auto-grow-textarea.tsx b/frontend-web/src/components/ui/auto-grow-textarea.tsx new file mode 100644 index 00000000..eb695110 --- /dev/null +++ b/frontend-web/src/components/ui/auto-grow-textarea.tsx @@ -0,0 +1,59 @@ +import * as React from "react"; + +export interface AutoGrowTextareaProps + extends React.TextareaHTMLAttributes { + /** 内容增长高度上限(px),超过后框内滚动。默认 200。 */ + maxHeight?: number; +} + +/** + * 自适应高度的多行输入框。 + * + * 内容增多时自动扩展高度(输入框处于底部布局时即「向上扩展」),到 `maxHeight` 后框内滚动。 + * 体验对齐通用问答页(`/page/strategy/qa/general`)的 tdesign `autosize` 输入框,但保留原生 + * `