@linkdesk/ui 0.1.0
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/LICENSE +21 -0
- package/README.md +47 -0
- package/dist/button/Button.d.ts +19 -0
- package/dist/color-picker/ColorPicker.d.ts +51 -0
- package/dist/combobox/Combobox.d.ts +30 -0
- package/dist/context-menu/ContextMenu.d.ts +37 -0
- package/dist/file-path-input/FilePathInput.d.ts +15 -0
- package/dist/font-family-select/FontFamilySelect.d.ts +19 -0
- package/dist/form-row/FormRow.d.ts +7 -0
- package/dist/index.css +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +1629 -0
- package/dist/inline-input/InlineInput.d.ts +38 -0
- package/dist/number-input/NumberInput.d.ts +18 -0
- package/dist/overlay-portal/OverlayPortal.d.ts +27 -0
- package/dist/plugin-icon/PluginIcon.d.ts +24 -0
- package/dist/plugin-icon/iconUtils.d.ts +38 -0
- package/dist/segmented-radio/SegmentedRadio.d.ts +30 -0
- package/dist/select-box/DynamicSelect.d.ts +25 -0
- package/dist/select-box/SelectBox.d.ts +29 -0
- package/dist/select-box/SelectBoxDropdown.d.ts +35 -0
- package/dist/slider/Slider.d.ts +22 -0
- package/dist/slider/sliderStep.d.ts +10 -0
- package/dist/theme-picker/ThemePicker.d.ts +17 -0
- package/dist/toggle/Toggle.d.ts +7 -0
- package/package.json +50 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 LinkDesk
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# @linkdesk/ui
|
|
2
|
+
|
|
3
|
+
LinkDesk 共享 UI 组件库——右键菜单 / 下拉 / 组合框 / 开关 / 滑杆 / 取色器 / 表单行 / 文件路径输入 / 主题选择等。
|
|
4
|
+
|
|
5
|
+
组件源码单一宿主于 LinkDesk 壳的 `src/components/shared/`,本包是其编译分发面:**插件作者 `npm i @linkdesk/ui` 后拿到的组件与内置插件同款、自动跟随宿主主题与玻璃态**,无需关心实现细节。
|
|
6
|
+
|
|
7
|
+
## 安装
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm i @linkdesk/ui react react-dom
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## 用法
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import { SelectBox, Toggle } from "@linkdesk/ui";
|
|
17
|
+
import "@linkdesk/ui/index.css"; // 绝大多数组件 CSS 由入口 JS 自动带入;需显式引入时用此路径
|
|
18
|
+
|
|
19
|
+
export function MyView() {
|
|
20
|
+
return (
|
|
21
|
+
<Toggle
|
|
22
|
+
checked={checked}
|
|
23
|
+
onChange={setChecked}
|
|
24
|
+
label={{ title: "启用", description: "打开后生效" }}
|
|
25
|
+
/>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
> 🔥 样式走宿主 CSS 变量——无需(也不应)在插件里覆盖主题 hex。i18n 文案经宿主语言系统,组件内部文案由宿主翻译。
|
|
31
|
+
|
|
32
|
+
## 设计约束
|
|
33
|
+
|
|
34
|
+
- **单一源码,禁止拷贝**:本包 `dist/` 是构建产物(源码只在 LinkDesk 壳仓库),插件侧直接依赖 npm 分发;不要 fork 组件到插件内自维护。
|
|
35
|
+
- **跟随主题**:所有组件消费宿主 CSS 变量,插件换主题自动变色。
|
|
36
|
+
- 详见 LinkDesk 壳仓库 `docs/02-Electron架构/E6_插件生态与发布/01-插件独立构建/07-共享组件独立分发设计.md`。
|
|
37
|
+
|
|
38
|
+
## 开发(LinkDesk 壳仓库内)
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
cd packages/linkdesk-ui
|
|
42
|
+
npm run build # dist 构建:esm + 聚合 css + 声明文件
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## License
|
|
46
|
+
|
|
47
|
+
MIT
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* E5.8#99:实心 accent 动作按钮(按钮双轨制实心轨)——动作/提交,对标 VS Code。
|
|
3
|
+
* ghost 轨(选择/分组)= SegmentedRadio。双轨约定:选什么用 ghost 分段,做什么用实心按钮。
|
|
4
|
+
*
|
|
5
|
+
* 抽壳动机:动作按钮此前是 settings 插件 CSS 类 .settings-action-btn,却被壳共享组件
|
|
6
|
+
* FilePathInput 消费——壳组件依赖插件 CSS 的倒置耦合。收编壳后 shell 组件零依赖插件样式,
|
|
7
|
+
* 第三方插件直接用 <Button> 即得壳动作按钮(不用拷 CSS)。
|
|
8
|
+
*
|
|
9
|
+
* 消费方:settings renderHint:"action" 命令按钮、背景图三态按钮、FilePathInput「…」按钮。
|
|
10
|
+
*/
|
|
11
|
+
interface ButtonProps {
|
|
12
|
+
children: React.ReactNode;
|
|
13
|
+
onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
title?: string;
|
|
16
|
+
type?: "button" | "submit" | "reset";
|
|
17
|
+
}
|
|
18
|
+
declare function Button({ children, onClick, disabled, title, type }: ButtonProps): import("react").JSX.Element;
|
|
19
|
+
export default Button;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ColorPicker 归一化调色器——对标 VS Code ColorPicker。
|
|
3
|
+
* E3f #59e:二维饱和度面板 + 色相条 + hex 输入 + 预设色。
|
|
4
|
+
*
|
|
5
|
+
* 接口:{ open, value, onChange, onClose, presets? }
|
|
6
|
+
* 交互:拖动取色 / hex 输入 / 点击预设 / Enter 确认 / Esc 取消
|
|
7
|
+
*
|
|
8
|
+
* VS Code 对标:src/vs/editor/contrib/colorPicker/browser/colorPickerParts.ts
|
|
9
|
+
*/
|
|
10
|
+
export declare const PANEL_WIDTH = 232;
|
|
11
|
+
export interface PanelPos {
|
|
12
|
+
left: number;
|
|
13
|
+
top: number;
|
|
14
|
+
}
|
|
15
|
+
/** 纯函数——锚点右上角 + 视口 → 面板落点(右/下方放不下翻到左/上 + clamp 收拢入视口)。
|
|
16
|
+
* 恒在视口内(margin 8px)。抽出纯函数便于单测。 */
|
|
17
|
+
export declare function resolvePanelPosition(anchor: {
|
|
18
|
+
x: number;
|
|
19
|
+
y: number;
|
|
20
|
+
}, panelWidth: number, panelHeight: number, viewport: {
|
|
21
|
+
width: number;
|
|
22
|
+
height: number;
|
|
23
|
+
}, margin?: number): PanelPos;
|
|
24
|
+
export interface ColorPickerProps {
|
|
25
|
+
open: boolean;
|
|
26
|
+
value: string;
|
|
27
|
+
onChange: (hex: string) => void;
|
|
28
|
+
onClose: () => void;
|
|
29
|
+
presets?: string[];
|
|
30
|
+
/** 弹出位置——null 时居中 */
|
|
31
|
+
anchor?: {
|
|
32
|
+
x: number;
|
|
33
|
+
y: number;
|
|
34
|
+
} | null;
|
|
35
|
+
}
|
|
36
|
+
export default function ColorPicker({ open, value, onChange, onClose, presets, anchor }: ColorPickerProps): import("react").JSX.Element | null;
|
|
37
|
+
export interface ShowColorPickerOptions {
|
|
38
|
+
initialColor?: string;
|
|
39
|
+
presets?: string[];
|
|
40
|
+
anchor?: {
|
|
41
|
+
x: number;
|
|
42
|
+
y: number;
|
|
43
|
+
} | null;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* 命令式弹出 ColorPicker——对标 QuickPick show() 模式。
|
|
47
|
+
* 插件调 `linkdesk.commands.execute('color-picker.pick', { initialColor: '#f00' })`
|
|
48
|
+
* → 浮层挂到 #overlay-root(E5.8#107 浮层权威;壳 DOM 无此 root 回退 body)→ 选色 → resolve(hex) → 自动清理 DOM。
|
|
49
|
+
* .colorpicker-command-root 死类名已删(E5.8#107 死代码清理——挂载点语义由 target 表达,不需要类名知识)。
|
|
50
|
+
*/
|
|
51
|
+
export declare function showColorPicker(options?: ShowColorPickerOptions): Promise<string | undefined>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Combobox——可输入 + 下拉的归一化组件(SelectBox 超集)。E5.8#30.17(审视 ④)。
|
|
3
|
+
*
|
|
4
|
+
* 对标 VS Code ComboBox——文本可编辑 + 候选下拉;SelectBox 只读不可输入。
|
|
5
|
+
* SelectBox 场景(必须从候选选)仍用 SelectBox;Combobox 场景(允许非标自定义值
|
|
6
|
+
* + 快捷候选,如波特率 115200 快捷 + 手输 250000)用本组件。
|
|
7
|
+
*
|
|
8
|
+
* 🔥 提交语义(防误触发重副作用——波特率变更会关旧重开端口):
|
|
9
|
+
* 选择下拉项 / Enter / 失焦(文本有变更)→ onChange(新值);文本无变更 → 不触发。
|
|
10
|
+
*
|
|
11
|
+
* 归一化:壳组件库范畴(components/shared/)——壳视图 + 插件共用。
|
|
12
|
+
* 新插件用可输入下拉 → import Combobox from "@src/components/shared/combobox/Combobox"。
|
|
13
|
+
* 下拉面板视觉复用 SelectBox(.selectbox-dropdown/list/item/empty 类)——一个视觉语言
|
|
14
|
+
* 一处写,jscpd 0 克隆门禁:Combobox.css 只定义 field/input/arrow,下拉视觉零重写。
|
|
15
|
+
*/
|
|
16
|
+
interface ComboboxProps {
|
|
17
|
+
value: string;
|
|
18
|
+
options: string[];
|
|
19
|
+
onChange: (v: string) => void;
|
|
20
|
+
disabled?: boolean;
|
|
21
|
+
placeholder?: string;
|
|
22
|
+
title?: string;
|
|
23
|
+
className?: string;
|
|
24
|
+
/** 下拉打开时回调——刷新动态选项列表(COM 口热插拔;波特率静态可不传) */
|
|
25
|
+
onOpen?: () => void;
|
|
26
|
+
/** 输入法模式——波特率 numeric,默认 text */
|
|
27
|
+
inputMode?: "text" | "numeric" | "decimal";
|
|
28
|
+
}
|
|
29
|
+
declare function Combobox({ value, options, onChange, disabled, placeholder, title, className, onOpen, inputMode }: ComboboxProps): import("react").JSX.Element;
|
|
30
|
+
export default Combobox;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { MenuItemDescriptor } from "@linkdesk/contracts";
|
|
2
|
+
export interface ContextMenuProps {
|
|
3
|
+
/** 菜单槽位——字符串 API 契约 */
|
|
4
|
+
menuId: string;
|
|
5
|
+
anchor: {
|
|
6
|
+
x: number;
|
|
7
|
+
y: number;
|
|
8
|
+
};
|
|
9
|
+
/** 传给命令的上下文(when 过滤 + handler args) */
|
|
10
|
+
context?: Record<string, unknown>;
|
|
11
|
+
onClose: () => void;
|
|
12
|
+
/**
|
|
13
|
+
* E5#44d:动态子菜单解析器。
|
|
14
|
+
* 当 menu item 声明 children: [](空数组)时调用此函数获取子项。
|
|
15
|
+
* @param parentId 父菜单项的命令 ID(空字符串表示纯标签项)
|
|
16
|
+
* @param ctx 同 context prop
|
|
17
|
+
* @returns 子菜单项列表,或 undefined 表示无子项
|
|
18
|
+
*/
|
|
19
|
+
resolveChildren?: (parentId: string, ctx: Record<string, unknown>) => Array<{
|
|
20
|
+
id: string;
|
|
21
|
+
label: string;
|
|
22
|
+
}> | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* E5.8#55:外部注入菜单项——顶部/汉堡下拉复用本渲染器(menuId 仅作标识,
|
|
25
|
+
* 数据不走 menu.getItems IPC,布局快照 PoolMenuItem[] 转换后直接注入)。
|
|
26
|
+
* 提供时跳过 getItems 拉取——壳侧布局已 when 过滤 + t() 翻译(显示文本铁律)。
|
|
27
|
+
*/
|
|
28
|
+
items?: MenuItemDescriptor[];
|
|
29
|
+
/** E5.8#55:浮层形态——两行为耦合成一词(B1 合并,2026-08-23 拍板):
|
|
30
|
+
* - "overlay"(默认,右键/汉堡):全屏透明 backdrop 吞第一击 + 点外部关闭(现状行为)。
|
|
31
|
+
* - "non-modal"(E5.8#92 设置行齿轮):无 backdrop + 保留点外关闭——点击穿透下层元素
|
|
32
|
+
* (不吞首击):gear 菜单开着真实点击色块 → 菜单关 + 取色器开(修命中区漂移,14-档案 §七)。
|
|
33
|
+
* - "embedded"(顶部菜单栏下拉):无 backdrop + 点外关闭由宿主自管——菜单嵌在按钮行
|
|
34
|
+
* hover 切换交互里,backdrop 会吞掉按钮行第一击导致切换失效(硬约束 18 已钳制菜单 top≥30)。 */
|
|
35
|
+
variant?: "overlay" | "non-modal" | "embedded";
|
|
36
|
+
}
|
|
37
|
+
export default function ContextMenu({ menuId, anchor, context, onClose, resolveChildren, items, variant }: ContextMenuProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FilePathInput——文件/目录路径选择器。
|
|
3
|
+
* E5#57d: text input + 📂 按钮 → Electron dialog.openFile/openDirectory。
|
|
4
|
+
*
|
|
5
|
+
* 壳侧实现——uiHint: "file" | "directory" 配置项自动走此控件。
|
|
6
|
+
* 非 Electron 环境下 📂 按钮不可用——用户可手动输入路径。
|
|
7
|
+
*/
|
|
8
|
+
interface FilePathInputProps {
|
|
9
|
+
value: string;
|
|
10
|
+
onChange: (v: string) => void;
|
|
11
|
+
/** "file" → openFile 对话框 / "directory" → openDirectory 对话框 */
|
|
12
|
+
dialogType: "file" | "directory";
|
|
13
|
+
}
|
|
14
|
+
export default function FilePathInput({ value, onChange, dialogType }: FilePathInputProps): import("react").JSX.Element;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FontFamilySelect——系统字体选择器(等宽 / 全字族双模式)。
|
|
3
|
+
* E5#57c: queryLocalFonts() → Canvas 测等宽("i" vs "W") → SelectBox 下拉。
|
|
4
|
+
*
|
|
5
|
+
* 壳侧实现——uiHint: "fontFamily" 配置项自动走此控件。
|
|
6
|
+
* E5.8#50.20 全字族化:monoOnly=true(默认,兼容等宽场景——编辑器字体)只列等宽族;
|
|
7
|
+
* monoOnly=false 列全部本地字体(UI 字体——app.fontFamily 用户级覆盖写 --font-ui)。
|
|
8
|
+
* 当前值边界(E5.8#50.20 ③):非系统值(主题资产 @font-face 族、外部手工值)只显示不提供下拉选项——
|
|
9
|
+
* 下拉选项恒为系统字体;空值 = 跟随主题(全字族模式提供「跟随主题」选项复位)。
|
|
10
|
+
* queryLocalFonts 不可用时降级到常见字体列表——不抛错。
|
|
11
|
+
*/
|
|
12
|
+
interface FontFamilySelectProps {
|
|
13
|
+
value: string;
|
|
14
|
+
onChange: (v: string) => void;
|
|
15
|
+
/** true/缺省 = 只列等宽族(编辑器字体场景);false = 全字族(UI 字体) */
|
|
16
|
+
monoOnly?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export default function FontFamilySelect({ value, onChange, monoOnly }: FontFamilySelectProps): import("react").JSX.Element;
|
|
19
|
+
export {};
|
package/dist/index.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.button{background:var(--accent);color:var(--text-on-accent);border:none;border-radius:var(--radius-sm);padding:4px 12px;font-size:var(--font-size-sm);cursor:pointer;white-space:nowrap}.button:hover{opacity:.85}.button:disabled{opacity:.5;cursor:default}.colorpicker-overlay{position:fixed;top:0;right:0;bottom:0;left:0;z-index:var(--z-overlay-backdrop)}.colorpicker-panel{position:fixed;z-index:var(--z-overlay);background:var(--bg-window);border:1px solid var(--separator);border-radius:var(--radius-md);padding:12px;box-shadow:var(--shadow-float);display:flex;flex-wrap:wrap;width:232px;gap:8px;top:50%;left:50%;transform:translate(-50%,-50%)}.colorpicker-panel[style]{transform:none}.colorpicker-sv{width:180px;height:150px;border-radius:var(--radius-sm);position:relative;cursor:crosshair;overflow:hidden;-webkit-user-select:none;user-select:none}.colorpicker-sv-white{position:absolute;top:0;right:0;bottom:0;left:0;background:linear-gradient(to right,#fff,transparent)}.colorpicker-sv-black{position:absolute;top:0;right:0;bottom:0;left:0;background:linear-gradient(to top,#000,transparent)}.colorpicker-sv-cursor{position:absolute;width:10px;height:10px;border:2px solid #fff;border-radius:var(--radius-full);box-shadow:var(--shadow-tiny);transform:translate(-50%,-50%);pointer-events:none}.colorpicker-hue{width:180px;height:14px;border-radius:var(--radius-md);position:relative;cursor:pointer;-webkit-user-select:none;user-select:none;background:linear-gradient(to right,red,#ff0,#0f0,#0ff,#00f,#f0f,red)}.colorpicker-hue-cursor{position:absolute;top:-2px;width:4px;height:18px;background:#fff;border-radius:var(--radius-xs);box-shadow:var(--shadow-tiny);transform:translate(-50%);pointer-events:none}.colorpicker-footer{display:flex;align-items:center;gap:6px;width:100%}.colorpicker-preview{width:28px;height:28px;border-radius:var(--radius-sm);border:1px solid var(--separator);flex-shrink:0}.colorpicker-hex{flex:1;min-width:0;height:calc(26px * var(--ui-scale));padding:2px 6px;border:1px solid var(--separator);border-radius:var(--radius-sm);background:var(--bg-input);color:var(--text-primary);font-family:var(--font-mono);font-size:var(--font-size-sm);outline:none;box-sizing:border-box}.colorpicker-hex:focus{border-color:var(--accent)}.colorpicker-ok{height:calc(26px * var(--ui-scale));padding:0 12px;background:var(--accent);color:var(--text-on-accent);border:none;border-radius:var(--radius-sm);font-size:var(--font-size-sm);cursor:pointer;flex-shrink:0}.colorpicker-ok:hover{background:var(--accent-hover)}.colorpicker-presets{width:100%;display:flex;gap:6px;flex-wrap:wrap;padding-top:4px;border-top:1px solid var(--separator)}.colorpicker-preset{width:18px;height:18px;border-radius:var(--radius-full);border:1px solid var(--separator);cursor:pointer;transition:transform .1s}.colorpicker-preset:hover{transform:scale(1.2);border-color:var(--accent)}.combobox{position:relative;display:inline-block;min-width:100px}.combobox-field{display:flex;align-items:center;gap:4px;padding:3px 6px;background:var(--bg-input);color:var(--text-primary);border:1px solid var(--border);border-radius:var(--radius-xs);font-size:var(--font-size-md);line-height:1.6;cursor:text}.combobox-field:hover:not(.selectbox-disabled){border-color:var(--accent)}.combobox.selectbox-open .combobox-field{border-color:var(--accent)}.combobox-input{flex:1;min-width:0;padding:0;background:transparent;border:none;outline:none;color:inherit;font:inherit;line-height:1.6}.combobox-input::placeholder{color:var(--text-muted)}.combobox-input:disabled{opacity:.5}.selectbox{position:relative;display:inline-block;min-width:100px;-webkit-user-select:none;user-select:none}.selectbox-trigger{display:flex;align-items:center;justify-content:space-between;gap:4px;width:100%;padding:3px 6px;background:var(--bg-input);color:var(--text-primary);border:1px solid var(--border);border-radius:var(--radius-xs);font-size:var(--font-size-md);line-height:1.6;cursor:pointer;font-family:inherit;text-align:left}.selectbox-trigger:hover:not(:disabled){border-color:var(--accent)}.selectbox-trigger:disabled{opacity:.5;cursor:default}.selectbox-open .selectbox-trigger{border-color:var(--accent)}.selectbox-label{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.selectbox-swatch{display:inline-block;width:10px;height:10px;margin-right:5px;border-radius:50%;border:1px solid color-mix(in srgb,var(--text-primary) 35%,transparent);vertical-align:middle;flex-shrink:0}.selectbox-arrow{flex-shrink:0;font-size:var(--font-size-sm);transition:transform .15s ease}.selectbox-arrow-up{transform:rotate(180deg)}.selectbox-dropdown{position:absolute;top:100%;left:0;margin-top:2px;background:var(--bg-window);border:1px solid var(--border);border-radius:var(--radius-sm);box-shadow:var(--shadow-pop);overflow:hidden;z-index:var(--z-dropdown);max-height:260px;max-width:calc(100vw - 24px);display:flex;flex-direction:column;animation:selectbox-in .1s ease-out}@keyframes selectbox-in{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.selectbox-search{padding:4px;border-bottom:1px solid var(--separator)}.selectbox-search-input{width:100%;padding:2px 6px;background:var(--bg-input);color:var(--text-primary);border:1px solid var(--border);border-radius:var(--radius-xs);font-size:var(--font-size-sm);font-family:inherit;outline:none;box-sizing:border-box}.selectbox-search-input:focus{border-color:var(--accent)}.selectbox-list{list-style:none;margin:0;padding:2px 0;overflow-y:auto;outline:none}.selectbox-item{padding:3px 8px;font-size:var(--font-size-md);line-height:1.6;color:var(--text-primary);cursor:pointer}.selectbox-item-focus{background:var(--accent);color:var(--text-on-accent)}.selectbox-item-selected:not(.selectbox-item-focus){background:var(--bg-selection)}.selectbox-empty{padding:6px 8px;font-size:var(--font-size-sm);color:var(--text-muted);text-align:center}.selectbox-disabled{pointer-events:none}.ctx-menu{position:fixed;min-width:140px;max-width:280px;background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius-lg);box-shadow:var(--shadow-hairline),var(--context-menu-shadow);padding:4px 0;overflow:hidden;outline:none;opacity:0;transform:scale(.96);transform-origin:top left;transition:opacity .15s ease-out,transform .15s ease-out}.ctx-menu:focus-visible{outline:none}.ctx-menu.show{opacity:1;transform:scale(1)}.ctx-item{display:flex;align-items:center;gap:8px;padding:6px 12px;font-size:var(--font-size-md);font-weight:400;line-height:1.6;color:var(--text-primary);cursor:pointer;transition:background 60ms;-webkit-user-select:none;user-select:none}.ctx-item:hover,.ctx-item.focused{background:var(--tab-hover-bg);color:var(--text-primary);box-shadow:inset 2px 0 0 var(--accent)}.ctx-item-icon{flex-shrink:0;width:calc(16px * var(--ui-scale));height:calc(16px * var(--ui-scale));font-size:var(--font-size-lg);display:flex;align-items:center;justify-content:center;opacity:.75}.ctx-item:hover .ctx-item-icon,.ctx-item.focused .ctx-item-icon{opacity:1;color:var(--accent)}.ctx-item-shortcut{flex-shrink:0;margin-left:12px;font-size:var(--font-size-xs);opacity:.6;font-family:var(--font-mono);letter-spacing:.2px}.ctx-item:hover .ctx-item-shortcut,.ctx-item.focused .ctx-item-shortcut{opacity:.9;color:var(--text-secondary)}.ctx-item-check{flex-shrink:0;width:16px;font-size:var(--font-size-sm);color:var(--accent);display:flex;align-items:center}.ctx-item-label{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ctx-divider{height:1px;background:var(--separator);margin:4px 12px}.ctx-item-danger{color:var(--danger)}.ctx-item-danger .ctx-item-icon{opacity:1}.ctx-item-danger:hover,.ctx-item-danger.focused{background:var(--tab-hover-bg);color:var(--danger);box-shadow:inset 2px 0 0 var(--danger)}.ctx-item-danger:hover .ctx-item-shortcut,.ctx-item-danger.focused .ctx-item-shortcut{opacity:.9;color:var(--text-secondary)}.form-row{display:flex;align-items:center;gap:8px;margin-bottom:6px;font-size:var(--font-size-sm);color:var(--text-primary)}.form-row>label,.form-row>.form-label{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.form-row>.toggle,.form-row>.input,.form-row>select,.form-row>.form-control{flex-shrink:0}.number-input{display:inline-flex;align-items:center;gap:0}.number-input-btn{display:flex;align-items:center;justify-content:center;width:calc(22px * var(--ui-scale));height:calc(24px * var(--ui-scale));padding:0;border:1px solid var(--border);background:var(--bg-input);color:var(--text-primary);font-size:var(--font-size-lg);line-height:1;cursor:pointer;transition:background .15s,border-color .15s;-webkit-user-select:none;user-select:none}.number-input-btn:first-child{border-radius:var(--radius-sm) 0 0 var(--radius-sm);border-right:none}.number-input-btn:last-child{border-radius:0 var(--radius-sm) var(--radius-sm) 0;border-left:none}.number-input-btn:hover:not(:disabled){background:var(--tab-hover-bg)}.number-input-btn:active:not(:disabled){background:var(--accent);color:var(--text-on-accent)}.number-input-btn:disabled{opacity:.3;cursor:default}.number-input-field{border-radius:0;text-align:center;width:44px}.number-input-unit{margin:0 6px 0 8px;color:var(--text-muted);font-size:var(--font-size-sm);line-height:1;-webkit-user-select:none;user-select:none}.segmented-radio{display:flex;gap:4px}.segmented-radio__option{display:flex;flex-direction:column;align-items:center;gap:4px;padding:4px 8px;background:transparent;border:1px solid var(--separator);border-radius:var(--radius-sm);cursor:pointer;transition:border-color .12s ease,background .12s ease}.segmented-radio__option:hover{border-color:var(--text-muted);background:var(--hover-overlay)}.segmented-radio__option:focus-visible{outline:1px solid var(--accent);outline-offset:1px}.segmented-radio__option.is-selected{border-color:var(--accent);background:color-mix(in srgb,var(--accent) 12%,transparent)}.segmented-radio__label{font-size:var(--font-size-sm);color:var(--text-secondary);white-space:nowrap}.segmented-radio__option.is-selected .segmented-radio__label{color:var(--text-primary)}.slider{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:130px;height:4px;margin:0;border-radius:var(--radius-sm);background:linear-gradient(to right,var(--accent) var(--slider-pct, 0%),var(--border) var(--slider-pct, 0%));cursor:pointer;outline-offset:4px}.slider:disabled{opacity:.4;cursor:default}.slider::-webkit-slider-thumb{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:12px;height:12px;border-radius:50%;background:var(--text-primary);box-shadow:var(--shadow-tiny);cursor:grab}.slider::-webkit-slider-thumb:active{cursor:grabbing}.theme-picker{display:grid;grid-template-columns:repeat(4,1fr);gap:8px;width:100%}.theme-card{border:1px solid var(--border);border-radius:var(--radius-lg);overflow:hidden;cursor:pointer;background:var(--bg-card);padding:0;text-align:left;color:inherit;font:inherit;transition:border-color .15s ease,transform .15s ease}.theme-card:hover{border-color:var(--separator);transform:translateY(-1px)}.theme-card:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.theme-card.active{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent)}.theme-preview{height:44px;display:flex;align-items:center;gap:3px;padding:8px;overflow:hidden}.theme-preview .pv-bar{width:26px;height:12px;border-radius:var(--radius-xs)}.theme-preview .pv-dots{display:flex;gap:3px}.theme-preview .pv-dot{width:12px;height:12px;border-radius:50%;border:1px solid color-mix(in srgb,var(--text-primary) 35%,transparent)}.theme-card .tname{padding:6px 8px;font-size:var(--font-size-xs);color:var(--text-secondary);display:flex;align-items:center;justify-content:space-between;gap:4px;overflow:hidden}.theme-card.active .tname{color:var(--text-primary)}.theme-card .tname-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.theme-card .badge{font-size:var(--font-size-2xs);color:var(--accent);white-space:nowrap}.theme-picker-empty{color:var(--text-muted);font-size:var(--font-size-sm);padding:8px 0}.toggle{width:36px;height:20px;border-radius:var(--radius-pill);background:var(--border);transition:background .15s;cursor:pointer;position:relative;flex-shrink:0}.toggle.on{background:var(--accent)}.toggle:after{content:"";position:absolute;top:2px;left:2px;width:16px;height:16px;border-radius:var(--radius-pill);background:var(--toggle-knob);transition:transform .15s}.toggle.on:after{transform:translate(16px);background:var(--toggle-knob-on, var(--text-on-accent))}.toggle.disabled{opacity:.4;cursor:not-allowed}.inline-input{border:1px solid var(--border);background:var(--bg-input);color:var(--text-primary);font-family:inherit;outline:none;box-sizing:border-box;width:100%;transition:border-color .15s ease-out,box-shadow .15s ease-out}.inline-input:focus{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent)}.inline-input--compact{height:calc(22px * var(--ui-scale));font-size:var(--font-size-sm);padding:0 4px}.inline-input--normal{height:calc(32px * var(--ui-scale));font-size:var(--font-size-md);padding:0 8px}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** @linkdesk/ui 公共类型入口(E6#54b 构建生成——勿手改;改导出面改 packages/linkdesk-ui/src/index.ts 后重跑 build) */
|
|
2
|
+
export { default as Button } from "./button/Button";
|
|
3
|
+
export { default as ColorPicker } from "./color-picker/ColorPicker";
|
|
4
|
+
export { default as Combobox } from "./combobox/Combobox";
|
|
5
|
+
export { default as ContextMenu } from "./context-menu/ContextMenu";
|
|
6
|
+
export { default as DynamicSelect } from "./select-box/DynamicSelect";
|
|
7
|
+
export { default as FilePathInput } from "./file-path-input/FilePathInput";
|
|
8
|
+
export { default as FontFamilySelect } from "./font-family-select/FontFamilySelect";
|
|
9
|
+
export { default as FormRow } from "./form-row/FormRow";
|
|
10
|
+
export { default as NumberInput } from "./number-input/NumberInput";
|
|
11
|
+
export { default as SegmentedRadio } from "./segmented-radio/SegmentedRadio";
|
|
12
|
+
export { default as SelectBox } from "./select-box/SelectBox";
|
|
13
|
+
export { default as Slider } from "./slider/Slider";
|
|
14
|
+
export { default as ThemePicker } from "./theme-picker/ThemePicker";
|
|
15
|
+
export { default as Toggle } from "./toggle/Toggle";
|
|
16
|
+
export { InlineInput } from "./inline-input/InlineInput";
|
|
17
|
+
export { PluginIcon } from "./plugin-icon/PluginIcon";
|
|
18
|
+
export { inferSliderStep } from "./slider/sliderStep";
|
|
19
|
+
export type { InlineInputHandle } from "./inline-input/InlineInput";
|
|
20
|
+
export type { ContextMenuProps } from "./context-menu/ContextMenu";
|
|
21
|
+
export type { ManifestIconShape, ResolvedIcon } from "./plugin-icon/iconUtils";
|