knight-ui 1.2.6 → 1.2.7
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 +56 -8
- package/component/plus/AttachmentPickerGroup.d.ts +29 -0
- package/component/plus/index.d.ts +2 -0
- package/index.js +1 -1
- package/index.js.map +1 -1
- package/package.json +1 -1
- package/tokens/tokens.css +15 -0
package/README.md
CHANGED
|
@@ -84,6 +84,7 @@
|
|
|
84
84
|
- [Feedback 反馈组件](#feedback-反馈组件)
|
|
85
85
|
- Plus 增强
|
|
86
86
|
- [AttachmentPicker 附件选择器](#attachmentpicker-附件选择器)
|
|
87
|
+
- [AttachmentPickerGroup 附件按钮组](#attachmentpickergroup-附件按钮组)
|
|
87
88
|
- [Markdown](#markdown)
|
|
88
89
|
- [CodeEditor 代码编辑器](#codeeditor-代码编辑器)
|
|
89
90
|
- [CodeHighlight 代码高亮](#codehighlight-代码高亮)
|
|
@@ -479,6 +480,7 @@ Token(`--ku-color-glass` / `-strong` / `-head`、`--ku-blur-base` … `-xl`)
|
|
|
479
480
|
| `ku-focus-ring` | 键盘焦点环(见下) | `Button` / `FloatButton` 原先内联 `outline: none` 关掉了 UA 环又不给替代,现已移除该内联——不删内联,挂类也出不来环 |
|
|
480
481
|
| `ku-modal-close` | 关闭按钮的悬停底 | 内联没写 `background-color`(`color` 被内联占了,所以悬停只改底、不改色) |
|
|
481
482
|
| `ku-modal-btn-primary` | 主按钮的悬停 / 按下 | 底色内联恒有值(还要随 `confirmLoading` 变),只有 `filter` 没被内联占住 |
|
|
483
|
+
| `ku-attachment-picker` | 悬停提亮(`color`)与过渡 | 内联只给了 `display` / `cursor` / 禁用态三件,`color` 与 `transition` 都没写——**基色**刻意留在外面(调用方或 `AttachmentPickerGroup` 给),这条只管悬停那一档 |
|
|
482
484
|
|
|
483
485
|
推论:**给这些组件挂自定义类去改悬停色是无效的**,得改组件内联里的那一份,或把该属性从内联挪进类。
|
|
484
486
|
|
|
@@ -2634,6 +2636,50 @@ import { AttachmentPicker, IconPaperclip } from "knight-ui";
|
|
|
2634
2636
|
>
|
|
2635
2637
|
> 每次 `change` 之后会把 input 的值置空,否则**连续选同一个文件不会再触发 change** —— 浏览器只在值发生变化时才派发事件。
|
|
2636
2638
|
|
|
2639
|
+
> **"图片口 + 文件口"这对常见组合,直接用 [AttachmentPickerGroup](#attachmentpickergroup-附件按钮组)** —— 它就是把两个 `AttachmentPicker` 加两枚图标收成一件,并且保证两条口共用同一份名额。**只有当两条口需要不同的 `accept` / `maxSize` 时**(比如文件口单独限 1MB),才需要像上面这样自己拼两个。
|
|
2640
|
+
|
|
2641
|
+
### AttachmentPickerGroup 附件按钮组
|
|
2642
|
+
|
|
2643
|
+
图片口 + 文件口两枚按钮,直接塞进 `AIChatInput` 的 `toolbar` 插槽:
|
|
2644
|
+
|
|
2645
|
+
```tsx
|
|
2646
|
+
import { AIChatInput, AttachmentPickerGroup, Toast } from "knight-ui";
|
|
2647
|
+
|
|
2648
|
+
<AIChatInput
|
|
2649
|
+
attachments={attachments}
|
|
2650
|
+
toolbar={
|
|
2651
|
+
<AttachmentPickerGroup
|
|
2652
|
+
accept="image/*,.pdf"
|
|
2653
|
+
count={attachments.length}
|
|
2654
|
+
maxCount={4}
|
|
2655
|
+
maxSize={5 * 1024 * 1024}
|
|
2656
|
+
onPick={(files) => addFiles(files)}
|
|
2657
|
+
onValidateFail={(reason) => Toast.warning(reason.message)}
|
|
2658
|
+
/>
|
|
2659
|
+
}
|
|
2660
|
+
/>
|
|
2661
|
+
```
|
|
2662
|
+
|
|
2663
|
+
| 属性 | 类型 | 默认值 | 说明 |
|
|
2664
|
+
|---|---|---|---|
|
|
2665
|
+
| `accept` | `string` | — | **文件口**的可收类型(`<input accept>` 同语法)。图片口固定 `image/*` |
|
|
2666
|
+
| `multiple` | `boolean` | `true` | 是否多选 |
|
|
2667
|
+
| `maxCount` | `number` | — | 数量上限,与 `count` 一起算剩余名额 |
|
|
2668
|
+
| `maxSize` | `number` | — | 单个文件字节上限 |
|
|
2669
|
+
| `count` | `number` | — | 已有数量 —— 由调用方喂当前附件数 |
|
|
2670
|
+
| `onPick` | `(files: Array<File>) => void` | — | 通过校验的文件 |
|
|
2671
|
+
| `onValidateFail` | `(reason: RejectReason) => void` | — | 未通过校验,见 [AttachmentUtility](#attachmentutility-附件工具) |
|
|
2672
|
+
| `imageIcon` / `fileIcon` | `React.ReactNode` | `<IconImage/>` / `<IconPaperclip/>`(均 `size="small"`) | 两枚按钮的图标 |
|
|
2673
|
+
| `imageTip` / `fileTip` | `React.ReactNode` | `"添加图片"` / `"添加文件"` | 悬停气泡文案,传空则不挂气泡 |
|
|
2674
|
+
| `disabled` | `boolean` | — | 禁用 —— 由两个选择器各自走 `pointerEvents:none` + `opacity:.5`,容器不再叠一层 |
|
|
2675
|
+
| `style` / `className` | — | — | 作用在容器上(容器另挂 `ku-attachment-picker-group`),换色档走这里 |
|
|
2676
|
+
|
|
2677
|
+
> **补的是 `AIChatInput` 留出的那块空**:库把 `toolbar` / `toolbarEnd` 插槽交给调用方,却没收"附件按钮"这一件,于是每个用它的地方都得自己抄一遍两个 `AttachmentPicker` + 两枚 `Icon` + 色档 + 提示文案。
|
|
2678
|
+
>
|
|
2679
|
+
> **两条口共用一份名额**:`multiple` / `maxCount` / `maxSize` / `count` / `onPick` / `onValidateFail` 原样下沉给两个选择器 —— 各算各的 `count` 会突破 `maxCount`。这与单独用 `AttachmentPicker` 时的注意事项是同一条,见上一节。
|
|
2680
|
+
>
|
|
2681
|
+
> **色档定在容器上**:容器给 `color: var(--ku-color-text-2)`,两枚图标靠 `currentColor` 继承,随明暗主题走;要别的档位传 `style={{ color: ... }}` 覆盖。悬停那一档由 `.ku-attachment-picker:hover` 提到 `--ku-color-text-0`,见「类规则与内联样式」。
|
|
2682
|
+
|
|
2637
2683
|
### Markdown
|
|
2638
2684
|
|
|
2639
2685
|
```tsx
|
|
@@ -2979,7 +3025,7 @@ import { AIChatInput } from "knight-ui";
|
|
|
2979
3025
|
多模态(工具栏放附件按钮,附件状态由调用方持有):
|
|
2980
3026
|
|
|
2981
3027
|
```tsx
|
|
2982
|
-
import { AIChatInput,
|
|
3028
|
+
import { AIChatInput, AttachmentPickerGroup, AttachmentUtility, Toast } from "knight-ui";
|
|
2983
3029
|
|
|
2984
3030
|
const [attachments, setAttachments] = React.useState<Attachment[]>([]);
|
|
2985
3031
|
|
|
@@ -2988,15 +3034,15 @@ const addFiles = (files: Array<File>) =>
|
|
|
2988
3034
|
|
|
2989
3035
|
<AIChatInput
|
|
2990
3036
|
attachments={attachments}
|
|
2991
|
-
accept="image
|
|
3037
|
+
accept="image/*,.pdf"
|
|
2992
3038
|
maxCount={4}
|
|
2993
3039
|
toolbar={
|
|
2994
|
-
<
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
3040
|
+
<AttachmentPickerGroup
|
|
3041
|
+
accept="image/*,.pdf"
|
|
3042
|
+
count={attachments.length} maxCount={4}
|
|
3043
|
+
onPick={addFiles} onValidateFail={(r) => Toast.warning(r.message)}
|
|
3044
|
+
/>
|
|
2998
3045
|
}
|
|
2999
|
-
toolbarEnd={<McpSelect servers={servers} />}
|
|
3000
3046
|
onAddAttachments={addFiles}
|
|
3001
3047
|
onRemoveAttachment={(a) => setAttachments((prev) => prev.filter((item) => item !== a))}
|
|
3002
3048
|
onSend={(text, files) => {
|
|
@@ -3007,6 +3053,8 @@ const addFiles = (files: Array<File>) =>
|
|
|
3007
3053
|
/>
|
|
3008
3054
|
```
|
|
3009
3055
|
|
|
3056
|
+
> `toolbar` / `toolbarEnd` 是**两个通用插槽**,库里不认识里面是什么。左端这个示例用了现成的 `AttachmentPickerGroup`(见 [附件按钮组](#attachmentpickergroup-附件按钮组));**两条口需要不同的 `accept` / `maxSize` 时就自己拼两个 `AttachmentPicker`**,见 [附件选择器](#attachmentpicker-附件选择器)。右端同理 —— 想放状态徽标、模型选择、发送设置都行,库不预设。
|
|
3057
|
+
|
|
3010
3058
|
**`Attachment` 字段**:
|
|
3011
3059
|
|
|
3012
3060
|
| 字段 | 类型 | 说明 |
|
|
@@ -3020,7 +3068,7 @@ const addFiles = (files: Array<File>) =>
|
|
|
3020
3068
|
| `url` | `string` | 远程地址(已有附件回填时用) |
|
|
3021
3069
|
| `file` | `File` | 原始文件 —— **编码责任在上层** |
|
|
3022
3070
|
|
|
3023
|
-
> **三种添加入口**:点 `toolbar`
|
|
3071
|
+
> **三种添加入口**:点 `toolbar` 里的附件按钮(`AttachmentPickerGroup`,或自己拼的 `AttachmentPicker`)、直接 `Ctrl+V` 粘贴、把文件拖到输入框上。三条都按同一套规则校验(`accept` / `maxSize` / `maxCount`,都走 `AttachmentUtility.filter`),但**回调分两个口子**:选择器那条从 `AttachmentPicker.onPick` 出来(`AttachmentPickerGroup` 原样透传,只是把两枚按钮的口并到同一个 `onPick`),粘贴与拖拽那条从 `AIChatInput.onAddAttachments` 出来 —— 因为选择器是独立组件、有自己的 `<input>`,它的 change 不经过 `AIChatInput`。示例里两个口子都接到同一个 `addFiles`,所以不会重复添加;选择器那边记得把当前附件数喂给它的 `count`,否则两条入口各自算名额,`maxCount` 会被突破。粘贴时**以文件为准**:截图粘贴是主场景,剪贴板里顺带的文字与地址不会被一并塞进正文。
|
|
3024
3072
|
>
|
|
3025
3073
|
> 拖拽悬停时输入框上会盖一层磨砂遮罩并提示「拖拽可添加附件」,此时描边也换成主色。遮罩走 `.ku-mask`(遮罩档:压暗 34% + `blur(10px) saturate(120%)`),不用玻璃档 —— 带颗粒的 `ku-glass-overlay` / `-tip` / `-menu` **三档叠的是同一颗 `--ku-glass-grain`**,铺满整块输入框时颗粒感明显;遮罩档不含颗粒,且"压暗"本就是遮罩要表达的语义。
|
|
3026
3074
|
>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { AbstractUI, type UIState } from "../../base";
|
|
3
|
+
import { type AttachmentPickerProps } from "./AttachmentPicker";
|
|
4
|
+
/**
|
|
5
|
+
* 附件按钮组 — 图片口 + 文件口两枚按钮
|
|
6
|
+
* - 补的是 AIChatInput 留出的那块空: 库把 toolbar 插槽交给调用方, 却没收"附件按钮"这一件,
|
|
7
|
+
* 于是每个用它的地方都得自己抄一遍两个 AttachmentPicker + 两枚 Icon + 色档 + 提示文案
|
|
8
|
+
* - 两枚按钮共用同一份限制与同一个 count —— 各算各的名额会突破 maxCount
|
|
9
|
+
* - 色档定在容器上, 两枚 Icon 靠 currentColor 继承; 需要别的档位用 style 覆盖
|
|
10
|
+
* - 禁用态不在这里处理: disabled 随 pickerProps 下沉, 由两个选择器各自走 pointerEvents + opacity,
|
|
11
|
+
* 这层再叠一次会变成 0.25
|
|
12
|
+
*/
|
|
13
|
+
export declare class AttachmentPickerGroup extends AbstractUI<AttachmentPickerGroupProps, UIState> {
|
|
14
|
+
/** 提示文案为空时不挂气泡 —— 气泡是可选的一层, 不是这枚按钮的定义 */
|
|
15
|
+
private renderPicker;
|
|
16
|
+
protected OnRenderContent(): React.ReactElement;
|
|
17
|
+
}
|
|
18
|
+
export interface AttachmentPickerGroupProps extends Omit<AttachmentPickerProps, "accept" | "children"> {
|
|
19
|
+
/** 文件口的可收类型(<input accept> 同语法, 如 "image/*,.pdf"); 图片口固定 image/* */
|
|
20
|
+
accept?: string;
|
|
21
|
+
/** 图片口图标 @default <IconImage size="small" /> */
|
|
22
|
+
imageIcon?: React.ReactNode;
|
|
23
|
+
/** 文件口图标 @default <IconPaperclip size="small" /> */
|
|
24
|
+
fileIcon?: React.ReactNode;
|
|
25
|
+
/** 图片口悬停提示, 传空则不挂气泡 @default "添加图片" */
|
|
26
|
+
imageTip?: React.ReactNode;
|
|
27
|
+
/** 文件口悬停提示, 传空则不挂气泡 @default "添加文件" */
|
|
28
|
+
fileTip?: React.ReactNode;
|
|
29
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { AttachmentPicker } from "./AttachmentPicker";
|
|
2
2
|
export type { AttachmentPickerProps } from "./AttachmentPicker";
|
|
3
|
+
export { AttachmentPickerGroup } from "./AttachmentPickerGroup";
|
|
4
|
+
export type { AttachmentPickerGroupProps } from "./AttachmentPickerGroup";
|
|
3
5
|
export { AudioPlayer } from "./AudioPlayer";
|
|
4
6
|
export type { AudioPlayerProps } from "./AudioPlayer";
|
|
5
7
|
export { Chat } from "./Chat";
|