9111 Commits

Author SHA1 Message Date
dolphin 3f27641987 Merge branch 'feat/2.7-common' into feat/2.7-ui
First sync down from the trunk under the agreed flow: common syncs into ui
whenever the trunk moves, and ui delivers back at milestones — always after
a sync, so the delivery merge carries no conflicts of its own.

Only two files conflicted, both trivial, which confirms the two branches
resolved the shared 2.6-vs-mainline hunks the same way:

- PlusMenu.tsx: kept the UI branch's semantic color tokens over the trunk's
  hardcoded hex; the tokens travel back to common on the next delivery.
- AiMessageBubble.tsx: identical code, comment wrapping only.

Both apps lint clean; platform typecheck clean; i18n parity clean; the six
remaining client typecheck errors are byte-identical to main.
2026-08-20 19:00:28 +08:00
dolphin 056c3b0252 Merge branch 'feat/2.6.0' into feat/2.7-common
Carries the 2.6 work that landed after the fix2 release onto the 2.7
trunk. feat/2.7-common now equals origin/main (2.6.0 + hotfix1 + hotfix2)
plus everything feat/2.6.0 added since.

The nine conflicts are the same "mainline fixed it / the 2.6 line changed
it" files resolved in the feat/2.7-ui merge, and they are resolved
identically here so the two branches stay reconcilable:

- chat_service.py: mainline's interrupted-stream persistence plus the 2.6
  line's classified error payload.
- redis_callback.py: mainline — the 2.6 line still referenced the outer
  `node_id` inside the closure instead of its `one_node_id` parameter.
- chatApi.ts: all three error fields (errorText, errorType, errorDetail).
- useAiChat.ts / useAiChatSSE.ts: the 2.6 line's 3-arg onError and
  classification payload, with mainline's localized connection-lost copy.
- AiMessageBubble.tsx: mainline's rule that a partially streamed answer
  still renders, with the 2.6 line's rich ChatErrorCard below it.
- PlusMenu.tsx / Roles.tsx / linsight.py: the 2.6 line's icon, its
  extracted roleMenuSelection module, and mainline's comment wording.

Housekeeping matches the UI merge: froze mainline's lint violations, added
the missing 11059 error copy in zh-Hans/en/ja, guarded one optional-typed
artifact preview callback.

Both apps lint clean; platform typecheck clean; i18n parity clean. The six
remaining client typecheck errors are byte-identical to main.
2026-08-20 18:49:57 +08:00
dolphin f519954911 Merge branch 'feat/2.6.0-ui' into feat/2.7-ui
Rebases the UI line onto the released mainline: feat/2.7-ui starts from
origin/main (2.6.0 + hotfix1 + hotfix2) and takes in all the UI work that
accumulated on feat/2.6.0-ui — which had already merged feat/2.6.0, so the
2.6 feature work rides along in the same merge.

Both sides forked before hotfix2, so the twelve conflicts are the same
"mainline fixed it / the UI line rewrote it" files. Resolution rule:
mainline wins on logic, the UI line wins on presentation and design tokens.

- chat_service.py: mainline's interrupted-stream persistence plus the UI
  line's classified error payload.
- redis_callback.py: mainline — the UI line still referenced the outer
  `node_id` inside the closure instead of its `one_node_id` parameter.
- chatApi.ts: all three error fields (errorText, errorType, errorDetail).
- useAiChat.ts / useAiChatSSE.ts: the UI line's 3-arg onError and
  classification payload, with mainline's localized connection-lost copy.
- AiMessageBubble.tsx: mainline's rule that a partially streamed answer
  still renders, with the UI line's rich ChatErrorCard below it.
- ContextChips.tsx / SaveAsButton.tsx: mainline — it unified the
  uploading/uploaded chip lists and added download-without-preview.
- PlusMenu.tsx / WorkspacePanel.tsx: the UI line's semantic color tokens,
  kept together with mainline's icon choice and its no-flex-1 layout fix.
- Roles.tsx: the UI line's extracted roleMenuSelection module (constants
  verified identical to mainline's inline copy).

Housekeeping: froze mainline's lint violations (its branch carries no
suppressions file), added the missing 11059 error copy in zh-Hans/en/ja,
and guarded one optional-typed artifact preview callback.

Both apps lint clean; platform typecheck clean; i18n parity clean. The six
remaining client typecheck errors are byte-identical to main and predate
this merge.
2026-08-20 18:29:43 +08:00
Kinyoo abac74df41 feat(ui): Modal and the Input family land in @bisheng/ui
Two more baseline components, plus the tokens they forced into the open.

Modal is one shell for every business dialog: 56px header, a body that
is the only scrolling area, a fixed footer. The spec's pinned values —
four sizes with their step-down ladder, the 40% black mask with no blur,
the z-tier, the 200/160ms curves, the three exits — live in the
component, so a business page never restates them. Radix Dialog is the
base, which is what adds @radix-ui/react-dialog to the catalog.

Input covers every single-line field (plus password, search and the
multi-line Textarea) behind one shell: sizes, the four states and the
touch rules baked in, every string a prop.

Two token groups come with them:

- `shadow-focus`, the control focus ring. Deliberately not a third
  elevation tier — it indicates focus and is built from existing values,
  the exception 圆角与阴影规范 §4 allows. Its colour sits in its own var
  so an error / warning field swaps the tint without a second token.
- Four `z-*` overlay tiers (modal / popover / toast / tooltip), ordered
  so each can cover the one below. Hand-rolled z-[…] values are what
  produced the old z-50 / z-[100] / z-[110] / z-[9999] zoo; 组件-Modal
  弹窗 §5 is now the SSOT and every new overlay picks a tier.

Client's style.css and tailwind.config keep their value-for-value copy
of the preset, per the token SSOT rule. Specs and demo pages updated;
the docs sidebar gains 输入框 Input.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 16:16:42 +08:00
Kinyoo dd254fab68 Merge branch 'feat/2.6.0' into feat/2.6.0-ui
Brings the main line's 14 commits onto the design-system branch. Where
the two disagreed, the rule was: take the incoming behaviour, keep this
branch's semantic tokens.

- The knowledge space header and its file list: incoming wins outright,
  since the whole point is to land the list-view rework here. FileTable
  is deleted, FileListView/FileListRow/FileListToolbar arrive, and the
  hex those ported files carried is converted to the semantic tokens
  this branch runs on (#1d2129/#4e5969/#86909c/#e5e6eb/#f7f8fa →
  text-1/text-2/text-3/border-base/fill-1). #8a94a6, #ebebeb and
  #ECECEC stay: no semantic token maps to them.
- DropdownMenuCheckboxItem keeps this branch's tokens while taking the
  incoming indicatorSide behaviour.
- ChannelPreviewDrawer keeps its tokens and takes the incoming removal
  of the `rejected` branch (a rejected application must be resubmittable).
- The two square cards take incoming whole — the missing-icon and
  broken-favicon fixes are behaviour, and their new #A9AEB8 has no token.
- useFileUpload and useWebsocket take incoming: those are the main
  line's lint cleanup, already the newer form.

Suppressions pruned after the merge; client and @bisheng/ui typecheck
clean, client lint reports nothing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 11:28:49 +08:00
Kinyoo 0ed56aa5c8 style(subscription): align AddToKnowledgeModal with the modal spec
Shell per spec settled values: rounded-2xl, gray overlay (bg-gray-500/90),
border #ebebeb + soft shadow, p-5 edge / gap-4 block rhythm, title
text-base font-medium leading-6 with semantic text-text-1.

Footer buttons move to the Button dual-axis API (default/outlined cancel,
primary/solid confirm, built-in loading) in both the main picker and the
duplicate-files dialog. Tree density: container padding 12->4, uniform
4px row spacing via space-y-1. Mobile fullscreen: shrink the fixed-footer
reservation 84->64px to remove the dead gap above the action bar.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-20 11:19:48 +08:00
Kinyoo 4a2956b315 feat(knowledge): list view and its toolbar land on the main line
Ports the file-list rework from the cofco branch. FileTable's column
layout goes away; the space detail renders FileListRow inside
FileListView, and everything the table used to own in its headers —
sorting — plus the search / filter / view-toggle that lived in the space
header now share one 44px FileListToolbar, identical in list and card
mode. The header keeps identity, batch actions and 新增.

Along for the ride, all from the same rework:

- Toolbar chrome: the grey band paints only in list mode (it reads as
  the list's header row); the view toggle names the view you are in, not
  the one clicking would take you to; the status filter can key off
  "待审核" via a UI-only sentinel, since pending uploads have no
  file_status the backend could filter on.
- Rows: the meta line (date | tags | encoding) drops to 10px so its
  three parts share one 18px line, with a matching TagGroup text-list
  variant; the `#` prefix rule flips to "every variant except pill" so a
  new text variant cannot silently lose it.
- Horizontal padding moves from the page container down to the header,
  toolbar and card grid, so an H5 row's selected background spans the
  full width instead of stopping 16px short.
- FileIcon holds icon components rather than pre-sized elements, so a
  caller passing a smaller slot gets a scaled glyph instead of a cropped
  one — only the folder branch honoured that before.
- Selection checkbox styling moves into selectionCheckboxStyles, and
  RoundCheckbox gains `disabled` so unselectable rows keep the column's
  alignment.

AddToKnowledgeModal's footer buttons switch to the Button dual-axis API
(color × variant × size) and its `loading` prop, dropping the
hand-rolled heights, paddings and Loader2 spinner the design system
already provides.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 10:34:15 +08:00
Kinyoo 88f5989025 style(knowledge): header filter and sort menus adopt the action-menu panel
On this branch the two menus still live in the space header (the list
view that moves them into a toolbar is cofco-only), but they were the
last ones built from raw DropdownMenu primitives — their own surface,
their own row geometry. Both switch to ActionMenuContent /
ActionMenuItem, so they match every other menu, and their eleven
hand-written rows collapse into two option tables.

Same two shared additions as cofco's deda10b27:

- DropdownMenuCheckboxItem gains `indicatorSide`. Its check has always
  been a left gutter, indenting every label by 32px whether or not
  anything is checked; "right" keeps labels flush and puts the check in a
  trailing slot. Default stays "left", so existing menus are untouched.
- ActionMenu exports ActionMenuCheckboxItem and
  actionMenuSectionLabelClassName for the group headings.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 17:53:00 +08:00
Kinyoo 3f0b47df56 fix(channel): a rejected application returns to plain 订阅
"已驳回" was modelled as a fourth status across the square card, the
discovery home and the preview drawer, and it rendered as a disabled
button — so a user whose application was turned down had no way to ask
again, on any of the three surfaces. Rejection is not a state the UI
needs: the backend's `rejected` now collapses into "join", which is what
a resubmittable application looks like.

The square card also stops standing in a /default-avatar.png when a
channel has no source icons — freshly created channels legitimately have
none — and a source whose favicon fails to load hides that one image
instead of leaving the browser's broken-image glyph. Icon overlap and the
meta line's grey follow the design.

Suppressions shrink with the removed branches.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 16:38:47 +08:00
Kinyoo 3cd62f3909 refactor(chatApi): type the request builders as unknown
The four params/body objects these endpoints assemble are written to and
handed straight to http — nothing reads a property back — so they carry
Record<string, unknown> instead of Record<string, any>. Suppression
budget shrinks with them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 11:56:02 +08:00
Kinyoo f489c1dd1d refactor(client): clear the lint backlog outside the chat core
Twenty-one of the fifty-three standing violations, fixed rather than
suppressed:

- useWebsocket: two switch cases get block scopes, so their `const`s
  stop leaking into sibling cases (9 × no-case-declarations).
- FilePreview: both @ts-ignore comments were dead — __APP_ENV__ has had
  a global declaration for a while — so they just go.
- useFileUpload: the `_raw` duplicate-row passthrough is now modelled on
  KnowledgeFile as RawSpaceChild instead of being cast in and out at
  three call sites; upload rejections narrow through one readUploadError
  helper rather than `err: any` plus two `(err as any)` reads; and the
  three `as any` on showToast were never needed — TShowToast has always
  accepted `severity`.

Two things the removed `any`s had been hiding: i18next's t() rejects a
runtime-assembled `api_errors.{code}` key (it is not in the generated
key union — exists() is the real guard, now stated as such), and its
interpolation values must be scalars, so errorData is filtered to them.

Suppressions pruned accordingly; typecheck still clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 11:51:37 +08:00
Kinyoo 3c6ae4b4a4 style(knowledge): default tooltip skin for the upload-capability hint
The supported-formats hint was the only tooltip here still hand-skinned
— white panel, no arrow, its own text colour — so it read as a
different kind of popup from every other tooltip on the page. It drops
back to the shared TooltipContent defaults and moves to `side="top"`,
keeping only the width cap and z-index it actually needs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 11:16:47 +08:00
Kinyoo 580c8d3518 refactor(ui): StateView centers plainly; callers nudge with padding
The top-offset cap (min((container − content) ÷ 2, 200px), built from
two flexible spacers) was a second positioning mechanism living beside
plain centering, and it only ever mattered for a handful of unusually
tall containers.

It goes: StateView now centers on both axes, full stop. The few areas
that do need the block higher — much taller than their content, or with
a floating dock over the bottom edge — pass an asymmetric bottom
padding through className (`pb-16`, `pb-[112px]`), and centering
resolves inside what's left, lifting the content by half that amount.
twMerge keeps the caller's padding over the component's own.

Docs follow: §6 rewritten around the one mechanism, the demo swaps its
360-vs-900px height comparison for a default-vs-nudged pair, and the
props table records className as the nudge channel. With this the
spec's four visual open questions are all closed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 14:57:11 +08:00
Kinyoo 4ad975e1a5 feat(ui): StateView — the state-page shell, and one typography rule
The State spec landed yesterday as prose; this is the component, plus
the revision the designer asked for on review: drop the two typography
sets. Scenario decides what you write and which artwork you pick, but
never how it is typeset — so there is one layout (16 / 4 / 16), and
whether 主提示 renders as a title or as description depends on a single
question: does a 辅助说明 follow it. A lone line is not a heading; 16/24
bold makes "还没有知识库" shout louder than the page around it.

StateView therefore has one axis, `size` (page / panel / inline) — the
container's size, per §3. The inline tier types `image`/`action` as
`never`: the spec says they're ignored there, and a compile error says
it better than a dev warning this package has no channel for. Exported
as StateView, not State, so it never reads as a React state variable.

First four consumers migrate off their hand-rolled shells:
PermissionEmptyState (which feeds five call sites), ChatEmptyState,
KnowledgeListPanel and SkillSelector.

Docs site: the demo page, and a preview-card exemption in the doc CSS —
the prose rules were flattening every <p> a live component renders to
14/22/400, so the State page's title tier looked identical to its
compact one. That page is exactly where a designer comes to eyeball the
hierarchy.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 14:30:27 +08:00
Kinyoo 3c3ca78eaf fix(ui): align a wrapped toast's icon to its first line
On a toast whose copy wraps, the flex row centred the icon against the
whole text block — it floated somewhere beside the second line instead
of marking the start of the message. It now sits in a one-line-tall box
aligned to the top, so it rides the first line; the height comes from
--leading-body, which remaps per breakpoint on its own.

The close button also drops to text-4 and moves 16px off the copy,
matching the action button: it is an escape hatch, not something to
draw the eye.

Docs: the Toast spec records both, and the feedback demo page now
mounts a real Toaster so its "thanks for the feedback" confirmation
shows up in the demo the way it does in the app.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 11:37:53 +08:00
Kinyoo 4429125a27 docs(ui): State spec v1, and register the unwired specs in the sidebar
状态页规范建档,由 2026-08-14 当天的《组件-Empty空状态》整份重写而来:
结果反馈不再当成空状态的一个间距变体,两者合成一份文档,按「插画尺寸
看容器、文字排版看场景」两轴独立分层。调研 antd / Arco / TDesign /
Semi / Polaris / Atlassian 六家后由设计师拍板——全用插画不引图标、结果
反馈只做成功与 404 / 500、无权限归「没有内容」那组、标题 16/24 字重
500、区域内居中且顶距封顶 200px。实现待开工:先做声明式 `<State />`
(size × variant),再照插画台账逐处迁移。

侧栏「组件规范」同时补上三条早已建档却没接线的入口——面包屑、二次确认、
抽屉:文档在仓库里,站点上却点不到。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 11:37:43 +08:00
Kinyoo fbd52a01f2 feat(ui): illustrations move into @bisheng/ui with their own palette
The eight status illustrations lived in client, so platform had no way
to reach them and their brand greens rode --brand-*, which renders the
darker UI green rather than the artwork's own.

They move to packages/ui/src/components/Illustration behind an
IllustrationProps type, and the palette becomes its own ramp: --illus-
100/300/500, blue mirroring the brand blue, green keeping the source
SVGs' vivid greens, plus a theme-independent .illus-grey draft mode
(grey collapses 300 onto 100 — pure white washed out on white, e.g. the
magnifier halo). Declared in design-token.cjs as the SSOT and carried by
tokens.css. No Tailwind classes: SVG presentation attributes ignore
var(), so components set them via inline style.

client keeps a re-export shim at ~/components/illustrations so call
sites are untouched. KnowledgeSpaceIcon stays behind: it reads the
Recoil theme, which the library contract forbids, and swaps whole
drawings rather than re-tinting one. The gallery gains the eighth
illustration (SystemError), which the inventory had been missing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 11:37:29 +08:00
GuoQing Zhang 901fa1adab fix(permission): use bindings for permission list reads 2026-08-13 18:33:35 +08:00
Kinyoo f7e23ea248 style(permission): both grant dialogs bottom out the same way
The two "新增授权" dialogs disagreed at the footer: one had a cancel
button, the other only submit; one used DialogFooter's own responsive
rules, the other a hand-rolled row. On mobile — where both become
full-screen sheets — that read as two different dialogs.

PERMISSION_FOOTER_ACTIONS_CLASS holds the shared rule: right-aligned at
natural width on desktop, two half-width buttons tiling one row under
768px (same breakpoint as the shell). The draft picker drops
DialogFooter for a plain row so the divider can sit directly above the
buttons on mobile, with the relation picker stacked above it. The
resource grant tab gains the missing cancel button via an optional
onCancel, and its submit switches to the Button loading prop instead of
appending "..." to its own label.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 17:47:39 +08:00
GuoQing Zhang eb4940fa58 Merge remote-tracking branch 'origin/feat/2.6.0-0811' into feat/2.6.0 2026-08-13 17:32:37 +08:00
dolphin f5a521039a docs(client): document useLocalize unstable-reference pitfall
Root cause of the channel edit page's unresponsive inputs/switches:
useLocalize() returns a new function identity every render, which
cascaded through a useCallback dep chain into a "hydrate form from
server data" effect, causing it to re-fire (and reset the form) on
every keystroke.
2026-08-13 17:28:52 +08:00
GuoQing Zhang 34746ed0bb fix(knowledge): enable mobile settings scrolling 2026-08-13 17:27:30 +08:00
GuoQing Zhang 384c7662d4 fix(settings): align action footer divider 2026-08-13 17:19:53 +08:00
Kinyoo f11ab5d738 fix(ui): stop the toast row's collapse box from clipping its shadow
The exit animation collapses a grid row to 0fr, which only takes effect
if the row clips its content — so the inner box carried overflow-hidden
permanently. That box hugs the toast exactly, and shadow-popup is drawn
entirely outside those bounds, so every toast rendered with its shadow
cut off.

Clip only while the row is collapsing; the toast has faded out by then,
so the shadow's absence is invisible and the collapse still animates.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 17:13:20 +08:00
GuoQing Zhang dfadbdace7 fix(channel): keep edit form initialization stable 2026-08-13 17:11:38 +08:00
Kinyoo c44a36e3be refactor(client): fold the merged-in neutral hexes onto the semantic tokens
The 2.6.0 merge brought back ~70 bare neutral hexes in code written while this
branch was folding them away — including permissionDialogStyles.ts, where the
classes from the deleted ChannelPermissionDialog landed.

Dark values used as fills (bg-[#1D2129], border-[#212121], the #999999 dot and
scrollbar-handle backgrounds) are left as they are: fill-1..4 are all light, so
there is no token that carries that intent, and mapping them would silently
turn a dark surface near-white.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 16:38:58 +08:00
Kinyoo fffa7a1e46 Merge branch 'feat/2.6.0' into feat/2.6.0-ui
Conflicts were all the same shape: this branch swapped hardcoded hexes for
semantic tokens while 2.6.0 changed the logic on the same lines. Resolved by
taking 2.6.0's version and replaying the token swaps onto it — verified
mechanically that every one of the 57 hunks changed nothing but colour on
this side.

FileListRow, ChannelPermissionDialog, CreateChannel{Drawer,Success} and
CreateKnowledgeSpaceDrawer were deleted by 2.6.0's unified-permission work;
our only edits to them were colour swaps, so the deletions stand (2.6.0 ships
a test asserting those paths are gone).

The 圆角与阴影 doc keeps this branch's 2026-07-31 filename and its §4
「例外与新增」 heading, and takes 2.6.0's new §3 on blur/frosted glass.

Both branches independently added violations to the same four legacy files,
pushing them past their frozen suppression counts; typed the five newcomers
rather than raising the ceilings, then pruned.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 16:31:54 +08:00
Kinyoo e96ee2a340 feat(ui): Toast moves into @bisheng/ui
Toaster + toastStore now live in packages/ui and the client consumes
them instead of its own Recoil store, so knowledge-move undo and every
other caller share one implementation. Drops client store/toast.ts and
moveUndoToast.tsx, adds the rspress toast demo page and the tokens the
new component needs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 16:16:36 +08:00
Kinyoo 65669cd5b7 style(permission): one empty state for every permission list
The authorization panel had the illustration, the member list and the
three subject pickers had a bare grey line of text — so the same "there
is nothing here" read as two different states depending on which list
you were looking at, and the pickers' `py-4` left the message floating
near the top of a tall scroll area.

PermissionEmptyState holds the illustration + message and centers in
whatever height its parent gives it; all five lists consume it. Each
keeps its own copy — the pickers still distinguish no-results from
no-departments/no-groups.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 15:16:29 +08:00
Kinyoo 658c30ce12 docs(ui): Toast spec v1
轻提示规范定稿:四类型(成功/失败/警告/信息,不做 loading——异步等待
归按钮的 loading 态)、顶部居中最多 3 条堆叠、至多一个操作按钮且必须
是可撤回/可跟进的实际动作、停留时长按字数在 3~10 秒间动态延长并在
悬停时暂停。另附现状扫描:全局 Toast 一套(135 文件 / 404 个调用点,
recoil 单例只显 1 条)+ 一处绕开它的私有撤销 toast,壳解剖列出 13 项
与规范的差距,以及 `duration: 0` 语义反转等迁移坑。

站点接线:rspress 侧栏「组件规范」注册入口,00-总纲进度看板与
01-设计规范 §0 索引各加一行。01 §5 的层级待定稿补一条——轻提示要盖住
弹窗与抽屉,与 Tooltip 谁在最上随阶梯一并拍板。

本分支适配:文内圆角规范引用指向已改名的《基础-圆角与阴影规范.mdx》。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 15:05:08 +08:00
Kinyoo b6f48b6f06 style(chat): mobile drill panels match the menu chrome
The org / skill drill panels kept their pre-restyle back row — a
lucide chevron on slate greys, separated from the list by a hairline
that no other panel in this menu draws — and only the knowledge popup
had been switched to the list padding, so the "+" menu's drill panels
still doubled the gap under their last row.

The back row now uses Outlined.ArrowLeft on the menu's own greys with
a short divider before the title, drops the underline, and the list
padding rule keys off mobileTallPanel so every mobile list panel gets
it, not just the knowledge popup.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 15:03:13 +08:00
Kinyoo f9b3bbe821 refactor(client): share the file-type icon map, drop two dead modules
AiMessageBubble carried its own extension → bisheng-icon table, a copy
of what FileIcon already knows. FileIcon exports getFileTypeIcon and the
bubble consumes it, so a new file type is registered once.

Also deletes FileListRow and the knowledge mock: neither has had a
call site for a while (the file list renders through FileTable /
FileCard). Suppressions shrink accordingly. The share dialog picks up
the icon import while it is here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 14:28:26 +08:00
Kinyoo a9409dd9b4 style(chat): one chrome for the input menus, one resting icon tint
The "+" menu, knowledge pill, skill panel and the model / group selects
had each drifted: 8px vs 16px radius, per-menu padding, rows sized by
padding instead of height, and icon greys ranging over #999 / slate-600.

- Menus settle on rounded-2xl, 12px padding, 8px gap and 32px rows with
  8px radius. Select grows a viewportClassName so its scroll viewport
  can carry that padding instead of the outer panel (which would clip
  the scrollbar).
- Resting icons all use #4E5969; active stays brand. The upload item
  drops its baked-colour link.svg for Outlined.Attachment so it can
  follow that tint.
- The "+" panel aligns to the input box's left edge rather than its own
  trigger, which sits 13px inside it. The knowledge pill keeps trigger
  alignment — mid-toolbar, where the input edge means nothing.
- Skill rows carry selection themselves (brand tint + trailing check)
  now that the leading checkbox is gone; name and description each
  clamp to one line and move the full text into a tooltip, but only
  when measurement says they actually overflow. Empty search gets the
  illustration instead of bare text.
- The knowledge panel takes its own 320px cap (skills/org sit at 440)
  so it still opens downward more often, and the hand-rolled
  useSubMenuLayout measuring goes away — align="center" plus Radix's
  own collision handling already place these panels.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 14:28:26 +08:00
Kinyoo e2ab7d2576 style(chat): keep a conversation row filled while it owns transient UI
Opening a row's options menu or its rename input moves the pointer off
the row — into the portaled menu — so the hover fill dropped away and
the row you were acting on stopped looking involved.

Both sidebars now pin the hover fill for as long as the row owns that
UI. It stays the hover fill, never the active row's: this row is not
the open conversation. On coarse pointers it applies too, where it
reads as "this row owns the menu" rather than as a hover affordance.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 14:28:26 +08:00
Kinyoo 1ccd2635bf fix(knowledge): commit an inline rename when the click lands outside
Rename is entered from a Radix dropdown item, and Radix restores focus
to the menu trigger as it unmounts — after our synchronous focus() call.
The input therefore never held focus, never fired blur, and clicking
away left the field open with the edit unsaved.

Two halves: re-assert focus once that restore has run (in the existing
selection timeout), and commit from a capture-phase pointerdown listener
so an outside click saves even when blur never fires. Since an outside
click now reaches the commit twice — the listener, then the input's own
blur — a ref mirrors isRenaming so the second one is a no-op instead of
a duplicate onRename. Tests cover both paths.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 14:28:26 +08:00
GuoQing Zhang 78fb5e122f Feat/2.6.0 0811 (#2293)
## What

简要描述做了什么改动。

## Why

为什么需要这个改动?

## How

实现方式、设计决策(如有)。

## Test

- [ ] 本地测试通过
- [ ] 114 测试服务器验证通过

## Related

- Issue/ticket:
2026-08-13 11:34:44 +08:00
Kinyoo cbe1cace36 style(error-page): plain copy link, toast instead of a label swap
The copy link was underlined on hover and swapped its own label to
"copied" for two seconds. Both read as emphasis the sentence does not
want, and a label that changes under the cursor is easy to miss on the
one screen a user is likely to be screenshotting rather than reading.

The link now never underlines and never changes text; ErrorPage fires a
new `onCopied` callback instead, and the client raises its usual toast
(existing `com_ui_copied_to_clipboard` key, all three languages). The
package still carries no i18n and no toast of its own. `labels.copied`
and the now-dead `com_error_page.copied` key are gone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 10:53:48 +08:00
Kinyoo d9ef9785af feat(ui): ErrorPage — the shared crash screen
Ported from feat/2.6.0, where it was written in place: it is a
packages/ui component, so the library branch is where it belongs.
Both apps hand a support engineer the same identifiers, the same QR
payload and the same log file, so an engineer reading a user's
screenshot never has to know which app produced it.

Contract-clean: every string, the artwork and the "copied" confirmation
arrive as props/callbacks — the package carries no i18n and no toast.
The copy link is a plain brand-coloured button (no underline, no label
swap); the app confirms with its own toast via `onCopied`.

Adds qrcode.react to the workspace catalog for the QR block.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 10:44:18 +08:00
GuoQing Zhang 787314c194 Merge remote-tracking branch 'origin/feat/2.6.0' into feat/2.6.0 2026-08-12 19:34:28 +08:00
Kinyoo ec1f09b692 style(client): knowledge icons from bisheng-icons, semibold sidebar titles
The two hand-drawn knowledge glyphs (BookOpen / Books) predate the
matching bisheng-icons entries. Every call site — hub nav tab, the
attachment bar's KB card, the task-mode context chips — switches to
Outlined.Book / Outlined.Books, and the local SVG components are
deleted (ChatKnowledge only carried dead imports).

Sidebar section titles drop from bold to semibold, matching the weight
the rest of the sidebar chrome uses.

Suppressions: pruned the entries the deleted icons left behind.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 18:24:52 +08:00
Kinyoo f94d370883 style(chat): center submenu panels on their trigger row, drop dup headings
The knowledge / skill submenus aligned their top edge with the parent
menu via a hand-computed alignOffset (measure the trigger, subtract
rects, re-measure on resize), and each panel repeated a heading that
its own trigger row already carried right next to it.

- DropdownMenuSubContent accepts align="center". Radix types SubContent
  as 'start' | 'end' but hands the value straight to Popper, which does
  support 'center', so the type is widened rather than the offset
  hand-computed. The knowledge, org-knowledge and skill panels switch
  to it and the alignOffset machinery goes away — useSubMenuLayout now
  only clamps max height against the viewport.
- Panel headings drop where the trigger already names the list; the
  mobile drill panels keep theirs, since there the heading IS the
  back-navigation label. The knowledge pill's two variants collapse
  into one branch with a mobile-only heading (the narrow toolbar can
  render its trigger icon-only).
- Skill panels grow to 280×440 so the list isn't a letterbox.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 18:12:22 +08:00
Kinyoo b66398e272 feat(chat): confirm a recorded feedback verdict with a toast
Rating an answer changed only the icon's highlight, which is easy to
miss and says nothing about what the rating is for. Every persisted
verdict — thumbs-up, or a dislike once its dialog is submitted — now
raises a "thanks, we'll use it to improve our answers" toast (new key,
all three languages).

Un-toggling stays silent: the icon losing its highlight is feedback
enough, and a toast there would read as if cancelling had itself been
recorded. onLike may now return the request promise, and the two call
sites do — so a failed request shows only the interceptor's error
toast, never both.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 18:12:22 +08:00
Kinyoo d34f2511d1 style(permission): selectable access-mode rows, centered settings footer
- AccessModeSelector rows now behave like the options they are: hover
  tint, brand-tinted background while checked, and the label kept on
  one line so only the description truncates. Disabled rows opt out of
  the hover tint so they still read as unavailable.
- That truncated description gets the same hover-only tooltip the
  member list uses, so TruncatedTooltip moves out of PermissionListTab
  into its own module. Its polymorphic ref keeps one `any` — now with
  an inline reason — and the file's suppression entry shrinks.
- Both settings pages center their footer actions unconditionally; the
  knowledge page had centered only in create mode, so the same footer
  jumped between layouts depending on how you entered it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 18:04:22 +08:00
LineWalker e8132a78ee Revert "fix(file-viewers): 把 xlsx 图片绑定到它真正所属的 sheet"
This reverts commit 516130dd8258f8ac2e2f68e0e5b3d1ba38b98c9a.

修复留在 3.0 线,2.6 不再跟进——客户后续升级到 3.0 时一并拿到。
2026-08-12 17:57:53 +08:00
Kinyoo 748fc43455 refactor(permission): one level menu and one dialog shell across the surfaces
The member list, the create-page draft and the two "新增授权" dialogs had
each grown their own version of the same two things — so the level
control looked different depending on where you opened it, and the
draft picker was a look-alike of the share dialog rather than the same
dialog.

- PermissionLevelMenu extracts the list's dropdown (levels, separator,
  destructive 移除) and the draft editor adopts it, dropping its
  RelationSelect + separate delete icon button. Removal now always
  lives inside the menu, never as a button beside it; a row with
  neither permission renders the plain label at the same width, so the
  column never jitters.
- permissionDialogStyles holds the share dialog's chrome verbatim —
  shell sizing (80vh card / full-screen under 768px), subject tabs,
  包含子部门 toggle, footer captions — and the draft picker consumes it
  instead of re-approximating it.
- Empty authorization lists get their own copy (new key, three
  languages) instead of borrowing the search-result empty text.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 17:40:23 +08:00
GuoQing Zhang 611f142a84 fix(frontend): prevent repeated knowledge space error toast 2026-08-12 17:05:10 +08:00
GuoQing Zhang f127c26ef6 fix(frontend): normalize permission revoke scope 2026-08-12 16:51:37 +08:00
GuoQing Zhang 9e66f290e2 fix(frontend): use white settings page backgrounds 2026-08-12 16:42:54 +08:00
LineWalker 516130dd85 fix(file-viewers): 把 xlsx 图片绑定到它真正所属的 sheet
图片索引原本是 Record<cellAddress, imageId[]>,没有 sheet 维度,getCellImage
也只按单元格地址查、不看 activeSheet —— 任何 sheet 的图都会漏到所有 sheet 的
同名单元格。一个首个 sheet 是整页报表截图的文件因此表现为:第一个 sheet 空白,
图跑到第二个 sheet 的 B2。

图归属只能顺 OPC 关系链查出来(workbook.xml 的 r:id → workbook.xml.rels →
worksheets/sheetN.xml → sheetN.xml.rels 的 drawing → drawings/drawingM.xml),
sheet 顺序和文件编号没有关系,删过 sheet 就会留下空号,不能按序号配对。

同批修掉的其它缺陷:
- 只有单元格没有 drawing 的 sheet 被判成「无数据」,纯图片 sheet 永远空白
- cleanData 删空行空列后坐标被压缩,锚点却按原始网格寻址,表中间有空行就错位
- 只认 twoCellAnchor,oneCellAnchor(WPS/报表导出常用)与 absoluteAnchor 的图不显示
- 按 xdr: 字面前缀匹配标签,换个生成器的命名空间前缀就整体失效
- DISPIMG 找不到对应图时返回 images[0],拿第一张图冒充
- cleanData 取值用 falsy 判断,把数字 0 吞成空串

锚在数据区之外的图不再静默消失,与纯图片 sheet 一起按 EMU 换算出的原始尺寸
展示。原文件 643 行已超单文件 600 行上限,顺势拆成 types / sheetUtils /
xlsxImages / ImageGallery 四个模块。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 16:26:06 +08:00
GuoQing Zhang d07580a992 fix(frontend): align channel settings layout styles 2026-08-12 11:03:56 +08:00