@vsreact/core 0.0.4 → 0.0.6
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/controls.d.ts +57 -6
- package/dist/controls.js +79 -14
- package/dist/hooks.d.ts +8 -0
- package/dist/hooks.js +10 -0
- package/dist/hostConfig.js +3 -0
- package/dist/index.d.ts +7 -4
- package/dist/index.js +4 -2
- package/dist/overlay.d.ts +9 -0
- package/dist/overlay.js +47 -0
- package/dist/parameters.d.ts +3 -0
- package/dist/parameters.js +3 -1
- package/dist/popover.d.ts +27 -0
- package/dist/popover.js +55 -0
- package/dist/primitives.d.ts +21 -0
- package/dist/render.d.ts +2 -1
- package/dist/render.js +5 -2
- package/package.json +2 -2
- package/src/controls.test.tsx +243 -1
- package/src/controls.tsx +216 -13
- package/src/hooks.ts +12 -0
- package/src/hostConfig.ts +3 -0
- package/src/index.ts +9 -2
- package/src/overlay.tsx +70 -0
- package/src/parameters.ts +6 -1
- package/src/popover.tsx +137 -0
- package/src/primitives.tsx +20 -0
- package/src/render.tsx +12 -2
package/dist/controls.d.ts
CHANGED
|
@@ -6,22 +6,32 @@ export interface KnobProps {
|
|
|
6
6
|
label?: string;
|
|
7
7
|
size?: number;
|
|
8
8
|
disabled?: boolean;
|
|
9
|
+
/** Double-click resets to this (DAW convention). */
|
|
10
|
+
defaultValue?: number;
|
|
11
|
+
/** Value arc sweeps from 12 o'clock instead of the left stop — for
|
|
12
|
+
centre-based parameters like pan. */
|
|
13
|
+
bipolar?: boolean;
|
|
14
|
+
/** Value change per wheel notch fraction. 0 disables. Default 0.4. */
|
|
15
|
+
wheelSensitivity?: number;
|
|
9
16
|
trackColor?: string;
|
|
10
17
|
valueColor?: string;
|
|
11
18
|
onChange: (value: number) => void;
|
|
12
19
|
onBegin?: () => void;
|
|
13
20
|
onEnd?: () => void;
|
|
14
21
|
}
|
|
15
|
-
export declare function Knob({ value, text, label, size, disabled, trackColor, valueColor, onChange, onBegin, onEnd, }: KnobProps): import("react").JSX.Element;
|
|
22
|
+
export declare function Knob({ value, text, label, size, disabled, defaultValue, bipolar, wheelSensitivity, trackColor, valueColor, onChange, onBegin, onEnd, }: KnobProps): import("react").JSX.Element;
|
|
16
23
|
export interface ParamKnobProps {
|
|
17
24
|
paramId: string;
|
|
18
25
|
label?: string;
|
|
19
26
|
size?: number;
|
|
27
|
+
bipolar?: boolean;
|
|
28
|
+
wheelSensitivity?: number;
|
|
20
29
|
trackColor?: string;
|
|
21
30
|
valueColor?: string;
|
|
22
31
|
}
|
|
23
|
-
/** A Knob bound to an APVTS parameter
|
|
24
|
-
|
|
32
|
+
/** A Knob bound to an APVTS parameter — double-click resets to the
|
|
33
|
+
host's default, the wheel nudges, gestures stay automation-safe. */
|
|
34
|
+
export declare function ParamKnob({ paramId, label, ...rest }: ParamKnobProps): import("react").JSX.Element;
|
|
25
35
|
export interface SliderProps {
|
|
26
36
|
value: number;
|
|
27
37
|
/** Track length when horizontal (default 160). */
|
|
@@ -32,22 +42,28 @@ export interface SliderProps {
|
|
|
32
42
|
vertical?: boolean;
|
|
33
43
|
label?: string;
|
|
34
44
|
disabled?: boolean;
|
|
45
|
+
/** Double-click resets to this (DAW convention). */
|
|
46
|
+
defaultValue?: number;
|
|
47
|
+
/** Value change per wheel notch fraction. 0 disables. Default 0.4. */
|
|
48
|
+
wheelSensitivity?: number;
|
|
35
49
|
trackColor?: string;
|
|
36
50
|
valueColor?: string;
|
|
37
51
|
onChange: (value: number) => void;
|
|
38
52
|
onBegin?: () => void;
|
|
39
53
|
onEnd?: () => void;
|
|
40
54
|
}
|
|
41
|
-
export declare function Slider({ value, width, height, vertical, label, disabled, trackColor, valueColor, onChange, onBegin, onEnd, }: SliderProps): import("react").JSX.Element;
|
|
55
|
+
export declare function Slider({ value, width, height, vertical, label, disabled, defaultValue, wheelSensitivity, trackColor, valueColor, onChange, onBegin, onEnd, }: SliderProps): import("react").JSX.Element;
|
|
42
56
|
export interface ParamSliderProps {
|
|
43
57
|
paramId: string;
|
|
44
58
|
label?: string;
|
|
45
59
|
width?: number;
|
|
46
60
|
height?: number;
|
|
47
61
|
vertical?: boolean;
|
|
62
|
+
wheelSensitivity?: number;
|
|
48
63
|
}
|
|
49
|
-
/** A Slider bound to an APVTS parameter
|
|
50
|
-
|
|
64
|
+
/** A Slider bound to an APVTS parameter — double-click resets to the
|
|
65
|
+
host's default, the wheel nudges, gestures stay automation-safe. */
|
|
66
|
+
export declare function ParamSlider({ paramId, label, ...rest }: ParamSliderProps): import("react").JSX.Element;
|
|
51
67
|
export interface ToggleProps {
|
|
52
68
|
on: boolean;
|
|
53
69
|
label?: string;
|
|
@@ -135,3 +151,38 @@ export interface GenericEditorProps {
|
|
|
135
151
|
/** The zero-effort editor: one knob per APVTS parameter, laid out in
|
|
136
152
|
rows. `render(<GenericEditor />)` is a complete, working plugin UI. */
|
|
137
153
|
export declare function GenericEditor({ columns, size, trackColor, valueColor }: GenericEditorProps): import("react").JSX.Element;
|
|
154
|
+
export interface SelectProps {
|
|
155
|
+
options: string[];
|
|
156
|
+
index: number;
|
|
157
|
+
/** Trigger width; the menu matches it. Default 160. */
|
|
158
|
+
width?: number;
|
|
159
|
+
label?: string;
|
|
160
|
+
disabled?: boolean;
|
|
161
|
+
trackColor?: string;
|
|
162
|
+
menuColor?: string;
|
|
163
|
+
activeColor?: string;
|
|
164
|
+
textColor?: string;
|
|
165
|
+
activeTextColor?: string;
|
|
166
|
+
/** Menu scrolls beyond this height. Default 190. */
|
|
167
|
+
maxMenuHeight?: number;
|
|
168
|
+
onChange: (index: number) => void;
|
|
169
|
+
}
|
|
170
|
+
/** A dropdown: the menu renders in the overlay layer, positioned under
|
|
171
|
+
the trigger via onLayout, with a click-away backdrop and a scrolling
|
|
172
|
+
option list. */
|
|
173
|
+
export declare function Select({ options, index, width, label, disabled, trackColor, menuColor, activeColor, textColor, activeTextColor, maxMenuHeight, onChange, }: SelectProps): import("react").JSX.Element;
|
|
174
|
+
export interface ParamSelectProps {
|
|
175
|
+
paramId: string;
|
|
176
|
+
options: string[];
|
|
177
|
+
width?: number;
|
|
178
|
+
label?: string;
|
|
179
|
+
trackColor?: string;
|
|
180
|
+
menuColor?: string;
|
|
181
|
+
activeColor?: string;
|
|
182
|
+
textColor?: string;
|
|
183
|
+
activeTextColor?: string;
|
|
184
|
+
maxMenuHeight?: number;
|
|
185
|
+
}
|
|
186
|
+
/** A Select bound to a choice-style APVTS parameter (same value↔index
|
|
187
|
+
mapping as ParamSegmented). */
|
|
188
|
+
export declare function ParamSelect({ paramId, options, label, ...rest }: ParamSelectProps): import("react").JSX.Element;
|
package/dist/controls.js
CHANGED
|
@@ -2,10 +2,12 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
// The built-in VST controls — knobs, sliders, toggles, XY pads, segmented
|
|
3
3
|
// switches — drawn by the VSReacT painter and driven by drag gestures.
|
|
4
4
|
// Each has a Param* variant bound to an APVTS parameter.
|
|
5
|
-
import { useRef } from "react";
|
|
5
|
+
import { useEffect, useRef, useState } from "react";
|
|
6
6
|
import { View, Text } from "./primitives";
|
|
7
7
|
import { useParameter, useParameterList } from "./parameters";
|
|
8
8
|
import { useSpring } from "./animation";
|
|
9
|
+
import { useLayoutRect } from "./hooks";
|
|
10
|
+
import { useOverlay } from "./overlay";
|
|
9
11
|
const clamp01 = (v) => Math.min(1, Math.max(0, v));
|
|
10
12
|
/** Vertical-drag-to-value mapping shared by Knob (and tested in isolation). */
|
|
11
13
|
export function dragToValue(startValue, dy, sensitivity = 0.005) {
|
|
@@ -13,8 +15,16 @@ export function dragToValue(startValue, dy, sensitivity = 0.005) {
|
|
|
13
15
|
}
|
|
14
16
|
const ARC_START = -135;
|
|
15
17
|
const ARC_END = 135;
|
|
16
|
-
export function Knob({ value, text, label, size = 64, disabled, trackColor = "#2A2F27", valueColor = "#C6F135", onChange, onBegin, onEnd, }) {
|
|
18
|
+
export function Knob({ value, text, label, size = 64, disabled, defaultValue, bipolar, wheelSensitivity = 0.4, trackColor = "#2A2F27", valueColor = "#C6F135", onChange, onBegin, onEnd, }) {
|
|
17
19
|
const startValue = useRef(0);
|
|
20
|
+
const clamped = clamp01(value);
|
|
21
|
+
const angle = ARC_START + (ARC_END - ARC_START) * clamped;
|
|
22
|
+
const center = (ARC_START + ARC_END) / 2;
|
|
23
|
+
const nudge = (target) => {
|
|
24
|
+
onBegin?.();
|
|
25
|
+
onChange(clamp01(target));
|
|
26
|
+
onEnd?.();
|
|
27
|
+
};
|
|
18
28
|
return (_jsxs(View, { className: "items-center gap-2", children: [_jsx(View, { className: `items-center justify-center ${disabled ? "opacity-40" : "cursor-pointer"}`, style: {
|
|
19
29
|
width: size,
|
|
20
30
|
height: size,
|
|
@@ -22,21 +32,25 @@ export function Knob({ value, text, label, size = 64, disabled, trackColor = "#2
|
|
|
22
32
|
arcColor: valueColor,
|
|
23
33
|
arcStart: ARC_START,
|
|
24
34
|
arcEnd: ARC_END,
|
|
25
|
-
|
|
35
|
+
arcValueStart: bipolar ? Math.min(center, angle) : ARC_START,
|
|
36
|
+
arcValueEnd: bipolar ? Math.max(center, angle) : angle,
|
|
26
37
|
arcThickness: Math.max(3, size * 0.08),
|
|
27
38
|
}, onDragStart: disabled
|
|
28
39
|
? undefined
|
|
29
40
|
: () => {
|
|
30
|
-
startValue.current =
|
|
41
|
+
startValue.current = clamped;
|
|
31
42
|
onBegin?.();
|
|
32
|
-
}, onDrag: disabled ? undefined : (e) => onChange(dragToValue(startValue.current, e.dy)), onDragEnd: disabled ? undefined : () => onEnd?.(),
|
|
43
|
+
}, onDrag: disabled ? undefined : (e) => onChange(dragToValue(startValue.current, e.dy)), onDragEnd: disabled ? undefined : () => onEnd?.(), onDoubleClick: disabled || defaultValue === undefined ? undefined : () => nudge(defaultValue), onWheel: disabled || wheelSensitivity === 0
|
|
44
|
+
? undefined
|
|
45
|
+
: (e) => nudge(clamped + e.dy * wheelSensitivity), children: text !== undefined ? (_jsx(Text, { className: "text-text font-bold text-center", style: { fontSize: Math.max(10, size * 0.16) }, children: text })) : null }), label !== undefined ? (_jsx(Text, { className: "text-faint text-[10] font-bold tracking-widest", children: label })) : null] }));
|
|
33
46
|
}
|
|
34
|
-
/** A Knob bound to an APVTS parameter
|
|
35
|
-
|
|
47
|
+
/** A Knob bound to an APVTS parameter — double-click resets to the
|
|
48
|
+
host's default, the wheel nudges, gestures stay automation-safe. */
|
|
49
|
+
export function ParamKnob({ paramId, label, ...rest }) {
|
|
36
50
|
const param = useParameter(paramId);
|
|
37
|
-
return (_jsx(Knob, { value: param.value, text: param.text, label: label ?? param.name.toUpperCase(),
|
|
51
|
+
return (_jsx(Knob, { value: param.value, text: param.text, label: label ?? param.name.toUpperCase(), defaultValue: param.defaultValue, onChange: param.set, onBegin: param.begin, onEnd: param.end, ...rest }));
|
|
38
52
|
}
|
|
39
|
-
export function Slider({ value, width = 160, height = 160, vertical, label, disabled, trackColor = "#2A2F27", valueColor = "#C6F135", onChange, onBegin, onEnd, }) {
|
|
53
|
+
export function Slider({ value, width = 160, height = 160, vertical, label, disabled, defaultValue, wheelSensitivity = 0.4, trackColor = "#2A2F27", valueColor = "#C6F135", onChange, onBegin, onEnd, }) {
|
|
40
54
|
const startValue = useRef(0);
|
|
41
55
|
const clamped = clamp01(value);
|
|
42
56
|
const onDragStart = disabled
|
|
@@ -46,15 +60,25 @@ export function Slider({ value, width = 160, height = 160, vertical, label, disa
|
|
|
46
60
|
onBegin?.();
|
|
47
61
|
};
|
|
48
62
|
const onDragEnd = disabled ? undefined : () => onEnd?.();
|
|
63
|
+
const nudge = (target) => {
|
|
64
|
+
onBegin?.();
|
|
65
|
+
onChange(clamp01(target));
|
|
66
|
+
onEnd?.();
|
|
67
|
+
};
|
|
68
|
+
const onDoubleClick = disabled || defaultValue === undefined ? undefined : () => nudge(defaultValue);
|
|
69
|
+
const onWheel = disabled || wheelSensitivity === 0
|
|
70
|
+
? undefined
|
|
71
|
+
: (e) => nudge(clamped + e.dy * wheelSensitivity);
|
|
49
72
|
if (vertical) {
|
|
50
|
-
return (_jsxs(View, { className: "items-center gap-2", children: [_jsxs(View, { className: `w-[18] relative ${disabled ? "opacity-40" : "cursor-pointer"}`, style: { height }, onDragStart: onDragStart, onDrag: disabled ? undefined : (e) => onChange(clamp01(startValue.current - e.dy / height)), onDragEnd: onDragEnd, children: [_jsx(View, { className: "absolute w-[4] rounded-full left-[7] top-0 bottom-0", style: { backgroundColor: trackColor } }), _jsx(View, { className: "absolute w-[4] rounded-full left-[7] bottom-0", style: { height: clamped * height, backgroundColor: valueColor } }), _jsx(View, { className: "absolute w-[12] h-[12] rounded-full left-[3]", style: { top: (1 - clamped) * (height - 12), backgroundColor: valueColor } })] }), label !== undefined ? (_jsx(Text, { className: "text-faint text-[10] font-bold tracking-widest", children: label })) : null] }));
|
|
73
|
+
return (_jsxs(View, { className: "items-center gap-2", children: [_jsxs(View, { className: `w-[18] relative ${disabled ? "opacity-40" : "cursor-pointer"}`, style: { height }, onDragStart: onDragStart, onDrag: disabled ? undefined : (e) => onChange(clamp01(startValue.current - e.dy / height)), onDragEnd: onDragEnd, onDoubleClick: onDoubleClick, onWheel: onWheel, children: [_jsx(View, { className: "absolute w-[4] rounded-full left-[7] top-0 bottom-0", style: { backgroundColor: trackColor } }), _jsx(View, { className: "absolute w-[4] rounded-full left-[7] bottom-0", style: { height: clamped * height, backgroundColor: valueColor } }), _jsx(View, { className: "absolute w-[12] h-[12] rounded-full left-[3]", style: { top: (1 - clamped) * (height - 12), backgroundColor: valueColor } })] }), label !== undefined ? (_jsx(Text, { className: "text-faint text-[10] font-bold tracking-widest", children: label })) : null] }));
|
|
51
74
|
}
|
|
52
|
-
return (_jsxs(View, { className: "gap-2", children: [_jsxs(View, { className: `h-[18] justify-center relative ${disabled ? "opacity-40" : "cursor-pointer"}`, style: { width }, onDragStart: onDragStart, onDrag: disabled ? undefined : (e) => onChange(clamp01(startValue.current + e.dx / width)), onDragEnd: onDragEnd, children: [_jsx(View, { className: "h-[4] rounded-full", style: { backgroundColor: trackColor } }), _jsx(View, { className: "absolute h-[4] rounded-full top-[7]", style: { width: clamped * width, backgroundColor: valueColor } }), _jsx(View, { className: "absolute w-[12] h-[12] rounded-full top-[3]", style: { left: clamped * (width - 12), backgroundColor: valueColor } })] }), label !== undefined ? (_jsx(Text, { className: "text-faint text-[10] font-bold tracking-widest", children: label })) : null] }));
|
|
75
|
+
return (_jsxs(View, { className: "gap-2", children: [_jsxs(View, { className: `h-[18] justify-center relative ${disabled ? "opacity-40" : "cursor-pointer"}`, style: { width }, onDragStart: onDragStart, onDrag: disabled ? undefined : (e) => onChange(clamp01(startValue.current + e.dx / width)), onDragEnd: onDragEnd, onDoubleClick: onDoubleClick, onWheel: onWheel, children: [_jsx(View, { className: "h-[4] rounded-full", style: { backgroundColor: trackColor } }), _jsx(View, { className: "absolute h-[4] rounded-full top-[7]", style: { width: clamped * width, backgroundColor: valueColor } }), _jsx(View, { className: "absolute w-[12] h-[12] rounded-full top-[3]", style: { left: clamped * (width - 12), backgroundColor: valueColor } })] }), label !== undefined ? (_jsx(Text, { className: "text-faint text-[10] font-bold tracking-widest", children: label })) : null] }));
|
|
53
76
|
}
|
|
54
|
-
/** A Slider bound to an APVTS parameter
|
|
55
|
-
|
|
77
|
+
/** A Slider bound to an APVTS parameter — double-click resets to the
|
|
78
|
+
host's default, the wheel nudges, gestures stay automation-safe. */
|
|
79
|
+
export function ParamSlider({ paramId, label, ...rest }) {
|
|
56
80
|
const param = useParameter(paramId);
|
|
57
|
-
return (_jsx(Slider, { value: param.value, label: label ?? param.name.toUpperCase(),
|
|
81
|
+
return (_jsx(Slider, { value: param.value, label: label ?? param.name.toUpperCase(), defaultValue: param.defaultValue, onChange: param.set, onBegin: param.begin, onEnd: param.end, ...rest }));
|
|
58
82
|
}
|
|
59
83
|
/** A switch with a spring-animated thumb — bypass, on/off, A/B. */
|
|
60
84
|
export function Toggle({ on, label, size = 22, disabled, trackColor = "#2A2F27", onColor = "#C6F135", thumbColor = "#F4F4F5", onChange, }) {
|
|
@@ -137,3 +161,44 @@ export function GenericEditor({ columns = 4, size = 72, trackColor, valueColor }
|
|
|
137
161
|
rows.push(params.slice(i, i + Math.max(1, columns)));
|
|
138
162
|
return (_jsx(View, { className: "flex-1 flex-col items-center justify-center gap-6 p-4", children: rows.map((row, index) => (_jsx(View, { className: "flex-row gap-7", children: row.map((param) => (_jsx(ParamKnob, { paramId: param.id, size: size, trackColor: trackColor, valueColor: valueColor }, param.id))) }, index))) }));
|
|
139
163
|
}
|
|
164
|
+
const SELECT_ROW_HEIGHT = 30;
|
|
165
|
+
/** A dropdown: the menu renders in the overlay layer, positioned under
|
|
166
|
+
the trigger via onLayout, with a click-away backdrop and a scrolling
|
|
167
|
+
option list. */
|
|
168
|
+
export function Select({ options, index, width = 160, label, disabled, trackColor = "#2A2F27", menuColor = "#20241F", activeColor = "#C6F135", textColor = "#d4d4d8", activeTextColor = "#09090b", maxMenuHeight = 190, onChange, }) {
|
|
169
|
+
const [open, setOpen] = useState(false);
|
|
170
|
+
const [rect, onLayout] = useLayoutRect();
|
|
171
|
+
const overlay = useOverlay();
|
|
172
|
+
const current = Math.min(options.length - 1, Math.max(0, index));
|
|
173
|
+
useEffect(() => {
|
|
174
|
+
if (!open || rect === null) {
|
|
175
|
+
overlay.hide();
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
const menuHeight = Math.min(options.length * SELECT_ROW_HEIGHT + 8, maxMenuHeight);
|
|
179
|
+
overlay.show(_jsx(View, { className: "absolute inset-0", onClick: () => setOpen(false), children: _jsx(View, { className: "absolute rounded-lg border shadow-lg overflow-hidden", style: {
|
|
180
|
+
left: rect.x,
|
|
181
|
+
top: rect.y + rect.height + 4,
|
|
182
|
+
width: rect.width,
|
|
183
|
+
backgroundColor: menuColor,
|
|
184
|
+
borderColor: "#00000066",
|
|
185
|
+
}, children: _jsx(View, { className: "overflow-y-scroll p-[4] gap-[2]", style: { height: menuHeight }, children: options.map((option, i) => (_jsx(View, { className: "px-3 py-[6] rounded cursor-pointer hover:bg-white/10", style: { backgroundColor: i === current ? activeColor : "#00000000" }, onClick: () => {
|
|
186
|
+
onChange(i);
|
|
187
|
+
setOpen(false);
|
|
188
|
+
}, children: _jsx(Text, { className: "text-[12] font-medium", style: { color: i === current ? activeTextColor : textColor }, children: option }) }, `${option}-${i}`))) }) }) }));
|
|
189
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
190
|
+
}, [open, rect, options, current, menuColor, activeColor, textColor, activeTextColor, maxMenuHeight]);
|
|
191
|
+
return (_jsxs(View, { className: "items-center gap-2", children: [_jsxs(View, { className: `flex-row items-center justify-between rounded-lg border px-3 py-[8] ${disabled ? "opacity-40" : "cursor-pointer"}`, style: { width, backgroundColor: trackColor, borderColor: "#00000055" }, onLayout: onLayout, onClick: disabled ? undefined : () => setOpen((v) => !v), children: [_jsx(Text, { className: "text-[12] font-medium", style: { color: textColor }, children: options[current] ?? "" }), _jsx(Text, { className: "text-[9]", style: { color: textColor, opacity: 0.7 }, children: open ? "▲" : "▼" })] }), label !== undefined ? (_jsx(Text, { className: "text-faint text-[10] font-bold tracking-widest", children: label })) : null] }));
|
|
192
|
+
}
|
|
193
|
+
/** A Select bound to a choice-style APVTS parameter (same value↔index
|
|
194
|
+
mapping as ParamSegmented). */
|
|
195
|
+
export function ParamSelect({ paramId, options, label, ...rest }) {
|
|
196
|
+
const param = useParameter(paramId);
|
|
197
|
+
const count = Math.max(1, options.length);
|
|
198
|
+
const index = Math.round(param.value * (count - 1));
|
|
199
|
+
return (_jsx(Select, { options: options, index: index, label: label ?? param.name.toUpperCase(), onChange: (i) => {
|
|
200
|
+
param.begin();
|
|
201
|
+
param.set(count <= 1 ? 0 : i / (count - 1));
|
|
202
|
+
param.end();
|
|
203
|
+
}, ...rest }));
|
|
204
|
+
}
|
package/dist/hooks.d.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
import type { LayoutRect } from "./primitives";
|
|
2
|
+
/**
|
|
3
|
+
* Captures a node's root-space rect from its onLayout prop:
|
|
4
|
+
*
|
|
5
|
+
* const [rect, onLayout] = useLayoutRect();
|
|
6
|
+
* <View onLayout={onLayout} /> // rect updates whenever layout moves it
|
|
7
|
+
*/
|
|
8
|
+
export declare function useLayoutRect(): [LayoutRect | null, (rect: LayoutRect) => void];
|
|
1
9
|
/**
|
|
2
10
|
* Subscribes to a C++ event (RootView::sendNativeEvent) for the lifetime
|
|
3
11
|
* of the component. The handler can close over fresh state — it is kept
|
package/dist/hooks.js
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
// Convenience hooks over the native bridge.
|
|
2
2
|
import { useEffect, useRef, useState } from "react";
|
|
3
3
|
import { native } from "./native";
|
|
4
|
+
/**
|
|
5
|
+
* Captures a node's root-space rect from its onLayout prop:
|
|
6
|
+
*
|
|
7
|
+
* const [rect, onLayout] = useLayoutRect();
|
|
8
|
+
* <View onLayout={onLayout} /> // rect updates whenever layout moves it
|
|
9
|
+
*/
|
|
10
|
+
export function useLayoutRect() {
|
|
11
|
+
const [rect, setRect] = useState(null);
|
|
12
|
+
return [rect, setRect];
|
|
13
|
+
}
|
|
4
14
|
/**
|
|
5
15
|
* Subscribes to a C++ event (RootView::sendNativeEvent) for the lifetime
|
|
6
16
|
* of the component. The handler can close over fresh state — it is kept
|
package/dist/hostConfig.js
CHANGED
|
@@ -13,6 +13,8 @@ const hostTypes = {
|
|
|
13
13
|
};
|
|
14
14
|
const eventPropNames = {
|
|
15
15
|
onClick: "click",
|
|
16
|
+
onDoubleClick: "dblclick",
|
|
17
|
+
onWheel: "wheel",
|
|
16
18
|
onMouseEnter: "mouseenter",
|
|
17
19
|
onMouseLeave: "mouseleave",
|
|
18
20
|
onMouseDown: "mousedown",
|
|
@@ -20,6 +22,7 @@ const eventPropNames = {
|
|
|
20
22
|
onDragStart: "dragstart",
|
|
21
23
|
onDrag: "drag",
|
|
22
24
|
onDragEnd: "dragend",
|
|
25
|
+
onLayout: "layout",
|
|
23
26
|
onChange: "change",
|
|
24
27
|
onSubmit: "submit",
|
|
25
28
|
onFocus: "focus",
|
package/dist/index.d.ts
CHANGED
|
@@ -4,7 +4,10 @@ export { View, Text, Image, TextInput, NativeView } from "./primitives";
|
|
|
4
4
|
export type { CommonProps, TextProps, ImageProps, TextInputProps, NativeViewProps, } from "./primitives";
|
|
5
5
|
export { render, unmount } from "./render";
|
|
6
6
|
export { native } from "./native";
|
|
7
|
-
export { useNativeEvent, useDebounced } from "./hooks";
|
|
7
|
+
export { useNativeEvent, useDebounced, useLayoutRect } from "./hooks";
|
|
8
|
+
export { useOverlay, OverlayLayer } from "./overlay";
|
|
9
|
+
export { Tooltip, Modal } from "./popover";
|
|
10
|
+
export type { TooltipProps, ModalProps } from "./popover";
|
|
8
11
|
export { configureTheme, tw } from "./tw";
|
|
9
12
|
export type { Style, ResolvedClasses } from "./tw";
|
|
10
13
|
export { cx } from "./cx";
|
|
@@ -13,8 +16,8 @@ export { useTween, useSpring, springStep, lerp, Easing } from "./animation";
|
|
|
13
16
|
export type { TweenOptions, SpringOptions, EasingFn } from "./animation";
|
|
14
17
|
export { useParameter, useParameterList } from "./parameters";
|
|
15
18
|
export type { ParameterState, ParameterHandle, ParameterInfo } from "./parameters";
|
|
16
|
-
export { Knob, Slider, Toggle, XYPad, Segmented, GenericEditor, ParamKnob, ParamSlider, ParamToggle, ParamXYPad, ParamSegmented, dragToValue, } from "./controls";
|
|
17
|
-
export type { KnobProps, SliderProps, ToggleProps, XYPadProps, SegmentedProps, GenericEditorProps, ParamKnobProps, ParamSliderProps, ParamToggleProps, ParamXYPadProps, ParamSegmentedProps, } from "./controls";
|
|
19
|
+
export { Knob, Slider, Toggle, XYPad, Segmented, Select, GenericEditor, ParamKnob, ParamSlider, ParamToggle, ParamXYPad, ParamSegmented, ParamSelect, dragToValue, } from "./controls";
|
|
20
|
+
export type { KnobProps, SliderProps, ToggleProps, XYPadProps, SegmentedProps, SelectProps, GenericEditorProps, ParamKnobProps, ParamSliderProps, ParamToggleProps, ParamXYPadProps, ParamSegmentedProps, ParamSelectProps, } from "./controls";
|
|
18
21
|
export { Meter, usePeakHold, peakHoldStep } from "./meter";
|
|
19
22
|
export type { MeterProps, PeakHoldOptions, PeakHoldState } from "./meter";
|
|
20
|
-
export type { DragEventPayload } from "./primitives";
|
|
23
|
+
export type { DragEventPayload, LayoutRect, WheelEventPayload } from "./primitives";
|
package/dist/index.js
CHANGED
|
@@ -5,10 +5,12 @@ import "./bridge";
|
|
|
5
5
|
export { View, Text, Image, TextInput, NativeView } from "./primitives";
|
|
6
6
|
export { render, unmount } from "./render";
|
|
7
7
|
export { native } from "./native";
|
|
8
|
-
export { useNativeEvent, useDebounced } from "./hooks";
|
|
8
|
+
export { useNativeEvent, useDebounced, useLayoutRect } from "./hooks";
|
|
9
|
+
export { useOverlay, OverlayLayer } from "./overlay";
|
|
10
|
+
export { Tooltip, Modal } from "./popover";
|
|
9
11
|
export { configureTheme, tw } from "./tw";
|
|
10
12
|
export { cx } from "./cx";
|
|
11
13
|
export { useTween, useSpring, springStep, lerp, Easing } from "./animation";
|
|
12
14
|
export { useParameter, useParameterList } from "./parameters";
|
|
13
|
-
export { Knob, Slider, Toggle, XYPad, Segmented, GenericEditor, ParamKnob, ParamSlider, ParamToggle, ParamXYPad, ParamSegmented, dragToValue, } from "./controls";
|
|
15
|
+
export { Knob, Slider, Toggle, XYPad, Segmented, Select, GenericEditor, ParamKnob, ParamSlider, ParamToggle, ParamXYPad, ParamSegmented, ParamSelect, dragToValue, } from "./controls";
|
|
14
16
|
export { Meter, usePeakHold, peakHoldStep } from "./meter";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
/** A per-component slot in the overlay layer. show() replaces the slot's
|
|
3
|
+
content; hide() removes it; unmounting cleans up automatically. */
|
|
4
|
+
export declare function useOverlay(): {
|
|
5
|
+
show: (node: ReactNode) => void;
|
|
6
|
+
hide: () => void;
|
|
7
|
+
};
|
|
8
|
+
/** Mounted automatically by render() as the last sibling of your app. */
|
|
9
|
+
export declare function OverlayLayer(): import("react").JSX.Element;
|
package/dist/overlay.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
// The overlay layer — content that paints above everything else (menus,
|
|
3
|
+
// tooltips, modals). render() mounts <OverlayLayer/> after your app
|
|
4
|
+
// automatically, so painting order puts overlays on top and hit-testing
|
|
5
|
+
// reaches them first. Position entries absolutely using rects from
|
|
6
|
+
// onLayout / useLayoutRect.
|
|
7
|
+
import { Fragment, useCallback, useEffect, useRef, useSyncExternalStore } from "react";
|
|
8
|
+
let entries = [];
|
|
9
|
+
const subscribers = new Set();
|
|
10
|
+
let nextOverlayKey = 1;
|
|
11
|
+
function emit() {
|
|
12
|
+
for (const subscriber of subscribers)
|
|
13
|
+
subscriber();
|
|
14
|
+
}
|
|
15
|
+
function setEntry(key, node) {
|
|
16
|
+
entries = [...entries.filter((e) => e.key !== key), { key, node }];
|
|
17
|
+
emit();
|
|
18
|
+
}
|
|
19
|
+
function clearEntry(key) {
|
|
20
|
+
if (!entries.some((e) => e.key === key))
|
|
21
|
+
return;
|
|
22
|
+
entries = entries.filter((e) => e.key !== key);
|
|
23
|
+
emit();
|
|
24
|
+
}
|
|
25
|
+
/** A per-component slot in the overlay layer. show() replaces the slot's
|
|
26
|
+
content; hide() removes it; unmounting cleans up automatically. */
|
|
27
|
+
export function useOverlay() {
|
|
28
|
+
const key = useRef(0);
|
|
29
|
+
if (key.current === 0)
|
|
30
|
+
key.current = nextOverlayKey++;
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
const k = key.current;
|
|
33
|
+
return () => clearEntry(k);
|
|
34
|
+
}, []);
|
|
35
|
+
return {
|
|
36
|
+
show: useCallback((node) => setEntry(key.current, node), []),
|
|
37
|
+
hide: useCallback(() => clearEntry(key.current), []),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/** Mounted automatically by render() as the last sibling of your app. */
|
|
41
|
+
export function OverlayLayer() {
|
|
42
|
+
const list = useSyncExternalStore((onStoreChange) => {
|
|
43
|
+
subscribers.add(onStoreChange);
|
|
44
|
+
return () => subscribers.delete(onStoreChange);
|
|
45
|
+
}, () => entries);
|
|
46
|
+
return (_jsx(_Fragment, { children: list.map((entry) => (_jsx(Fragment, { children: entry.node }, entry.key))) }));
|
|
47
|
+
}
|
package/dist/parameters.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ export interface ParameterState {
|
|
|
3
3
|
text: string;
|
|
4
4
|
name: string;
|
|
5
5
|
label: string;
|
|
6
|
+
/** The host's normalized default — double-click-reset target. */
|
|
7
|
+
defaultValue: number;
|
|
6
8
|
}
|
|
7
9
|
export interface ParameterHandle extends ParameterState {
|
|
8
10
|
set: (normalized: number) => void;
|
|
@@ -16,6 +18,7 @@ export interface ParameterInfo {
|
|
|
16
18
|
/** Normalized 0..1 snapshot at mount — use useParameter(id) for live values. */
|
|
17
19
|
value: number;
|
|
18
20
|
text: string;
|
|
21
|
+
defaultValue: number;
|
|
19
22
|
}
|
|
20
23
|
/**
|
|
21
24
|
* Enumerates every APVTS parameter (via param:list) once at mount — the
|
package/dist/parameters.js
CHANGED
|
@@ -18,6 +18,7 @@ export function useParameterList() {
|
|
|
18
18
|
label: String(entry?.label ?? ""),
|
|
19
19
|
value: Number(entry?.value ?? 0),
|
|
20
20
|
text: String(entry?.text ?? ""),
|
|
21
|
+
defaultValue: Number(entry?.defaultValue ?? 0),
|
|
21
22
|
}));
|
|
22
23
|
});
|
|
23
24
|
return list;
|
|
@@ -31,8 +32,9 @@ export function useParameter(id) {
|
|
|
31
32
|
text: String(initial.text ?? ""),
|
|
32
33
|
name: String(initial.name ?? id),
|
|
33
34
|
label: String(initial.label ?? ""),
|
|
35
|
+
defaultValue: Number(initial.defaultValue ?? 0),
|
|
34
36
|
}
|
|
35
|
-
: { value: 0, text: "", name: id, label: "" };
|
|
37
|
+
: { value: 0, text: "", name: id, label: "", defaultValue: 0 };
|
|
36
38
|
});
|
|
37
39
|
useEffect(() => native.on("param", (p) => {
|
|
38
40
|
if (p?.id === id)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
export interface TooltipProps {
|
|
3
|
+
/** The tip text. */
|
|
4
|
+
label: string;
|
|
5
|
+
/** Hover dwell before showing. Default 450ms. */
|
|
6
|
+
delayMs?: number;
|
|
7
|
+
/** Gap between the child and the tip. Default 6. */
|
|
8
|
+
offset?: number;
|
|
9
|
+
backgroundColor?: string;
|
|
10
|
+
color?: string;
|
|
11
|
+
children: ReactNode;
|
|
12
|
+
}
|
|
13
|
+
/** Wraps its child; hovering it long enough shows a tip below. */
|
|
14
|
+
export declare function Tooltip({ label, delayMs, offset, backgroundColor, color, children, }: TooltipProps): import("react").JSX.Element;
|
|
15
|
+
export interface ModalProps {
|
|
16
|
+
open: boolean;
|
|
17
|
+
/** Backdrop click (and nothing else) calls this. */
|
|
18
|
+
onClose: () => void;
|
|
19
|
+
title?: string;
|
|
20
|
+
width?: number;
|
|
21
|
+
backgroundColor?: string;
|
|
22
|
+
backdropColor?: string;
|
|
23
|
+
children: ReactNode;
|
|
24
|
+
}
|
|
25
|
+
/** A centered dialog over a click-away backdrop — confirms, settings,
|
|
26
|
+
about boxes. Content is yours; the chrome is minimal. */
|
|
27
|
+
export declare function Modal({ open, onClose, title, width, backgroundColor, backdropColor, children, }: ModalProps): null;
|
package/dist/popover.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// Tooltip + Modal — the dialog kit, built on the overlay layer and
|
|
3
|
+
// onLayout. Both paint above everything via useOverlay.
|
|
4
|
+
import { useEffect, useRef, useState } from "react";
|
|
5
|
+
import { View, Text } from "./primitives";
|
|
6
|
+
import { useLayoutRect } from "./hooks";
|
|
7
|
+
import { useOverlay } from "./overlay";
|
|
8
|
+
/** Wraps its child; hovering it long enough shows a tip below. */
|
|
9
|
+
export function Tooltip({ label, delayMs = 450, offset = 6, backgroundColor = "#20241F", color = "#d4d4d8", children, }) {
|
|
10
|
+
const [rect, onLayout] = useLayoutRect();
|
|
11
|
+
const [visible, setVisible] = useState(false);
|
|
12
|
+
const overlay = useOverlay();
|
|
13
|
+
const timer = useRef(null);
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
if (!visible || rect === null) {
|
|
16
|
+
overlay.hide();
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
overlay.show(_jsx(View, { className: "absolute rounded-md border px-2 py-[4]", style: {
|
|
20
|
+
left: rect.x,
|
|
21
|
+
top: rect.y + rect.height + offset,
|
|
22
|
+
backgroundColor,
|
|
23
|
+
borderColor: "#00000066",
|
|
24
|
+
}, children: _jsx(Text, { className: "text-[11]", style: { color }, children: label }) }));
|
|
25
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
26
|
+
}, [visible, rect, label, offset, backgroundColor, color]);
|
|
27
|
+
useEffect(() => () => {
|
|
28
|
+
if (timer.current !== null)
|
|
29
|
+
clearTimeout(timer.current);
|
|
30
|
+
}, []);
|
|
31
|
+
return (_jsx(View, { onLayout: onLayout, onMouseEnter: () => {
|
|
32
|
+
if (timer.current !== null)
|
|
33
|
+
clearTimeout(timer.current);
|
|
34
|
+
timer.current = setTimeout(() => setVisible(true), delayMs);
|
|
35
|
+
}, onMouseLeave: () => {
|
|
36
|
+
if (timer.current !== null)
|
|
37
|
+
clearTimeout(timer.current);
|
|
38
|
+
timer.current = null;
|
|
39
|
+
setVisible(false);
|
|
40
|
+
}, children: children }));
|
|
41
|
+
}
|
|
42
|
+
/** A centered dialog over a click-away backdrop — confirms, settings,
|
|
43
|
+
about boxes. Content is yours; the chrome is minimal. */
|
|
44
|
+
export function Modal({ open, onClose, title, width = 320, backgroundColor = "#1C201B", backdropColor = "#000000B0", children, }) {
|
|
45
|
+
const overlay = useOverlay();
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
if (!open) {
|
|
48
|
+
overlay.hide();
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
overlay.show(_jsx(View, { className: "absolute inset-0 items-center justify-center", style: { backgroundColor: backdropColor }, onClick: onClose, children: _jsxs(View, { className: "rounded-xl border p-5 gap-3 shadow-xl", style: { width, backgroundColor, borderColor: "#00000066" }, onClick: () => { }, children: [title !== undefined ? (_jsx(Text, { className: "text-[13] font-bold tracking-wide", children: title })) : null, children] }) }));
|
|
52
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
53
|
+
}, [open, title, width, backgroundColor, backdropColor, children]);
|
|
54
|
+
return null;
|
|
55
|
+
}
|
package/dist/primitives.d.ts
CHANGED
|
@@ -8,6 +8,18 @@ export interface DragEventPayload {
|
|
|
8
8
|
x: number;
|
|
9
9
|
y: number;
|
|
10
10
|
}
|
|
11
|
+
/** A node's laid-out rect in root coordinates (scroll-adjusted). */
|
|
12
|
+
export interface LayoutRect {
|
|
13
|
+
x: number;
|
|
14
|
+
y: number;
|
|
15
|
+
width: number;
|
|
16
|
+
height: number;
|
|
17
|
+
}
|
|
18
|
+
/** Mouse-wheel payload. dy is JUCE's notch fraction (~0.1 per notch,
|
|
19
|
+
positive = wheel up). */
|
|
20
|
+
export interface WheelEventPayload {
|
|
21
|
+
dy: number;
|
|
22
|
+
}
|
|
11
23
|
export interface CommonProps {
|
|
12
24
|
className?: string;
|
|
13
25
|
style?: Style;
|
|
@@ -15,6 +27,9 @@ export interface CommonProps {
|
|
|
15
27
|
/** Resets the scroll offset of an overflow-y-scroll container. */
|
|
16
28
|
scrollTop?: number;
|
|
17
29
|
onClick?: () => void;
|
|
30
|
+
onDoubleClick?: () => void;
|
|
31
|
+
/** Wheel over this node (controls win the wheel over scroll containers). */
|
|
32
|
+
onWheel?: (e: WheelEventPayload) => void;
|
|
18
33
|
onMouseEnter?: () => void;
|
|
19
34
|
onMouseLeave?: () => void;
|
|
20
35
|
onMouseDown?: () => void;
|
|
@@ -22,6 +37,9 @@ export interface CommonProps {
|
|
|
22
37
|
onDragStart?: (e: DragEventPayload) => void;
|
|
23
38
|
onDrag?: (e: DragEventPayload) => void;
|
|
24
39
|
onDragEnd?: (e: DragEventPayload) => void;
|
|
40
|
+
/** Fires after layout whenever this node's root-space rect changes —
|
|
41
|
+
the foundation for popovers, menus, and tooltips. */
|
|
42
|
+
onLayout?: (rect: LayoutRect) => void;
|
|
25
43
|
}
|
|
26
44
|
export declare function View(props: CommonProps): import("react").ReactElement<CommonProps, string | import("react").JSXElementConstructor<any>>;
|
|
27
45
|
export interface TextProps extends Omit<CommonProps, "children"> {
|
|
@@ -55,6 +73,8 @@ export declare function TextInput({ onChange, onSubmit, ...rest }: TextInputProp
|
|
|
55
73
|
className?: string | undefined;
|
|
56
74
|
scrollTop?: number | undefined;
|
|
57
75
|
onClick?: (() => void) | undefined;
|
|
76
|
+
onDoubleClick?: (() => void) | undefined;
|
|
77
|
+
onWheel?: ((e: WheelEventPayload) => void) | undefined;
|
|
58
78
|
onMouseEnter?: (() => void) | undefined;
|
|
59
79
|
onMouseLeave?: (() => void) | undefined;
|
|
60
80
|
onMouseDown?: (() => void) | undefined;
|
|
@@ -62,6 +82,7 @@ export declare function TextInput({ onChange, onSubmit, ...rest }: TextInputProp
|
|
|
62
82
|
onDragStart?: ((e: DragEventPayload) => void) | undefined;
|
|
63
83
|
onDrag?: ((e: DragEventPayload) => void) | undefined;
|
|
64
84
|
onDragEnd?: ((e: DragEventPayload) => void) | undefined;
|
|
85
|
+
onLayout?: ((rect: LayoutRect) => void) | undefined;
|
|
65
86
|
}, string | import("react").JSXElementConstructor<any>>;
|
|
66
87
|
export interface NativeViewProps extends CommonProps {
|
|
67
88
|
/** Id of a component factory registered in the C++ NativeRegistry. */
|
package/dist/render.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
|
-
/** Mounts (or re-renders) the app into the plugin window.
|
|
2
|
+
/** Mounts (or re-renders) the app into the plugin window. The overlay
|
|
3
|
+
layer (menus, tooltips) mounts after the app so it paints on top. */
|
|
3
4
|
export declare function render(element: ReactNode): void;
|
|
4
5
|
/** Unmounts the current app (used by hot reload teardown). */
|
|
5
6
|
export declare function unmount(): void;
|
package/dist/render.js
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
2
|
import Reconciler from "react-reconciler";
|
|
2
3
|
import { LegacyRoot } from "react-reconciler/constants";
|
|
3
4
|
import { hostConfig } from "./hostConfig";
|
|
4
5
|
import { flushOps } from "./bridge";
|
|
6
|
+
import { OverlayLayer } from "./overlay";
|
|
5
7
|
const reconciler = Reconciler(hostConfig);
|
|
6
8
|
let container = null;
|
|
7
|
-
/** Mounts (or re-renders) the app into the plugin window.
|
|
9
|
+
/** Mounts (or re-renders) the app into the plugin window. The overlay
|
|
10
|
+
layer (menus, tooltips) mounts after the app so it paints on top. */
|
|
8
11
|
export function render(element) {
|
|
9
12
|
if (!container) {
|
|
10
13
|
container = reconciler.createContainer({}, LegacyRoot, null, false, null, "vsreact", (error) => console.error("[vsreact] recoverable error:", error), null);
|
|
11
14
|
}
|
|
12
|
-
reconciler.updateContainer(element, container, null, null);
|
|
15
|
+
reconciler.updateContainer(_jsxs(_Fragment, { children: [element, _jsx(OverlayLayer, {})] }), container, null, null);
|
|
13
16
|
flushOps();
|
|
14
17
|
}
|
|
15
18
|
/** Unmounts the current app (used by hot reload teardown). */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vsreact/core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"description": "Write React. Ship native VST. A React renderer for JUCE audio plugins — QuickJS + Yoga + juce::Graphics, no webview.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"bugs": "https://github.com/N9RecordsTechnologiesIL/VSReacT/issues",
|
|
21
21
|
"repository": {
|
|
22
22
|
"type": "git",
|
|
23
|
-
"url": "https://github.com/N9RecordsTechnologiesIL/VSReacT.git",
|
|
23
|
+
"url": "git+https://github.com/N9RecordsTechnologiesIL/VSReacT.git",
|
|
24
24
|
"directory": "vsreact/js"
|
|
25
25
|
},
|
|
26
26
|
"main": "dist/index.js",
|