knight-ui 1.1.2 → 1.1.4

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.
@@ -44,6 +44,10 @@ export declare class AIChatDialogue extends AbstractUI<AIChatDialogueProps, AICh
44
44
  private scrollToBottom;
45
45
  private getMsgKey;
46
46
  private isEmptyNode;
47
+ /** 正文区是否正处于流式输出(有内容)—— 内容活跃时思考区不再显示等待指示,保证同一时刻只有一个指示 */
48
+ private isStreamingContent;
49
+ /** “思考中”流式指示:纯思考/工具执行阶段正文尚未流出时,以动效文字替代孤立光标 */
50
+ private renderThinkingIndicator;
47
51
  /** 思考显示开关:showThinking !== false 时显示思考块 */
48
52
  private thinkingEnabled;
49
53
  /** 是否正处于"只吐思考、还没出正文"的流式阶段 */
@@ -60,6 +64,16 @@ export declare class AIChatDialogue extends AbstractUI<AIChatDialogueProps, AICh
60
64
  private syncThinkingClock;
61
65
  private renderThinkingBlock;
62
66
  private renderAvatar;
67
+ /**
68
+ * 点击"复制":先跑调用方的 onCopy,再弹 Toast 提示。
69
+ *
70
+ * 组件自己不碰剪贴板 —— content 是 ReactNode,可能是节点而不是文本,"该拷什么"只有调用方
71
+ * 说得清(演示里就是 String(msg.content)),所以复制动作始终由 onCopy 完成,这里只负责反馈。
72
+ * 也正因为如此,提示是"复制动作已发出",不是组件核实过的结果:onCopy 若是异步写入且被浏览器
73
+ * 拒绝,这里无从得知(要真结果就得把组件改成自己写剪贴板,并把 content 收窄成 string)。
74
+ * 传 copyTip={false} 可关掉提示,把反馈交回调用方。
75
+ */
76
+ private handleCopy;
63
77
  private renderActionBtn;
64
78
  protected OnRenderContent(): React.ReactElement;
65
79
  }
@@ -73,6 +87,8 @@ export interface AIChatDialogueProps extends UIProps {
73
87
  showAvatar?: boolean;
74
88
  showNickname?: boolean;
75
89
  maxHeight?: number;
90
+ /** 是否给助手回复内容加边框(含内边距与圆角) @default false */
91
+ showAssistantBorder?: boolean;
76
92
  /** 是否显示"思考过程"块 @default true */
77
93
  showThinking?: boolean;
78
94
  /** 思考块标题 @default "思考过程" */
@@ -83,6 +99,8 @@ export interface AIChatDialogueProps extends UIProps {
83
99
  thinkingBodyMaxHeight?: number;
84
100
  emptyContent?: React.ReactNode;
85
101
  onCopy?: (msg: AIMessage) => void;
102
+ /** 点击复制后弹出的 Toast 文案;传 false 关闭提示,反馈由调用方自理 @default "已复制" */
103
+ copyTip?: React.ReactNode | false;
86
104
  onRetry?: (msg: AIMessage, index: number) => void;
87
105
  }
88
106
  interface AIChatDialogueState extends UIState {
@@ -33,7 +33,7 @@ export interface AIChatInputProps extends UIProps {
33
33
  disabled?: boolean;
34
34
  loading?: boolean;
35
35
  attachments?: Attachment[];
36
- /** 最小行数 @default 1 */
36
+ /** 最小(初始)行数 @default 2 */
37
37
  minRows?: number;
38
38
  /** 最大行数,超出后滚动 @default 4 */
39
39
  maxRows?: number;
@@ -10,8 +10,9 @@ export interface ButtonGroupProps extends UIProps {
10
10
  /**
11
11
  * 按钮。
12
12
  */
13
- export declare class Button extends AbstractUI<ButtonProps, UIState> {
13
+ export declare class Button extends AbstractUI<ButtonProps, ButtonState> {
14
14
  static Group: typeof ButtonGroup;
15
+ constructor(props: ButtonProps);
15
16
  /** 按钮聚焦 */
16
17
  focus(): void;
17
18
  /** 按钮失焦 */
@@ -21,6 +22,10 @@ export declare class Button extends AbstractUI<ButtonProps, UIState> {
21
22
  private getPadding;
22
23
  private getColorStyle;
23
24
  }
25
+ interface ButtonState extends UIState {
26
+ hovered: boolean;
27
+ pressed: boolean;
28
+ }
24
29
  export interface ButtonProps extends UIProps {
25
30
  type?: ButtonVariant;
26
31
  theme?: ButtonTheme;
@@ -1,6 +1,5 @@
1
1
  import React from "react";
2
2
  import { AbstractUI, type UIProps, type UIState } from "../../base";
3
- type CalendarMode = "month" | "year" | "day";
4
3
  /**
5
4
  * 日历。
6
5
  */
@@ -15,7 +14,6 @@ export declare class Calendar extends AbstractUI<CalendarProps, CalendarState> {
15
14
  }
16
15
  export interface CalendarProps extends UIProps {
17
16
  value?: Date;
18
- mode?: CalendarMode;
19
17
  isToday?: (date: Date) => boolean;
20
18
  disabledDate?: (date: Date) => boolean;
21
19
  onChange?: (date: Date) => void;
@@ -23,6 +21,5 @@ export interface CalendarProps extends UIProps {
23
21
  interface CalendarState extends UIState {
24
22
  viewDate: Date;
25
23
  value: Date;
26
- hoveredDate: Date | null;
27
24
  }
28
25
  export {};
@@ -35,7 +35,6 @@ export interface CropResult {
35
35
  export interface CropperProps extends UIProps {
36
36
  src?: string;
37
37
  cropShape?: CropShape;
38
- aspectRatio?: number;
39
38
  onCrop?: (result: CropResult) => void;
40
39
  }
41
40
  interface CropperState extends UIState {
@@ -1,33 +1,65 @@
1
1
  import React from "react";
2
2
  import { AbstractUI, type UIProps, type UIState } from "../../base";
3
+ export interface TableFilterOption {
4
+ text: string;
5
+ value: string;
6
+ }
3
7
  export interface TableColumn {
4
8
  key: string;
5
9
  title: React.ReactNode;
6
10
  dataIndex?: string;
7
11
  width?: number;
8
12
  render?: (value: any, record: Record<string, any>, index: number) => React.ReactNode;
9
- sorter?: (a: any, b: any) => number;
10
- filters?: {
11
- text: string;
12
- value: string;
13
- }[];
13
+ /**
14
+ * 排序比较函数。给了就在表头出排序按钮。
15
+ * 给 true 则"只出按钮、不排本地数据" —— 排序发生在服务端, 拿到 onChange 后自己重新取数。
16
+ * (入参是该列的取值, 不是整条记录)
17
+ */
18
+ sorter?: ((a: any, b: any) => number) | boolean;
19
+ /**
20
+ * 不想写比较函数时给 true —— 按该列的原始值排(数字按数值比, 其余按本地化字符串比)。
21
+ * 与 sorter 二选一, 同时给以 sorter 为准。
22
+ */
23
+ sortable?: boolean;
24
+ /** 初始排序方向。设了它, 这一列挂载时就是排好序的状态 */
25
+ defaultSortOrder?: "ascend" | "descend";
26
+ /** 表头筛选候选。给了就在表头出漏斗按钮 */
27
+ filters?: TableFilterOption[];
28
+ /** 判定某条记录是否命中某个筛选值。列内多选取"或", 跨列取"与" */
14
29
  onFilter?: (value: string, record: Record<string, any>) => boolean;
30
+ /** 是否多选, 默认 true; 给 false 走单选(等价于点一条即确定) */
31
+ filterMultiple?: boolean;
15
32
  }
16
33
  /**
17
34
  * 数据表格。
18
35
  */
19
36
  export declare class Table extends AbstractUI<TableProps, TableState> {
20
37
  private scrollEl;
38
+ private filterZIndex;
39
+ private filterOutsideHandler;
21
40
  constructor(props: TableProps);
41
+ componentWillUnmount(): void;
22
42
  private onScrollRef;
23
43
  private onScrollWheel;
24
44
  componentDidUpdate(prevProps: Readonly<TableProps>): void;
25
45
  protected OnRenderContent(): React.ReactElement;
26
46
  private sortData;
47
+ private filterData;
27
48
  private toggleSort;
49
+ private notifyChange;
50
+ private openFilter;
51
+ private closeFilter;
52
+ private toggleDraft;
53
+ private commitFilter;
54
+ private resetFilter;
55
+ private renderFilterPanel;
28
56
  private toggleSelectAll;
29
57
  private toggleSelectRow;
30
58
  }
59
+ export interface TableSorter {
60
+ key: string | null;
61
+ order: "ascend" | "descend" | null;
62
+ }
31
63
  export interface TableProps extends UIProps {
32
64
  columns?: TableColumn[];
33
65
  dataSource?: Record<string, any>[];
@@ -50,10 +82,23 @@ export interface TableProps extends UIProps {
50
82
  selectedRowKeys?: (string | number)[];
51
83
  defaultSelectedRowKeys?: (string | number)[];
52
84
  onRowSelectionChange?: (selectedRowKeys: (string | number)[]) => void;
85
+ /** 表头排序/筛选发生变化时回调(服务端分页时可据此重新取数) */
86
+ onChange?: (sorter: TableSorter, filters: Record<string, string[]>) => void;
53
87
  }
54
88
  interface TableState extends UIState {
55
89
  sortKey: string | null;
56
90
  sortOrder: string | null;
57
91
  selectedRowKeys: (string | number)[];
92
+ /** 已生效的筛选: 列 key → 选中的值 */
93
+ filters: Record<string, string[]>;
94
+ filterOpenKey: string | null;
95
+ filterRect: {
96
+ top: number;
97
+ bottom: number;
98
+ left: number;
99
+ right: number;
100
+ } | null;
101
+ /** 面板里的草稿, 点"确定"才写回 filters */
102
+ filterDraft: string[];
58
103
  }
59
104
  export {};
@@ -11,11 +11,24 @@ export interface GuideStep {
11
11
  */
12
12
  export declare class UserGuide extends AbstractUI<UserGuideProps, UserGuideState> {
13
13
  constructor(props: UserGuideProps);
14
+ componentDidMount(): void;
14
15
  componentDidUpdate(prevProps: Readonly<UserGuideProps>): void;
16
+ componentWillUnmount(): void;
17
+ private rafId;
18
+ private onViewportChange;
15
19
  private goTo;
16
20
  private handleSkip;
17
21
  private handleFinish;
18
22
  protected OnRenderContent(): React.ReactElement;
23
+ /** 量目标的视口矩形; 选择器为空或选不中时返回 null */
24
+ private measureTarget;
25
+ /**
26
+ * 卡片落位。有目标就贴着高亮环按 placement 摆, 没有目标就退回底部居中。
27
+ * 水平方向做视口夹取 —— 卡片固定 360 宽, 目标贴边时不做夹取会有一半在屏幕外。
28
+ * 竖直方向必须留"塞不塞得下"的判断: 引导卡片是整屏唯一的操作入口, 飞到视口外
29
+ * 就等于引导卡死(遮罩还在、点不掉、也看不到按钮), 宁可压住目标也不能出屏。
30
+ */
31
+ private calcCardPosition;
19
32
  }
20
33
  export interface UserGuideProps extends UIProps {
21
34
  steps?: GuideStep[];
@@ -39,7 +39,7 @@ export type { ScrollListProps } from "./ScrollList";
39
39
  export { SideSheet } from "./SideSheet";
40
40
  export type { SideSheetProps } from "./SideSheet";
41
41
  export { Table } from "./Table";
42
- export type { TableProps, TableColumn } from "./Table";
42
+ export type { TableProps, TableColumn, TableFilterOption, TableSorter } from "./Table";
43
43
  export { Timeline } from "./Timeline";
44
44
  export type { TimelineProps, TimelineItemProps } from "./Timeline";
45
45
  export { Tooltip } from "./Tooltip";
@@ -14,7 +14,11 @@ export declare class Banner extends AbstractUI<BannerProps, BannerState> {
14
14
  componentDidMount(): void;
15
15
  componentDidUpdate(prevProps: Readonly<BannerProps>): void;
16
16
  componentWillUnmount(): void;
17
+ /** 可关闭与否由 closable 明确控制, 不看有没有接 onClose —— 要不要能关是横幅自己的呈现属性,
18
+ * 和"关了要不要通知别人"是两回事: 有的横幅关掉不需要回调(纯粹自己消失即可)。 */
19
+ private get closable();
17
20
  private handleClose;
21
+ private closeSelf;
18
22
  protected OnRenderContent(): React.ReactElement;
19
23
  }
20
24
  export interface BannerProps extends UIProps {
@@ -26,6 +30,9 @@ export interface BannerProps extends UIProps {
26
30
  bordered?: boolean;
27
31
  fullMode?: boolean;
28
32
  actions?: React.ReactNode;
33
+ /** 是否可关闭(点击横幅主体关闭)。默认 true; 置 false 得到一条纯静态横幅: 光标不变手型, 点击无反应。 */
34
+ closable?: boolean;
35
+ /** 关闭回调。触发时机由 closable 决定, 与本回调是否传入无关。 */
29
36
  onClose?: () => void;
30
37
  }
31
38
  export {};
@@ -29,7 +29,7 @@ export declare class Popconfirm extends AbstractUI<PopconfirmProps, PopconfirmSt
29
29
  private handleCancel;
30
30
  private handleTriggerClick;
31
31
  protected OnRenderContent(): React.ReactElement;
32
- private arrowPosition;
32
+ private arrowStyle;
33
33
  }
34
34
  type PopconfirmAnimPhase = "entering" | "entered" | "leaving" | "exited";
35
35
  export interface PopconfirmProps extends UIProps {
@@ -35,6 +35,9 @@ export declare class ColorPicker extends AbstractUI<ColorPickerProps, ColorPicke
35
35
  constructor(props: ColorPickerProps);
36
36
  private resolveInitValue;
37
37
  componentDidUpdate(prevProps: Readonly<ColorPickerProps>): void;
38
+ componentDidMount(): void;
39
+ componentWillUnmount(): void;
40
+ private onClickOutside;
38
41
  private openEyeDropper;
39
42
  private startDrag;
40
43
  private onDocMouseMove;
@@ -1,4 +1,28 @@
1
1
  import React from "react";
2
+ /**
3
+ * 把一张 mask 图写成遮罩声明: 铺满整块、不平铺, 标准属性与 -webkit- 前缀都写。
4
+ * 前缀只写一套的话旧 Safari 会整个忽略挖洞, 且不报错, 只是蒙版糊满全图。
5
+ */
6
+ export declare function maskStyle(image: string): React.CSSProperties;
7
+ /**
8
+ * 矩形挖洞的 mask 图 —— **一条** SVG 路径, 不是四条 mask 图层拼出来的。
9
+ *
10
+ * 多图层接边必留缝, 而且和"四块元素拼"是同一个病根: 每个图层都是一张独立纹理, 按
11
+ * size/position 摆位时要重新采样一次, 边缘那一两个像素会跟图层外的透明混掉, alpha 掉到 1 以下;
12
+ * 相邻两条图层的边缘于是都"半透", 蒙版正好在那条线上缺了一角。位置还会被吸附到整数设备像素,
13
+ * 缝隙/重叠同样是 1px 级的。两种机制都指向同一句话: 别在多图层之间接边。
14
+ * 半透明 + 模糊把这点差异放大成了肉眼可见的一道缝(纯色蒙版上根本看不出来)。
15
+ *
16
+ * 一条路径里没有"图层边界"这回事: 外框一个子路径、洞一个子路径, evenodd 把中间挖掉。
17
+ * viewBox 取遮罩自身的像素尺寸(1 用户单位 = 1px), 配 mask-size 100% 100% + preserveAspectRatio='none',
18
+ * 于是它按布局尺寸一次性栅格化, 全程没有二次缩放 —— 洞的边上只剩那一次抗锯齿。
19
+ *
20
+ * 注意 mask 图是"白色 = 保留": 只画洞的话是反的, 得连外框一起画再用 evenodd 挖。
21
+ *
22
+ * @param boxW/boxH 遮罩自身的尺寸(px, 不是图片的)
23
+ * @param x/y/w/h 洞的位置与大小, 同一坐标系
24
+ */
25
+ export declare function rectHoleMaskImage(boxW: number, boxH: number, x: number, y: number, w: number, h: number): string;
2
26
  /**
3
27
  * Mask — 遮罩层组件.
4
28
  * 用于 Modal/SideSheet/Drawer 等浮层的背景遮罩.
@@ -12,4 +36,23 @@ export declare class Mask {
12
36
  * @param duration 过渡时长(ms)
13
37
  */
14
38
  static render(zIndex: number, opacity: number, onClick?: () => void, duration?: number): React.ReactElement;
39
+ /**
40
+ * 带"洞"的遮罩 —— rect 那一块不被遮住, 用于 UserGuide 高亮目标。
41
+ *
42
+ * 洞是在**同一块遮罩、同一条 mask 路径**上挖的(见 rectHoleMaskImage 的说明),
43
+ * 不是四块遮罩拼的, 也不是几条 mask 图层拼的 —— 那两种都会在接边处留下一道缝,
44
+ * 半透明 + 模糊会把它放大到肉眼可见。
45
+ *
46
+ * 这里只管几何: 洞只保证"不压暗、不模糊"。洞是否可点, 取决于调用方有没有在
47
+ * 遮罩之上再放一层铺满视口的容器 —— 有的话点击会被那层收走(它仍是模态的),
48
+ * 没有的话点击就会落到页面上(能直接操作被高亮的控件)。两种都是合理设计, 由调用方定。
49
+ *
50
+ * @param rect 视口坐标; 传 null 退化成整片遮罩(没有目标的步骤)
51
+ */
52
+ static renderHole(rect: {
53
+ left: number;
54
+ top: number;
55
+ right: number;
56
+ bottom: number;
57
+ } | null, zIndex: number, duration?: number): React.ReactElement;
15
58
  }
@@ -1,5 +1,5 @@
1
1
  export { Portal } from "./Portal";
2
- export { Mask } from "./Mask";
2
+ export { Mask, maskStyle, rectHoleMaskImage } from "./Mask";
3
3
  export { OverlayZIndex } from "./OverlayZIndex";
4
4
  export { BodyScrollLock } from "./BodyScrollLock";
5
5
  export { ClickOutside } from "./ClickOutside";
@@ -16,6 +16,8 @@ export interface AudioPlayerState {
16
16
  playing: boolean;
17
17
  currentTime: number;
18
18
  duration: number;
19
+ /** 已缓冲到的位置(秒)。进度体上"已加载"那一段用它。 */
20
+ buffered: number;
19
21
  volume: number;
20
22
  muted: boolean;
21
23
  playbackRate: number;
@@ -25,6 +27,7 @@ interface InternalState extends UIState {
25
27
  playing: boolean;
26
28
  currentTime: number;
27
29
  duration: number;
30
+ buffered: number;
28
31
  volume: number;
29
32
  muted: boolean;
30
33
  playbackRate: number;
@@ -37,6 +40,12 @@ export declare class AudioPlayer extends AbstractUI<AudioPlayerProps, InternalSt
37
40
  private volumeLeaveTimer;
38
41
  private speedLeaveTimer;
39
42
  private _api;
43
+ /** 正在拖进度。放字段而不是 state —— 它只影响事件处理, 不参与渲染, 进 state 每动一下都白渲一次。 */
44
+ private scrubbing;
45
+ /** 正在拖音量。同上, 也是字段。 */
46
+ private volumeScrubbing;
47
+ /** 音量条当前的 DOM 节点 —— 滚轮要挂原生监听, 得留住引用才能解绑。 */
48
+ private volumeBarEl;
40
49
  constructor(props: AudioPlayerProps);
41
50
  componentWillUnmount(): void;
42
51
  getAPI(): AudioPlayerAPI;
@@ -45,8 +54,28 @@ export declare class AudioPlayer extends AbstractUI<AudioPlayerProps, InternalSt
45
54
  private handleTimeUpdate;
46
55
  private handleLoadedMetadata;
47
56
  private handleEnded;
48
- private handleProgressClick;
49
- private handleVolumeBarClick;
57
+ /** 把横坐标折算成进度并写回 audio。拖动过程中的每一次移动都走它。 */
58
+ private seekFromClientX;
59
+ private handleScrubDown;
60
+ private handleScrubMove;
61
+ private handleScrubUp;
62
+ /** 音量写回 audio。拖 / 滚轮都收敛到这里, 免得两边各写一份 clamp。 */
63
+ private setVolumeFromRatio;
64
+ /** 纵坐标折成 0-1 的音量。条子底端是 0, 所以是 1 减。 */
65
+ private volumeFromClientY;
66
+ private handleVolumeScrubDown;
67
+ private handleVolumeScrubMove;
68
+ private handleVolumeScrubUp;
69
+ /**
70
+ * 滚轮调音量, 每格 5%。
71
+ * 必须用**原生**监听而不是 onWheel: React 19 在根节点上把 wheel 注册成 passive 的,
72
+ * onWheel 里调 preventDefault 不生效(只会打一条警告), 结果是音量变了、页面也跟着滚了。
73
+ * 每次 ±0.05 而不是按 deltaY 的原始值 —— 不同鼠标一格 deltaY 从 1 到 120 都有, 直接用会
74
+ * 一格从 0 蹦到爆表。
75
+ */
76
+ private handleVolumeWheel;
77
+ /** 回调 ref: 气泡是条件渲染的, 挂载/卸载时在这里接上/摘掉滚轮监听。 */
78
+ private bindVolumeWheel;
50
79
  private openVolume;
51
80
  private scheduleVolumeClose;
52
81
  private openSpeed;
@@ -15,11 +15,31 @@ interface ChatMessage {
15
15
  */
16
16
  export declare class Chat extends AbstractUI<ChatProps, ChatState> {
17
17
  private listRef;
18
+ private taRef;
19
+ private emojiBtnRef;
20
+ private emojiPanelRef;
21
+ private emojiZIndex;
22
+ private outsideHandler;
23
+ private trackHandler;
18
24
  constructor(props: ChatProps);
19
- componentDidUpdate(prevProps: Readonly<ChatProps>): void;
25
+ componentDidUpdate(prevProps: Readonly<ChatProps>, prevState: Readonly<ChatState>): void;
20
26
  componentDidMount(): void;
27
+ componentWillUnmount(): void;
21
28
  private scrollToBottom;
29
+ /**
30
+ * 让输入框在 rows ~ maxRows 行之间跟着内容长高, 越过 maxRows 之后框内滚动。
31
+ * 改前是一个单行 <input>, 长文本只能左右滚动, 换行内容完全看不见 —— 这是本次要修的问题。
32
+ */
33
+ private resizeInput;
22
34
  private handleSend;
35
+ private insertEmoji;
36
+ private toggleEmoji;
37
+ private openEmoji;
38
+ private closeEmoji;
39
+ private teardownEmoji;
40
+ private emojiPanelStyle;
41
+ private renderEmojiPanel;
42
+ private renderInput;
23
43
  protected OnRenderContent(): React.ReactElement;
24
44
  }
25
45
  export interface ChatProps extends UIProps {
@@ -32,8 +52,23 @@ export interface ChatProps extends UIProps {
32
52
  showInput?: boolean;
33
53
  placeholder?: string;
34
54
  sendText?: string;
55
+ /** 是否显示头像, 默认 true */
56
+ showAvatar?: boolean;
57
+ /** 是否显示名称(msg.name), 默认 true */
58
+ showName?: boolean;
59
+ /** 是否显示气泡底板, 默认 true。关掉后内容直接铺在面板上, 并放宽到整行 —— 适合长文 / Markdown 正文 */
60
+ showBubble?: boolean;
61
+ /** 是否显示消息时间(msg.time), 默认 false */
62
+ showTime?: boolean;
63
+ /** 输入框的初始行数(最小高度), 默认 1 */
64
+ rows?: number;
65
+ /** 输入框最多撑到几行, 超出后框内滚动, 默认 5 */
66
+ maxRows?: number;
67
+ /** 输入框左下角的表情按钮与表情面板, 默认 true(仅 showInput 时有效) */
68
+ showEmoji?: boolean;
35
69
  }
36
70
  interface ChatState extends UIState {
37
71
  inputValue: string;
72
+ emojiOpen: boolean;
38
73
  }
39
74
  export {};
@@ -57,7 +57,7 @@ export interface CodeEditorProps extends UIProps {
57
57
  width?: number | string;
58
58
  /** 编辑器最小宽度(px) */
59
59
  minWidth?: number;
60
- /** Monaco 主题 @default "vs-dark" */
60
+ /** Monaco 主题 @default "vs-dark" —— 代码面按 VS Code 自己的暗色一套, **不随本库明暗主题变化** */
61
61
  theme?: "vs-dark" | "light" | "hc-black";
62
62
  /** 只读 @default false */
63
63
  readOnly?: boolean;
@@ -21,6 +21,7 @@ export declare class CodeHighlight extends AbstractUI<CodeHighlightProps, CodeHi
21
21
  export interface CodeHighlightProps extends UIProps {
22
22
  code?: string;
23
23
  language?: string;
24
+ /** Monaco 主题 @default "vs-dark" —— 代码面按 VS Code 自己的暗色一套, **不随本库明暗主题变化** */
24
25
  theme?: "vs-dark" | "light" | "hc-black";
25
26
  showLineNumbers?: boolean;
26
27
  height?: number | string;
@@ -26,6 +26,7 @@ export interface JsonViewerProps extends UIProps {
26
26
  readOnly?: boolean;
27
27
  onChange?: (value: string) => void;
28
28
  height?: number;
29
+ /** Monaco 主题 @default "vs-dark" —— 代码面按 VS Code 自己的暗色一套, **不随本库明暗主题变化** */
29
30
  theme?: "vs-dark" | "light";
30
31
  }
31
32
  interface JsonViewerState extends UIState {
@@ -44,6 +44,12 @@ export declare class VideoPlayer extends AbstractUI<VideoPlayerProps, InternalSt
44
44
  private volumeLeaveTimer;
45
45
  private speedLeaveTimer;
46
46
  private _api;
47
+ /** 正在拖进度。放字段而不是 state —— 它只影响事件处理, 不参与渲染, 进 state 每动一下都白渲一次。 */
48
+ private scrubbing;
49
+ /** 正在拖音量。同上, 也是字段。 */
50
+ private volumeScrubbing;
51
+ /** 音量条当前的 DOM 节点 —— 滚轮要挂原生监听, 得留住引用才能解绑。 */
52
+ private volumeBarEl;
47
53
  constructor(props: VideoPlayerProps);
48
54
  componentDidMount(): void;
49
55
  componentWillUnmount(): void;
@@ -55,8 +61,28 @@ export declare class VideoPlayer extends AbstractUI<VideoPlayerProps, InternalSt
55
61
  private handleTimeUpdate;
56
62
  private handleLoadedMetadata;
57
63
  private handleEnded;
58
- private handleProgressClick;
59
- private handleVolumeBarClick;
64
+ /** 把横坐标折算成进度并写回 video。拖动过程中的每一次移动都走它。 */
65
+ private seekFromClientX;
66
+ private handleScrubDown;
67
+ private handleScrubMove;
68
+ private handleScrubUp;
69
+ /** 音量写回 video。拖 / 滚轮都收敛到这里, 免得两边各写一份 clamp。 */
70
+ private setVolumeFromRatio;
71
+ /** 纵坐标折成 0-1 的音量。条子底端是 0, 所以是 1 减。 */
72
+ private volumeFromClientY;
73
+ private handleVolumeScrubDown;
74
+ private handleVolumeScrubMove;
75
+ private handleVolumeScrubUp;
76
+ /**
77
+ * 滚轮调音量, 每格 5%。
78
+ * 必须用**原生**监听而不是 onWheel: React 19 在根节点上把 wheel 注册成 passive 的,
79
+ * onWheel 里调 preventDefault 不生效(只会打一条警告), 结果是音量变了、页面也跟着滚了。
80
+ * 每次 ±0.05 而不是按 deltaY 的原始值 —— 不同鼠标一格 deltaY 从 1 到 120 都有, 直接用会
81
+ * 一格从 0 蹦到爆表。
82
+ */
83
+ private handleVolumeWheel;
84
+ /** 回调 ref: 气泡是条件渲染的, 挂载/卸载时在这里接上/摘掉滚轮监听。 */
85
+ private bindVolumeWheel;
60
86
  private openVolume;
61
87
  private scheduleVolumeClose;
62
88
  private openSpeed;
@@ -1,33 +1,36 @@
1
1
  /**
2
- * 编程式 Token 访问 — 读取 :root 上定义的 --ku-* CSS 变量.
3
- * 主要用于组件内需要动态计算样式值时通过 JS 获取 token 值.
2
+ * 内容色板的 JS 出口 —— 全库仅剩这一处需要 JS 参与的颜色拼串。
3
+ *
4
+ * 为什么这点事也得有 JS: **CSS 里写不出"随主题变档位号"的颜色**。内容色板的同一个色相在两个
5
+ * 主题下落的是不同档(blue 暗色第 7 档、亮色第 4 档), 而组件拿到的只是色相名。拼成
6
+ * `rgb(var(--ku-content-blue))` 交给内联样式是最省的做法 —— 它跟随主题自动变, 不需要 JS 读色、
7
+ * 不需要重渲染, 调用方也不必知道档位号。
8
+ *
9
+ * 真正定义颜色的是 palette.css 里的 `--ku-content-<hue>`, 由 `scripts/gen-palette.mjs` 按
10
+ * "该主题下 L 最接近该主题目标明度"的规则选出; **这里一个字面量都不存**。
11
+ *
12
+ * ── 剪掉的整族 ──
13
+ * 曾经还有一层"读取已解析颜色"的桥: `currentTheme` / `readToken` / `readTokens` / `readTriplet` /
14
+ * `rgb` / `hex` / `valueToHex` / `contentStep` / `contentColor` / `contentHex`。它们服务的两个
15
+ * 消费者现在都不存在了 ——
16
+ * ① 内容色板不需要读色: 字符串拼装就够了(这正是 contentVar 存在的理由);
17
+ * ② Monaco 那一版已整体撤掉: 代码面现在完全用 VS 自带的 `vs-dark`, 不注册自定义主题,
18
+ * 于是 `valueToHex`(为"只吃 #RRGGBB 的注册表"而写)也一并没了用处。
19
+ * 所以按"整族剪"处理, 一个不留 —— 养一批无人调用的导出, 比真要用时重写它们(~70 行:
20
+ * `getComputedStyle` + 展开嵌套 `var()` + 十六进制转换)贵得多。
21
+ *
22
+ * ── 连带删掉的产物 ──
23
+ * 那一族原本依赖 `palette.gen.ts`(生成器产出的色板 JS 常量镜像)做 SSR 兜底。既然没有 importer
24
+ * 了, 这份常量表也一并从生成器里删掉 —— 没有消费者的副本只会漂移。所以这个文件里**一个色值
25
+ * 都没有**, 颜色的唯一定义处是 `palette.css`。
4
26
  */
5
- export declare const Tokens: {
6
- primary: () => string;
7
- primaryHover: () => string;
8
- primaryActive: () => string;
9
- primaryDisabled: () => string;
10
- primaryLightDefault: () => string;
11
- primaryLightHover: () => string;
12
- primaryLightActive: () => string;
13
- success: () => string;
14
- warning: () => string;
15
- danger: () => string;
16
- info: () => string;
17
- bgDeepest: () => string;
18
- bgPanel: () => string;
19
- bgElevated: () => string;
20
- bgHover: () => string;
21
- bgBorder: () => string;
22
- textPrimary: () => string;
23
- textSecondary: () => string;
24
- textPlaceholder: () => string;
25
- textDisabled: () => string;
26
- fontSize: (key: "xs" | "sm" | "base" | "md" | "lg") => string;
27
- radius: (key: "xs" | "sm" | "base" | "md" | "lg" | "xl") => string;
28
- spacing: (key: "xs" | "sm" | "base" | "md" | "lg" | "xl") => string;
29
- zMask: () => number;
30
- zOverlay: () => number;
31
- zModal: () => number;
32
- zToast: () => number;
33
- };
27
+ /**
28
+ * 内容色的**字符串形式** —— `rgb(var(--ku-content-blue))`。
29
+ *
30
+ * 这是 Tag / Avatar / VChart 该用的那一个: 它原样进内联样式, 跟随主题自动变, 不需要 JS 读色、
31
+ * 不需要重渲染、也不需要知道档位号。改造前 Tag 那套 `hexToRgba(hex, alpha)` 就是被它取代的 ——
32
+ * 那种做法必须在 JS 里持有 hex, 于是每个消费方都攒出自己的一份内容色板。
33
+ */
34
+ export declare function contentVar(hue: string): string;
35
+ /** 内容色的浅底 / 描边 —— `rgba(var(--ku-content-blue), 0.15)` */
36
+ export declare function contentTintVar(hue: string, alpha: number): string;