@coding-script/script-engine 0.0.2 → 0.0.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.
- package/README.md +46 -5
- package/dist/components/index.d.ts +1 -1
- package/dist/components/section-header.d.ts +2 -0
- package/dist/components/section-header.js +10 -2
- package/dist/components/toolbar-button.d.ts +1 -9
- package/dist/components/toolbar-button.js +4 -4
- package/dist/components/toolbar.d.ts +4 -0
- package/dist/components/toolbar.js +167 -20
- package/dist/components/variables-section.js +12 -9
- package/dist/script-code.js +4 -2
- package/dist/types/index.d.ts +26 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
- **动态类型自动补全**:基于 `ScriptMetadata` 提供变量名补全和点号链式访问补全(如 `request.test.name`)
|
|
10
10
|
- **Groovy 语法提示**:内置 `if`/`for`/`while`/`println`/`return` 等常用 Groovy 语法片段
|
|
11
11
|
- **代码格式化**:内置 Groovy 格式化器,也可通过 `onFormat` 自定义格式化逻辑
|
|
12
|
-
-
|
|
12
|
+
- **属性面板**:右侧侧边栏展示主函数签名、函数入参、绑定参数、数据类型(字段和方法),支持折叠/展开和拖拽调节宽度
|
|
13
|
+
- **脚本说明**:工具栏"脚本说明"按钮,点击展开/收起脚本描述弹框,支持多行文本和 `key: value` 格式高亮
|
|
13
14
|
- **主题切换**:暗色/亮色两套主题,编辑器、补全弹窗、属性面板同步切换,编辑器内部管理主题状态
|
|
14
15
|
- **全屏模式**:支持 CSS 全屏(`position: fixed` 覆盖视口),不影响编辑器内容
|
|
15
16
|
- **自定义工具栏**:通过 `toolbarExtra` 传入任意 React 节点
|
|
@@ -31,6 +32,7 @@ import type { ScriptMetadata } from '@coding-script/script-engine';
|
|
|
31
32
|
|
|
32
33
|
const metadata: ScriptMetadata = {
|
|
33
34
|
mainMethod: 'run',
|
|
35
|
+
description: '脚本主入口,接收请求参数并返回执行结果状态码',
|
|
34
36
|
returnType: 'Integer',
|
|
35
37
|
binds: [
|
|
36
38
|
{ dataType: 'GroovyBindObject', name: '$request' },
|
|
@@ -126,7 +128,44 @@ function App() {
|
|
|
126
128
|
|
|
127
129
|
| 属性 | 类型 | 默认值 | 说明 |
|
|
128
130
|
|---|---|---|---|
|
|
129
|
-
| `
|
|
131
|
+
| `toolbar` | `ToolbarItem[]` | `undefined` | 工具栏自定义按钮列表,渲染在内置按钮之后、`toolbarExtra` 之前 |
|
|
132
|
+
| `toolbarExtra` | `React.ReactNode` | `undefined` | 工具栏额外内容,渲染在最后,可传入任意 JSX |
|
|
133
|
+
|
|
134
|
+
`ToolbarItem` 类型:
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
interface ToolbarItem {
|
|
138
|
+
key: string; // 唯一标识
|
|
139
|
+
label: ReactNode; // 按钮内容
|
|
140
|
+
title: string; // 鼠标悬停提示
|
|
141
|
+
backgroundColor: string;
|
|
142
|
+
hoverBackgroundColor: string;
|
|
143
|
+
textColor: string;
|
|
144
|
+
borderColor: string;
|
|
145
|
+
onClick: () => void;
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
**使用按钮列表:**
|
|
150
|
+
|
|
151
|
+
```tsx
|
|
152
|
+
<ScriptCodeEditor
|
|
153
|
+
toolbar={[
|
|
154
|
+
{
|
|
155
|
+
key: 'save',
|
|
156
|
+
label: '💾 保存',
|
|
157
|
+
title: '保存脚本',
|
|
158
|
+
backgroundColor: '#007bff',
|
|
159
|
+
hoverBackgroundColor: '#0056b3',
|
|
160
|
+
textColor: '#fff',
|
|
161
|
+
borderColor: '#007bff',
|
|
162
|
+
onClick: () => saveCode(),
|
|
163
|
+
},
|
|
164
|
+
]}
|
|
165
|
+
/>
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
**使用自定义内容:**
|
|
130
169
|
|
|
131
170
|
```tsx
|
|
132
171
|
<ScriptCodeEditor
|
|
@@ -151,8 +190,10 @@ function App() {
|
|
|
151
190
|
|
|
152
191
|
```typescript
|
|
153
192
|
interface ScriptMetadata {
|
|
154
|
-
/**
|
|
155
|
-
mainMethod
|
|
193
|
+
/** 主函数名称(可选) */
|
|
194
|
+
mainMethod?: string;
|
|
195
|
+
/** 脚本说明(可选,提供后在工具栏显示"脚本说明"按钮,点击展开/收起描述弹框) */
|
|
196
|
+
description?: string;
|
|
156
197
|
/** 注入变量(如 $request,name 含 $ 前缀) */
|
|
157
198
|
binds: ScriptBindInfo[];
|
|
158
199
|
/** 主函数参数 */
|
|
@@ -241,4 +282,4 @@ pnpm run dev:app-pc
|
|
|
241
282
|
|
|
242
283
|
## License
|
|
243
284
|
|
|
244
|
-
|
|
285
|
+
Apache-2.0 license
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { themes, SCROLL_CLASS, ensureScrollbarStyle } from './theme-colors';
|
|
2
2
|
export type { ThemeColors } from './theme-colors';
|
|
3
3
|
export { ToolbarButton } from './toolbar-button';
|
|
4
|
-
export type { ToolbarButtonProps } from '
|
|
4
|
+
export type { ToolbarButtonProps } from '../types';
|
|
5
5
|
export { Toolbar } from './toolbar';
|
|
6
6
|
export type { ToolbarProps } from './toolbar';
|
|
7
7
|
export { ExpandSidebarButton } from './expand-sidebar-button';
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import react from "react";
|
|
2
|
-
const SectionHeader = ({ colors, label })=>/*#__PURE__*/ react.createElement("div", {
|
|
2
|
+
const SectionHeader = ({ colors, label, extra })=>/*#__PURE__*/ react.createElement("div", {
|
|
3
3
|
style: {
|
|
4
|
+
display: 'flex',
|
|
5
|
+
alignItems: 'center',
|
|
6
|
+
justifyContent: 'space-between',
|
|
4
7
|
padding: '6px 12px 4px',
|
|
5
8
|
fontSize: 11,
|
|
6
9
|
textTransform: 'uppercase',
|
|
@@ -8,5 +11,10 @@ const SectionHeader = ({ colors, label })=>/*#__PURE__*/ react.createElement("di
|
|
|
8
11
|
color: colors.textSecondary,
|
|
9
12
|
fontWeight: 600
|
|
10
13
|
}
|
|
11
|
-
}, label)
|
|
14
|
+
}, /*#__PURE__*/ react.createElement("span", null, label), extra && /*#__PURE__*/ react.createElement("div", {
|
|
15
|
+
style: {
|
|
16
|
+
display: 'flex',
|
|
17
|
+
alignItems: 'center'
|
|
18
|
+
}
|
|
19
|
+
}, extra));
|
|
12
20
|
export { SectionHeader };
|
|
@@ -1,11 +1,3 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
|
|
3
|
-
label: React.ReactNode;
|
|
4
|
-
title: string;
|
|
5
|
-
bg: string;
|
|
6
|
-
hoverBg: string;
|
|
7
|
-
color: string;
|
|
8
|
-
border: string;
|
|
9
|
-
onClick: () => void;
|
|
10
|
-
}
|
|
2
|
+
import type { ToolbarButtonProps } from '../types';
|
|
11
3
|
export declare const ToolbarButton: React.FC<ToolbarButtonProps>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import react, { useState } from "react";
|
|
2
|
-
const ToolbarButton = ({ label, title,
|
|
2
|
+
const ToolbarButton = ({ label, title, backgroundColor, hoverBackgroundColor, textColor, borderColor, onClick })=>{
|
|
3
3
|
const [hovered, setHovered] = useState(false);
|
|
4
4
|
return /*#__PURE__*/ react.createElement("button", {
|
|
5
5
|
onClick: onClick,
|
|
@@ -9,9 +9,9 @@ const ToolbarButton = ({ label, title, bg, hoverBg, color, border, onClick })=>{
|
|
|
9
9
|
display: 'inline-flex',
|
|
10
10
|
alignItems: 'center',
|
|
11
11
|
gap: 4,
|
|
12
|
-
background: hovered ?
|
|
13
|
-
border: `1px solid ${
|
|
14
|
-
color,
|
|
12
|
+
background: hovered ? hoverBackgroundColor : backgroundColor,
|
|
13
|
+
border: `1px solid ${borderColor}`,
|
|
14
|
+
color: textColor,
|
|
15
15
|
cursor: 'pointer',
|
|
16
16
|
fontSize: 12,
|
|
17
17
|
padding: '4px 10px',
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import type { ToolbarItem } from '../types';
|
|
2
3
|
export interface ToolbarProps {
|
|
3
4
|
title?: string;
|
|
4
5
|
theme: 'dark' | 'light';
|
|
@@ -11,6 +12,9 @@ export interface ToolbarProps {
|
|
|
11
12
|
enableFullscreen?: boolean;
|
|
12
13
|
isFullscreen?: boolean;
|
|
13
14
|
onToggleFullscreen?: () => void;
|
|
15
|
+
toolbar?: ToolbarItem[];
|
|
14
16
|
toolbarExtra?: React.ReactNode;
|
|
17
|
+
/** 脚本说明内容(有值时显示"脚本说明"按钮) */
|
|
18
|
+
description?: string;
|
|
15
19
|
}
|
|
16
20
|
export declare const Toolbar: React.FC<ToolbarProps>;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import react from "react";
|
|
1
|
+
import react, { useCallback, useEffect, useRef, useState } from "react";
|
|
2
2
|
import { ToolbarButton } from "./toolbar-button.js";
|
|
3
3
|
import { FormatIcon } from "./format-icon.js";
|
|
4
4
|
import { MaximizeIcon, MinimizeIcon } from "./fullscreen-icon.js";
|
|
5
|
+
import { themes } from "./theme-colors.js";
|
|
5
6
|
const HammerIcon = ({ color })=>/*#__PURE__*/ react.createElement("svg", {
|
|
6
7
|
width: "14",
|
|
7
8
|
height: "14",
|
|
@@ -28,13 +29,103 @@ const HammerIcon = ({ color })=>/*#__PURE__*/ react.createElement("svg", {
|
|
|
28
29
|
opacity: 0.8,
|
|
29
30
|
transform: "rotate(-25 12.25 14.5)"
|
|
30
31
|
}));
|
|
31
|
-
const
|
|
32
|
+
const TIP_MARGIN = 6;
|
|
33
|
+
const TIP_MAX_WIDTH = 380;
|
|
34
|
+
function calcTooltipPos(anchorRect, el) {
|
|
35
|
+
const vw = window.innerWidth;
|
|
36
|
+
const vh = window.innerHeight;
|
|
37
|
+
const tipH = el?.scrollHeight ?? 200;
|
|
38
|
+
const spaceBelow = vh - anchorRect.bottom - TIP_MARGIN;
|
|
39
|
+
const spaceAbove = anchorRect.top - TIP_MARGIN;
|
|
40
|
+
const placeBelow = spaceBelow >= Math.min(tipH, 260) || spaceBelow >= spaceAbove;
|
|
41
|
+
const top = placeBelow ? anchorRect.bottom + TIP_MARGIN : Math.max(TIP_MARGIN, anchorRect.top - tipH - TIP_MARGIN);
|
|
42
|
+
let left = anchorRect.left;
|
|
43
|
+
const maxRight = vw - TIP_MARGIN;
|
|
44
|
+
if (left + TIP_MAX_WIDTH > maxRight) left = Math.max(TIP_MARGIN, maxRight - TIP_MAX_WIDTH);
|
|
45
|
+
const maxWidth = Math.min(TIP_MAX_WIDTH, maxRight - left);
|
|
46
|
+
const maxHeight = placeBelow ? spaceBelow - TIP_MARGIN : spaceAbove - TIP_MARGIN;
|
|
47
|
+
return {
|
|
48
|
+
top,
|
|
49
|
+
left,
|
|
50
|
+
maxWidth,
|
|
51
|
+
maxHeight: Math.max(maxHeight, 80)
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
const KV_RE = /^([^:]+):\s*([\s\S]+)$/;
|
|
55
|
+
function renderDescLine(line, accent) {
|
|
56
|
+
const m = line.match(KV_RE);
|
|
57
|
+
if (m) return /*#__PURE__*/ react.createElement("span", null, /*#__PURE__*/ react.createElement("span", {
|
|
58
|
+
style: {
|
|
59
|
+
color: accent,
|
|
60
|
+
fontWeight: 600
|
|
61
|
+
}
|
|
62
|
+
}, m[1]), /*#__PURE__*/ react.createElement("span", null, ": ", m[2]));
|
|
63
|
+
return line;
|
|
64
|
+
}
|
|
65
|
+
const QuestionIcon = ({ color })=>/*#__PURE__*/ react.createElement("svg", {
|
|
66
|
+
width: "13",
|
|
67
|
+
height: "13",
|
|
68
|
+
viewBox: "0 0 24 24",
|
|
69
|
+
fill: "none",
|
|
70
|
+
stroke: color,
|
|
71
|
+
strokeWidth: 2.2,
|
|
72
|
+
strokeLinecap: "round",
|
|
73
|
+
strokeLinejoin: "round",
|
|
74
|
+
style: {
|
|
75
|
+
flexShrink: 0
|
|
76
|
+
}
|
|
77
|
+
}, /*#__PURE__*/ react.createElement("circle", {
|
|
78
|
+
cx: "12",
|
|
79
|
+
cy: "12",
|
|
80
|
+
r: "10"
|
|
81
|
+
}), /*#__PURE__*/ react.createElement("path", {
|
|
82
|
+
d: "M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"
|
|
83
|
+
}), /*#__PURE__*/ react.createElement("line", {
|
|
84
|
+
x1: "12",
|
|
85
|
+
y1: "17",
|
|
86
|
+
x2: "12.01",
|
|
87
|
+
y2: "17"
|
|
88
|
+
}));
|
|
89
|
+
const Toolbar = ({ title, theme, onThemeChange, enableThemeToggle, enableFormat, onFormat, enableCompile, onCompile, enableFullscreen, isFullscreen, onToggleFullscreen, toolbar, toolbarExtra, description })=>{
|
|
32
90
|
const isDark = 'dark' === theme;
|
|
91
|
+
const colors = themes[theme];
|
|
33
92
|
const borderColor = isDark ? '#434343' : '#d9d9d9';
|
|
34
93
|
const toolbarBg = isDark ? '#282c34' : '#fafafa';
|
|
35
94
|
const toolbarText = isDark ? '#abb2bf' : '#333';
|
|
36
95
|
const btnBg = isDark ? '#2c313a' : '#f0f0f0';
|
|
37
96
|
const btnHoverBg = isDark ? '#3e4451' : '#e0e0e0';
|
|
97
|
+
const [showDesc, setShowDesc] = useState(false);
|
|
98
|
+
const [tipPos, setTipPos] = useState(null);
|
|
99
|
+
const descBtnRef = useRef(null);
|
|
100
|
+
const tipRef = useRef(null);
|
|
101
|
+
const updateTipPos = useCallback(()=>{
|
|
102
|
+
if (!descBtnRef.current) return;
|
|
103
|
+
setTipPos(calcTooltipPos(descBtnRef.current.getBoundingClientRect(), tipRef.current));
|
|
104
|
+
}, []);
|
|
105
|
+
useEffect(()=>{
|
|
106
|
+
if (!showDesc) return;
|
|
107
|
+
updateTipPos();
|
|
108
|
+
window.addEventListener('scroll', updateTipPos, true);
|
|
109
|
+
window.addEventListener('resize', updateTipPos);
|
|
110
|
+
return ()=>{
|
|
111
|
+
window.removeEventListener('scroll', updateTipPos, true);
|
|
112
|
+
window.removeEventListener('resize', updateTipPos);
|
|
113
|
+
};
|
|
114
|
+
}, [
|
|
115
|
+
showDesc,
|
|
116
|
+
updateTipPos
|
|
117
|
+
]);
|
|
118
|
+
useEffect(()=>{
|
|
119
|
+
if (!showDesc) setTipPos(null);
|
|
120
|
+
}, [
|
|
121
|
+
showDesc
|
|
122
|
+
]);
|
|
123
|
+
useEffect(()=>{
|
|
124
|
+
if (!description) setShowDesc(false);
|
|
125
|
+
}, [
|
|
126
|
+
description
|
|
127
|
+
]);
|
|
128
|
+
const descLines = description ? description.replace(/\\n/g, '\n').split('\n').filter((l)=>l.trim()) : [];
|
|
38
129
|
const handleThemeToggle = ()=>{
|
|
39
130
|
const next = isDark ? 'light' : 'dark';
|
|
40
131
|
onThemeChange?.(next);
|
|
@@ -64,33 +155,54 @@ const Toolbar = ({ title, theme, onThemeChange, enableThemeToggle, enableFormat,
|
|
|
64
155
|
style: {
|
|
65
156
|
marginRight: 'auto'
|
|
66
157
|
}
|
|
67
|
-
}),
|
|
158
|
+
}), descLines.length > 0 && /*#__PURE__*/ react.createElement("button", {
|
|
159
|
+
ref: descBtnRef,
|
|
160
|
+
onClick: ()=>setShowDesc((v)=>!v),
|
|
161
|
+
style: {
|
|
162
|
+
display: 'inline-flex',
|
|
163
|
+
alignItems: 'center',
|
|
164
|
+
gap: 4,
|
|
165
|
+
padding: '4px 10px',
|
|
166
|
+
borderRadius: 4,
|
|
167
|
+
border: `1px solid ${isDark ? '#2d5a27' : '#b7eb8f'}`,
|
|
168
|
+
background: showDesc ? isDark ? '#3a7033' : '#d4ecd3' : isDark ? '#2d5a27' : '#e6f4e5',
|
|
169
|
+
color: isDark ? '#98c379' : '#389e0d',
|
|
170
|
+
cursor: 'pointer',
|
|
171
|
+
fontSize: 12,
|
|
172
|
+
lineHeight: 1.4,
|
|
173
|
+
whiteSpace: 'nowrap',
|
|
174
|
+
transition: 'background 0.15s'
|
|
175
|
+
},
|
|
176
|
+
title: showDesc ? '隐藏脚本说明' : '查看脚本说明'
|
|
177
|
+
}, /*#__PURE__*/ react.createElement(QuestionIcon, {
|
|
178
|
+
color: "currentColor"
|
|
179
|
+
}), "脚本说明"), enableThemeToggle && /*#__PURE__*/ react.createElement(ToolbarButton, {
|
|
68
180
|
label: isDark ? '🌞 浅色模式' : '🌙 深色模式',
|
|
69
181
|
title: isDark ? '切换到浅色主题' : '切换到深色主题',
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
182
|
+
backgroundColor: btnBg,
|
|
183
|
+
hoverBackgroundColor: btnHoverBg,
|
|
184
|
+
textColor: toolbarText,
|
|
185
|
+
borderColor: borderColor,
|
|
74
186
|
onClick: handleThemeToggle
|
|
75
187
|
}), enableFormat && onFormat && /*#__PURE__*/ react.createElement(ToolbarButton, {
|
|
76
188
|
label: /*#__PURE__*/ react.createElement(react.Fragment, null, /*#__PURE__*/ react.createElement(FormatIcon, {
|
|
77
189
|
color: isDark ? '#61afef' : '#1677ff'
|
|
78
190
|
}), "格式化"),
|
|
79
191
|
title: "格式化代码",
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
192
|
+
backgroundColor: isDark ? '#1e3a5f' : '#e6f4ff',
|
|
193
|
+
hoverBackgroundColor: isDark ? '#264a73' : '#bae0ff',
|
|
194
|
+
textColor: isDark ? '#61afef' : '#1677ff',
|
|
195
|
+
borderColor: isDark ? '#1e3a5f' : '#91caff',
|
|
84
196
|
onClick: onFormat
|
|
85
197
|
}), enableCompile && onCompile && /*#__PURE__*/ react.createElement(ToolbarButton, {
|
|
86
198
|
label: /*#__PURE__*/ react.createElement(react.Fragment, null, /*#__PURE__*/ react.createElement(HammerIcon, {
|
|
87
199
|
color: isDark ? '#98c379' : '#389e0d'
|
|
88
200
|
}), "编译验证"),
|
|
89
201
|
title: "编译并验证脚本",
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
202
|
+
backgroundColor: isDark ? '#2d5a27' : '#e6f4e5',
|
|
203
|
+
hoverBackgroundColor: isDark ? '#3a7033' : '#d4ecd3',
|
|
204
|
+
textColor: isDark ? '#98c379' : '#389e0d',
|
|
205
|
+
borderColor: isDark ? '#2d5a27' : '#b7eb8f',
|
|
94
206
|
onClick: onCompile
|
|
95
207
|
}), enableFullscreen && /*#__PURE__*/ react.createElement(ToolbarButton, {
|
|
96
208
|
label: /*#__PURE__*/ react.createElement(react.Fragment, null, isFullscreen ? /*#__PURE__*/ react.createElement(MinimizeIcon, {
|
|
@@ -99,11 +211,46 @@ const Toolbar = ({ title, theme, onThemeChange, enableThemeToggle, enableFormat,
|
|
|
99
211
|
color: isDark ? '#c678dd' : '#722ed1'
|
|
100
212
|
}), isFullscreen ? '退出全屏' : '全屏模式'),
|
|
101
213
|
title: isFullscreen ? '退出全屏(ESC)' : '全屏显示',
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
214
|
+
backgroundColor: isDark ? '#3a2d5a' : '#f3e8ff',
|
|
215
|
+
hoverBackgroundColor: isDark ? '#4a3d6a' : '#e9d5ff',
|
|
216
|
+
textColor: isDark ? '#c678dd' : '#722ed1',
|
|
217
|
+
borderColor: isDark ? '#3a2d5a' : '#d3adf7',
|
|
106
218
|
onClick: ()=>onToggleFullscreen?.()
|
|
107
|
-
}),
|
|
219
|
+
}), toolbar?.map((item)=>/*#__PURE__*/ react.createElement(ToolbarButton, {
|
|
220
|
+
key: item.key,
|
|
221
|
+
label: item.label,
|
|
222
|
+
title: item.title,
|
|
223
|
+
backgroundColor: item.backgroundColor,
|
|
224
|
+
hoverBackgroundColor: item.hoverBackgroundColor,
|
|
225
|
+
textColor: item.textColor,
|
|
226
|
+
borderColor: item.borderColor,
|
|
227
|
+
onClick: item.onClick
|
|
228
|
+
})), toolbarExtra, showDesc && tipPos && descLines.length > 0 && /*#__PURE__*/ react.createElement("div", {
|
|
229
|
+
ref: tipRef,
|
|
230
|
+
style: {
|
|
231
|
+
position: 'fixed',
|
|
232
|
+
top: tipPos.top,
|
|
233
|
+
left: tipPos.left,
|
|
234
|
+
maxWidth: tipPos.maxWidth,
|
|
235
|
+
maxHeight: tipPos.maxHeight,
|
|
236
|
+
overflowY: 'auto',
|
|
237
|
+
padding: '10px 14px',
|
|
238
|
+
background: colors.headerBg,
|
|
239
|
+
border: `1px solid ${colors.border}`,
|
|
240
|
+
borderRadius: 6,
|
|
241
|
+
color: colors.text,
|
|
242
|
+
fontSize: 12,
|
|
243
|
+
lineHeight: 1.65,
|
|
244
|
+
wordBreak: 'break-word',
|
|
245
|
+
zIndex: 9999,
|
|
246
|
+
boxShadow: '0 4px 16px rgba(0,0,0,0.3)',
|
|
247
|
+
pointerEvents: 'auto'
|
|
248
|
+
}
|
|
249
|
+
}, descLines.map((line, i)=>/*#__PURE__*/ react.createElement("div", {
|
|
250
|
+
key: i,
|
|
251
|
+
style: {
|
|
252
|
+
marginBottom: i < descLines.length - 1 ? 4 : 0
|
|
253
|
+
}
|
|
254
|
+
}, renderDescLine(line, colors.accent)))));
|
|
108
255
|
};
|
|
109
256
|
export { Toolbar };
|
|
@@ -3,21 +3,24 @@ import { SectionHeader } from "./section-header.js";
|
|
|
3
3
|
import { VariableRow } from "./variable-row.js";
|
|
4
4
|
const VariablesSection = ({ sortedBinds, sortedRequests, colors })=>{
|
|
5
5
|
if (0 === sortedBinds.length && 0 === sortedRequests.length) return null;
|
|
6
|
-
return /*#__PURE__*/ react.createElement("div", null, /*#__PURE__*/ react.createElement(SectionHeader, {
|
|
6
|
+
return /*#__PURE__*/ react.createElement("div", null, sortedRequests.length > 0 && /*#__PURE__*/ react.createElement(react.Fragment, null, /*#__PURE__*/ react.createElement(SectionHeader, {
|
|
7
7
|
colors: colors,
|
|
8
|
-
label: "
|
|
8
|
+
label: "函数入参"
|
|
9
|
+
}), sortedRequests.map((req)=>/*#__PURE__*/ react.createElement(VariableRow, {
|
|
10
|
+
key: req.name,
|
|
11
|
+
name: req.name,
|
|
12
|
+
dataType: req.dataType,
|
|
13
|
+
description: req.description,
|
|
14
|
+
colors: colors
|
|
15
|
+
}))), sortedBinds.length > 0 && /*#__PURE__*/ react.createElement(react.Fragment, null, /*#__PURE__*/ react.createElement(SectionHeader, {
|
|
16
|
+
colors: colors,
|
|
17
|
+
label: "绑定参数"
|
|
9
18
|
}), sortedBinds.map((bind)=>/*#__PURE__*/ react.createElement(VariableRow, {
|
|
10
19
|
key: bind.name,
|
|
11
20
|
name: bind.name,
|
|
12
21
|
dataType: bind.dataType,
|
|
13
22
|
description: bind.description,
|
|
14
23
|
colors: colors
|
|
15
|
-
}))
|
|
16
|
-
key: req.name,
|
|
17
|
-
name: req.name,
|
|
18
|
-
dataType: req.dataType,
|
|
19
|
-
description: req.description,
|
|
20
|
-
colors: colors
|
|
21
|
-
})));
|
|
24
|
+
}))));
|
|
22
25
|
};
|
|
23
26
|
export { VariablesSection };
|
package/dist/script-code.js
CHANGED
|
@@ -181,7 +181,7 @@ function buildLayoutExtensions(fontSize, minHeight, maxHeight, isFullscreen) {
|
|
|
181
181
|
});
|
|
182
182
|
}
|
|
183
183
|
const ScriptCodeEditor = (props)=>{
|
|
184
|
-
const { value, readonly = false, onChange, onCompile, onFormat, onThemeChange, placeholder = '请输入 Groovy 脚本...', defaultTheme, title, metadata, defaultSidebarOpen, enableThemeToggle, enableFormat, enableCompile, enableFullscreen, toolbarExtra, options = {} } = props;
|
|
184
|
+
const { value, readonly = false, onChange, onCompile, onFormat, onThemeChange, placeholder = '请输入 Groovy 脚本...', defaultTheme, title, metadata, defaultSidebarOpen, enableThemeToggle, enableFormat, enableCompile, enableFullscreen, toolbar, toolbarExtra, options = {} } = props;
|
|
185
185
|
const { fontSize = 14, minHeight = 300, maxHeight = 300 } = options;
|
|
186
186
|
const [internalTheme, setInternalTheme] = useState(defaultTheme ?? 'dark');
|
|
187
187
|
useEffect(()=>{
|
|
@@ -368,7 +368,9 @@ const ScriptCodeEditor = (props)=>{
|
|
|
368
368
|
enableFullscreen: enableFullscreen,
|
|
369
369
|
isFullscreen: isFullscreen,
|
|
370
370
|
onToggleFullscreen: ()=>setIsFullscreen((prev)=>!prev),
|
|
371
|
-
|
|
371
|
+
toolbar: toolbar,
|
|
372
|
+
toolbarExtra: toolbarExtra,
|
|
373
|
+
description: metadata?.description
|
|
372
374
|
}), /*#__PURE__*/ react.createElement("div", {
|
|
373
375
|
style: {
|
|
374
376
|
display: 'flex',
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,4 +1,26 @@
|
|
|
1
1
|
import type React from 'react';
|
|
2
|
+
/** 工具栏按钮属性 */
|
|
3
|
+
export interface ToolbarButtonProps {
|
|
4
|
+
/** 按钮内容(支持 ReactNode,可包含图标 + 文字) */
|
|
5
|
+
label: React.ReactNode;
|
|
6
|
+
/** 鼠标悬停提示文字(原生 title 属性) */
|
|
7
|
+
title: string;
|
|
8
|
+
/** 按钮背景色 */
|
|
9
|
+
backgroundColor: string;
|
|
10
|
+
/** 鼠标悬停时的背景色 */
|
|
11
|
+
hoverBackgroundColor: string;
|
|
12
|
+
/** 文字颜色 */
|
|
13
|
+
textColor: string;
|
|
14
|
+
/** 边框颜色 */
|
|
15
|
+
borderColor: string;
|
|
16
|
+
/** 点击回调 */
|
|
17
|
+
onClick: () => void;
|
|
18
|
+
}
|
|
19
|
+
/** 工具栏自定义按钮项(用于 toolbar 数组) */
|
|
20
|
+
export interface ToolbarItem extends ToolbarButtonProps {
|
|
21
|
+
/** 唯一标识(React key) */
|
|
22
|
+
key: string;
|
|
23
|
+
}
|
|
2
24
|
/** 函数参数信息 */
|
|
3
25
|
export interface ScriptParameterInfo {
|
|
4
26
|
dataType: string;
|
|
@@ -41,7 +63,8 @@ export interface ScriptRequestInfo {
|
|
|
41
63
|
export interface ScriptMetadata {
|
|
42
64
|
binds: ScriptBindInfo[];
|
|
43
65
|
requests: ScriptRequestInfo[];
|
|
44
|
-
|
|
66
|
+
description?: string;
|
|
67
|
+
mainMethod?: string;
|
|
45
68
|
returnType?: string;
|
|
46
69
|
types: Record<string, ScriptTypeInfo>;
|
|
47
70
|
}
|
|
@@ -76,6 +99,8 @@ export interface ScriptCodeEditorProps {
|
|
|
76
99
|
onFormat?: () => void;
|
|
77
100
|
/** 编译/测试脚本回调(enableCompile 时需提供) */
|
|
78
101
|
onCompile?: (code: string) => void;
|
|
102
|
+
/** 工具栏自定义按钮列表(渲染在内置按钮之后) */
|
|
103
|
+
toolbar?: ToolbarItem[];
|
|
79
104
|
/** 工具栏额外内容(渲染在内置按钮之后,任意 React 节点) */
|
|
80
105
|
toolbarExtra?: React.ReactNode;
|
|
81
106
|
/** 其他选项 */
|