cabbage-react 1.0.0-beta.8 → 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 +108 -19
- package/dist/hooks/useCabbageProperties.d.ts +9 -0
- package/dist/hooks/useCabbageProperties.d.ts.map +1 -0
- package/dist/hooks/useCabbageState.d.ts +0 -1
- package/dist/hooks/useCabbageState.d.ts.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +101 -61
- package/package.json +2 -2
- package/dist/hooks/useGetCabbageFormData.d.ts +0 -9
- package/dist/hooks/useGetCabbageFormData.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -1,19 +1,108 @@
|
|
|
1
|
-
# Cabbage React
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
1
|
+
# Cabbage React
|
|
2
|
+
|
|
3
|
+
Cabbage React provides React hooks for synchronizing [Cabbage](https://cabbageaudio.com) with [React](https://github.com/facebook/react), making it easier to build a custom UI that communicates with the Cabbage host.
|
|
4
|
+
|
|
5
|
+
## Example Project
|
|
6
|
+
|
|
7
|
+
An example of implementation is available [here](https://github.com/hdale94/cabbage-react-example).
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```jsx
|
|
12
|
+
yarn add cabbage-react
|
|
13
|
+
# or
|
|
14
|
+
npm install cabbage-react
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Hooks
|
|
18
|
+
|
|
19
|
+
### useCabbageState
|
|
20
|
+
|
|
21
|
+
Synchronize a parameter with Cabbage. This hook:
|
|
22
|
+
|
|
23
|
+
- Listens for value updates from Cabbage.
|
|
24
|
+
- Sends local changes (e.g., via sliders, knobs) back to Cabbage.
|
|
25
|
+
|
|
26
|
+
### useCabbageProperties
|
|
27
|
+
|
|
28
|
+
Get properties for a parameter from Cabbage.
|
|
29
|
+
This hook:
|
|
30
|
+
|
|
31
|
+
- Listens for property updates from Cabbage.
|
|
32
|
+
- Updates local state automatically when data changes.
|
|
33
|
+
|
|
34
|
+
## Usage
|
|
35
|
+
|
|
36
|
+
```jsx
|
|
37
|
+
import { InputHTMLAttributes } from "react";
|
|
38
|
+
import { useCabbageProperties, useCabbageState } from "cabbage-react";
|
|
39
|
+
|
|
40
|
+
const HorizontalSlider = ({
|
|
41
|
+
channel,
|
|
42
|
+
paramIdx,
|
|
43
|
+
inputProps,
|
|
44
|
+
}: {
|
|
45
|
+
channel: string,
|
|
46
|
+
paramIdx: number,
|
|
47
|
+
inputProps?: InputHTMLAttributes<HTMLInputElement>,
|
|
48
|
+
}) => {
|
|
49
|
+
const { properties } = useCabbageProperties(channel);
|
|
50
|
+
const { value, setValue } = useCabbageState < number > (channel, paramIdx);
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<div>
|
|
54
|
+
{/* Label */}
|
|
55
|
+
<p style={{ marginBottom: "4px" }}>{properties?.text ?? "Label"}</p>
|
|
56
|
+
|
|
57
|
+
<input
|
|
58
|
+
type="range"
|
|
59
|
+
min={properties?.range?.min ?? 0}
|
|
60
|
+
max={properties?.range?.max ?? 1}
|
|
61
|
+
step={properties?.range?.increment ?? 0.01}
|
|
62
|
+
value={value}
|
|
63
|
+
onChange={(e) => setValue(e.target.valueAsNumber)}
|
|
64
|
+
{...inputProps}
|
|
65
|
+
style={{
|
|
66
|
+
accentColor: "rgb(148, 242, 254)",
|
|
67
|
+
marginTop: "20px",
|
|
68
|
+
...inputProps?.style,
|
|
69
|
+
}}
|
|
70
|
+
/>
|
|
71
|
+
|
|
72
|
+
{/* Displaying the value */}
|
|
73
|
+
<p style={{ marginTop: "4px" }}>{value ?? 0}</p>
|
|
74
|
+
</div>
|
|
75
|
+
);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export default HorizontalSlider;
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Interact directly with Cabbage
|
|
82
|
+
|
|
83
|
+
You can also import the [Cabbage class](https://github.com/hdale94/cabbage-react/blob/main/src/cabbage/cabbage.js) to send custom messages or interact directly with Cabbage.
|
|
84
|
+
|
|
85
|
+
## Notify Cabbage When UI Is Ready
|
|
86
|
+
|
|
87
|
+
To let Cabbage know your UI is ready to receive data, send a `cabbageIsReadyToLoad` message when your app initializes.
|
|
88
|
+
|
|
89
|
+
Place this call before rendering your app — typically in your main.tsx or index.tsx file:
|
|
90
|
+
|
|
91
|
+
```jsx
|
|
92
|
+
import { StrictMode } from "react";
|
|
93
|
+
import { createRoot } from "react-dom/client";
|
|
94
|
+
import "./index.css";
|
|
95
|
+
import App from "./App.tsx";
|
|
96
|
+
import { Cabbage } from "cabbage-react";
|
|
97
|
+
|
|
98
|
+
if (import.meta.env.PROD) {
|
|
99
|
+
// Notify Cabbage that the UI is ready to receive data
|
|
100
|
+
Cabbage.sendCustomCommand("cabbageIsReadyToLoad");
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
createRoot(document.getElementById("root")!).render(
|
|
104
|
+
<StrictMode>
|
|
105
|
+
<App />
|
|
106
|
+
</StrictMode>
|
|
107
|
+
);
|
|
108
|
+
```
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom hook to get a parameter's properties from Cabbage.
|
|
3
|
+
* This hook listens for updates to parameter properties via Cabbage and updates the local state
|
|
4
|
+
* whenever new data is received.
|
|
5
|
+
*/
|
|
6
|
+
export declare const useCabbageProperties: (channel: string) => {
|
|
7
|
+
properties: Record<string, any> | undefined;
|
|
8
|
+
};
|
|
9
|
+
//# sourceMappingURL=useCabbageProperties.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCabbageProperties.d.ts","sourceRoot":"","sources":["../../src/hooks/useCabbageProperties.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,YAAa,MAAM;;CAgCnD,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCabbageState.d.ts","sourceRoot":"","sources":["../../src/hooks/useCabbageState.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useCabbageState.d.ts","sourceRoot":"","sources":["../../src/hooks/useCabbageState.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,WAAW,MAAM,YAAY,MAAM;;yBAM9B,CAAC;CAwFtC,CAAC"}
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const d=require("react");console.log("Cabbage: loading cabbage.js");class p{static sendParameterUpdate(n,e=null){const a={command:"parameterChange",obj:JSON.stringify(n)};e!==null?e.postMessage(a):(console.log("Cabbage: sending parameter change from UI",a),window.sendMessageFromUI(a))}static sendCustomCommand(n,e=null){const a={command:n,text:JSON.stringify({})};console.log("Cabbage: sending custom command from UI",a),e!==null?e.postMessage(a):window.sendMessageFromUI(a)}static sendWidgetUpdate(n,e=null){console.log("Cabbage: sending widget update from UI",n.props);const a={command:"widgetStateUpdate",obj:JSON.stringify(n.props)};e!==null?e.postMessage(a):window.sendMessageFromUI(a)}static sendMidiMessageFromUI(n,e,a,t=null){var r={statusByte:n,dataByte1:e,dataByte2:a};const o={command:"midiMessage",obj:JSON.stringify(r)};console.log("Cabbage: sending midi message from UI",r),t!==null?t.postMessage(o):window.sendMessageFromUI(o)}static MidiMessageFromHost(n,e,a){console.log("Cabbage: Got MIDI Message"+n+":"+e+":"+a)}static triggerFileOpenDialog(n,e){var a={channel:e};const t={command:"fileOpen",obj:JSON.stringify(a)};n!==null?n.postMessage(t):window.sendMessageFromUI(t)}}const C=l=>{const[n,e]=d.useState();return d.useEffect(()=>{const a=t=>{const{channel:r,data:o,command:f}=t.data;if(r===l&&o&&f==="widgetUpdate"){const m=JSON.parse(o);console.log(`[Cabbage-React] ${f}: Received properties for channel: ${r}`,m),e(m)}};return window.addEventListener("message",a),()=>{window.removeEventListener("message",a)}},[]),{properties:n}},h=(l,n)=>{const{properties:e}=C(l),[a,t]=d.useState(),[r,o]=d.useState(),f=s=>{t(s);const i={paramIdx:n,channelType:r,channel:l,value:s};p.sendParameterUpdate(i,null)},m=s=>{if(typeof s=="number")return"number";if(typeof s=="string")return"string"};return d.useEffect(()=>{var i;if((e==null?void 0:e.channel)!==l)return;const s=(i=e==null?void 0:e.range)==null?void 0:i.defaultValue;if(a===void 0&&s!==void 0){console.log(`[Cabbage-React]: Received default value for channel "${e==null?void 0:e.channel}"`,s),t(s);const c=m(s);c&&o(c)}},[e]),d.useEffect(()=>{const s=i=>{const{command:c}=i.data;if(c==="widgetUpdate"){const{channel:u,value:g}=i.data;if(u!==l)return;if(console.log(`[Cabbage-React] ${c}: Received initial value for channel "${u}"`,g),g!==void 0){t(g);const b=m(g);b&&o(b)}}if(c==="parameterChange"){const{paramIdx:u,value:g}=i.data.data||{};u===n&&g!==void 0&&(console.log(`[Cabbage-React] ${c}: Received value change for paramIdx: ${u}`,g),t(g))}};return window.addEventListener("message",s),()=>{window.removeEventListener("message",s)}},[]),{value:a,setValue:f}};exports.Cabbage=p;exports.useCabbageProperties=C;exports.useCabbageState=h;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { useCabbageState } from './hooks/useCabbageState';
|
|
2
|
-
export {
|
|
2
|
+
export { useCabbageProperties } from './hooks/useCabbageProperties';
|
|
3
|
+
export { Cabbage } from './cabbage/cabbage';
|
|
3
4
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,92 +1,132 @@
|
|
|
1
|
-
import { useState as
|
|
1
|
+
import { useState as f, useEffect as b } from "react";
|
|
2
2
|
console.log("Cabbage: loading cabbage.js");
|
|
3
|
-
class
|
|
4
|
-
static sendParameterUpdate(
|
|
5
|
-
const
|
|
3
|
+
class C {
|
|
4
|
+
static sendParameterUpdate(n, e = null) {
|
|
5
|
+
const a = {
|
|
6
6
|
command: "parameterChange",
|
|
7
|
-
obj: JSON.stringify(
|
|
7
|
+
obj: JSON.stringify(n)
|
|
8
8
|
};
|
|
9
|
-
|
|
9
|
+
e !== null ? e.postMessage(a) : (console.log("Cabbage: sending parameter change from UI", a), window.sendMessageFromUI(a));
|
|
10
10
|
}
|
|
11
|
-
static sendCustomCommand(
|
|
12
|
-
const
|
|
13
|
-
command:
|
|
11
|
+
static sendCustomCommand(n, e = null) {
|
|
12
|
+
const a = {
|
|
13
|
+
command: n,
|
|
14
14
|
text: JSON.stringify({})
|
|
15
15
|
};
|
|
16
|
-
console.log("Cabbage: sending custom command from UI",
|
|
16
|
+
console.log("Cabbage: sending custom command from UI", a), e !== null ? e.postMessage(a) : window.sendMessageFromUI(a);
|
|
17
17
|
}
|
|
18
|
-
static sendWidgetUpdate(
|
|
19
|
-
console.log("Cabbage: sending widget update from UI",
|
|
20
|
-
const
|
|
18
|
+
static sendWidgetUpdate(n, e = null) {
|
|
19
|
+
console.log("Cabbage: sending widget update from UI", n.props);
|
|
20
|
+
const a = {
|
|
21
21
|
command: "widgetStateUpdate",
|
|
22
|
-
obj: JSON.stringify(
|
|
22
|
+
obj: JSON.stringify(n.props)
|
|
23
23
|
};
|
|
24
|
-
|
|
24
|
+
e !== null ? e.postMessage(a) : window.sendMessageFromUI(a);
|
|
25
25
|
}
|
|
26
|
-
static sendMidiMessageFromUI(
|
|
27
|
-
var
|
|
28
|
-
statusByte:
|
|
29
|
-
dataByte1:
|
|
30
|
-
dataByte2:
|
|
26
|
+
static sendMidiMessageFromUI(n, e, a, t = null) {
|
|
27
|
+
var d = {
|
|
28
|
+
statusByte: n,
|
|
29
|
+
dataByte1: e,
|
|
30
|
+
dataByte2: a
|
|
31
31
|
};
|
|
32
|
-
const
|
|
32
|
+
const o = {
|
|
33
33
|
command: "midiMessage",
|
|
34
|
-
obj: JSON.stringify(
|
|
34
|
+
obj: JSON.stringify(d)
|
|
35
35
|
};
|
|
36
|
-
console.log("Cabbage: sending midi message from UI",
|
|
36
|
+
console.log("Cabbage: sending midi message from UI", d), t !== null ? t.postMessage(o) : window.sendMessageFromUI(o);
|
|
37
37
|
}
|
|
38
|
-
static MidiMessageFromHost(
|
|
39
|
-
console.log("Cabbage: Got MIDI Message" +
|
|
38
|
+
static MidiMessageFromHost(n, e, a) {
|
|
39
|
+
console.log("Cabbage: Got MIDI Message" + n + ":" + e + ":" + a);
|
|
40
40
|
}
|
|
41
|
-
static triggerFileOpenDialog(
|
|
42
|
-
var
|
|
43
|
-
channel:
|
|
41
|
+
static triggerFileOpenDialog(n, e) {
|
|
42
|
+
var a = {
|
|
43
|
+
channel: e
|
|
44
44
|
};
|
|
45
|
-
const
|
|
45
|
+
const t = {
|
|
46
46
|
command: "fileOpen",
|
|
47
|
-
obj: JSON.stringify(
|
|
47
|
+
obj: JSON.stringify(a)
|
|
48
48
|
};
|
|
49
|
-
|
|
49
|
+
n !== null ? n.postMessage(t) : window.sendMessageFromUI(t);
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
|
-
const
|
|
53
|
-
const [
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
52
|
+
const h = (l) => {
|
|
53
|
+
const [n, e] = f();
|
|
54
|
+
return b(() => {
|
|
55
|
+
const a = (t) => {
|
|
56
|
+
const { channel: d, data: o, command: u } = t.data;
|
|
57
|
+
if (d === l && o && u === "widgetUpdate") {
|
|
58
|
+
const c = JSON.parse(o);
|
|
59
|
+
console.log(
|
|
60
|
+
`[Cabbage-React] ${u}: Received properties for channel: ${d}`,
|
|
61
|
+
c
|
|
62
|
+
), e(c);
|
|
63
|
+
}
|
|
59
64
|
};
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
return m(() => {
|
|
63
|
-
const g = (r) => {
|
|
64
|
-
const { data: t } = r;
|
|
65
|
-
console.log("Cabbage-react: receiving parameter change", t), t.channel === o && t.command === "widgetUpdate" && (t.value && e(t.value), t.data && i(JSON.parse(t.data)));
|
|
66
|
-
};
|
|
67
|
-
return window.addEventListener("message", g), () => {
|
|
68
|
-
window.removeEventListener("message", g);
|
|
65
|
+
return window.addEventListener("message", a), () => {
|
|
66
|
+
window.removeEventListener("message", a);
|
|
69
67
|
};
|
|
70
68
|
}, []), {
|
|
71
|
-
|
|
72
|
-
setValue: l,
|
|
73
|
-
data: n
|
|
69
|
+
properties: n
|
|
74
70
|
};
|
|
75
|
-
},
|
|
76
|
-
const [
|
|
77
|
-
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
71
|
+
}, M = (l, n) => {
|
|
72
|
+
const { properties: e } = h(l), [a, t] = f(), [d, o] = f(), u = (s) => {
|
|
73
|
+
t(s);
|
|
74
|
+
const i = {
|
|
75
|
+
paramIdx: n,
|
|
76
|
+
channelType: d,
|
|
77
|
+
channel: l,
|
|
78
|
+
value: s
|
|
81
79
|
};
|
|
82
|
-
|
|
83
|
-
|
|
80
|
+
C.sendParameterUpdate(i, null);
|
|
81
|
+
}, c = (s) => {
|
|
82
|
+
if (typeof s == "number") return "number";
|
|
83
|
+
if (typeof s == "string") return "string";
|
|
84
|
+
};
|
|
85
|
+
return b(() => {
|
|
86
|
+
var i;
|
|
87
|
+
if ((e == null ? void 0 : e.channel) !== l) return;
|
|
88
|
+
const s = (i = e == null ? void 0 : e.range) == null ? void 0 : i.defaultValue;
|
|
89
|
+
if (a === void 0 && s !== void 0) {
|
|
90
|
+
console.log(
|
|
91
|
+
`[Cabbage-React]: Received default value for channel "${e == null ? void 0 : e.channel}"`,
|
|
92
|
+
s
|
|
93
|
+
), t(s);
|
|
94
|
+
const r = c(s);
|
|
95
|
+
r && o(r);
|
|
96
|
+
}
|
|
97
|
+
}, [e]), b(() => {
|
|
98
|
+
const s = (i) => {
|
|
99
|
+
const { command: r } = i.data;
|
|
100
|
+
if (r === "widgetUpdate") {
|
|
101
|
+
const { channel: m, value: g } = i.data;
|
|
102
|
+
if (m !== l) return;
|
|
103
|
+
if (console.log(
|
|
104
|
+
`[Cabbage-React] ${r}: Received initial value for channel "${m}"`,
|
|
105
|
+
g
|
|
106
|
+
), g !== void 0) {
|
|
107
|
+
t(g);
|
|
108
|
+
const p = c(g);
|
|
109
|
+
p && o(p);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (r === "parameterChange") {
|
|
113
|
+
const { paramIdx: m, value: g } = i.data.data || {};
|
|
114
|
+
m === n && g !== void 0 && (console.log(
|
|
115
|
+
`[Cabbage-React] ${r}: Received value change for paramIdx: ${m}`,
|
|
116
|
+
g
|
|
117
|
+
), t(g));
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
return window.addEventListener("message", s), () => {
|
|
121
|
+
window.removeEventListener("message", s);
|
|
84
122
|
};
|
|
85
123
|
}, []), {
|
|
86
|
-
|
|
124
|
+
value: a,
|
|
125
|
+
setValue: u
|
|
87
126
|
};
|
|
88
127
|
};
|
|
89
128
|
export {
|
|
90
|
-
|
|
91
|
-
|
|
129
|
+
C as Cabbage,
|
|
130
|
+
h as useCabbageProperties,
|
|
131
|
+
M as useCabbageState
|
|
92
132
|
};
|
package/package.json
CHANGED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Custom hook to get form data from Cabbage.
|
|
3
|
-
* This hook listens for updates to form data via Cabbage and updates the local state
|
|
4
|
-
* whenever new data is received.
|
|
5
|
-
*/
|
|
6
|
-
export declare const useGetCabbageFormData: () => {
|
|
7
|
-
data: any;
|
|
8
|
-
};
|
|
9
|
-
//# sourceMappingURL=useGetCabbageFormData.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useGetCabbageFormData.d.ts","sourceRoot":"","sources":["../../src/hooks/useGetCabbageFormData.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,eAAO,MAAM,qBAAqB;;CA4BjC,CAAC"}
|