jvs-draw 1.0.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/README.md +66 -0
- package/dist/App.vue.d.ts +2 -0
- package/dist/components/BoardName.vue.d.ts +5 -0
- package/dist/components/ContextMenu.vue.d.ts +18 -0
- package/dist/components/ExcalidrawCanvas.vue.d.ts +13 -0
- package/dist/components/FloatingPopovers.vue.d.ts +2 -0
- package/dist/components/FloatingPropertiesBar.vue.d.ts +7 -0
- package/dist/components/Footer.vue.d.ts +2 -0
- package/dist/components/HelpDialog.vue.d.ts +2 -0
- package/dist/components/PropertiesPanel.vue.d.ts +11 -0
- package/dist/components/Toolbar.vue.d.ts +2 -0
- package/dist/components/panelCom/alignStyle.vue.d.ts +2 -0
- package/dist/components/panelCom/arrowStyle.vue.d.ts +2 -0
- package/dist/components/panelCom/borderStyle.vue.d.ts +2 -0
- package/dist/components/panelCom/colorStyle.vue.d.ts +11 -0
- package/dist/components/panelCom/fillStyle.vue.d.ts +2 -0
- package/dist/components/panelCom/lineStyle.vue.d.ts +2 -0
- package/dist/components/panelCom/textStyle.vue.d.ts +2 -0
- package/dist/core/element.d.ts +25 -0
- package/dist/core/renderer.d.ts +7 -0
- package/dist/elbow/algorithms/a-star.d.ts +10 -0
- package/dist/elbow/algorithms/data-structures/graph.d.ts +14 -0
- package/dist/elbow/algorithms/data-structures/index.d.ts +2 -0
- package/dist/elbow/algorithms/data-structures/priority-queue.d.ts +16 -0
- package/dist/elbow/algorithms/index.d.ts +2 -0
- package/dist/elbow/constants/default.d.ts +10 -0
- package/dist/elbow/constants/index.d.ts +4 -0
- package/dist/elbow/constants/media.d.ts +6 -0
- package/dist/elbow/constants/property.d.ts +5 -0
- package/dist/elbow/constants/resize.d.ts +10 -0
- package/dist/elbow/core.d.ts +37 -0
- package/dist/elbow/utils/elbow-line-route.d.ts +38 -0
- package/dist/elbow/utils/index.d.ts +4 -0
- package/dist/elbow/utils/line-path.d.ts +7 -0
- package/dist/elbow/utils/math.d.ts +3 -0
- package/dist/elbow/utils/vector.d.ts +6 -0
- package/dist/index.d.ts +3 -0
- package/dist/jvs-draw.css +1 -0
- package/dist/jvs-draw.es.js +6810 -0
- package/dist/jvs-draw.umd.js +6 -0
- package/dist/jvs-ui-public/icon/icon.ico +0 -0
- package/dist/jvs-ui-public/icon/logo.ico +0 -0
- package/dist/jvs-ui-public/icon-fonts/iconfont.css +627 -0
- package/dist/jvs-ui-public/icon-fonts/iconfont.js +1 -0
- package/dist/jvs-ui-public/icon-fonts/iconfont.json +1080 -0
- package/dist/jvs-ui-public/icon-fonts/iconfont.ttf +0 -0
- package/dist/jvs-ui-public/icon-fonts/iconfont.woff +0 -0
- package/dist/jvs-ui-public/icon-fonts/iconfont.woff2 +0 -0
- package/dist/jvs-ui-public/public-fonts/iconfont.css +265 -0
- package/dist/jvs-ui-public/public-fonts/iconfont.js +1 -0
- package/dist/jvs-ui-public/public-fonts/iconfont.json +443 -0
- package/dist/jvs-ui-public/public-fonts/iconfont.svg +143 -0
- package/dist/jvs-ui-public/public-fonts/iconfont.ttf +0 -0
- package/dist/jvs-ui-public/public-fonts/iconfont.woff +0 -0
- package/dist/jvs-ui-public/public-fonts/iconfont.woff2 +0 -0
- package/dist/main.d.ts +0 -0
- package/dist/store/index.d.ts +1518 -0
- package/dist/types/element.d.ts +128 -0
- package/dist/utils/calcDomHeight.d.ts +6 -0
- package/dist/utils/image.d.ts +10 -0
- package/dist/utils/isPointOnElement.d.ts +1 -0
- package/dist/utils/math.d.ts +71 -0
- package/dist/utils/routing.d.ts +3 -0
- package/dist/utils/utils.d.ts +2 -0
- package/dist/vite.svg +1 -0
- package/package.json +51 -0
package/README.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# JVS-Draw (Excalidraw Reconstruction)
|
|
2
|
+
|
|
3
|
+
本项目是对 [Excalidraw](https://github.com/excalidraw/excalidraw) 的重构,采用 **Vite + Vue 3 + TypeScript** 技术栈。
|
|
4
|
+
|
|
5
|
+
## 技术栈
|
|
6
|
+
|
|
7
|
+
- **核心框架**: Vue 3 (Composition API)
|
|
8
|
+
- **状态管理**: Pinia
|
|
9
|
+
- **绘图引擎**: RoughJS
|
|
10
|
+
- **构建工具**: Vite
|
|
11
|
+
- **图标库**: Remix Icon
|
|
12
|
+
- **样式**: CSS/SCSS
|
|
13
|
+
|
|
14
|
+
## 项目结构
|
|
15
|
+
|
|
16
|
+
```text
|
|
17
|
+
jvs-draw/
|
|
18
|
+
├── src/
|
|
19
|
+
│ ├── components/ # UI 组件 (Canvas, Toolbar, Properties 等)
|
|
20
|
+
│ ├── core/ # 核心逻辑 (Renderer, Scene 管理)
|
|
21
|
+
│ ├── store/ # 状态管理 (Pinia Store)
|
|
22
|
+
│ ├── types/ # 类型定义
|
|
23
|
+
│ ├── utils/ # 工具函数 (Math, Helper)
|
|
24
|
+
│ └── App.vue # 主应用组件
|
|
25
|
+
├── public/ # 静态资源
|
|
26
|
+
└── package.json # 依赖配置
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## 已实现功能
|
|
30
|
+
- [x] Vue 3 + Vite 环境搭建
|
|
31
|
+
- [x] 核心渲染引擎 (Rectangle, Ellipse, Diamond, Frame, Image)
|
|
32
|
+
- [x] 基础 Toolbar UI (Hand, Selection, Shapes, Arrow, Line, Freedraw, Text, Image, Frame, Eraser)
|
|
33
|
+
- [x] 响应式画布与手势支持 (Zoom, Pan)
|
|
34
|
+
- [x] 元素的移动、缩放和旋转
|
|
35
|
+
- [x] 线条与箭头 (支持自动吸附与动态绑定)
|
|
36
|
+
- [x] 画框工具 (Frame) - 支持自动吸附与容器跟随
|
|
37
|
+
- [x] 文本编辑 (双击编辑、自动换行、动态调整)
|
|
38
|
+
- [x] 图片插入与渲染
|
|
39
|
+
- [x] 右侧属性面板 (自定义颜色、边框样式、粗细、透明度、图层、对齐)
|
|
40
|
+
- [x] 画布操作 (清除、锁定)
|
|
41
|
+
- [x] 快捷键支持
|
|
42
|
+
|
|
43
|
+
## 环境要求
|
|
44
|
+
|
|
45
|
+
- **Node.js**: v20.0.0 或更高版本 (推荐使用 v20.19.6)
|
|
46
|
+
- **包管理器**: npm (或其他均可)
|
|
47
|
+
|
|
48
|
+
## 运行项目
|
|
49
|
+
|
|
50
|
+
1. 安装依赖:
|
|
51
|
+
```bash
|
|
52
|
+
npm install
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
2. 启动开发服务器:
|
|
56
|
+
```bash
|
|
57
|
+
npm run dev
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## 后续计划
|
|
61
|
+
|
|
62
|
+
- [ ] 实现连线和箭头逻辑
|
|
63
|
+
- [ ] 实现自由绘制 (Freehand)
|
|
64
|
+
- [ ] 实现文字编辑功能
|
|
65
|
+
- [ ] 实现元素的移动、缩放和选择
|
|
66
|
+
- [ ] 完善右侧属性面板 (颜色、边框、透明度等)
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
declare const _default: import('vue').DefineComponent<{}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, HTMLDivElement>;
|
|
2
|
+
export default _default;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
declare const _default: import('vue').DefineComponent<{}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {
|
|
2
|
+
firstPopoverRef: unknown;
|
|
3
|
+
inputRef: unknown;
|
|
4
|
+
}, HTMLDivElement>;
|
|
5
|
+
export default _default;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
visible: boolean;
|
|
3
|
+
x: number;
|
|
4
|
+
y: number;
|
|
5
|
+
isElementSelected: boolean;
|
|
6
|
+
isElementLocked?: boolean;
|
|
7
|
+
showGrid: boolean;
|
|
8
|
+
};
|
|
9
|
+
declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
10
|
+
close: (...args: any[]) => void;
|
|
11
|
+
action: (...args: any[]) => void;
|
|
12
|
+
}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
13
|
+
onClose?: ((...args: any[]) => any) | undefined;
|
|
14
|
+
onAction?: ((...args: any[]) => any) | undefined;
|
|
15
|
+
}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {
|
|
16
|
+
menuRef: HTMLDivElement;
|
|
17
|
+
}, any>;
|
|
18
|
+
export default _default;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ExcalidrawElement } from '../types/element';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
elements: ExcalidrawElement[];
|
|
4
|
+
zoom: number;
|
|
5
|
+
scrollX: number;
|
|
6
|
+
scrollY: number;
|
|
7
|
+
};
|
|
8
|
+
declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {
|
|
9
|
+
container: HTMLDivElement;
|
|
10
|
+
canvas: HTMLCanvasElement;
|
|
11
|
+
textInput: HTMLTextAreaElement;
|
|
12
|
+
}, HTMLDivElement>;
|
|
13
|
+
export default _default;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
declare const _default: import('vue').DefineComponent<{}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, HTMLDivElement>;
|
|
2
|
+
export default _default;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
zoom: number;
|
|
3
|
+
scrollX: number;
|
|
4
|
+
scrollY: number;
|
|
5
|
+
};
|
|
6
|
+
declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
7
|
+
export default _default;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
declare const _default: import('vue').DefineComponent<{}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, HTMLDivElement>;
|
|
2
|
+
export default _default;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
declare const _default: import('vue').DefineComponent<{}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
|
|
2
|
+
export default _default;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
declare const _default: import('vue').DefineComponent<{}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {
|
|
2
|
+
bgColorPickerRef: unknown;
|
|
3
|
+
bgColorRef: HTMLDivElement;
|
|
4
|
+
strokeColorPickerRef: unknown;
|
|
5
|
+
strokeColorRef: HTMLDivElement;
|
|
6
|
+
fontColorPickerRef: unknown;
|
|
7
|
+
fontColorRef: HTMLDivElement;
|
|
8
|
+
fontBgColorPickerRef: unknown;
|
|
9
|
+
fontBgColorRef: HTMLDivElement;
|
|
10
|
+
}, any>;
|
|
11
|
+
export default _default;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
declare const _default: import('vue').DefineComponent<{}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, HTMLDivElement>;
|
|
2
|
+
export default _default;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
declare const _default: import('vue').DefineComponent<{}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, HTMLDivElement>;
|
|
2
|
+
export default _default;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
declare const _default: import('vue').DefineComponent<{}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, HTMLDivElement>;
|
|
2
|
+
export default _default;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
declare const _default: import('vue').DefineComponent<{}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, HTMLDivElement>;
|
|
2
|
+
export default _default;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
type __VLS_PublicProps = {
|
|
2
|
+
modelValue?: any;
|
|
3
|
+
};
|
|
4
|
+
declare const _default: import('vue').DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
5
|
+
colorChange: (...args: any[]) => void;
|
|
6
|
+
"update:modelValue": (value: any) => void;
|
|
7
|
+
}, string, import('vue').PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
8
|
+
onColorChange?: ((...args: any[]) => any) | undefined;
|
|
9
|
+
"onUpdate:modelValue"?: ((value: any) => any) | undefined;
|
|
10
|
+
}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, HTMLDivElement>;
|
|
11
|
+
export default _default;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
declare const _default: import('vue').DefineComponent<{}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, HTMLDivElement>;
|
|
2
|
+
export default _default;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
declare const _default: import('vue').DefineComponent<{}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, HTMLDivElement>;
|
|
2
|
+
export default _default;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
declare const _default: import('vue').DefineComponent<{}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, HTMLDivElement>;
|
|
2
|
+
export default _default;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ExcalidrawElement } from '../types/element';
|
|
2
|
+
export interface ElementOptions {
|
|
3
|
+
strokeColor?: string;
|
|
4
|
+
backgroundColor?: string;
|
|
5
|
+
fillStyle?: string;
|
|
6
|
+
strokeWidth?: number;
|
|
7
|
+
strokeStyle?: string;
|
|
8
|
+
roughness?: number;
|
|
9
|
+
opacity?: number;
|
|
10
|
+
strokeOpacity?: number;
|
|
11
|
+
backgroundOpacity?: number;
|
|
12
|
+
roundness?: number;
|
|
13
|
+
fontSize?: number;
|
|
14
|
+
fontFamily?: string;
|
|
15
|
+
fontColor?: string;
|
|
16
|
+
fontBgColor?: string;
|
|
17
|
+
textAlign?: "left" | "center" | "right";
|
|
18
|
+
verticalAlign?: "top" | "middle" | "bottom";
|
|
19
|
+
isBold?: boolean;
|
|
20
|
+
isItalic?: boolean;
|
|
21
|
+
isUnderline?: boolean;
|
|
22
|
+
isStrikethrough?: boolean;
|
|
23
|
+
arrowType?: "sharp" | "round" | "elbow";
|
|
24
|
+
}
|
|
25
|
+
export declare const newElement: (type: ExcalidrawElement["type"], x: number, y: number, options?: ElementOptions) => ExcalidrawElement;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ExcalidrawElement } from '../types/element';
|
|
2
|
+
export declare const renderScene: (canvas: HTMLCanvasElement, elements: readonly ExcalidrawElement[], scrollX: number, scrollY: number, zoom: number, selectedElementIds?: Record<string, boolean>, editingElementId?: string | null, erasingElementIds?: Record<string, boolean>, highlightedFrameId?: string | null, highlightedElementIds?: Record<string, boolean>, gridType?: string, hoveredLockIconId?: string | null, viewBackgroundColor?: string) => void;
|
|
3
|
+
export declare const renderLaserTrails: (canvas: HTMLCanvasElement, trails: {
|
|
4
|
+
x: number;
|
|
5
|
+
y: number;
|
|
6
|
+
time: number;
|
|
7
|
+
}[][], zoom: number, scrollX: number, scrollY: number) => void;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Point } from '../core';
|
|
2
|
+
import { PointGraph, PointNode } from './data-structures/graph';
|
|
3
|
+
export declare class AStar {
|
|
4
|
+
private graph;
|
|
5
|
+
cameFrom: Map<PointNode, PointNode>;
|
|
6
|
+
constructor(graph: PointGraph);
|
|
7
|
+
heuristic(a: Point, b: Point): number;
|
|
8
|
+
search(start: Point, end: Point, previousStart: Point): void;
|
|
9
|
+
getRoute(start: Point, end: Point): Point[];
|
|
10
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Point } from '../../core';
|
|
2
|
+
export declare class PointNode {
|
|
3
|
+
data: Point;
|
|
4
|
+
distance: number;
|
|
5
|
+
adjacentNodes: PointNode[];
|
|
6
|
+
constructor(data: Point);
|
|
7
|
+
}
|
|
8
|
+
export declare class PointGraph {
|
|
9
|
+
private index;
|
|
10
|
+
add(p: Point): void;
|
|
11
|
+
connect(a: Point, b: Point): void;
|
|
12
|
+
has(p: Point): boolean;
|
|
13
|
+
get(p: Point): PointNode | null;
|
|
14
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { PointNode } from './graph';
|
|
2
|
+
export declare class PriorityQueue {
|
|
3
|
+
list: {
|
|
4
|
+
node: PointNode;
|
|
5
|
+
priority: number;
|
|
6
|
+
}[];
|
|
7
|
+
constructor();
|
|
8
|
+
enqueue(item: {
|
|
9
|
+
node: PointNode;
|
|
10
|
+
priority: number;
|
|
11
|
+
}): void;
|
|
12
|
+
dequeue(): {
|
|
13
|
+
node: PointNode;
|
|
14
|
+
priority: number;
|
|
15
|
+
} | undefined;
|
|
16
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const BASE = 4;
|
|
2
|
+
export declare const PRIMARY_COLOR = "#6698FF";
|
|
3
|
+
export declare const RESIZE_HANDLE_DIAMETER = 9;
|
|
4
|
+
export declare const WithTextPluginKey = "plait-text-plugin-key";
|
|
5
|
+
export declare const DEFAULT_ROUTE_MARGIN = 30;
|
|
6
|
+
export declare const TRANSPARENT = "transparent";
|
|
7
|
+
export declare const ROTATE_HANDLE_DISTANCE_TO_ELEMENT = 4;
|
|
8
|
+
export declare const ROTATE_HANDLE_SIZE = 18;
|
|
9
|
+
export declare const DEFAULT_FONT_FAMILY = "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'PingFang SC', 'Noto Sans', 'Noto Sans CJK SC', 'Microsoft Yahei', 'Hiragino Sans GB', Arial, sans-serif";
|
|
10
|
+
export declare const DEFAULT_FILL = "none";
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export type Point = [number, number];
|
|
2
|
+
export type Vector = [number, number];
|
|
3
|
+
export declare enum Direction {
|
|
4
|
+
top = "top",
|
|
5
|
+
bottom = "bottom",
|
|
6
|
+
left = "left",
|
|
7
|
+
right = "right"
|
|
8
|
+
}
|
|
9
|
+
export interface PlaitBoard {
|
|
10
|
+
}
|
|
11
|
+
export interface DebugGenerator {
|
|
12
|
+
clear(): void;
|
|
13
|
+
drawRectangle(board: PlaitBoard, rect: RectangleClient): void;
|
|
14
|
+
drawLine(board: PlaitBoard, points: Point[]): void;
|
|
15
|
+
}
|
|
16
|
+
export declare const createDebugGenerator: (name: string) => DebugGenerator;
|
|
17
|
+
export declare const distanceBetweenPointAndPoint: (x1: number, y1: number, x2: number, y2: number) => number;
|
|
18
|
+
export declare class RectangleClient {
|
|
19
|
+
x: number;
|
|
20
|
+
y: number;
|
|
21
|
+
width: number;
|
|
22
|
+
height: number;
|
|
23
|
+
constructor(x: number, y: number, width: number, height: number);
|
|
24
|
+
static getRectangleByPoints(points: Point[]): RectangleClient;
|
|
25
|
+
static getCornerPointsByPoints(points: Point[]): Point[];
|
|
26
|
+
static isHit(rect1: RectangleClient, rect2: RectangleClient): boolean;
|
|
27
|
+
static isHitX(rect1: RectangleClient, rect2: RectangleClient): boolean;
|
|
28
|
+
static isHitY(rect1: RectangleClient, rect2: RectangleClient): boolean;
|
|
29
|
+
static isPointInRectangle(rect: RectangleClient, point: Point): boolean;
|
|
30
|
+
static getGapCenter(rect1: RectangleClient, rect2: RectangleClient, isHorizontal: boolean): number;
|
|
31
|
+
static expand(rect: RectangleClient, left: number, top: number, right: number, bottom: number): RectangleClient;
|
|
32
|
+
}
|
|
33
|
+
export declare namespace Point {
|
|
34
|
+
function isEquals(p1: Point, p2: Point): boolean;
|
|
35
|
+
function isOverHorizontal(points: Point[]): boolean;
|
|
36
|
+
function isOverVertical(points: Point[]): boolean;
|
|
37
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Direction, PlaitBoard, Point, RectangleClient } from '../core';
|
|
2
|
+
import { PointGraph } from '../algorithms';
|
|
3
|
+
export interface ElbowLineRouteOptions {
|
|
4
|
+
sourcePoint: Point;
|
|
5
|
+
nextSourcePoint: Point;
|
|
6
|
+
sourceRectangle: RectangleClient;
|
|
7
|
+
sourceOuterRectangle: RectangleClient;
|
|
8
|
+
targetPoint: Point;
|
|
9
|
+
nextTargetPoint: Point;
|
|
10
|
+
targetOuterRectangle: RectangleClient;
|
|
11
|
+
targetRectangle: RectangleClient;
|
|
12
|
+
}
|
|
13
|
+
export interface RouteAdjustOptions {
|
|
14
|
+
centerX?: number;
|
|
15
|
+
centerY?: number;
|
|
16
|
+
sourceRectangle: RectangleClient;
|
|
17
|
+
targetRectangle: RectangleClient;
|
|
18
|
+
}
|
|
19
|
+
export interface AdjustOptions {
|
|
20
|
+
parallelPaths: [Point, Point][];
|
|
21
|
+
pointOfHit: Point;
|
|
22
|
+
sourceRectangle: RectangleClient;
|
|
23
|
+
targetRectangle: RectangleClient;
|
|
24
|
+
}
|
|
25
|
+
export declare const generateElbowLineRoute: (options: ElbowLineRouteOptions, board?: PlaitBoard) => Point[];
|
|
26
|
+
export declare const routeAdjust: (path: Point[], options: RouteAdjustOptions, board?: PlaitBoard) => Point[];
|
|
27
|
+
export declare const getGraphPoints: (options: ElbowLineRouteOptions) => Point[];
|
|
28
|
+
export declare const createGraph: (points: Point[]) => PointGraph;
|
|
29
|
+
export declare const reduceRouteMargin: (sourceRectangle: RectangleClient, targetRectangle: RectangleClient) => {
|
|
30
|
+
sourceOffset: number[];
|
|
31
|
+
targetOffset: number[];
|
|
32
|
+
};
|
|
33
|
+
export declare const getNextPoint: (point: Point, outerRectangle: RectangleClient, direction: Direction) => Point;
|
|
34
|
+
export declare const getSourceAndTargetOuterRectangle: (sourceRectangle: RectangleClient, targetRectangle: RectangleClient) => {
|
|
35
|
+
sourceOuterRectangle: RectangleClient;
|
|
36
|
+
targetOuterRectangle: RectangleClient;
|
|
37
|
+
};
|
|
38
|
+
export declare const isSourceAndTargetIntersect: (options: ElbowLineRouteOptions) => boolean;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Point } from '../core';
|
|
2
|
+
export declare function getPointOnPolyline(points: Point[], ratio: number): any[] | undefined;
|
|
3
|
+
export declare function calculatePolylineLength(points: Point[]): number;
|
|
4
|
+
export declare function getRatioByPoint(points: Point[], point: Point): number;
|
|
5
|
+
export declare const removeDuplicatePoints: (points: Point[]) => Point[];
|
|
6
|
+
export declare function simplifyOrthogonalPoints(points: Point[]): Point[];
|
|
7
|
+
export declare const getExtendPoint: (source: Point, target: Point, extendDistance: number) => Point;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Point, Vector } from '../core';
|
|
2
|
+
export declare function getUnitVectorByPointAndPoint(point1: Point, point2: Point): Point;
|
|
3
|
+
export declare function getPointByVectorComponent(point: Point, vector: Vector, component: number): Point;
|
|
4
|
+
export declare function getPointByVectorDirectionComponent(point: Point, unitVector: Vector, directionComponent: number, isHorizontal: boolean): Point;
|
|
5
|
+
export declare function rotateVectorAnti90(vector: Vector): Vector;
|
|
6
|
+
export declare function rotateVector(vector: Vector, angle: number): Vector;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.toolbar[data-v-b6ea5f5c]{position:absolute;top:50%;left:24px;transform:translateY(-50%);display:flex;flex-direction:column;background:var(--color-bg-panel);padding:8px 4px;box-sizing:border-box;border-radius:var(--radius-md);box-shadow:0 0 15px #363b4c1a;z-index:100;align-items:center;width:56px}.tool-group[data-v-b6ea5f5c]{display:flex;flex-direction:column;gap:8px}.tool-group .tool-item[data-v-b6ea5f5c]{height:36px;width:36px;cursor:pointer;display:flex;align-items:center;justify-content:center;border-radius:4px;position:relative}.tool-group .tool-item[data-v-b6ea5f5c]:hover{background:#eeeff0}.tool-group .tool-item .svg-icon[data-v-b6ea5f5c]{width:20px;height:20px}.tool-group .active[data-v-b6ea5f5c]{background:#1e6fff1f;color:var(--color-primary)}.tool-group .active[data-v-b6ea5f5c]:hover{background:#1e6fff1f}.tool-group .trand-line[data-v-b6ea5f5c]{height:1px;background:#e4e7eb;width:36px;cursor:default}.tool-group .trand-line[data-v-b6ea5f5c]:hover{background:#e4e7eb}.separator[data-v-b6ea5f5c]{width:20px;height:1px;background:var(--color-border);margin:8px 0}button[data-v-b6ea5f5c]{display:flex;align-items:center;justify-content:center;width:36px;height:36px;border-radius:var(--radius-sm);color:var(--color-text);position:relative;outline:none}button i[data-v-b6ea5f5c]{font-size:16px;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.shortcut[data-v-b6ea5f5c]{position:absolute;bottom:2px;right:2px;font-size:8px;font-weight:500;opacity:.6}button.destructive[data-v-b6ea5f5c]:hover{background:#fff0f0;color:#ff4d4f}.sub-tools-container[data-v-b6ea5f5c]{display:grid;grid-template-columns:repeat(5,1fr);gap:6px}.sub-tools-container .sub-tool-item[data-v-b6ea5f5c]{display:flex;justify-content:space-between;align-items:center;border-radius:4px;justify-content:center;font-size:13px;color:var(--color-text);transition:all .2s;cursor:pointer;width:36px;height:36px}.sub-tools-container .sub-tool-item .svg-icon[data-v-b6ea5f5c]{width:24px;height:24px;min-width:24px}.sub-tools-container .sub-tool-item[data-v-b6ea5f5c]:hover{background:var(--color-bg-hover, #f0f2f5)}.sub-tools-container .sub-tool-item.active[data-v-b6ea5f5c]{background:#1e6fff1f;color:var(--color-primary, #1e6fff)}.sub-tools-container .sub-tool-item .sub-tool-label[data-v-b6ea5f5c]{font-weight:500}.sub-tools-container .sub-tool-item .sub-tool-shortcut[data-v-b6ea5f5c]{font-size:12px;color:var(--color-text-secondary, #8c909e)}.fill-style-box[data-v-9356d2e1]{height:32px;min-height:32px;background:#f5f6f7;border-radius:4px;display:grid;padding:3px 8px;box-sizing:border-box;grid-template-columns:repeat(3,1fr);grid-column-gap:8px}.fill-style-box .fill-style-item[data-v-9356d2e1]{display:flex;align-items:center;justify-content:center;cursor:pointer;border-radius:4px;height:26px}.fill-style-box .fill-style-item .svg-icon[data-v-9356d2e1]{min-width:24px;width:24px;height:16px}.fill-style-box .active[data-v-9356d2e1]{background:#fff}.fill-style-box[data-v-dcf857c8]{height:32px;min-height:32px;background:#f5f6f7;border-radius:4px;display:grid;padding:3px 8px;box-sizing:border-box;grid-template-columns:repeat(3,1fr);grid-column-gap:8px}.fill-style-box .fill-style-item[data-v-dcf857c8]{display:flex;align-items:center;justify-content:center;cursor:pointer;border-radius:4px;height:26px}.fill-style-box .fill-style-item .svg-icon[data-v-dcf857c8]{min-width:24px;width:24px;height:16px}.fill-style-box .active[data-v-dcf857c8]{background:#fff}.fill-style-box[data-v-794ba895]{height:32px;min-height:32px;background:#f5f6f7;border-radius:4px;display:grid;padding:3px 8px;box-sizing:border-box;grid-template-columns:repeat(3,1fr);grid-column-gap:8px}.fill-style-box .fill-style-item[data-v-794ba895]{display:flex;align-items:center;justify-content:center;cursor:pointer;border-radius:4px;height:26px}.fill-style-box .fill-style-item .svg-icon[data-v-794ba895]{min-width:24px;width:24px;height:16px}.fill-style-box .active[data-v-794ba895]{background:#fff}.fill-style-box[data-v-36600f47]{height:32px;min-height:32px;background:#f5f6f7;border-radius:4px;display:grid;padding:3px 8px;box-sizing:border-box;grid-template-columns:repeat(4,1fr);grid-column-gap:8px}.fill-style-box .fill-style-item[data-v-36600f47]{display:flex;align-items:center;justify-content:center;cursor:pointer;border-radius:4px;height:26px}.fill-style-box .fill-style-item .svg-icon[data-v-36600f47]{min-width:16px;width:16px;height:16px}.fill-style-box .active[data-v-36600f47]{background:#fff}.fill-style-box[data-v-b408686c]{height:32px;min-height:32px;background:#f5f6f7;border-radius:4px;display:grid;padding:3px 8px;box-sizing:border-box;grid-template-columns:repeat(6,1fr);grid-column-gap:8px}.fill-style-box .fill-style-item[data-v-b408686c]{display:flex;align-items:center;justify-content:center;cursor:pointer;border-radius:4px;height:26px}.fill-style-box .fill-style-item .svg-icon[data-v-b408686c]{min-width:16px;width:16px;height:16px}.fill-style-box .active[data-v-b408686c]{background:#fff}.no-vertical-align[data-v-b408686c]{grid-template-columns:repeat(3,1fr)}.fill-style-box[data-v-b56fafad]{height:32px;min-height:32px;background:#f5f6f7;border-radius:4px;display:grid;padding:3px 8px;box-sizing:border-box;grid-template-columns:repeat(2,1fr);grid-column-gap:8px}.fill-style-box .fill-style-item[data-v-b56fafad]{display:flex;align-items:center;justify-content:center;cursor:pointer;border-radius:4px;height:26px}.fill-style-box .fill-style-item .svg-icon[data-v-b56fafad]{min-width:24px;width:24px;height:16px}.fill-style-box .active[data-v-b56fafad]{background:#fff}.properties-panel[data-v-00889af3]{position:absolute;top:0;right:0;bottom:0;width:320px;box-sizing:border-box;background:#fff;border-left:1px solid var(--color-border);box-shadow:var(--shadow-lg);z-index:90;display:flex;flex-direction:column;gap:16px;overflow-y:auto;border-radius:0}.properties-panel .el-slider[data-v-00889af3]{--el-slider-button-size: 16px;width:100%}.properties-panel .el-slider[data-v-00889af3] .el-input__wrapper,.properties-panel .el-slider[data-v-00889af3] .el-select__wrapper,.properties-panel .el-slider[data-v-00889af3] .el-textarea__inner{background:#f5f6f7;box-shadow:none}.properties-panel .el-slider[data-v-00889af3] .el-slider__button{border-width:1px}.properties-panel .el-slider[data-v-00889af3] .el-slider__runway.show-input{margin-right:20px}.properties-panel .el-slider[data-v-00889af3] .el-input__wrapper{padding-left:8px;padding-right:8px}.properties-panel .el-slider[data-v-00889af3] .el-input__wrapper .el-input__inner{text-align:left}.properties-panel .el-slider[data-v-00889af3] .el-slider__input{width:56px}.header[data-v-00889af3]{display:flex;justify-content:space-between;align-items:center;padding:16px 16px 0}.header div[data-v-00889af3]{font-size:14px;color:#363b4c;font-weight:600}.header svg[data-v-00889af3]{width:14px;height:14px;fill:#363b4c;cursor:pointer}.content[data-v-00889af3]{display:flex;flex-direction:column;gap:16px;padding-top:2px}.shape-panel-box[data-v-00889af3]{display:flex;gap:8px;align-items:center;padding:0 16px;box-sizing:border-box}.shape-panel-row[data-v-00889af3]{display:grid;grid-template-columns:repeat(2,1fr);gap:8px}.shape-panel-row[data-v-00889af3] .el-input-number__decrease,.shape-panel-row[data-v-00889af3] .el-input-number__increase{display:none!important}.shape-panel-row .input-box[data-v-00889af3]{width:120px;display:flex;align-items:center;font-weight:400;font-size:12px;color:#6f7588;gap:8px;background:#f5f6f7;border-radius:4px;padding:0 8px;box-sizing:border-box;position:relative}.shape-panel-row .input-box .unit[data-v-00889af3]{font-size:16px;position:absolute;left:32px;top:50%;transform:translateY(-60%)}.shape-panel-row .input-box .icon[data-v-00889af3]{width:16px;font-size:14px;display:flex;align-items:center;justify-content:center}.shape-panel-row .input-box .svg-icon[data-v-00889af3]{width:16px;height:16px;min-width:16px}.shape-panel-row .input-box .el-input-number[data-v-00889af3]{width:100%}.shape-panel-row .input-box .el-input-number[data-v-00889af3] .el-input__wrapper{padding:0!important;box-shadow:none!important;background:transparent!important}.shape-panel-row .input-box .el-input-number[data-v-00889af3] .el-input__wrapper .el-input__inner{text-align:left}.shape-panel-row .btn-box[data-v-00889af3]{display:grid;grid-template-columns:repeat(2,1fr);gap:8px;cursor:pointer}.shape-panel-row .btn-box .btn-item[data-v-00889af3]{display:flex;align-items:center;justify-content:center;cursor:pointer;width:100%;background:#f5f6f7;border-radius:4px}.shape-panel-row .btn-box .btn-item .svg-icon[data-v-00889af3]{width:16px;height:16px;min-width:16px}.shape-panel-row .input-box[data-v-00889af3]:has(.is-focus){box-shadow:0 0 0 1px #1e6fff}.not-bind-width-height .input-box[data-v-00889af3]{width:100%}.bind-box[data-v-00889af3]{width:32px;height:32px;cursor:pointer;background:#f5f6f7;border-radius:4px;display:flex;align-items:center;justify-content:center}.bind-box .svg-icon[data-v-00889af3]{width:16px;height:16px;min-width:16px}.bind-box.active[data-v-00889af3]{background-color:#1e6fff!important}.roundness-box[data-v-00889af3]{display:flex;align-items:center;gap:8px;padding:0 16px}.roundness-box .btn-box[data-v-00889af3]{cursor:pointer;width:32px;min-width:32px;height:32px;background:#f5f6f7;border-radius:4px;display:flex;align-items:center;justify-content:center}.roundness-box .btn-box .svg-icon[data-v-00889af3]{width:16px;height:16px;min-width:16px}.item-label[data-v-00889af3]{font-weight:400;font-size:14px;color:#6f7588;word-break:keep-all}.divider[data-v-00889af3]{width:100%;height:1px;background:#eeeff0}.type-item-title[data-v-00889af3]{padding:0 16px}.type-item-title .title[data-v-00889af3]{font-weight:700;font-size:14px}.style-item[data-v-00889af3]{display:flex;gap:8px;padding:0 16px}.style-item .title[data-v-00889af3]{width:80px;min-width:80px;font-weight:400;font-size:14px;color:#6f7588;height:32px;line-height:32px}.style-item .style-boxs[data-v-00889af3]{width:100%;gap:8px;display:grid}.style-item .style-boxs .style-boxs-item[data-v-00889af3]{display:grid;grid-template-columns:repeat(2,1fr);gap:8px}.style-item .style-boxs .style-boxs-item[data-v-00889af3] .el-select__wrapper{box-shadow:none!important;background:#f5f6f7!important;padding:0 8px}.style-item .style-boxs .style-boxs-item[data-v-00889af3] .el-select__wrapper:hover{background:#f5f6f7}.style-item .style-boxs .style-boxs-item[data-v-00889af3] .is-focused{box-shadow:0 0 0 1px #1e6fff!important}.style-item .style-boxs .style-boxs-item[data-v-00889af3] .el-input__inner{color:#666;font-size:14px;text-align:center}.style-item .style-boxs .style-boxs-item[data-v-00889af3] .el-input__wrapper{background:#f5f6f7;box-shadow:none}.style-item .style-boxs .style-boxs-item[data-v-00889af3] .el-input__wrapper .el-input__inner{text-align:left!important}.style-item .style-boxs .style-boxs-item[data-v-00889af3] .el-input__wrapper.is-focus{box-shadow:0 0 0 1px #1e6fff!important}.style-item .input-value-box[data-v-00889af3]{background:#f5f6f7;border-radius:4px;width:100%;height:32px;display:flex;align-items:center;padding:0 8px}.style-item .input-value-box .svg-icon[data-v-00889af3]{width:20px;height:20px;border-radius:4px}.style-item .input-value-box .color-box[data-v-00889af3]{width:19px;height:19px;border-radius:4px;border:1px solid #eeeff0}.style-item .input-value-box .color-text[data-v-00889af3]{height:20px;display:flex;align-items:center;margin-left:8px}.context-menu[data-v-22c72497]{position:fixed;z-index:1000;background:#fff;border-radius:4px;box-shadow:0 2px 10px #0003;padding:4px 0;min-width:200px;font-family:sans-serif;font-size:14px;color:#333}.context-menu ul[data-v-22c72497]{list-style:none;margin:0;padding:0}.context-menu li[data-v-22c72497]{padding:8px 16px;cursor:pointer;display:flex;justify-content:space-between;align-items:center}.context-menu li[data-v-22c72497]:hover{background-color:#f0f0f0}.divider[data-v-22c72497]{height:1px;background-color:#e0e0e0;margin:8px 0}.delete[data-v-22c72497]{color:red}.shortcut[data-v-22c72497]{font-size:12px;color:#999;margin-left:16px}.excalidraw-container[data-v-b4198a57]{width:100%;height:100%;overflow:hidden;background-color:#fff;background-image:linear-gradient(rgba(0,0,0,.03) 1px,transparent 1px),linear-gradient(90px,rgba(0,0,0,.03) 1px,transparent 1px);background-size:20px 20px}canvas[data-v-b4198a57]{display:block}.text-editor[data-v-b4198a57]{background:transparent;border:none;padding:4px;box-sizing:border-box;margin:0;resize:none;outline:none;overflow:hidden;z-index:50;font-family:Virgil,sans-serif;white-space:pre-wrap;word-break:break-all;overflow-wrap:break-word;width:100%;text-align:center;vertical-align:center}.excalidraw-textContainer[data-v-b4198a57]{display:flex;align-items:center;position:absolute;background:transparent!important;cursor:text}.color-box[data-v-9b04b94a]{display:grid;grid-template-columns:repeat(8,1fr);grid-gap:6px}.color-box .color-item[data-v-9b04b94a]{width:20px;height:20px;border-radius:4px;cursor:pointer;border:1px solid #EEEFF0;position:relative}.color-box .active[data-v-9b04b94a]:after{content:"";position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:calc(100% + 2px);height:calc(100% + 2px);border-radius:6px;border:2px solid #B7D1FF}.color-box .trans-icon[data-v-9b04b94a]{width:22px;height:22px;position:relative;cursor:pointer}.color-box .trans-icon .svg-icon[data-v-9b04b94a]{width:22px;height:22px}.color-box .color-item-add[data-v-9b04b94a]{width:20px;height:20px;border-radius:4px;cursor:pointer;border:1px solid transparent}.color-box .color-item-add .svg-icon[data-v-9b04b94a]{width:20px;height:20px}.board-name-container[data-v-772e3fac]{position:absolute;top:24px;left:24px;height:44px;z-index:101;background:#ffffffe6;border-radius:4px;display:flex;align-items:center;box-shadow:0 2px 8px #363b4c26;padding:0 16px}.board-name-input[data-v-772e3fac]{height:44px;font-size:16px;color:#363b4c;border:none;padding:0 12px;font-family:inherit;outline:none;min-width:200px}.board-name-input[data-v-772e3fac] .el-input__wrapper{box-shadow:none;background-color:transparent!important}.board-name-input[data-v-772e3fac]:focus{box-shadow:0 2px 8px #363b4c40}.action-item[data-v-772e3fac]{width:32px;height:32px;min-width:32px;border-radius:4px;cursor:pointer;display:flex;align-items:center;justify-content:center}.action-item[data-v-772e3fac]:hover{background:#f5f6f7}.svg-icon[data-v-772e3fac]{width:20px;height:20px;cursor:pointer}.board-settings-popover,.preferences-popover{padding:8px 0!important}.menu-list[data-v-772e3fac]{display:flex;flex-direction:column}.menu-item[data-v-772e3fac]{height:36px;display:flex;align-items:center;justify-content:space-between;margin:0 8px;padding:0 8px;cursor:pointer;color:#363b4c;font-size:14px;transition:background .2s;border-radius:4px}.menu-item[data-v-772e3fac]:hover{background:#f5f6f7}.menu-item-content[data-v-772e3fac]{display:flex;align-items:center;gap:8px}.menu-icon[data-v-772e3fac]{font-size:16px;width:16px;height:16px;color:#8c909e}.menu-arrow[data-v-772e3fac]{font-size:16px;color:#8c909e}.preferences-list[data-v-772e3fac]{display:flex;flex-direction:column;gap:8px}.preferences-list .line[data-v-772e3fac]{width:100%;height:1px;background:#eeeff0}.preferences-list .grid-item[data-v-772e3fac]{display:flex;align-items:center;padding:8px 16px;gap:8px;cursor:pointer;border-radius:4px}.preferences-list .grid-item .svg-icon[data-v-772e3fac]{width:16px;height:16px}.preferences-list .grid-item[data-v-772e3fac]:hover{background:#f5f6f7}.preferences-list .active[data-v-772e3fac]{background:#d2e2ff!important;color:#1e6fff}.preference-item[data-v-772e3fac]{display:flex;align-items:center;justify-content:space-between;padding:8px 16px}.preference-info[data-v-772e3fac]{display:flex;flex-direction:column;gap:4px;width:100%}.preference-title[data-v-772e3fac]{font-size:14px;color:#363b4c;display:flex;align-items:center;justify-content:space-between}.preference-desc[data-v-772e3fac]{font-size:12px;color:#8c909e}.board-settings-popover{transform:translate(-10px)!important}.preferences-popover{transform:translate(6px)}.sub-preferences-popover{padding:0!important;transform:translate(6px)}.sub-preferences-popover .preferences-list{padding:16px!important;gap:8px}.sub-preferences-popover .preferences-list .title{font-weight:400;font-size:14px;color:#6f7588}.sub-preferences-popover .grid-list{padding:8px!important}.footer-controls[data-v-06dd6954]{position:absolute;bottom:24px;right:24px;display:flex;gap:12px;z-index:100;transition:right .3s ease}.zoom-controls[data-v-06dd6954],.history-controls[data-v-06dd6954],.help-controls[data-v-06dd6954]{display:flex;align-items:center;padding:4px;box-sizing:border-box;border-radius:var(--radius-md);box-shadow:var(--shadow-md);height:44px;gap:4px;border:0px}.tool-item[data-v-06dd6954]{cursor:pointer;width:28px;height:28px;display:flex;align-items:center;justify-content:center;border-radius:var(--radius-sm)}.tool-item[data-v-06dd6954]:hover{background:#f5f6f7}.tool-item .svg-icon[data-v-06dd6954]{width:24px;height:24px}.disabled[data-v-06dd6954]{cursor:not-allowed}.disabled[data-v-06dd6954]:hover{background-color:transparent}.separator[data-v-06dd6954]{width:1px;height:20px;background:var(--color-border);margin:0 4px}button[data-v-06dd6954]{width:28px;height:28px;display:flex;align-items:center;justify-content:center;border-radius:var(--radius-sm);color:var(--color-text);font-size:16px;border:none;background:transparent;cursor:pointer}button[data-v-06dd6954]:hover:not(:disabled){background:#0000000d}button[data-v-06dd6954]:disabled{opacity:.3;cursor:not-allowed}.active[data-v-06dd6954]{background:#1e6fff1f!important;color:var(--color-primary)}button.destructive[data-v-06dd6954]:hover{background:#fff0f0;color:#ff4d4f}span[data-v-06dd6954]{font-size:12px;font-weight:500;color:var(--color-text);min-width:40px;text-align:center;-webkit-user-select:none;user-select:none}.modal-overlay[data-v-0caac542]{position:fixed;top:0;left:0;width:100vw;height:100vh;background:#0006;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);z-index:1000;display:flex;align-items:center;justify-content:center}.help-dialog[data-v-0caac542]{width:800px;max-width:90vw;max-height:85vh;background:#fff;border-radius:var(--radius-lg);box-shadow:var(--shadow-xl);display:flex;flex-direction:column;overflow:hidden}.header[data-v-0caac542]{padding:16px 24px;border-bottom:1px solid rgba(0,0,0,.1);display:flex;justify-content:space-between;align-items:center}.header h2[data-v-0caac542]{margin:0;font-size:18px;font-weight:600}.close-btn[data-v-0caac542]{background:none;border:none;font-size:24px;cursor:pointer;color:var(--color-text-muted);width:32px;height:32px;display:flex;align-items:center;justify-content:center;border-radius:var(--radius-sm)}.close-btn[data-v-0caac542]:hover{background:#0000000d;color:var(--color-text)}.content[data-v-0caac542]{padding:24px;overflow-y:auto;display:grid;grid-template-columns:1fr 1fr;gap:40px}.column[data-v-0caac542]{display:flex;flex-direction:column;gap:24px}h3[data-v-0caac542]{margin:0;font-size:14px;font-weight:600;color:var(--color-text-muted);text-transform:uppercase}.shortcut-list[data-v-0caac542]{display:flex;flex-direction:column;gap:8px}.item[data-v-0caac542]{display:flex;justify-content:space-between;align-items:center;font-size:14px;color:var(--color-text);padding:4px 0}.keys[data-v-0caac542]{display:flex;gap:4px;align-items:center}kbd[data-v-0caac542]{background:#f8f9fa;border:1px solid #dee2e6;border-radius:4px;padding:2px 6px;font-family:monospace;font-size:12px;box-shadow:0 1px #0000001a;min-width:20px;text-align:center}.popovers-container[data-v-1fe4c384]{display:flex;align-items:center;gap:4px}.settings-btn[data-v-1fe4c384]{background:transparent;border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;color:#666;width:32px;height:32px;transition:all .2s;border-radius:4px}.settings-btn .svg-icon[data-v-1fe4c384]{width:20px;height:20px;border-radius:4px}.settings-btn .color-box[data-v-1fe4c384]{width:19px;height:19px;border-radius:4px;border:1px solid #eeeff0}.settings-btn .border-box[data-v-1fe4c384]{width:16px;height:16px;border-radius:4px;border:2px solid #eeeff0}.settings-btn[data-v-1fe4c384]:hover{background:#f5f6f7;color:var(--color-primary, #4b9fff)}.popover-content[data-v-1fe4c384]{display:flex;flex-direction:column;gap:8px}.popover-content .title[data-v-1fe4c384]{font-weight:400;font-size:14px;color:#6f7588;display:flex;align-items:center;justify-content:space-between}.popover-content .el-slider[data-v-1fe4c384]{--el-slider-button-size: 16px;width:100%}.popover-content .el-slider[data-v-1fe4c384] .el-input__wrapper,.popover-content .el-slider[data-v-1fe4c384] .el-select__wrapper,.popover-content .el-slider[data-v-1fe4c384] .el-textarea__inner{background:#f5f6f7;box-shadow:none}.popover-content .el-slider[data-v-1fe4c384] .el-slider__button{border-width:1px}.popover-content .el-slider[data-v-1fe4c384] .el-slider__runway.show-input{margin-right:20px}.popover-content .el-slider[data-v-1fe4c384] .el-input__wrapper{padding-left:8px;padding-right:8px}.popover-content .el-slider[data-v-1fe4c384] .el-input__wrapper .el-input__inner{text-align:left}.popover-content .el-slider[data-v-1fe4c384] .el-slider__input{width:56px}.separator[data-v-1fe4c384]{height:12px}.separator-horizontal[data-v-1fe4c384]{width:100%;height:1px;background-color:var(--color-border, #e0e0e0);margin:4px 0}.popover-title[data-v-1fe4c384]{font-size:12px;font-weight:500;color:var(--color-text, #333)}.font-size-select[data-v-1fe4c384]{display:flex;align-items:center;margin:0 4px}.font-size-select[data-v-1fe4c384] .el-select__wrapper{box-shadow:none!important;background:#f5f6f7!important;padding:0 8px}.font-size-select[data-v-1fe4c384] .el-select__wrapper:hover{background:#f5f6f7}.font-size-select[data-v-1fe4c384] .is-focused{box-shadow:0 0 0 1px #1e6fff!important}.font-size-select[data-v-1fe4c384] .el-input__inner{color:#666;font-size:14px;text-align:center}.button-group[data-v-1fe4c384]{display:flex;gap:4px}.button-group button[data-v-1fe4c384]{flex:1;height:32px;display:flex;align-items:center;justify-content:center;border-radius:4px;background:#0000000a;color:var(--color-text, #333);border:1px solid transparent;cursor:pointer;transition:all .2s}.button-group button[data-v-1fe4c384]:hover{background:#f5f6f7}.button-group button.active[data-v-1fe4c384]{background:#f5f6f7;color:var(--color-primary, #4b9fff)}.button-group button i[data-v-1fe4c384]{font-size:18px}.divider[data-v-1fe4c384]{width:1px;height:20px;background:#e4e7eb}.sub-tools-container[data-v-1fe4c384]{display:grid;grid-template-columns:repeat(5,1fr);gap:6px}.sub-tools-container .sub-tool-item[data-v-1fe4c384]{display:flex;justify-content:space-between;align-items:center;border-radius:4px;justify-content:center;font-size:13px;color:var(--color-text);transition:all .2s;cursor:pointer;width:36px;height:36px}.sub-tools-container .sub-tool-item .svg-icon[data-v-1fe4c384]{width:24px;height:24px;min-width:24px}.sub-tools-container .sub-tool-item[data-v-1fe4c384]:hover{background:var(--color-bg-hover, #f0f2f5)}.sub-tools-container .sub-tool-item.active[data-v-1fe4c384]{background:#1e6fff1f;color:var(--color-primary, #1e6fff)}.sub-tools-container .sub-tool-item .sub-tool-label[data-v-1fe4c384]{font-weight:500}.sub-tools-container .sub-tool-item .sub-tool-shortcut[data-v-1fe4c384]{font-size:12px;color:var(--color-text-secondary, #8c909e)}.custom-toolbar-popover{border-radius:4px!important;padding:16px!important;box-shadow:0 4px 20px #00000026!important;border:1px solid rgba(0,0,0,.1)!important}.floating-toolbar[data-v-206d2632]{position:absolute;height:40px;background:#ffffffe6;border-radius:8px;box-shadow:0 4px 12px #00000026;display:flex;align-items:center;padding:0 8px;gap:8px;z-index:100;border:1px solid var(--color-border);-webkit-user-select:none;user-select:none}.drag-handle[data-v-206d2632]{cursor:grab;display:flex;align-items:center;justify-content:center;color:#666;width:24px;height:24px;border-radius:4px}.drag-handle .svg-icon[data-v-206d2632]{width:16px;height:16px}.drag-handle[data-v-206d2632]:active{cursor:grabbing}.divider[data-v-206d2632]{width:1px;height:20px;background:#e4e7eb}.settings-btn[data-v-206d2632]{background:transparent;border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;color:#666;width:32px;height:32px;border-radius:4px;transition:all .2s}.settings-btn .svg-icon[data-v-206d2632]{width:20px;height:20px}.settings-btn[data-v-206d2632]:hover{background:#0000000d;color:var(--color-primary)}.settings-btn i[data-v-206d2632]{font-size:18px}body{margin:0;padding:0;overflow:hidden;font-family:Inter,sans-serif}.app-container{width:100vw;height:100vh;position:relative}
|