vzcode 2.10.0 → 2.12.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/dist/assets/index-858eZSOF.js +457 -0
- package/dist/assets/{index-Bjj9VRMn.css → index-DLm5FQ0E.css} +1 -1
- package/dist/assets/{worker-DHxSPM7N.js → worker-CHvi_bXc.js} +13 -13
- package/dist/index.html +2 -2
- package/package.json +26 -24
- package/src/client/VZSidebar/AIChat/Message.tsx +4 -1
- package/src/client/VZSidebar/AIChat/MessageList.tsx +13 -1
- package/src/client/VZSidebar/VisualEditor/CheckboxWidget.tsx +46 -0
- package/src/client/VZSidebar/VisualEditor/ColorWidget.tsx +173 -0
- package/src/client/VZSidebar/VisualEditor/DropdownWidget.tsx +84 -0
- package/src/client/VZSidebar/VisualEditor/SliderWidget.tsx +63 -0
- package/src/client/VZSidebar/VisualEditor/TextInputWidget.tsx +36 -0
- package/src/client/VZSidebar/{VisualEditor.tsx → VisualEditor/VisualEditor.tsx} +202 -211
- package/src/client/VZSidebar/VisualEditor/constants.ts +1 -0
- package/src/client/VZSidebar/VisualEditor/index.tsx +1 -0
- package/src/client/VZSidebar/VisualEditor/utils.ts +102 -0
- package/src/client/VZSidebar/index.tsx +0 -23
- package/src/client/VZSidebar/styles.scss +246 -0
- package/src/types.ts +2 -1
- package/dist/assets/index-CsDf6ybm.js +0 -457
package/dist/index.html
CHANGED
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap"
|
|
21
21
|
rel="stylesheet"
|
|
22
22
|
/>
|
|
23
|
-
<script type="module" crossorigin src="/assets/index-
|
|
24
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
23
|
+
<script type="module" crossorigin src="/assets/index-858eZSOF.js"></script>
|
|
24
|
+
<link rel="stylesheet" crossorigin href="/assets/index-DLm5FQ0E.css">
|
|
25
25
|
</head>
|
|
26
26
|
<body>
|
|
27
27
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vzcode",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.12.0",
|
|
4
4
|
"description": "Multiplayer code editor system",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -110,7 +110,7 @@
|
|
|
110
110
|
},
|
|
111
111
|
"homepage": "https://github.com/vizhub-core/vzcode#readme",
|
|
112
112
|
"dependencies": {
|
|
113
|
-
"@codemirror/autocomplete": "^6.18.
|
|
113
|
+
"@codemirror/autocomplete": "^6.18.7",
|
|
114
114
|
"@codemirror/lang-css": "^6.3.1",
|
|
115
115
|
"@codemirror/lang-html": "^6.4.9",
|
|
116
116
|
"@codemirror/lang-javascript": "^6.2.4",
|
|
@@ -119,9 +119,9 @@
|
|
|
119
119
|
"@codemirror/lint": "^6.8.5",
|
|
120
120
|
"@codemirror/state": "^6.5.2",
|
|
121
121
|
"@codemirror/theme-one-dark": "^6.1.3",
|
|
122
|
-
"@codemirror/view": "^6.38.
|
|
123
|
-
"@langchain/core": "^0.3.
|
|
124
|
-
"@langchain/openai": "^0.6.
|
|
122
|
+
"@codemirror/view": "^6.38.2",
|
|
123
|
+
"@langchain/core": "^0.3.73",
|
|
124
|
+
"@langchain/openai": "^0.6.11",
|
|
125
125
|
"@lezer/highlight": "^1.2.1",
|
|
126
126
|
"@livekit/components-react": "^2.9.14",
|
|
127
127
|
"@replit/codemirror-indentation-markers": "^6.5.3",
|
|
@@ -141,8 +141,8 @@
|
|
|
141
141
|
"@uiw/codemirror-themes": "^4.25.1",
|
|
142
142
|
"@valtown/codemirror-ts": "^2.3.1",
|
|
143
143
|
"@vizhub/runtime": "^4.4.0",
|
|
144
|
-
"@vizhub/viz-types": "^0.
|
|
145
|
-
"@vizhub/viz-utils": "^1.
|
|
144
|
+
"@vizhub/viz-types": "^0.5.0",
|
|
145
|
+
"@vizhub/viz-utils": "^1.5.0",
|
|
146
146
|
"body-parser": "^2.2.0",
|
|
147
147
|
"bootstrap-icons": "^1.13.1",
|
|
148
148
|
"codemirror": "^6.0.2",
|
|
@@ -151,19 +151,20 @@
|
|
|
151
151
|
"color-hash": "^2.0.2",
|
|
152
152
|
"comlink": "^4.4.2",
|
|
153
153
|
"d3-array": "^3.2.4",
|
|
154
|
+
"d3-color": "^3.1.0",
|
|
154
155
|
"diff": "^8.0.2",
|
|
155
156
|
"diff-match-patch": "^1.0.5",
|
|
156
157
|
"diff2html": "^3.4.52",
|
|
157
|
-
"dotenv": "^17.2.
|
|
158
|
+
"dotenv": "^17.2.2",
|
|
158
159
|
"editcodewithai": "^2.3.0",
|
|
159
|
-
"eslint-linter-browserify": "^9.
|
|
160
|
+
"eslint-linter-browserify": "^9.34.0",
|
|
160
161
|
"express": "^5.1.0",
|
|
161
162
|
"ignore": "^7.0.5",
|
|
162
163
|
"json0-ot-diff": "^1.1.2",
|
|
163
164
|
"jszip": "^3.10.1",
|
|
164
|
-
"livekit-server-sdk": "^2.13.
|
|
165
|
+
"livekit-server-sdk": "^2.13.3",
|
|
165
166
|
"llm-code-format": "^3.0.0",
|
|
166
|
-
"lucide-react": "^0.
|
|
167
|
+
"lucide-react": "^0.542.0",
|
|
167
168
|
"npm": "^11.5.2",
|
|
168
169
|
"open": "^10.2.0",
|
|
169
170
|
"prettier-plugin-svelte": "^3.4.0",
|
|
@@ -171,7 +172,7 @@
|
|
|
171
172
|
"react-bootstrap": "^2.10.10",
|
|
172
173
|
"react-dom": "^18",
|
|
173
174
|
"react-markdown": "^10.1.0",
|
|
174
|
-
"react-router-dom": "^7.8.
|
|
175
|
+
"react-router-dom": "^7.8.2",
|
|
175
176
|
"remark-gfm": "^4.0.1",
|
|
176
177
|
"sharedb": "^5.2.2",
|
|
177
178
|
"sharedb-client-browser": "^5.2.2",
|
|
@@ -180,30 +181,31 @@
|
|
|
180
181
|
"ws": "^8.18.3"
|
|
181
182
|
},
|
|
182
183
|
"devDependencies": {
|
|
183
|
-
"@eslint/js": "^9.
|
|
184
|
-
"@tauri-apps/cli": "^2.
|
|
184
|
+
"@eslint/js": "^9.34.0",
|
|
185
|
+
"@tauri-apps/cli": "^2.8.4",
|
|
186
|
+
"@types/d3-color": "^3.1.3",
|
|
185
187
|
"@types/react": "^18",
|
|
186
188
|
"@types/react-dom": "^18",
|
|
187
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
188
|
-
"@typescript-eslint/parser": "^8.
|
|
189
|
-
"@vitejs/plugin-react": "^5.0.
|
|
190
|
-
"concurrently": "^9.2.
|
|
189
|
+
"@typescript-eslint/eslint-plugin": "^8.42.0",
|
|
190
|
+
"@typescript-eslint/parser": "^8.42.0",
|
|
191
|
+
"@vitejs/plugin-react": "^5.0.2",
|
|
192
|
+
"concurrently": "^9.2.1",
|
|
191
193
|
"cross-env": "^10.0.0",
|
|
192
|
-
"eslint": "^9.
|
|
194
|
+
"eslint": "^9.34.0",
|
|
193
195
|
"eslint-plugin-jsx-a11y": "^6.10.2",
|
|
194
196
|
"eslint-plugin-react": "^7.37.5",
|
|
195
197
|
"eslint-plugin-react-hooks": "^5.2.0",
|
|
196
198
|
"globals": "^16.3.0",
|
|
197
|
-
"npm-check-updates": "^18.0.
|
|
199
|
+
"npm-check-updates": "^18.0.3",
|
|
198
200
|
"prettier": "^3.6.2",
|
|
199
|
-
"sass": "^1.
|
|
201
|
+
"sass": "^1.92.0",
|
|
200
202
|
"ts-node": "^10.9.2",
|
|
201
203
|
"typescript": "^5.9.2",
|
|
202
|
-
"vite": "^7.1.
|
|
204
|
+
"vite": "^7.1.4",
|
|
203
205
|
"vitest": "^3.2.4"
|
|
204
206
|
},
|
|
205
207
|
"optionalDependencies": {
|
|
206
|
-
"@rollup/rollup-darwin-arm64": "^4.
|
|
207
|
-
"@rollup/rollup-win32-x64-msvc": "^4.
|
|
208
|
+
"@rollup/rollup-darwin-arm64": "^4.50.0",
|
|
209
|
+
"@rollup/rollup-win32-x64-msvc": "^4.50.0"
|
|
208
210
|
}
|
|
209
211
|
}
|
|
@@ -15,6 +15,7 @@ interface MessageProps {
|
|
|
15
15
|
isStreaming?: boolean;
|
|
16
16
|
diffData?: UnifiedFilesDiff;
|
|
17
17
|
chatId?: string;
|
|
18
|
+
showAdditionalWidgets?: boolean;
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
const MessageComponent = ({
|
|
@@ -25,6 +26,7 @@ const MessageComponent = ({
|
|
|
25
26
|
isStreaming,
|
|
26
27
|
diffData,
|
|
27
28
|
chatId,
|
|
29
|
+
showAdditionalWidgets = false,
|
|
28
30
|
}: MessageProps) => {
|
|
29
31
|
const { additionalWidgets, handleSendMessage } =
|
|
30
32
|
useContext(VZCodeContext);
|
|
@@ -56,7 +58,8 @@ const MessageComponent = ({
|
|
|
56
58
|
Object.keys(diffData).length > 0 && (
|
|
57
59
|
<DiffView diffData={diffData} />
|
|
58
60
|
)}
|
|
59
|
-
{
|
|
61
|
+
{showAdditionalWidgets &&
|
|
62
|
+
additionalWidgets &&
|
|
60
63
|
chatId &&
|
|
61
64
|
additionalWidgets({
|
|
62
65
|
messageId: id,
|
|
@@ -152,13 +152,24 @@ const MessageListComponent = ({
|
|
|
152
152
|
aiScratchpad && aiScratchpad.trim(),
|
|
153
153
|
);
|
|
154
154
|
|
|
155
|
+
// Find the most recent assistant message index
|
|
156
|
+
const lastAssistantMessageIndex = messages
|
|
157
|
+
.map((m, i) => (m.role === 'assistant' ? i : -1))
|
|
158
|
+
.filter((i) => i !== -1)
|
|
159
|
+
.pop();
|
|
160
|
+
|
|
155
161
|
return (
|
|
156
162
|
<div
|
|
157
163
|
className="ai-chat-messages"
|
|
158
164
|
ref={messagesContainerRef}
|
|
159
165
|
onScroll={handleScroll}
|
|
160
166
|
>
|
|
161
|
-
{messages.map((msg,
|
|
167
|
+
{messages.map((msg, index) => {
|
|
168
|
+
// Only show additionalWidgets for the most recent assistant message
|
|
169
|
+
const showAdditionalWidgets =
|
|
170
|
+
msg.role === 'assistant' &&
|
|
171
|
+
index === lastAssistantMessageIndex;
|
|
172
|
+
|
|
162
173
|
return (
|
|
163
174
|
<Message
|
|
164
175
|
key={msg.id}
|
|
@@ -168,6 +179,7 @@ const MessageListComponent = ({
|
|
|
168
179
|
timestamp={msg.timestamp}
|
|
169
180
|
diffData={(msg as any).diffData}
|
|
170
181
|
chatId={chatId}
|
|
182
|
+
showAdditionalWidgets={showAdditionalWidgets}
|
|
171
183
|
/>
|
|
172
184
|
);
|
|
173
185
|
})}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
interface CheckboxWidgetProps {
|
|
4
|
+
property: string;
|
|
5
|
+
label: string;
|
|
6
|
+
currentValue: boolean;
|
|
7
|
+
onChange: (
|
|
8
|
+
event: React.ChangeEvent<HTMLInputElement>,
|
|
9
|
+
) => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const CheckboxWidget: React.FC<
|
|
13
|
+
CheckboxWidgetProps
|
|
14
|
+
> = ({ property, label, currentValue, onChange }) => {
|
|
15
|
+
return (
|
|
16
|
+
<div className="visual-editor-checkbox">
|
|
17
|
+
<div className="checkbox-header">
|
|
18
|
+
<label
|
|
19
|
+
htmlFor={property}
|
|
20
|
+
className="checkbox-label"
|
|
21
|
+
>
|
|
22
|
+
{label}
|
|
23
|
+
</label>
|
|
24
|
+
<span className="checkbox-value">
|
|
25
|
+
{currentValue ? 'On' : 'Off'}
|
|
26
|
+
</span>
|
|
27
|
+
</div>
|
|
28
|
+
<div className="checkbox-container">
|
|
29
|
+
<input
|
|
30
|
+
type="checkbox"
|
|
31
|
+
id={property}
|
|
32
|
+
className="checkbox-input"
|
|
33
|
+
checked={currentValue}
|
|
34
|
+
onChange={onChange}
|
|
35
|
+
/>
|
|
36
|
+
<div className="checkbox-visual">
|
|
37
|
+
<div
|
|
38
|
+
className={`checkbox-indicator ${
|
|
39
|
+
currentValue ? 'checked' : ''
|
|
40
|
+
}`}
|
|
41
|
+
/>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
);
|
|
46
|
+
};
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import React, { useRef, useEffect } from 'react';
|
|
2
|
+
import { renderSliderBackground } from './utils';
|
|
3
|
+
|
|
4
|
+
interface ColorWidgetProps {
|
|
5
|
+
property: string;
|
|
6
|
+
label: string;
|
|
7
|
+
currentHex: string;
|
|
8
|
+
currentH: number;
|
|
9
|
+
currentC: number;
|
|
10
|
+
currentL: number;
|
|
11
|
+
onColorChange: (
|
|
12
|
+
property: string,
|
|
13
|
+
lchComponent: 'l' | 'c' | 'h',
|
|
14
|
+
) => (event: React.ChangeEvent<HTMLInputElement>) => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const ColorWidget: React.FC<ColorWidgetProps> = ({
|
|
18
|
+
property,
|
|
19
|
+
label,
|
|
20
|
+
currentHex,
|
|
21
|
+
currentH,
|
|
22
|
+
currentC,
|
|
23
|
+
currentL,
|
|
24
|
+
onColorChange,
|
|
25
|
+
}) => {
|
|
26
|
+
// Refs for canvas elements (for color slider backgrounds)
|
|
27
|
+
const canvasRefs = useRef<{
|
|
28
|
+
[key: string]: HTMLCanvasElement | null;
|
|
29
|
+
}>({});
|
|
30
|
+
|
|
31
|
+
// Update color slider backgrounds when values change
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
const values = {
|
|
34
|
+
h: currentH,
|
|
35
|
+
c: currentC,
|
|
36
|
+
l: currentL,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// Render backgrounds for each slider
|
|
40
|
+
renderSliderBackground(
|
|
41
|
+
canvasRefs.current[`${property}_l_bg`],
|
|
42
|
+
'l',
|
|
43
|
+
values,
|
|
44
|
+
);
|
|
45
|
+
renderSliderBackground(
|
|
46
|
+
canvasRefs.current[`${property}_c_bg`],
|
|
47
|
+
'c',
|
|
48
|
+
values,
|
|
49
|
+
);
|
|
50
|
+
renderSliderBackground(
|
|
51
|
+
canvasRefs.current[`${property}_h_bg`],
|
|
52
|
+
'h',
|
|
53
|
+
values,
|
|
54
|
+
);
|
|
55
|
+
}, [property, currentH, currentC, currentL]);
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<div className="visual-editor-color">
|
|
59
|
+
<div className="color-header">
|
|
60
|
+
<label className="color-label">{label}</label>
|
|
61
|
+
<span className="color-value">{currentHex}</span>
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
<div
|
|
65
|
+
className="color-preview"
|
|
66
|
+
style={{ backgroundColor: currentHex }}
|
|
67
|
+
/>
|
|
68
|
+
|
|
69
|
+
<div className="lch-sliders">
|
|
70
|
+
{/* Lightness Slider */}
|
|
71
|
+
<div className="lch-slider">
|
|
72
|
+
<div className="slider-header">
|
|
73
|
+
<label className="slider-label">
|
|
74
|
+
Lightness
|
|
75
|
+
</label>
|
|
76
|
+
<span className="slider-value">
|
|
77
|
+
{Math.round(currentL)}
|
|
78
|
+
</span>
|
|
79
|
+
</div>
|
|
80
|
+
<div className="slider-container">
|
|
81
|
+
<canvas
|
|
82
|
+
ref={(canvas) => {
|
|
83
|
+
canvasRefs.current[`${property}_l_bg`] =
|
|
84
|
+
canvas;
|
|
85
|
+
}}
|
|
86
|
+
className="slider-bg-canvas"
|
|
87
|
+
width={200}
|
|
88
|
+
/>
|
|
89
|
+
<input
|
|
90
|
+
type="range"
|
|
91
|
+
className="slider-input"
|
|
92
|
+
min={0}
|
|
93
|
+
max={100}
|
|
94
|
+
step={1}
|
|
95
|
+
value={currentL}
|
|
96
|
+
onChange={onColorChange(property, 'l')}
|
|
97
|
+
/>
|
|
98
|
+
</div>
|
|
99
|
+
<div className="slider-bounds">
|
|
100
|
+
<span className="min-value">0</span>
|
|
101
|
+
<span className="max-value">100</span>
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
|
|
105
|
+
{/* Chroma Slider */}
|
|
106
|
+
<div className="lch-slider">
|
|
107
|
+
<div className="slider-header">
|
|
108
|
+
<label className="slider-label">Chroma</label>
|
|
109
|
+
<span className="slider-value">
|
|
110
|
+
{Math.round(currentC)}
|
|
111
|
+
</span>
|
|
112
|
+
</div>
|
|
113
|
+
<div className="slider-container">
|
|
114
|
+
<canvas
|
|
115
|
+
ref={(canvas) => {
|
|
116
|
+
canvasRefs.current[`${property}_c_bg`] =
|
|
117
|
+
canvas;
|
|
118
|
+
}}
|
|
119
|
+
className="slider-bg-canvas"
|
|
120
|
+
width={200}
|
|
121
|
+
/>
|
|
122
|
+
<input
|
|
123
|
+
type="range"
|
|
124
|
+
className="slider-input"
|
|
125
|
+
min={0}
|
|
126
|
+
max={100}
|
|
127
|
+
step={1}
|
|
128
|
+
value={currentC}
|
|
129
|
+
onChange={onColorChange(property, 'c')}
|
|
130
|
+
/>
|
|
131
|
+
</div>
|
|
132
|
+
<div className="slider-bounds">
|
|
133
|
+
<span className="min-value">0</span>
|
|
134
|
+
<span className="max-value">100</span>
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
|
|
138
|
+
{/* Hue Slider */}
|
|
139
|
+
<div className="lch-slider">
|
|
140
|
+
<div className="slider-header">
|
|
141
|
+
<label className="slider-label">Hue</label>
|
|
142
|
+
<span className="slider-value">
|
|
143
|
+
{Math.round(currentH)}°
|
|
144
|
+
</span>
|
|
145
|
+
</div>
|
|
146
|
+
<div className="slider-container">
|
|
147
|
+
<canvas
|
|
148
|
+
ref={(canvas) => {
|
|
149
|
+
canvasRefs.current[`${property}_h_bg`] =
|
|
150
|
+
canvas;
|
|
151
|
+
}}
|
|
152
|
+
className="slider-bg-canvas"
|
|
153
|
+
width={200}
|
|
154
|
+
/>
|
|
155
|
+
<input
|
|
156
|
+
type="range"
|
|
157
|
+
className="slider-input"
|
|
158
|
+
min={0}
|
|
159
|
+
max={360}
|
|
160
|
+
step={1}
|
|
161
|
+
value={currentH}
|
|
162
|
+
onChange={onColorChange(property, 'h')}
|
|
163
|
+
/>
|
|
164
|
+
</div>
|
|
165
|
+
<div className="slider-bounds">
|
|
166
|
+
<span className="min-value">0°</span>
|
|
167
|
+
<span className="max-value">360°</span>
|
|
168
|
+
</div>
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
172
|
+
);
|
|
173
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
interface DropdownWidgetProps {
|
|
4
|
+
property: string;
|
|
5
|
+
label: string;
|
|
6
|
+
options: string[];
|
|
7
|
+
currentValue: string;
|
|
8
|
+
isOpen: boolean;
|
|
9
|
+
onToggle: () => void;
|
|
10
|
+
onOptionClick: (value: string) => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const DropdownWidget: React.FC<
|
|
14
|
+
DropdownWidgetProps
|
|
15
|
+
> = ({
|
|
16
|
+
property,
|
|
17
|
+
label,
|
|
18
|
+
options,
|
|
19
|
+
currentValue,
|
|
20
|
+
isOpen,
|
|
21
|
+
onToggle,
|
|
22
|
+
onOptionClick,
|
|
23
|
+
}) => {
|
|
24
|
+
return (
|
|
25
|
+
<div className="visual-editor-dropdown">
|
|
26
|
+
<div className="dropdown-header">
|
|
27
|
+
<label
|
|
28
|
+
htmlFor={property}
|
|
29
|
+
className="dropdown-label"
|
|
30
|
+
>
|
|
31
|
+
{label}
|
|
32
|
+
</label>
|
|
33
|
+
</div>
|
|
34
|
+
<div className="dropdown-container">
|
|
35
|
+
<button
|
|
36
|
+
type="button"
|
|
37
|
+
className={`dropdown-button ${isOpen ? 'open' : ''}`}
|
|
38
|
+
onClick={onToggle}
|
|
39
|
+
aria-expanded={isOpen}
|
|
40
|
+
aria-haspopup="listbox"
|
|
41
|
+
>
|
|
42
|
+
<span className="dropdown-button-text">
|
|
43
|
+
{currentValue}
|
|
44
|
+
</span>
|
|
45
|
+
<div
|
|
46
|
+
className={`dropdown-arrow ${isOpen ? 'open' : ''}`}
|
|
47
|
+
>
|
|
48
|
+
<svg
|
|
49
|
+
width="12"
|
|
50
|
+
height="8"
|
|
51
|
+
viewBox="0 0 12 8"
|
|
52
|
+
fill="none"
|
|
53
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
54
|
+
>
|
|
55
|
+
<path
|
|
56
|
+
d="M1 1.5L6 6.5L11 1.5"
|
|
57
|
+
stroke="currentColor"
|
|
58
|
+
strokeWidth="2"
|
|
59
|
+
strokeLinecap="round"
|
|
60
|
+
strokeLinejoin="round"
|
|
61
|
+
/>
|
|
62
|
+
</svg>
|
|
63
|
+
</div>
|
|
64
|
+
</button>
|
|
65
|
+
{isOpen && (
|
|
66
|
+
<div className="dropdown-options">
|
|
67
|
+
{options.map((option) => (
|
|
68
|
+
<button
|
|
69
|
+
key={option}
|
|
70
|
+
type="button"
|
|
71
|
+
className={`dropdown-option ${
|
|
72
|
+
option === currentValue ? 'selected' : ''
|
|
73
|
+
}`}
|
|
74
|
+
onClick={() => onOptionClick(option)}
|
|
75
|
+
>
|
|
76
|
+
{option}
|
|
77
|
+
</button>
|
|
78
|
+
))}
|
|
79
|
+
</div>
|
|
80
|
+
)}
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
);
|
|
84
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { calculatePercentage, formatValue } from './utils';
|
|
3
|
+
|
|
4
|
+
interface SliderWidgetProps {
|
|
5
|
+
property: string;
|
|
6
|
+
label: string;
|
|
7
|
+
min: number;
|
|
8
|
+
max: number;
|
|
9
|
+
step: number;
|
|
10
|
+
currentValue: number;
|
|
11
|
+
onChange: (
|
|
12
|
+
event: React.ChangeEvent<HTMLInputElement>,
|
|
13
|
+
) => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const SliderWidget: React.FC<SliderWidgetProps> = ({
|
|
17
|
+
property,
|
|
18
|
+
label,
|
|
19
|
+
min,
|
|
20
|
+
max,
|
|
21
|
+
step,
|
|
22
|
+
currentValue,
|
|
23
|
+
onChange,
|
|
24
|
+
}) => {
|
|
25
|
+
const percentage = calculatePercentage(
|
|
26
|
+
currentValue,
|
|
27
|
+
min,
|
|
28
|
+
max,
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<div className="visual-editor-slider">
|
|
33
|
+
<div className="slider-header">
|
|
34
|
+
<label htmlFor={property} className="slider-label">
|
|
35
|
+
{label}
|
|
36
|
+
</label>
|
|
37
|
+
<span className="slider-value">
|
|
38
|
+
{formatValue(currentValue, min, max)}
|
|
39
|
+
</span>
|
|
40
|
+
</div>
|
|
41
|
+
<div className="slider-container">
|
|
42
|
+
<input
|
|
43
|
+
type="range"
|
|
44
|
+
id={property}
|
|
45
|
+
className="slider-input"
|
|
46
|
+
min={min}
|
|
47
|
+
max={max}
|
|
48
|
+
step={step}
|
|
49
|
+
value={currentValue}
|
|
50
|
+
onChange={onChange}
|
|
51
|
+
/>
|
|
52
|
+
<div
|
|
53
|
+
className="slider-track-fill"
|
|
54
|
+
style={{ width: `${percentage}%` }}
|
|
55
|
+
/>
|
|
56
|
+
</div>
|
|
57
|
+
<div className="slider-bounds">
|
|
58
|
+
<span className="min-value">{min}</span>
|
|
59
|
+
<span className="max-value">{max}</span>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
);
|
|
63
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
interface TextInputWidgetProps {
|
|
4
|
+
property: string;
|
|
5
|
+
label: string;
|
|
6
|
+
currentValue: string;
|
|
7
|
+
onChange: (
|
|
8
|
+
event: React.ChangeEvent<HTMLInputElement>,
|
|
9
|
+
) => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const TextInputWidget: React.FC<
|
|
13
|
+
TextInputWidgetProps
|
|
14
|
+
> = ({ property, label, currentValue, onChange }) => {
|
|
15
|
+
return (
|
|
16
|
+
<div className="visual-editor-text-input">
|
|
17
|
+
<div className="text-input-header">
|
|
18
|
+
<label
|
|
19
|
+
htmlFor={property}
|
|
20
|
+
className="text-input-label"
|
|
21
|
+
>
|
|
22
|
+
{label}
|
|
23
|
+
</label>
|
|
24
|
+
</div>
|
|
25
|
+
<div className="text-input-container">
|
|
26
|
+
<input
|
|
27
|
+
type="text"
|
|
28
|
+
id={property}
|
|
29
|
+
className="text-input-field"
|
|
30
|
+
value={currentValue || ''}
|
|
31
|
+
onChange={onChange}
|
|
32
|
+
/>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
);
|
|
36
|
+
};
|