one-design-next 0.0.98 → 0.0.100

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -136,15 +136,8 @@ one-design-next/
136
136
  ├── scripts/ # 构建、benchmark、反馈等工具脚本
137
137
  ├── benchmark/ # benchmark 原始数据与对比结果
138
138
  ├── tests/ # 组件与文档回归测试
139
- ├── skills/ # 对外发布 Skill(多产品)
140
- ├── src/
141
- │ │ ├── shared/ # 共享:ODN 组件知识、流水线 GUIDE
142
- │ │ ├── ruyi/ # 如翼专属:业务知识、区块、页面、scaffold
143
- │ │ └── huxuan/ # 互选专属:业务知识、区块、页面、scaffold
144
- │ ├── dist/ # 构建产物(gitignored,由 CI 生成)
145
- │ │ ├── ruyi/ # 如翼 skill 发布包(自包含)
146
- │ │ └── huxuan/ # 互选 skill 发布包(自包含)
147
- │ └── build.sh # bash skills/build.sh [ruyi|huxuan]
139
+ ├── skills/
140
+ └── odn-design-ruyi/ # 如翼设计 Skill(直接发布此目录,无 src/dist)
148
141
  ├── dist/ # 组件库构建产物
149
142
  └── public/ # 静态资源
150
143
  ```
@@ -170,17 +163,11 @@ one-design-next/
170
163
  | **内部维护** | `p4-figma-to-block` · `p5-figma-to-page` · `p4-component-update-process` · `p5-dmp-web-restore` · `p5-fe-free-trade-restore` | 设计稿转区块/整页、组件更新、线上页面还原(仅维护者本地用) |
171
164
  | **辅助** | `ten-questions` · `philosophy-talks` | 方法论讨论与设计哲学 |
172
165
 
173
- ### 多产品 Skill 发布
166
+ ### Skill 发布
174
167
 
175
- `skills/` 目录支持多产品构建,`shared/` 中的 ODN 铁律和流水线由所有产品共享,各产品在自己的目录下维护专属业务知识和区块:
168
+ 对外发布目录就是 `skills/odn-design-ruyi/`,不再经过 `src` `dist` 拷贝。
176
169
 
177
- ```bash
178
- bash skills/build.sh # 构建全部产品
179
- bash skills/build.sh ruyi # 只构建如翼
180
- bash skills/build.sh huxuan # 只构建互选
181
- ```
182
-
183
- > **外部只读模式**:外部平台仅消费 `skills/dist/<product>/` 下的知识生成代码与建议,不修改本仓库的组件、区块与知识文件。
170
+ > 外部平台只读消费该目录,不修改本仓库的组件与区块。
184
171
 
185
172
  ## 发布
186
173
 
@@ -137,6 +137,7 @@ html {
137
137
  [data-odn-button-size=small] [data-odn-button-icon],
138
138
  [data-odn-button-size=small] [data-odn-button-loading-icon],
139
139
  [data-odn-button-size=small] [data-odn-button-right-icon] {
140
+ display: inline-block;
140
141
  width: var(--odn-button-small-icon-size);
141
142
  height: var(--odn-button-small-icon-size);
142
143
  }
@@ -163,6 +164,7 @@ html {
163
164
  [data-odn-button-size=medium] [data-odn-button-icon],
164
165
  [data-odn-button-size=medium] [data-odn-button-loading-icon],
165
166
  [data-odn-button-size=medium] [data-odn-button-right-icon] {
167
+ display: inline-block;
166
168
  width: var(--odn-button-medium-icon-size);
167
169
  height: var(--odn-button-medium-icon-size);
168
170
  }
@@ -189,6 +191,7 @@ html {
189
191
  [data-odn-button-size=large] [data-odn-button-icon],
190
192
  [data-odn-button-size=large] [data-odn-button-loading-icon],
191
193
  [data-odn-button-size=large] [data-odn-button-right-icon] {
194
+ display: inline-block;
192
195
  width: var(--odn-button-large-icon-size);
193
196
  height: var(--odn-button-large-icon-size);
194
197
  }
@@ -1,6 +1,6 @@
1
1
  import { type HTMLAttributes, type ReactNode } from 'react';
2
2
  import './style';
3
- /** 组内预览条数。仅收起后再打开时生效;默认展开态展示全部。 */
3
+ /** 组内每页条数。首次展开与后续「展开更多」都按此分页。 */
4
4
  export declare const CHAT_GROUP_PREVIEW_LIMIT = 5;
5
5
  export interface ChatGroupProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {
6
6
  /** 分组标题 */
@@ -34,14 +34,18 @@ export interface ChatGroupProps extends Omit<HTMLAttributes<HTMLDivElement>, 'ti
34
34
  /** 强制显示 extra(如菜单打开时) */
35
35
  extraVisible?: boolean;
36
36
  /**
37
- * 组内预览条数。默认 5;`false` 关闭截断(未分组 / 搜索命中)。
38
- * 仅在收起后再展开时截成前 N 条 +「展开其余 N 条」;默认展开态展示全部,
39
- * 其间新增超过 N 条也不出现展开按钮。
37
+ * 组内每页条数。默认 5;`false` 关闭分页(搜索命中等)。
38
+ * 首次展开与每次「展开更多」都只增加一页,收起再打开回到第一页。
40
39
  */
41
40
  previewLimit?: number | false;
41
+ /**
42
+ * 是否在截断时展示「展开更多」。默认 `true`。
43
+ * 未分组滚动分页时由 ChatList 关闭,改由外层滚动加载。
44
+ */
45
+ overflowHint?: boolean;
42
46
  children?: ReactNode;
43
47
  }
44
- export declare function ChatGroup({ title, collapsible, collapsed: collapsedProp, defaultCollapsed, onCollapsedChange, renaming: renamingProp, defaultRenaming, onRenamingChange, onRename, folderIcon, section, extra, extraVisible, previewLimit, children, className, ...rest }: ChatGroupProps): import("react").JSX.Element;
48
+ export declare function ChatGroup({ title, collapsible, collapsed: collapsedProp, defaultCollapsed, onCollapsedChange, renaming: renamingProp, defaultRenaming, onRenamingChange, onRename, folderIcon, section, extra, extraVisible, previewLimit, overflowHint, children, className, ...rest }: ChatGroupProps): import("react").JSX.Element;
45
49
  export declare namespace ChatGroup {
46
50
  var displayName: string;
47
51
  }
@@ -1,7 +1,7 @@
1
1
  import _extends from "@babel/runtime/helpers/esm/extends";
2
2
  import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
3
3
  import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
4
- var _excluded = ["title", "collapsible", "collapsed", "defaultCollapsed", "onCollapsedChange", "renaming", "defaultRenaming", "onRenamingChange", "onRename", "folderIcon", "section", "extra", "extraVisible", "previewLimit", "children", "className"];
4
+ var _excluded = ["title", "collapsible", "collapsed", "defaultCollapsed", "onCollapsedChange", "renaming", "defaultRenaming", "onRenamingChange", "onRename", "folderIcon", "section", "extra", "extraVisible", "previewLimit", "overflowHint", "children", "className"];
5
5
  import clsx from 'clsx';
6
6
  import { Children, useEffect, useLayoutEffect, useRef, useState } from 'react';
7
7
  import HoverFill from "../hover-fill";
@@ -9,7 +9,7 @@ import Icon from "../icon";
9
9
  import { PRESENCE_TOTAL_MS, PresenceSlots } from "./presence-list";
10
10
  import "./style";
11
11
 
12
- /** 组内预览条数。仅收起后再打开时生效;默认展开态展示全部。 */
12
+ /** 组内每页条数。首次展开与后续「展开更多」都按此分页。 */
13
13
  export var CHAT_GROUP_PREVIEW_LIMIT = 5;
14
14
  function useControllableBool(controlled, defaultValue, onChange) {
15
15
  var _useState = useState(defaultValue),
@@ -26,11 +26,9 @@ function useControllableBool(controlled, defaultValue, onChange) {
26
26
  function ChatGroupHintRow(_ref) {
27
27
  var kind = _ref.kind,
28
28
  onExpand = _ref.onExpand,
29
- _ref$remaining = _ref.remaining,
30
- remaining = _ref$remaining === void 0 ? 0 : _ref$remaining,
31
29
  _ref$expanded = _ref.expanded,
32
30
  expanded = _ref$expanded === void 0 ? false : _ref$expanded;
33
- var label = kind === 'empty' ? '暂无对话' : "\u5C55\u5F00\u5176\u4F59 ".concat(remaining, " \u6761");
31
+ var label = kind === 'empty' ? '暂无对话' : '展开更多';
34
32
  if (kind === 'expand') {
35
33
  return /*#__PURE__*/React.createElement(HoverFill, {
36
34
  "data-odn-chat-group-hint": true,
@@ -73,6 +71,8 @@ export function ChatGroup(_ref2) {
73
71
  extra = _ref2.extra,
74
72
  extraVisible = _ref2.extraVisible,
75
73
  previewLimit = _ref2.previewLimit,
74
+ _ref2$overflowHint = _ref2.overflowHint,
75
+ overflowHint = _ref2$overflowHint === void 0 ? true : _ref2$overflowHint,
76
76
  children = _ref2.children,
77
77
  className = _ref2.className,
78
78
  rest = _objectWithoutProperties(_ref2, _excluded);
@@ -114,21 +114,22 @@ export function ChatGroup(_ref2) {
114
114
  setDraft(titleText);
115
115
  setRenaming(false);
116
116
  };
117
- var startsExpanded = !(collapsedProp !== null && collapsedProp !== void 0 ? collapsedProp : defaultCollapsed);
118
- var _useState5 = useState(startsExpanded),
117
+ var pageSize = previewLimit === false ? Number.POSITIVE_INFINITY : Math.max(1, previewLimit !== null && previewLimit !== void 0 ? previewLimit : CHAT_GROUP_PREVIEW_LIMIT);
118
+ var _useState5 = useState(pageSize),
119
119
  _useState6 = _slicedToArray(_useState5, 2),
120
- overflowOpen = _useState6[0],
121
- setOverflowOpen = _useState6[1];
120
+ visibleCount = _useState6[0],
121
+ setVisibleCount = _useState6[1];
122
122
  var _useState7 = useState(false),
123
123
  _useState8 = _slicedToArray(_useState7, 2),
124
124
  hintLeaving = _useState8[0],
125
125
  setHintLeaving = _useState8[1];
126
126
  useLayoutEffect(function () {
127
127
  if (collapsed) {
128
- setOverflowOpen(false);
129
128
  setHintLeaving(false);
129
+ return;
130
130
  }
131
- }, [collapsed]);
131
+ setVisibleCount(pageSize);
132
+ }, [collapsed, pageSize]);
132
133
  useEffect(function () {
133
134
  if (!hintLeaving) return;
134
135
  var t = window.setTimeout(function () {
@@ -138,32 +139,32 @@ export function ChatGroup(_ref2) {
138
139
  return window.clearTimeout(t);
139
140
  };
140
141
  }, [hintLeaving]);
142
+ var childList = Children.toArray(children).filter(Boolean);
143
+ var paginating = Number.isFinite(pageSize);
144
+ var remaining = paginating ? Math.max(0, childList.length - visibleCount) : 0;
145
+ var truncated = !collapsed && paginating && remaining > 0;
141
146
  var expandOverflow = function expandOverflow() {
142
147
  var _window$matchMedia, _window;
143
- setOverflowOpen(true);
144
- if (typeof window !== 'undefined' && (_window$matchMedia = (_window = window).matchMedia) !== null && _window$matchMedia !== void 0 && _window$matchMedia.call(_window, '(prefers-reduced-motion: reduce)').matches) {
148
+ var next = visibleCount + pageSize;
149
+ setVisibleCount(next);
150
+ var willFinish = childList.length <= next;
151
+ if (!willFinish || typeof window !== 'undefined' && (_window$matchMedia = (_window = window).matchMedia) !== null && _window$matchMedia !== void 0 && _window$matchMedia.call(_window, '(prefers-reduced-motion: reduce)').matches) {
145
152
  return;
146
153
  }
147
154
  setHintLeaving(true);
148
155
  };
149
- var childList = Children.toArray(children).filter(Boolean);
150
- var resolvedLimit = previewLimit === false ? Number.POSITIVE_INFINITY : previewLimit !== null && previewLimit !== void 0 ? previewLimit : CHAT_GROUP_PREVIEW_LIMIT;
151
- // 折叠过程中不要截成预览:否则 scrollHeight 先跳一截,滚动锚定会把列表拽走。
152
- var truncated = !collapsed && !overflowOpen && Number.isFinite(resolvedLimit) && childList.length > resolvedLimit;
153
- var remaining = Math.max(0, childList.length - resolvedLimit);
154
156
  var expandHint = /*#__PURE__*/React.createElement(ChatGroupHintRow, {
155
157
  kind: "expand",
156
- remaining: remaining,
157
158
  expanded: hintLeaving,
158
159
  onExpand: expandOverflow
159
160
  });
160
161
  var body = /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(PresenceSlots, {
161
162
  enabled: !collapsed,
162
- displayLimit: truncated ? resolvedLimit : undefined,
163
+ displayLimit: collapsed || !paginating ? undefined : visibleCount,
163
164
  empty: /*#__PURE__*/React.createElement(ChatGroupHintRow, {
164
165
  kind: "empty"
165
166
  })
166
- }, children), truncated || hintLeaving ? hintLeaving ? /*#__PURE__*/React.createElement("div", {
167
+ }, children), overflowHint && (truncated || hintLeaving) ? hintLeaving ? /*#__PURE__*/React.createElement("div", {
167
168
  "data-odn-chat-group-slot": true,
168
169
  "data-odn-chat-group-leave": "swap",
169
170
  "aria-hidden": true
@@ -14,16 +14,18 @@ type Leaving = {
14
14
  index: number;
15
15
  };
16
16
  export declare function childSlotKey(child: ReactNode, index: number): string;
17
- export declare function usePresence(childList: ReactNode[], enabled: boolean): {
17
+ export declare function usePresence(childList: ReactNode[], enabled: boolean, emptyKind?: PresenceKind): {
18
18
  entering: Entering | null;
19
19
  leaving: Leaving | null;
20
20
  };
21
21
  export declare function buildPresenceSlots(childList: ReactNode[], entering: Entering | null, leaving: Leaving | null, displayLimit?: number, revealingKeys?: string[] | null): import("react").JSX.Element[];
22
- export declare function PresenceSlots({ children, enabled, displayLimit, empty, host, }: {
22
+ export declare function PresenceSlots({ children, enabled, displayLimit, empty, emptyKind, host, }: {
23
23
  children?: ReactNode;
24
24
  enabled?: boolean;
25
25
  displayLimit?: number;
26
26
  empty?: ReactNode;
27
+ /** 0 ↔ 1 条时的入场/离场。默认 `fade`;置顶组消失用 `shift` 收高度。 */
28
+ emptyKind?: PresenceKind;
27
29
  host?: (content: ReactNode, occupied: boolean) => ReactNode;
28
30
  }): import("react").JSX.Element;
29
31
  export {};
@@ -15,6 +15,7 @@ function prefersReducedMotion() {
15
15
  }
16
16
  export function usePresence(childList, enabled) {
17
17
  var _prevRef$current;
18
+ var emptyKind = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'fade';
18
19
  var keys = childList.map(function (child, i) {
19
20
  return childSlotKey(child, i);
20
21
  });
@@ -49,7 +50,7 @@ export function usePresence(childList, enabled) {
49
50
  setLeaving(null);
50
51
  setEntering({
51
52
  key: added[0],
52
- kind: prev.keys.length === 0 ? 'fade' : 'shift'
53
+ kind: prev.keys.length === 0 ? emptyKind : 'shift'
53
54
  });
54
55
  return;
55
56
  }
@@ -61,12 +62,12 @@ export function usePresence(childList, enabled) {
61
62
  key: key,
62
63
  node: prev.nodes[index],
63
64
  index: index,
64
- kind: nextKeys.length === 0 ? 'fade' : 'shift'
65
+ kind: nextKeys.length === 0 ? emptyKind : 'shift'
65
66
  });
66
67
  }
67
68
  // childList 与 keysSig 同步;只跟签名,避免引用抖动。
68
69
  // eslint-disable-next-line react-hooks/exhaustive-deps
69
- }, [keysSig, enabled]);
70
+ }, [keysSig, enabled, emptyKind]);
70
71
  useEffect(function () {
71
72
  if (!entering) return;
72
73
  var t = window.setTimeout(function () {
@@ -148,9 +149,11 @@ export function PresenceSlots(_ref) {
148
149
  displayLimit = _ref.displayLimit,
149
150
  _ref$empty = _ref.empty,
150
151
  empty = _ref$empty === void 0 ? null : _ref$empty,
152
+ _ref$emptyKind = _ref.emptyKind,
153
+ emptyKind = _ref$emptyKind === void 0 ? 'fade' : _ref$emptyKind,
151
154
  host = _ref.host;
152
155
  var childList = Children.toArray(children).filter(Boolean);
153
- var _usePresence = usePresence(childList, enabled),
156
+ var _usePresence = usePresence(childList, enabled, emptyKind),
154
157
  entering = _usePresence.entering,
155
158
  leaving = _usePresence.leaving;
156
159
  var keysSig = childList.map(function (child, i) {
@@ -170,6 +173,12 @@ export function PresenceSlots(_ref) {
170
173
  return;
171
174
  }
172
175
  if (!enabled || prefersReducedMotion()) return;
176
+ if (prevLimit !== undefined && Number.isFinite(prevLimit) && displayLimit !== undefined && Number.isFinite(displayLimit) && displayLimit > prevLimit && childList.length > prevLimit) {
177
+ setRevealingKeys(childList.slice(prevLimit, displayLimit).map(function (child, i) {
178
+ return childSlotKey(child, prevLimit + i);
179
+ }));
180
+ return;
181
+ }
173
182
  if (prevLimit !== undefined && Number.isFinite(prevLimit) && displayLimit === undefined && childList.length > prevLimit) {
174
183
  setRevealingKeys(childList.slice(prevLimit).map(function (child, i) {
175
184
  return childSlotKey(child, prevLimit + i);
@@ -3,6 +3,8 @@ import { type ChatItemProps } from '../chat-item';
3
3
  import './style';
4
4
  /** 系统「未分组」默认 id;不在「分组」模块内渲染。 */
5
5
  export declare const CHAT_LIST_UNGROUPED_ID = "ungrouped";
6
+ /** 系统「置顶」组 id。只能通过置顶操作进入,不出现在「移动到」。 */
7
+ export declare const CHAT_LIST_PINNED_ID = "pinned";
6
8
  /** 按会话时间筛选(面板不含时间组;`filterTime` 仅受控 API)。 */
7
9
  export type ChatListFilterTime = 'all' | 'today' | '7d' | '30d';
8
10
  /** 按会话类型筛选:全部 / AI 任务 / 人工客服。 */
@@ -14,10 +16,6 @@ export interface ChatListGroup {
14
16
  title: string;
15
17
  /** 是否允许重命名。默认 `true`。系统「未分组」建议 `false`。 */
16
18
  renamable?: boolean;
17
- /**
18
- * 置顶分组:仍在「分组」模块内,排到该模块最前;仍带文件夹 icon。
19
- */
20
- pinned?: boolean;
21
19
  }
22
20
  export type ChatListItem = Pick<ChatItemProps, 'id' | 'title' | 'active' | 'type' | 'status' | 'pinned' | 'time' | 'groupId'>;
23
21
  export interface ChatListProps {
@@ -25,14 +23,15 @@ export interface ChatListProps {
25
23
  /** 列表标题。默认「对话」。 */
26
24
  title?: ReactNode;
27
25
  /**
28
- * 自定义分组。传入后按「置顶区 → 分组模块 → 未分组」编排。
26
+ * 自定义分组。传入后按「置顶组 → 分组模块 → 未分组」编排。
27
+ * 置顶与分组互斥:置顶会话只出现在「置顶」组,不出现在原组。
29
28
  * 标题栏搜索默认关闭,需 `showSearch`。
30
29
  * 不传则保持「置顶 / 最近」分区。
31
30
  */
32
31
  groups?: ChatListGroup[];
33
32
  /**
34
- * 置顶分区标题。视觉同组头,无交互。传 `null` 隐藏。默认「置顶」。
35
- * 传入 `groups` 时置顶区默认不显示标题。
33
+ * 置顶分区标题。视觉同组头。传 `null` 在非分组模式隐藏标题。
34
+ * 传入 `groups` 时渲染为与「未分组」同款的「置顶」组头,默认「置顶」。
36
35
  */
37
36
  pinnedSectionTitle?: ReactNode | null;
38
37
  /**
@@ -101,8 +100,6 @@ export interface ChatListProps {
101
100
  onAddGroup?: (group: ChatListGroup) => void;
102
101
  /** 重命名分组 */
103
102
  onRenameGroup?: (id: string, title: string) => void;
104
- /** 置顶 / 取消置顶分组(组块在「分组」模块内排序) */
105
- onPinGroup?: (id: string, pinned: boolean) => void;
106
103
  /**
107
104
  * 删除分组。确认后回调;业务侧应将组内会话移入未分组并移除该组。
108
105
  */
@@ -116,6 +113,17 @@ export interface ChatListProps {
116
113
  collapsedGroupIds?: string[];
117
114
  defaultCollapsedGroupIds?: string[];
118
115
  onCollapsedGroupIdsChange?: (ids: string[]) => void;
116
+ /**
117
+ * 自定义组每页条数,传给 ChatGroup `previewLimit`。
118
+ * 首次展开与「展开更多」都按此分页。`false` 关闭。默认 5。
119
+ * 搜索命中仍不截断。
120
+ */
121
+ groupPageSize?: number | false;
122
+ /**
123
+ * 未分组每页条数。展开后随列表滚动追加,不展示「展开更多」。
124
+ * `false` 关闭(一次铺开)。默认 `false`。搜索命中仍不截断。
125
+ */
126
+ ungroupedPageSize?: number | false;
119
127
  /**
120
128
  * 将「最近」列表包进自定义滚动容器。
121
129
  * 标题栏与置顶区始终留在外侧,不进入该容器。
@@ -129,7 +137,7 @@ export declare function matchChatListFilterTime(time: ChatListItem['time'], filt
129
137
  export declare function matchChatListFilterType(type: ChatListItem['type'], filter: ChatListFilterType): boolean;
130
138
  /** 按状态筛选项判断会话是否命中。无 `status` 时仅在「全部」下保留。 */
131
139
  export declare function matchChatListFilterStatus(status: ChatListItem['status'], filter: ChatListFilterStatus): boolean;
132
- export declare function ChatList({ items, title, groups, pinnedSectionTitle, recentSectionTitle, groupsSectionTitle, ungroupedTitle, ungroupedGroupId, filterTime: filterTimeProp, defaultFilterTime, filterType: filterTypeProp, defaultFilterType, onFilterTypeChange, filterStatus: filterStatusProp, defaultFilterStatus, onFilterStatusChange, search: searchProp, defaultSearch, onSearchChange, showSearch, filterOpen: filterOpenProp, defaultFilterOpen, onFilterOpenChange, showEmptyGroups, empty, actionMenuZIndex, filterMenuZIndex, className, onItemClick, onRename, onPin, onMoveToGroup, onDelete, onAddGroup, onRenameGroup, onPinGroup, onDeleteGroup, onCreateConversation, renderItem, renderGroupHeader, collapsedGroupIds: collapsedProp, defaultCollapsedGroupIds, onCollapsedGroupIdsChange, renderRestScroll, }: ChatListProps): import("react").JSX.Element;
140
+ export declare function ChatList({ items, title, groups, pinnedSectionTitle, recentSectionTitle, groupsSectionTitle, ungroupedTitle, ungroupedGroupId, filterTime: filterTimeProp, defaultFilterTime, filterType: filterTypeProp, defaultFilterType, onFilterTypeChange, filterStatus: filterStatusProp, defaultFilterStatus, onFilterStatusChange, search: searchProp, defaultSearch, onSearchChange, showSearch, filterOpen: filterOpenProp, defaultFilterOpen, onFilterOpenChange, showEmptyGroups, empty, actionMenuZIndex, filterMenuZIndex, className, onItemClick, onRename, onPin, onMoveToGroup, onDelete, onAddGroup, onRenameGroup, onDeleteGroup, onCreateConversation, renderItem, renderGroupHeader, collapsedGroupIds: collapsedProp, defaultCollapsedGroupIds, onCollapsedGroupIdsChange, groupPageSize, ungroupedPageSize, renderRestScroll, }: ChatListProps): import("react").JSX.Element;
133
141
  export declare namespace ChatList {
134
142
  var displayName: string;
135
143
  }
@@ -15,8 +15,12 @@ import "./style";
15
15
 
16
16
  /** 系统「未分组」默认 id;不在「分组」模块内渲染。 */
17
17
  export var CHAT_LIST_UNGROUPED_ID = 'ungrouped';
18
+
19
+ /** 系统「置顶」组 id。只能通过置顶操作进入,不出现在「移动到」。 */
20
+ export var CHAT_LIST_PINNED_ID = 'pinned';
18
21
  var CHAT_ITEM_ROW_FALLBACK = 40;
19
22
  var UNGROUPED_EXPAND_SCROLL_MS = 320;
23
+ var UNGROUPED_SCROLL_LOAD_PX = 80;
20
24
  function findScrollParent(el) {
21
25
  var node = el;
22
26
  while (node) {
@@ -80,6 +84,9 @@ var STATUS_OPTIONS = [{
80
84
  value: 'error',
81
85
  label: '任务报错'
82
86
  }];
87
+ function isPinnedItem(item) {
88
+ return Boolean(item.pinned) || item.groupId === CHAT_LIST_PINNED_ID;
89
+ }
83
90
  function parseItemTime(time) {
84
91
  if (time === null || time === undefined) return null;
85
92
  var t = typeof time === 'number' ? time : time instanceof Date ? time.getTime() : Date.parse(time);
@@ -239,7 +246,6 @@ export function ChatList(_ref3) {
239
246
  onDelete = _ref3.onDelete,
240
247
  onAddGroup = _ref3.onAddGroup,
241
248
  onRenameGroup = _ref3.onRenameGroup,
242
- onPinGroup = _ref3.onPinGroup,
243
249
  onDeleteGroup = _ref3.onDeleteGroup,
244
250
  onCreateConversation = _ref3.onCreateConversation,
245
251
  _ref3$renderItem = _ref3.renderItem,
@@ -249,9 +255,12 @@ export function ChatList(_ref3) {
249
255
  _ref3$defaultCollapse = _ref3.defaultCollapsedGroupIds,
250
256
  defaultCollapsedGroupIds = _ref3$defaultCollapse === void 0 ? [] : _ref3$defaultCollapse,
251
257
  onCollapsedGroupIdsChange = _ref3.onCollapsedGroupIdsChange,
258
+ groupPageSize = _ref3.groupPageSize,
259
+ _ref3$ungroupedPageSi = _ref3.ungroupedPageSize,
260
+ ungroupedPageSize = _ref3$ungroupedPageSi === void 0 ? false : _ref3$ungroupedPageSi,
252
261
  renderRestScroll = _ref3.renderRestScroll;
253
262
  var grouped = Array.isArray(groups);
254
- var resolvedPinnedTitle = pinnedSectionTitle !== undefined ? pinnedSectionTitle : grouped ? null : '置顶';
263
+ var resolvedPinnedTitle = pinnedSectionTitle !== undefined ? pinnedSectionTitle : '置顶';
255
264
  var filterTime = filterTimeProp !== null && filterTimeProp !== void 0 ? filterTimeProp : defaultFilterTime;
256
265
  var _useControllable = useControllable(filterTypeProp, defaultFilterType, onFilterTypeChange),
257
266
  _useControllable2 = _slicedToArray(_useControllable, 2),
@@ -335,7 +344,7 @@ export function ChatList(_ref3) {
335
344
  revealUngroupedRef.current = false;
336
345
  return;
337
346
  }
338
- var collapse = scroller.querySelector('[data-odn-chat-group-section] [data-odn-chat-group-collapse]');
347
+ var collapse = scroller.querySelector('[data-odn-chat-list-ungrouped] [data-odn-chat-group-collapse]');
339
348
  var finished = false;
340
349
  var timer = 0;
341
350
  var onEnd;
@@ -376,37 +385,34 @@ export function ChatList(_ref3) {
376
385
  });
377
386
  }, [items, filterTime, filterType, filterStatus, highlightQuery]);
378
387
  var pinnedItems = useMemo(function () {
379
- return filteredItems.filter(function (item) {
380
- return item.pinned;
381
- });
388
+ return filteredItems.filter(isPinnedItem);
382
389
  }, [filteredItems]);
383
390
  var restItems = useMemo(function () {
384
391
  return filteredItems.filter(function (item) {
385
- return !item.pinned;
392
+ return !isPinnedItem(item);
386
393
  });
387
394
  }, [filteredItems]);
388
395
  var _useMemo = useMemo(function () {
389
- var _system$title;
396
+ var _system$title, _pinnedMeta$title;
390
397
  var list = groups !== null && groups !== void 0 ? groups : [];
391
398
  var system = list.find(function (g) {
392
399
  return g.id === ungroupedGroupId;
393
400
  });
394
- var custom = list.filter(function (g) {
395
- return g.id !== ungroupedGroupId;
396
- });
397
- var pinned = custom.filter(function (g) {
398
- return g.pinned;
401
+ var pinnedMeta = list.find(function (g) {
402
+ return g.id === CHAT_LIST_PINNED_ID;
399
403
  });
400
- var rest = custom.filter(function (g) {
401
- return !g.pinned;
404
+ var custom = list.filter(function (g) {
405
+ return g.id !== ungroupedGroupId && g.id !== CHAT_LIST_PINNED_ID;
402
406
  });
403
407
  return {
404
- customGroups: [].concat(_toConsumableArray(pinned), _toConsumableArray(rest)),
405
- resolvedUngroupedTitle: (_system$title = system === null || system === void 0 ? void 0 : system.title) !== null && _system$title !== void 0 ? _system$title : ungroupedTitle
408
+ customGroups: custom,
409
+ resolvedUngroupedTitle: (_system$title = system === null || system === void 0 ? void 0 : system.title) !== null && _system$title !== void 0 ? _system$title : ungroupedTitle,
410
+ pinnedGroupTitle: resolvedPinnedTitle === null || resolvedPinnedTitle === undefined ? (_pinnedMeta$title = pinnedMeta === null || pinnedMeta === void 0 ? void 0 : pinnedMeta.title) !== null && _pinnedMeta$title !== void 0 ? _pinnedMeta$title : '置顶' : resolvedPinnedTitle
406
411
  };
407
- }, [groups, ungroupedGroupId, ungroupedTitle]),
412
+ }, [groups, ungroupedGroupId, ungroupedTitle, resolvedPinnedTitle]),
408
413
  customGroups = _useMemo.customGroups,
409
- resolvedUngroupedTitle = _useMemo.resolvedUngroupedTitle;
414
+ resolvedUngroupedTitle = _useMemo.resolvedUngroupedTitle,
415
+ pinnedGroupTitle = _useMemo.pinnedGroupTitle;
410
416
  var groupOptions = useMemo(function () {
411
417
  return [].concat(_toConsumableArray(customGroups.map(function (g) {
412
418
  return {
@@ -418,6 +424,10 @@ export function ChatList(_ref3) {
418
424
  title: resolvedUngroupedTitle
419
425
  }]);
420
426
  }, [customGroups, ungroupedGroupId, resolvedUngroupedTitle]);
427
+ var emitMoveToGroup = useCallback(function (id, groupId) {
428
+ if (groupId === CHAT_LIST_PINNED_ID) return;
429
+ onMoveToGroup === null || onMoveToGroup === void 0 || onMoveToGroup(id, groupId);
430
+ }, [onMoveToGroup]);
421
431
  var isSearching = Boolean(highlightQuery);
422
432
  var customSections = useMemo(function () {
423
433
  return customGroups.map(function (group) {
@@ -425,7 +435,7 @@ export function ChatList(_ref3) {
425
435
  group: group,
426
436
  items: filteredItems.filter(function (item) {
427
437
  var _item$groupId;
428
- return !item.pinned && ((_item$groupId = item.groupId) !== null && _item$groupId !== void 0 ? _item$groupId : '') === group.id;
438
+ return !isPinnedItem(item) && ((_item$groupId = item.groupId) !== null && _item$groupId !== void 0 ? _item$groupId : '') === group.id;
429
439
  })
430
440
  };
431
441
  }).filter(function (section) {
@@ -435,10 +445,35 @@ export function ChatList(_ref3) {
435
445
  var ungroupedItems = useMemo(function () {
436
446
  return filteredItems.filter(function (item) {
437
447
  var _item$groupId2;
438
- return !item.pinned && ((_item$groupId2 = item.groupId) !== null && _item$groupId2 !== void 0 ? _item$groupId2 : ungroupedGroupId) === ungroupedGroupId;
448
+ return !isPinnedItem(item) && ((_item$groupId2 = item.groupId) !== null && _item$groupId2 !== void 0 ? _item$groupId2 : ungroupedGroupId) === ungroupedGroupId;
439
449
  });
440
450
  }, [filteredItems, ungroupedGroupId]);
441
451
  var showUngrouped = grouped ? isSearching ? ungroupedItems.length > 0 : showEmptyGroups || ungroupedItems.length > 0 : false;
452
+ var showPinnedGroup = grouped && pinnedItems.length > 0;
453
+ var ungroupedCollapsed = collapsedIds.includes(ungroupedGroupId);
454
+ var ungroupedPage = grouped && !isSearching && ungroupedPageSize !== false ? Math.max(1, ungroupedPageSize) : Number.POSITIVE_INFINITY;
455
+ var _useState13 = useState(ungroupedPage),
456
+ _useState14 = _slicedToArray(_useState13, 2),
457
+ ungroupedVisible = _useState14[0],
458
+ setUngroupedVisible = _useState14[1];
459
+ useLayoutEffect(function () {
460
+ setUngroupedVisible(Number.isFinite(ungroupedPage) ? ungroupedPage : Number.POSITIVE_INFINITY);
461
+ }, [ungroupedPage, ungroupedCollapsed]);
462
+ var loadMoreUngrouped = useCallback(function () {
463
+ if (!Number.isFinite(ungroupedPage) || ungroupedCollapsed) return;
464
+ var el = listBodyRef.current;
465
+ if (!el || el.clientHeight <= 0) return;
466
+ var remaining = el.scrollHeight - el.scrollTop - el.clientHeight;
467
+ var needMore = remaining < UNGROUPED_SCROLL_LOAD_PX || el.scrollHeight <= el.clientHeight + 1;
468
+ if (!needMore) return;
469
+ setUngroupedVisible(function (visible) {
470
+ if (visible >= ungroupedItems.length) return visible;
471
+ return Math.min(visible + ungroupedPage, ungroupedItems.length);
472
+ });
473
+ }, [ungroupedPage, ungroupedCollapsed, ungroupedItems.length]);
474
+ useLayoutEffect(function () {
475
+ loadMoreUngrouped();
476
+ }, [loadMoreUngrouped, ungroupedVisible]);
442
477
  var filterMenu = useMemo(function () {
443
478
  return buildFilterMenu(filterType, filterStatus);
444
479
  }, [filterType, filterStatus]);
@@ -451,16 +486,16 @@ export function ChatList(_ref3) {
451
486
  active: item.active,
452
487
  type: item.type,
453
488
  status: item.status,
454
- pinned: item.pinned,
489
+ pinned: grouped ? isPinnedItem(item) : item.pinned,
455
490
  time: item.time,
456
- groupId: grouped ? (_item$groupId3 = item.groupId) !== null && _item$groupId3 !== void 0 ? _item$groupId3 : ungroupedGroupId : item.groupId,
491
+ groupId: grouped ? isPinnedItem(item) ? CHAT_LIST_PINNED_ID : (_item$groupId3 = item.groupId) !== null && _item$groupId3 !== void 0 ? _item$groupId3 : ungroupedGroupId : item.groupId,
457
492
  groupOptions: grouped ? groupOptions : undefined,
458
493
  highlightQuery: highlightQuery || undefined,
459
494
  actionMenuZIndex: actionMenuZIndex,
460
495
  onClick: onItemClick,
461
496
  onRename: onRename,
462
497
  onPin: onPin,
463
- onMoveToGroup: grouped ? onMoveToGroup : undefined,
498
+ onMoveToGroup: grouped ? emitMoveToGroup : undefined,
464
499
  onDelete: onDelete
465
500
  });
466
501
  return renderItem(item, node);
@@ -493,10 +528,6 @@ export function ChatList(_ref3) {
493
528
  value: 'rename',
494
529
  label: '修改分组名称',
495
530
  icon: 'pencil-line'
496
- }] : []), _toConsumableArray(onPinGroup ? [{
497
- value: 'pin',
498
- label: group.pinned ? '取消置顶分组' : '置顶分组',
499
- icon: 'arrow-up-to-line'
500
531
  }] : []), _toConsumableArray(onDeleteGroup ? [{
501
532
  value: 'delete',
502
533
  label: '删除分组',
@@ -520,11 +551,6 @@ export function ChatList(_ref3) {
520
551
  setGroupMenuOpenId(null);
521
552
  return;
522
553
  }
523
- if (value === 'pin') {
524
- onPinGroup === null || onPinGroup === void 0 || onPinGroup(group.id, !group.pinned);
525
- setGroupMenuOpenId(null);
526
- return;
527
- }
528
554
  if (value === 'delete') {
529
555
  setDeletingGroup(group);
530
556
  setGroupMenuOpenId(null);
@@ -600,7 +626,10 @@ export function ChatList(_ref3) {
600
626
  ref: listBodyRef,
601
627
  "data-odn-chat-list-body": true,
602
628
  "data-odn-hover-fill-share": grouped || undefined,
603
- onScroll: updateEdgeFade
629
+ onScroll: function onScroll() {
630
+ updateEdgeFade();
631
+ loadMoreUngrouped();
632
+ }
604
633
  }, children), /*#__PURE__*/React.createElement("div", {
605
634
  "aria-hidden": true,
606
635
  "data-odn-chat-list-fade": "top",
@@ -617,7 +646,11 @@ export function ChatList(_ref3) {
617
646
  return;
618
647
  }
619
648
  updateEdgeFade();
620
- var ro = new ResizeObserver(updateEdgeFade);
649
+ loadMoreUngrouped();
650
+ var ro = new ResizeObserver(function () {
651
+ updateEdgeFade();
652
+ loadMoreUngrouped();
653
+ });
621
654
  ro.observe(el);
622
655
  if (el.firstElementChild) {
623
656
  ro.observe(el.firstElementChild);
@@ -625,7 +658,7 @@ export function ChatList(_ref3) {
625
658
  return function () {
626
659
  return ro.disconnect();
627
660
  };
628
- }, [updateEdgeFade, grouped, hasGroupedContent, hasLeanContent]);
661
+ }, [updateEdgeFade, loadMoreUngrouped, grouped, hasGroupedContent, hasLeanContent]);
629
662
  var pinnedSection = /*#__PURE__*/React.createElement(PresenceSlots, {
630
663
  host: function host(content, occupied) {
631
664
  return occupied ? /*#__PURE__*/React.createElement("div", {
@@ -719,7 +752,21 @@ export function ChatList(_ref3) {
719
752
  "data-odn-chat-list-toolbar-actions": true
720
753
  }, filterTrigger)), grouped ? !hasGroupedContent ? /*#__PURE__*/React.createElement("div", {
721
754
  "data-odn-chat-list-empty": true
722
- }, empty !== null && empty !== void 0 ? empty : null) : renderListScroll( /*#__PURE__*/React.createElement(React.Fragment, null, pinnedSection, showGroupsModule ? /*#__PURE__*/React.createElement("div", {
755
+ }, empty !== null && empty !== void 0 ? empty : null) : renderListScroll( /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(PresenceSlots, {
756
+ emptyKind: "shift"
757
+ }, showPinnedGroup ? /*#__PURE__*/React.createElement(ChatGroup, {
758
+ key: CHAT_LIST_PINNED_ID,
759
+ "data-odn-chat-list-pinned": true,
760
+ title: pinnedGroupTitle,
761
+ section: true,
762
+ collapsed: collapsedIds.includes(CHAT_LIST_PINNED_ID),
763
+ onCollapsedChange: function onCollapsedChange(next) {
764
+ setCollapsedIds(next ? Array.from(new Set([].concat(_toConsumableArray(collapsedIds), [CHAT_LIST_PINNED_ID]))) : collapsedIds.filter(function (id) {
765
+ return id !== CHAT_LIST_PINNED_ID;
766
+ }));
767
+ },
768
+ previewLimit: false
769
+ }, pinnedItems.map(renderChatItem)) : null), showGroupsModule ? /*#__PURE__*/React.createElement("div", {
723
770
  "data-odn-chat-list-groups": true
724
771
  }, /*#__PURE__*/React.createElement("div", {
725
772
  "data-odn-chat-list-section-header": true
@@ -759,15 +806,16 @@ export function ChatList(_ref3) {
759
806
  } : undefined,
760
807
  extraVisible: menuOpen,
761
808
  extra: renderGroupActions(group, {
762
- showMenu: Boolean(onRenameGroup || onPinGroup || onDeleteGroup),
809
+ showMenu: Boolean(onRenameGroup || onDeleteGroup),
763
810
  showAddConversation: Boolean(onCreateConversation)
764
811
  }),
765
- previewLimit: isSearching ? false : undefined
812
+ previewLimit: isSearching ? false : groupPageSize
766
813
  }, sectionItems.map(renderChatItem));
767
814
  }))) : null, showUngrouped ? /*#__PURE__*/React.createElement(ChatGroup, {
815
+ "data-odn-chat-list-ungrouped": true,
768
816
  title: resolvedUngroupedTitle,
769
817
  section: true,
770
- collapsed: collapsedIds.includes(ungroupedGroupId),
818
+ collapsed: ungroupedCollapsed,
771
819
  onCollapsedChange: function onCollapsedChange(next) {
772
820
  revealUngroupedRef.current = !next;
773
821
  setCollapsedIds(next ? Array.from(new Set([].concat(_toConsumableArray(collapsedIds), [ungroupedGroupId]))) : collapsedIds.filter(function (id) {
@@ -781,7 +829,8 @@ export function ChatList(_ref3) {
781
829
  }, {
782
830
  showAddConversation: Boolean(onCreateConversation)
783
831
  }),
784
- previewLimit: false
832
+ previewLimit: isSearching || !Number.isFinite(ungroupedVisible) ? false : ungroupedVisible,
833
+ overflowHint: false
785
834
  }, ungroupedItems.map(renderChatItem)) : null)) : !hasLeanContent ? /*#__PURE__*/React.createElement("div", {
786
835
  "data-odn-chat-list-empty": true
787
836
  }, empty !== null && empty !== void 0 ? empty : null) : renderRestScroll ? /*#__PURE__*/React.createElement(React.Fragment, null, pinnedSection, restSection ? renderRestScroll(restSection) : null) : renderListScroll( /*#__PURE__*/React.createElement(React.Fragment, null, pinnedSection, restSection)), /*#__PURE__*/React.createElement(Dialog, {
package/dist/index.d.ts CHANGED
@@ -59,7 +59,7 @@ export { ARTIFACT_DEDICATED_FILE_ICON_TYPES, ARTIFACT_FILE_GLOW_COLORS, ARTIFACT
59
59
  export { default as Attachments, type AttachmentsProps } from './attachments';
60
60
  export { CHAT_GROUP_PREVIEW_LIMIT, default as ChatGroup, type ChatGroupProps, } from './chat-group';
61
61
  export { default as ChatItem, formatRelativeTime, type ChatItemGroupOption, type ChatItemProps, } from './chat-item';
62
- export { CHAT_LIST_UNGROUPED_ID, default as ChatList, matchChatListFilterStatus, matchChatListFilterTime, matchChatListFilterType, type ChatListFilterStatus, type ChatListFilterTime, type ChatListFilterType, type ChatListGroup, type ChatListItem, type ChatListProps, } from './chat-list';
62
+ export { CHAT_LIST_PINNED_ID, CHAT_LIST_UNGROUPED_ID, default as ChatList, matchChatListFilterStatus, matchChatListFilterTime, matchChatListFilterType, type ChatListFilterStatus, type ChatListFilterTime, type ChatListFilterType, type ChatListGroup, type ChatListItem, type ChatListProps, } from './chat-list';
63
63
  export { CLARIFY_DOCK_MAX_OPTIONS, CLARIFY_DOCK_SKIPPED_LABEL, CLARIFY_DOCK_SKIPPED_VALUE, ClarifyDockPanel, ClarifyDockTrigger, clarifyDockLayoutId, parseClarifyBoundDate, toISODate, useClarifyDockState, type ClarifyDockAnswerItem, type ClarifyDockHostMessage, type ClarifyDockMessageState, type ClarifyDockPanelProps, type ClarifyDockQuestion, type ClarifyDockResolvePayload, type ClarifyDockSpec, type ClarifyDockState, type ClarifyDockTriggerProps, type ClarifyInput, type ClarifyOption, } from './clarify-dock';
64
64
  export { default as CodeBlock, type CodeBlockProps } from './code-block';
65
65
  export { default as Composer, type ComposerProps, type ComposerRef, type ComposerSkillSlotProps, type ComposerTool, type ComposerToolConfig, } from './composer';
package/dist/index.js CHANGED
@@ -65,7 +65,7 @@ export { ARTIFACT_DEDICATED_FILE_ICON_TYPES, ARTIFACT_FILE_GLOW_COLORS, ARTIFACT
65
65
  export { default as Attachments } from "./attachments";
66
66
  export { CHAT_GROUP_PREVIEW_LIMIT, default as ChatGroup } from "./chat-group";
67
67
  export { default as ChatItem, formatRelativeTime } from "./chat-item";
68
- export { CHAT_LIST_UNGROUPED_ID, default as ChatList, matchChatListFilterStatus, matchChatListFilterTime, matchChatListFilterType } from "./chat-list";
68
+ export { CHAT_LIST_PINNED_ID, CHAT_LIST_UNGROUPED_ID, default as ChatList, matchChatListFilterStatus, matchChatListFilterTime, matchChatListFilterType } from "./chat-list";
69
69
  export { CLARIFY_DOCK_MAX_OPTIONS, CLARIFY_DOCK_SKIPPED_LABEL, CLARIFY_DOCK_SKIPPED_VALUE, ClarifyDockPanel, ClarifyDockTrigger, clarifyDockLayoutId, parseClarifyBoundDate, toISODate, useClarifyDockState } from "./clarify-dock";
70
70
  export { default as CodeBlock } from "./code-block";
71
71
  export { default as Composer } from "./composer";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "one-design-next",
3
- "version": "0.0.98",
3
+ "version": "0.0.100",
4
4
  "description": "One Design Next from TAD@tencent.com",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -16,7 +16,7 @@
16
16
  "build": "father build && npm run build:styles",
17
17
  "build:benchmark": "node scripts/build-benchmark.mjs",
18
18
  "build:feedback": "node scripts/build-feedback.mjs",
19
- "build:skills": "bash skills/build.sh",
19
+ "build:skills": "test -f skills/odn-design-ruyi/SKILL.md",
20
20
  "build:styles": "node scripts/build-styles.js",
21
21
  "build:variables": "node scripts/build-variables.js",
22
22
  "build:watch": "father dev",