cabbage-react 1.0.24 → 1.0.25

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.
@@ -1,15 +1,11 @@
1
1
  export class Cabbage {
2
2
  /**
3
- * Main entry point for sending widget value changes to the Cabbage backend.
3
+ * Send a widget value change to the Cabbage backend.
4
4
  *
5
- * This function automatically routes messages to the appropriate backend function
6
- * based on the automatable flag:
7
- *
8
- * - `automatable=true`: Routes to `sendParameterUpdate()` for DAW-automatable parameters.
9
- * The value is sent to the DAW for automation recording and also forwarded to Csound.
10
- *
11
- * - `automatable=false`: Routes to `sendChannelData()` for non-automatable data.
12
- * The value is sent directly to Csound without DAW parameter involvement.
5
+ * The backend automatically determines whether the channel is DAW-automatable
6
+ * and routes accordingly:
7
+ * - Automatable channels -> DAW parameter system -> Csound
8
+ * - Non-automatable channels -> Csound directly
13
9
  *
14
10
  * **When to use**: Call this from widget event handlers (e.g., pointer events, input changes)
15
11
  * when the user interacts with a widget.
@@ -17,65 +13,64 @@ export class Cabbage {
17
13
  * **When NOT to use**: Do not call this when handling incoming `parameterChange` messages
18
14
  * from the backend. Those messages are for display updates only.
19
15
  *
20
- * @param {Object} message - The message object containing widget data
21
- * @param {string} message.channel - The channel name
22
- * @param {number} message.paramIdx - Parameter index (required if automatable)
23
- * @param {number|string} message.value - The value to send
16
+ * @param {Object} data - The control data to send
17
+ * @param {string} data.channel - The channel name
18
+ * @param {number|string} data.value - The value to send in its natural/meaningful range (e.g., 20-20000 Hz for filter frequency). The backend handles all normalization needed by the host DAW. This value can be a number or string depending on the widget type.
19
+ * @param {string} [data.gesture="complete"] - The gesture type: "begin" (start of interaction), "value" (during interaction), "end" (end of continuous interaction), or "complete" (discrete action e.g. button click).
24
20
  * @param {Object|null} vscode - VS Code API object (null for plugin mode)
25
- * @param {boolean} automatable - Whether this widget is DAW-automatable
26
21
  */
27
- static sendChannelUpdate(message: {
22
+ static sendControlData({ channel, value, gesture }: {
28
23
  channel: string;
29
- paramIdx: number;
30
24
  value: number | string;
31
- }, vscode?: Object | null, automatable?: boolean): void;
25
+ gesture?: string | undefined;
26
+ }, vscode?: Object | null): void;
32
27
  /**
33
- * Internal: Send a parameter update to the DAW for automation recording.
34
- * Use `sendChannelUpdate()` instead - it will route here automatically for automatable widgets.
28
+ * Signal that the UI is ready to load and initialize.
35
29
  *
36
- * This function sends the parameter value to the DAW's automation system.
37
- * The DAW will then send the value back via a `parameterChange` message,
38
- * which updates both the UI and Csound.
39
- *
40
- * **Important**: This creates a round-trip through the DAW:
41
- * 1. UI calls `sendParameterUpdate()` with value
42
- * 2. DAW receives and records/processes the value
43
- * 3. DAW sends `parameterChange` back to the plugin
44
- * 4. Plugin updates Csound and sends `parameterChange` to UI
45
- * 5. UI updates its display (but does NOT call sendParameterUpdate again!)
30
+ * @param {Object|null} vscode - VS Code API object (null for plugin mode)
31
+ * @param {Object} additionalData - Additional initialization data
32
+ */
33
+ static isReadyToLoad(vscode?: Object | null, additionalData?: Object): void;
34
+ /**
35
+ * Send a MIDI message from the UI to the Cabbage backend.
46
36
  *
47
- * @param {Object} message - The parameter message
48
- * @param {number} message.paramIdx - The parameter index (must be >= 0)
49
- * @param {string} message.channel - The channel name
50
- * @param {number} message.value - The parameter value (full range, not normalized)
51
- * @param {string} [message.channelType="number"] - The channel type
37
+ * @param {number} statusByte - MIDI status byte
38
+ * @param {number} dataByte1 - First MIDI data byte
39
+ * @param {number} dataByte2 - Second MIDI data byte
52
40
  * @param {Object|null} vscode - VS Code API object (null for plugin mode)
53
41
  */
54
- static sendParameterUpdate(message: {
55
- paramIdx: number;
56
- channel: string;
57
- value: number;
58
- channelType?: string | undefined;
59
- }, vscode?: Object | null): void;
60
- static sendCustomCommand(command: any, vscode?: null, additionalData?: {}): void;
61
- static sendWidgetUpdate(widget: any, vscode?: null): void;
62
- static sendMidiMessageFromUI(statusByte: any, dataByte1: any, dataByte2: any, vscode?: null): void;
42
+ static sendMidiMessageFromUI(statusByte: number, dataByte1: number, dataByte2: number, vscode?: Object | null): void;
63
43
  /**
64
- * Internal: Send channel data directly to Csound without DAW automation involvement.
65
- * Use `sendChannelUpdate()` instead - it will route here automatically for non-automatable widgets.
44
+ * Handle incoming MIDI messages from the backend.
66
45
  *
67
- * Used for non-automatable widgets like buttons, file selectors, or
68
- * any widget that sends string data. The value is sent directly to Csound's
69
- * channel system and is not recorded by DAW automation.
46
+ * @param {number} statusByte - MIDI status byte
47
+ * @param {number} dataByte1 - First MIDI data byte
48
+ * @param {number} dataByte2 - Second MIDI data byte
49
+ */
50
+ static MidiMessageFromHost(statusByte: number, dataByte1: number, dataByte2: number): void;
51
+ /**
52
+ * Trigger a native file open dialog for file selection widgets.
70
53
  *
71
- * @param {string} channel - The Csound channel name
72
- * @param {number|string} data - The data to send (number or string)
73
54
  * @param {Object|null} vscode - VS Code API object (null for plugin mode)
55
+ * @param {string} channel - The associated channel name
56
+ * @param {Object} options - Dialog options
57
+ * @param {string} [options.directory] - Starting directory path
58
+ * @param {string} [options.filters="*"] - File filters (e.g., "*.wav;*.aiff")
59
+ * @param {boolean} [options.openAtLastKnownLocation=true] - Whether to open at last known location
74
60
  */
75
- static sendChannelData(channel: string, data: number | string, vscode?: Object | null): void;
76
- static MidiMessageFromHost(statusByte: any, dataByte1: any, dataByte2: any): void;
77
- static triggerFileOpenDialog(vscode: any, channel: any, options?: {}): void;
78
- static openUrl(vscode: any, url: any, file: any): void;
61
+ static triggerFileOpenDialog(vscode: Object | null, channel: string, options?: {
62
+ directory?: string | undefined;
63
+ filters?: string | undefined;
64
+ openAtLastKnownLocation?: boolean | undefined;
65
+ }): void;
66
+ /**
67
+ * Open a URL or file in the system's default application.
68
+ *
69
+ * @param {Object|null} vscode - VS Code API object (null for plugin mode)
70
+ * @param {string} url - URL to open
71
+ * @param {string} file - File path to open
72
+ */
73
+ static openUrl(vscode: Object | null, url: string, file: string): void;
79
74
  /**
80
75
  * Request a resize of the plugin GUI window.
81
76
  * This is only supported in plugin mode (CLAP/VST3/AUv2).
@@ -89,5 +84,29 @@ export class Cabbage {
89
84
  * {command: "resizeResponse", accepted: boolean, width: number, height: number}
90
85
  */
91
86
  static requestResize(width: number, height: number, vscode?: object): void;
87
+ /**
88
+ * Send channel data directly to Csound without DAW automation involvement.
89
+ *
90
+ * @param {string} channel - The Csound channel name
91
+ * @param {number|string} data - The data to send (number or string)
92
+ * @param {Object|null} vscode - VS Code API object (null for plugin mode)
93
+ */
94
+ static sendChannelData(channel: string, data: number | string, vscode?: Object | null): void;
95
+ /**
96
+ * Send a widget state update to the Cabbage backend (used by property panel).
97
+ *
98
+ * @private
99
+ * @param {Object} widget - The widget configuration object to update
100
+ * @param {Object|null} vscode - VS Code API object (null for plugin mode)
101
+ */
102
+ private static sendWidgetUpdate;
103
+ /**
104
+ * Send a custom command to the Cabbage backend.
105
+ *
106
+ * @param {string} command - The command name to send
107
+ * @param {Object|null} vscode - VS Code API object (null for plugin mode)
108
+ * @param {Object} additionalData - Additional data to include in the command
109
+ */
110
+ static sendCustomCommand(command: string, vscode?: Object | null, additionalData?: Object): void;
92
111
  }
93
112
  //# sourceMappingURL=cabbage.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cabbage.d.ts","sourceRoot":"","sources":["../../src/cabbage/cabbage.js"],"names":[],"mappings":"AAqFA;IACC;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,kCANG;QAAwB,OAAO,EAAvB,MAAM;QACU,QAAQ,EAAxB,MAAM;QACiB,KAAK,EAA5B,MAAM,GAAC,MAAM;KACrB,WAAQ,MAAM,GAAC,IAAI,gBACX,OAAO,QAcjB;IAED;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,oCANG;QAAwB,QAAQ,EAAxB,MAAM;QACU,OAAO,EAAvB,MAAM;QACU,KAAK,EAArB,MAAM;QACW,WAAW;KACpC,WAAQ,MAAM,GAAC,IAAI,QAwCrB;IAED,iFAiCC;IAED,0DAiBC;IAED,mGA6BC;IAED;;;;;;;;;;;OAWG;IACH,gCAJW,MAAM,QACN,MAAM,GAAC,MAAM,WACb,MAAM,GAAC,IAAI,QAsCrB;IAED,kFASC;IAED,4EA2BC;IAED,uDAsBC;IAED;;;;;;;;;;;OAWG;IACH,4BAPW,MAAM,UACN,MAAM,WACN,MAAM,QA4BhB;CACD"}
1
+ {"version":3,"file":"cabbage.d.ts","sourceRoot":"","sources":["../../src/cabbage/cabbage.js"],"names":[],"mappings":"AA4FA;IACC;;;;;;;;;;;;;;;;;;;OAmBG;IACH,oDALG;QAAqB,OAAO,EAApB,MAAM;QACc,KAAK,EAAzB,MAAM,GAAC,MAAM;QACC,OAAO;KAC7B,WAAQ,MAAM,GAAC,IAAI,QAyBrB;IAED;;;;;OAKG;IACH,8BAHW,MAAM,GAAC,IAAI,mBACX,MAAM,QAIhB;IACD;;;;;;;OAOG;IACH,yCALW,MAAM,aACN,MAAM,aACN,MAAM,WACN,MAAM,GAAC,IAAI,QA+BrB;IAED;;;;;;OAMG;IACH,uCAJW,MAAM,aACN,MAAM,aACN,MAAM,QAWhB;IAED;;;;;;;;;OASG;IACH,qCAPW,MAAM,GAAC,IAAI,WACX,MAAM,YAEd;QAAyB,SAAS;QACT,OAAO;QACN,uBAAuB;KACnD,QA4BA;IAED;;;;;;OAMG;IACH,uBAJW,MAAM,GAAC,IAAI,OACX,MAAM,QACN,MAAM,QAwBhB;IAED;;;;;;;;;;;OAWG;IACH,4BAPW,MAAM,UACN,MAAM,WACN,MAAM,QA4BhB;IAED;;;;;;OAMG;IACH,gCAJW,MAAM,QACN,MAAM,GAAC,MAAM,WACb,MAAM,GAAC,IAAI,QAsCrB;IAED;;;;;;OAMG;IACH,gCAiBC;IAED;;;;;;OAMG;IACH,kCAJW,MAAM,WACN,MAAM,GAAC,IAAI,mBACX,MAAM,QAmChB;CACD"}
@@ -2,7 +2,7 @@
2
2
  * Custom hook to get a parameter's properties from Cabbage.
3
3
  * This hook listens for updates to parameter properties via Cabbage and updates the local state
4
4
  * whenever new data is received.
5
- * @param channelId - The Cabbage channel ID to bind to
5
+ * @param channelId - The channel name
6
6
  */
7
7
  export declare const useCabbageProperties: (channelId: string) => {
8
8
  properties: Record<string, any> | undefined;
@@ -2,10 +2,10 @@
2
2
  * Custom hook to sync a parameter with Cabbage.
3
3
  * This hook listens for updates to a parameter value from Cabbage and
4
4
  * sends updates to Cabbage when the parameter value changes locally (e.g., through a UI slider).
5
- * @param channelId - The Cabbage channel ID to bind to
6
- * @param isDragging - When true, suppresses incoming `parameterChange` updates so local UI control remains authoritative during active user interaction.
5
+ * @param channelId - The channel name
6
+ * @param gesture - The gesture type: "begin" (start of interaction), "value" (during interaction), "end" (end of continuous interaction), or "complete" (discrete action e.g. button click).
7
7
  */
8
- export declare const useCabbageState: <T>(channelId: string, isDragging?: boolean) => {
8
+ export declare const useCabbageState: <T>(channelId: string, gesture?: "begin" | "value" | "end" | "complete") => {
9
9
  value: T | undefined;
10
10
  setValue: (value: T) => void;
11
11
  parameterIndex: number | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"useCabbageState.d.ts","sourceRoot":"","sources":["../../src/hooks/useCabbageState.ts"],"names":[],"mappings":"AAIA;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,aAAa,MAAM,eAAe,OAAO;;sBAMvC,CAAC;;CAoGnC,CAAC"}
1
+ {"version":3,"file":"useCabbageState.d.ts","sourceRoot":"","sources":["../../src/hooks/useCabbageState.ts"],"names":[],"mappings":"AAIA;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,aACrB,MAAM,YACR,OAAO,GAAG,OAAO,GAAG,KAAK,GAAG,UAAU;;sBAOb,CAAC;;CA0FnC,CAAC"}
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const m=require("react");console.log("Cabbage: loading cabbage.js");class f{static sendChannelUpdate(e,a=null,n=!1){if(n===!0||n===1)f.sendParameterUpdate(e,a);else{const s=e.value!==void 0?e.value:e.stringData||e.floatData;f.sendChannelData(e.channel,s,a)}}static sendParameterUpdate(e,a=null){if(e.paramIdx===void 0||e.paramIdx===null){console.error("Cabbage.sendParameterUpdate: message missing paramIdx!",e);return}if(e.paramIdx<0){console.warn("Cabbage.sendParameterUpdate: paramIdx is -1, skipping (non-automatable widget)",e);return}const n={command:"parameterChange",paramIdx:e.paramIdx,channel:e.channel,value:e.value,channelType:e.channelType||"number"};a!==null?a.postMessage(n):typeof window.sendMessageFromUI=="function"?window.sendMessageFromUI(n):console.error("Cabbage: window.sendMessageFromUI is not available. Message:",n)}static sendCustomCommand(e,a=null,n={}){const s={command:e,text:JSON.stringify(n)};if(a!==null)a.postMessage(s);else if(typeof window.sendMessageFromUI=="function"){console.log("Cabbage: Calling window.sendMessageFromUI with:",s);try{const o=window.sendMessageFromUI(s);console.log("Cabbage: sendMessageFromUI returned:",o)}catch(o){console.error("Cabbage: sendMessageFromUI threw error:",o),console.error("Cabbage: Error stack:",o.stack)}}else console.error("Cabbage: window.sendMessageFromUI is not available yet. Message:",s),console.error("Cabbage: typeof window.sendMessageFromUI:",typeof window.sendMessageFromUI),console.error("Cabbage: window.sendMessageFromUI value:",window.sendMessageFromUI)}static sendWidgetUpdate(e,a=null){const n={command:"widgetStateUpdate",obj:JSON.stringify(CabbageUtils.sanitizeForEditor(e))};a!==null?a.postMessage(n):typeof window.sendMessageFromUI=="function"?window.sendMessageFromUI(n):console.error("Cabbage: window.sendMessageFromUI is not available. Message:",n)}static sendMidiMessageFromUI(e,a,n,s=null){var o={statusByte:e,dataByte1:a,dataByte2:n};const l={command:"midiMessage",obj:JSON.stringify(o)};s!==null?s.postMessage(l):typeof window.sendMessageFromUI=="function"?window.sendMessageFromUI(l):console.error("Cabbage: window.sendMessageFromUI is not available. Message:",l)}static sendChannelData(e,a,n=null){var s={channel:e};if(typeof a=="string")s.stringData=a;else if(typeof a=="number")s.floatData=a;else{console.warn("Cabbage: sendChannelData received unsupported data type:",typeof a);return}const o={command:"channelData",obj:JSON.stringify(s)};console.log("Cabbage: sending channel data from UI",s),n!==null?n.postMessage(o):typeof window.sendMessageFromUI=="function"?window.sendMessageFromUI(o):console.error("Cabbage: window.sendMessageFromUI is not available. Message:",o)}static MidiMessageFromHost(e,a,n){console.log("Cabbage: Got MIDI Message"+e+":"+a+":"+n)}static triggerFileOpenDialog(e,a,n={}){var s={channel:a,directory:n.directory||"",filters:n.filters||"*",openAtLastKnownLocation:n.openAtLastKnownLocation!==void 0?n.openAtLastKnownLocation:!0};const o={command:"fileOpen",obj:JSON.stringify(s)};e!==null?e.postMessage(o):typeof window.sendMessageFromUI=="function"?window.sendMessageFromUI(o):console.error("Cabbage: window.sendMessageFromUI is not available. Message:",o)}static openUrl(e,a,n){var s={url:a,file:n};const o={command:"openUrl",obj:JSON.stringify(s)};e!==null?e.postMessage(o):typeof window.sendMessageFromUI=="function"?window.sendMessageFromUI(o):console.error("Cabbage: window.sendMessageFromUI is not available. Message:",o)}static requestResize(e,a,n=null){const s={command:"requestResize",width:e,height:a};if(n!==null){console.warn("Cabbage: requestResize is not supported in VS Code extension mode");return}else typeof window.sendMessageFromUI=="function"?window.sendMessageFromUI(s):console.error("Cabbage: window.sendMessageFromUI is not available. Message:",s)}}const I=d=>{const[e,a]=m.useState();return m.useEffect(()=>{const n=s=>{const{id:o,widgetJson:l,command:b}=s.data;if(o===d&&l&&b==="widgetUpdate"){const t=JSON.parse(l);console.log(`[Cabbage-React] Received properties for channelId ${o}`,t),a(t)}};return window.addEventListener("message",n),()=>{window.removeEventListener("message",n)}},[]),{properties:e}},C=(d,e)=>{const{properties:a}=I(d),[n,s]=m.useState(),[o,l]=m.useState(),b=t=>{if(s(t),o===void 0){console.warn(`[Cabbage-React] parameterIndex not ready for "${d}"`);return}const r={paramIdx:o,channel:d,value:t,channelType:"number"};f.sendParameterUpdate(r,null)};return m.useEffect(()=>{var u;const t=a==null?void 0:a.channels.find(i=>i.id===d);if(!t)return;const r=t==null?void 0:t.parameterIndex;o===void 0&&r!==void 0&&(console.log(`[Cabbage-React] Received parameterIndex for channelId "${t.id}"`,r),l(r));const g=(u=t.range)==null?void 0:u.defaultValue;n===void 0&&g!==void 0&&(console.log(`[Cabbage-React] Received default value for channelId "${t.id}"`,g),s(g))},[a]),m.useEffect(()=>{const t=r=>{var u;const{command:g}=r.data;if(g==="parameterChange"){if(e===!0)return;const{value:i,paramIdx:c}=r.data.data;if(c!==o||i===null)return;console.log(`[Cabbage-React] Received parameterChange for parameterIndex ${c}`,i),s(i)}else if(g==="batchWidgetUpdate"){const i=r.data.widgets,c=i==null?void 0:i.find(p=>p.id===d);if(!c)return;const w=JSON.parse(c.widgetJson).channels.find(p=>p.id===d),M=(u=w==null?void 0:w.range)==null?void 0:u.value;console.log(`[Cabbage-React] Received batch widget update for channelId ${c.id}`,M),s(M)}};return window.addEventListener("message",t),()=>{window.removeEventListener("message",t)}},[o,e]),{value:n,setValue:b,parameterIndex:o}};exports.Cabbage=f;exports.useCabbageProperties=I;exports.useCabbageState=C;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const m=require("react");console.log("Cabbage: loading cabbage.js");class p{static sendControlData({channel:o,value:e,gesture:a="complete"},n=null){const s={command:"controlData",channel:o,value:e,gesture:a};n!==null?n.postMessage(s):typeof window.sendMessageFromUI=="function"?window.sendMessageFromUI(s):console.error("Cabbage: window.sendMessageFromUI is not available. Message:",s)}static isReadyToLoad(o=null,e={}){this.sendCustomCommand("isReadyToLoad",o)}static sendMidiMessageFromUI(o,e,a,n=null){var s={statusByte:o,dataByte1:e,dataByte2:a};const l={command:"midiMessage",obj:JSON.stringify(s)};n!==null?n.postMessage(l):typeof window.sendMessageFromUI=="function"?window.sendMessageFromUI(l):console.error("Cabbage: window.sendMessageFromUI is not available. Message:",l)}static MidiMessageFromHost(o,e,a){console.log("Cabbage: Got MIDI Message"+o+":"+e+":"+a)}static triggerFileOpenDialog(o,e,a={}){var n={channel:e,directory:a.directory||"",filters:a.filters||"*",openAtLastKnownLocation:a.openAtLastKnownLocation!==void 0?a.openAtLastKnownLocation:!0};const s={command:"fileOpen",obj:JSON.stringify(n)};o!==null?o.postMessage(s):typeof window.sendMessageFromUI=="function"?window.sendMessageFromUI(s):console.error("Cabbage: window.sendMessageFromUI is not available. Message:",s)}static openUrl(o,e,a){var n={url:e,file:a};const s={command:"openUrl",obj:JSON.stringify(n)};o!==null?o.postMessage(s):typeof window.sendMessageFromUI=="function"?window.sendMessageFromUI(s):console.error("Cabbage: window.sendMessageFromUI is not available. Message:",s)}static requestResize(o,e,a=null){const n={command:"requestResize",width:o,height:e};if(a!==null){console.warn("Cabbage: requestResize is not supported in VS Code extension mode");return}else typeof window.sendMessageFromUI=="function"?window.sendMessageFromUI(n):console.error("Cabbage: window.sendMessageFromUI is not available. Message:",n)}static sendChannelData(o,e,a=null){var n={channel:o};if(typeof e=="string")n.stringData=e;else if(typeof e=="number")n.floatData=e;else{console.warn("Cabbage: sendChannelData received unsupported data type:",typeof e);return}const s={command:"channelData",obj:JSON.stringify(n)};console.log("Cabbage: sending channel data from UI",n),a!==null?a.postMessage(s):typeof window.sendMessageFromUI=="function"?window.sendMessageFromUI(s):console.error("Cabbage: window.sendMessageFromUI is not available. Message:",s)}static sendWidgetUpdate(o,e=null){const a={command:"widgetStateUpdate",obj:JSON.stringify(CabbageUtils.sanitizeForEditor(o))};e!==null?e.postMessage(a):typeof window.sendMessageFromUI=="function"?window.sendMessageFromUI(a):console.error("Cabbage: window.sendMessageFromUI is not available. Message:",a)}static sendCustomCommand(o,e=null,a={}){const n={command:o,text:JSON.stringify(a)};if(e!==null)e.postMessage(n);else if(typeof window.sendMessageFromUI=="function"){console.log("Cabbage: Calling window.sendMessageFromUI with:",n);try{const s=window.sendMessageFromUI(n);console.log("Cabbage: sendMessageFromUI returned:",s)}catch(s){console.error("Cabbage: sendMessageFromUI threw error:",s),console.error("Cabbage: Error stack:",s.stack)}}else console.error("Cabbage: window.sendMessageFromUI is not available yet. Message:",n),console.error("Cabbage: typeof window.sendMessageFromUI:",typeof window.sendMessageFromUI),console.error("Cabbage: window.sendMessageFromUI value:",window.sendMessageFromUI)}}const C=d=>{const[o,e]=m.useState();return m.useEffect(()=>{const a=n=>{const{id:s,widgetJson:l,command:u}=n.data;if(s===d&&l&&u==="widgetUpdate"){const t=JSON.parse(l);console.log(`[Cabbage-React] Received properties for channelId ${s}`,t),e(t)}};return window.addEventListener("message",a),()=>{window.removeEventListener("message",a)}},[]),{properties:o}},I=(d,o="complete")=>{const{properties:e}=C(d),[a,n]=m.useState(),[s,l]=m.useState(),u=t=>{n(t);const i={channel:d,value:t,gesture:o};p.sendControlData(i,null)};return m.useEffect(()=>{var b;const t=e==null?void 0:e.channels.find(r=>r.id===d);if(!t)return;const i=t==null?void 0:t.parameterIndex;s===void 0&&i!==void 0&&(console.log(`[Cabbage-React] Received parameterIndex for channelId "${t.id}"`,i),l(i));const g=(b=t.range)==null?void 0:b.defaultValue;a===void 0&&g!==void 0&&(console.log(`[Cabbage-React] Received default value for channelId "${t.id}"`,g),n(g))},[e]),m.useEffect(()=>{const t=i=>{var b;const{command:g}=i.data;if(g==="parameterChange"){const{value:r,paramIdx:c}=i.data.data;if(c!==s||r===null)return;console.log(`[Cabbage-React] Received parameterChange for parameterIndex ${c}`,r),n(r)}else if(g==="batchWidgetUpdate"){const r=i.data.widgets,c=r==null?void 0:r.find(f=>f.id===d);if(!c)return;const w=JSON.parse(c.widgetJson).channels.find(f=>f.id===d),M=(b=w==null?void 0:w.range)==null?void 0:b.value;console.log(`[Cabbage-React] Received batch widget update for channelId ${c.id}`,M),n(M)}};return window.addEventListener("message",t),()=>{window.removeEventListener("message",t)}},[s]),{value:a,setValue:u,parameterIndex:s}};exports.Cabbage=p;exports.useCabbageProperties=C;exports.useCabbageState=I;
package/dist/index.mjs CHANGED
@@ -1,17 +1,13 @@
1
- import { useState as p, useEffect as M } from "react";
1
+ import { useState as u, useEffect as M } from "react";
2
2
  console.log("Cabbage: loading cabbage.js");
3
- class f {
3
+ class I {
4
4
  /**
5
- * Main entry point for sending widget value changes to the Cabbage backend.
5
+ * Send a widget value change to the Cabbage backend.
6
6
  *
7
- * This function automatically routes messages to the appropriate backend function
8
- * based on the automatable flag:
9
- *
10
- * - `automatable=true`: Routes to `sendParameterUpdate()` for DAW-automatable parameters.
11
- * The value is sent to the DAW for automation recording and also forwarded to Csound.
12
- *
13
- * - `automatable=false`: Routes to `sendChannelData()` for non-automatable data.
14
- * The value is sent directly to Csound without DAW parameter involvement.
7
+ * The backend automatically determines whether the channel is DAW-automatable
8
+ * and routes accordingly:
9
+ * - Automatable channels -> DAW parameter system -> Csound
10
+ * - Non-automatable channels -> Csound directly
15
11
  *
16
12
  * **When to use**: Call this from widget event handlers (e.g., pointer events, input changes)
17
13
  * when the user interacts with a widget.
@@ -19,191 +15,113 @@ class f {
19
15
  * **When NOT to use**: Do not call this when handling incoming `parameterChange` messages
20
16
  * from the backend. Those messages are for display updates only.
21
17
  *
22
- * @param {Object} message - The message object containing widget data
23
- * @param {string} message.channel - The channel name
24
- * @param {number} message.paramIdx - Parameter index (required if automatable)
25
- * @param {number|string} message.value - The value to send
18
+ * @param {Object} data - The control data to send
19
+ * @param {string} data.channel - The channel name
20
+ * @param {number|string} data.value - The value to send in its natural/meaningful range (e.g., 20-20000 Hz for filter frequency). The backend handles all normalization needed by the host DAW. This value can be a number or string depending on the widget type.
21
+ * @param {string} [data.gesture="complete"] - The gesture type: "begin" (start of interaction), "value" (during interaction), "end" (end of continuous interaction), or "complete" (discrete action e.g. button click).
26
22
  * @param {Object|null} vscode - VS Code API object (null for plugin mode)
27
- * @param {boolean} automatable - Whether this widget is DAW-automatable
28
23
  */
29
- static sendChannelUpdate(e, a = null, n = !1) {
30
- if (n === !0 || n === 1)
31
- f.sendParameterUpdate(e, a);
32
- else {
33
- const s = e.value !== void 0 ? e.value : e.stringData || e.floatData;
34
- f.sendChannelData(e.channel, s, a);
35
- }
36
- }
37
- /**
38
- * Internal: Send a parameter update to the DAW for automation recording.
39
- * Use `sendChannelUpdate()` instead - it will route here automatically for automatable widgets.
40
- *
41
- * This function sends the parameter value to the DAW's automation system.
42
- * The DAW will then send the value back via a `parameterChange` message,
43
- * which updates both the UI and Csound.
44
- *
45
- * **Important**: This creates a round-trip through the DAW:
46
- * 1. UI calls `sendParameterUpdate()` with value
47
- * 2. DAW receives and records/processes the value
48
- * 3. DAW sends `parameterChange` back to the plugin
49
- * 4. Plugin updates Csound and sends `parameterChange` to UI
50
- * 5. UI updates its display (but does NOT call sendParameterUpdate again!)
51
- *
52
- * @param {Object} message - The parameter message
53
- * @param {number} message.paramIdx - The parameter index (must be >= 0)
54
- * @param {string} message.channel - The channel name
55
- * @param {number} message.value - The parameter value (full range, not normalized)
56
- * @param {string} [message.channelType="number"] - The channel type
57
- * @param {Object|null} vscode - VS Code API object (null for plugin mode)
58
- */
59
- static sendParameterUpdate(e, a = null) {
60
- if (e.paramIdx === void 0 || e.paramIdx === null) {
61
- console.error(
62
- "Cabbage.sendParameterUpdate: message missing paramIdx!",
63
- e
64
- );
65
- return;
66
- }
67
- if (e.paramIdx < 0) {
68
- console.warn(
69
- "Cabbage.sendParameterUpdate: paramIdx is -1, skipping (non-automatable widget)",
70
- e
71
- );
72
- return;
73
- }
74
- const n = {
75
- command: "parameterChange",
76
- paramIdx: e.paramIdx,
77
- channel: e.channel,
78
- value: e.value,
79
- channelType: e.channelType || "number"
80
- };
81
- a !== null ? a.postMessage(n) : typeof window.sendMessageFromUI == "function" ? window.sendMessageFromUI(n) : console.error(
82
- "Cabbage: window.sendMessageFromUI is not available. Message:",
83
- n
84
- );
85
- }
86
- static sendCustomCommand(e, a = null, n = {}) {
24
+ static sendControlData({ channel: o, value: e, gesture: a = "complete" }, n = null) {
87
25
  const s = {
88
- command: e,
89
- text: JSON.stringify(n)
90
- };
91
- if (a !== null)
92
- a.postMessage(s);
93
- else if (typeof window.sendMessageFromUI == "function") {
94
- console.log("Cabbage: Calling window.sendMessageFromUI with:", s);
95
- try {
96
- const o = window.sendMessageFromUI(s);
97
- console.log("Cabbage: sendMessageFromUI returned:", o);
98
- } catch (o) {
99
- console.error("Cabbage: sendMessageFromUI threw error:", o), console.error("Cabbage: Error stack:", o.stack);
100
- }
101
- } else
102
- console.error(
103
- "Cabbage: window.sendMessageFromUI is not available yet. Message:",
104
- s
105
- ), console.error(
106
- "Cabbage: typeof window.sendMessageFromUI:",
107
- typeof window.sendMessageFromUI
108
- ), console.error(
109
- "Cabbage: window.sendMessageFromUI value:",
110
- window.sendMessageFromUI
111
- );
112
- }
113
- static sendWidgetUpdate(e, a = null) {
114
- const n = {
115
- command: "widgetStateUpdate",
116
- obj: JSON.stringify(CabbageUtils.sanitizeForEditor(e))
26
+ command: "controlData",
27
+ channel: o,
28
+ value: e,
29
+ gesture: a
117
30
  };
118
- a !== null ? a.postMessage(n) : typeof window.sendMessageFromUI == "function" ? window.sendMessageFromUI(n) : console.error(
31
+ n !== null ? n.postMessage(s) : typeof window.sendMessageFromUI == "function" ? window.sendMessageFromUI(s) : console.error(
119
32
  "Cabbage: window.sendMessageFromUI is not available. Message:",
120
- n
121
- );
122
- }
123
- static sendMidiMessageFromUI(e, a, n, s = null) {
124
- var o = {
125
- statusByte: e,
126
- dataByte1: a,
127
- dataByte2: n
128
- };
129
- const l = {
130
- command: "midiMessage",
131
- obj: JSON.stringify(o)
132
- };
133
- s !== null ? s.postMessage(l) : typeof window.sendMessageFromUI == "function" ? window.sendMessageFromUI(l) : console.error(
134
- "Cabbage: window.sendMessageFromUI is not available. Message:",
135
- l
33
+ s
136
34
  );
137
35
  }
138
36
  /**
139
- * Internal: Send channel data directly to Csound without DAW automation involvement.
140
- * Use `sendChannelUpdate()` instead - it will route here automatically for non-automatable widgets.
37
+ * Signal that the UI is ready to load and initialize.
141
38
  *
142
- * Used for non-automatable widgets like buttons, file selectors, or
143
- * any widget that sends string data. The value is sent directly to Csound's
144
- * channel system and is not recorded by DAW automation.
39
+ * @param {Object|null} vscode - VS Code API object (null for plugin mode)
40
+ * @param {Object} additionalData - Additional initialization data
41
+ */
42
+ static isReadyToLoad(o = null, e = {}) {
43
+ this.sendCustomCommand("isReadyToLoad", o);
44
+ }
45
+ /**
46
+ * Send a MIDI message from the UI to the Cabbage backend.
145
47
  *
146
- * @param {string} channel - The Csound channel name
147
- * @param {number|string} data - The data to send (number or string)
48
+ * @param {number} statusByte - MIDI status byte
49
+ * @param {number} dataByte1 - First MIDI data byte
50
+ * @param {number} dataByte2 - Second MIDI data byte
148
51
  * @param {Object|null} vscode - VS Code API object (null for plugin mode)
149
52
  */
150
- static sendChannelData(e, a, n = null) {
53
+ static sendMidiMessageFromUI(o, e, a, n = null) {
151
54
  var s = {
152
- channel: e
55
+ statusByte: o,
56
+ dataByte1: e,
57
+ dataByte2: a
153
58
  };
154
- if (typeof a == "string")
155
- s.stringData = a;
156
- else if (typeof a == "number")
157
- s.floatData = a;
158
- else {
159
- console.warn(
160
- "Cabbage: sendChannelData received unsupported data type:",
161
- typeof a
162
- );
163
- return;
164
- }
165
- const o = {
166
- command: "channelData",
59
+ const l = {
60
+ command: "midiMessage",
167
61
  obj: JSON.stringify(s)
168
62
  };
169
- console.log("Cabbage: sending channel data from UI", s), n !== null ? n.postMessage(o) : typeof window.sendMessageFromUI == "function" ? window.sendMessageFromUI(o) : console.error(
63
+ n !== null ? n.postMessage(l) : typeof window.sendMessageFromUI == "function" ? window.sendMessageFromUI(l) : console.error(
170
64
  "Cabbage: window.sendMessageFromUI is not available. Message:",
171
- o
65
+ l
172
66
  );
173
67
  }
174
- static MidiMessageFromHost(e, a, n) {
68
+ /**
69
+ * Handle incoming MIDI messages from the backend.
70
+ *
71
+ * @param {number} statusByte - MIDI status byte
72
+ * @param {number} dataByte1 - First MIDI data byte
73
+ * @param {number} dataByte2 - Second MIDI data byte
74
+ */
75
+ static MidiMessageFromHost(o, e, a) {
175
76
  console.log(
176
- "Cabbage: Got MIDI Message" + e + ":" + a + ":" + n
77
+ "Cabbage: Got MIDI Message" + o + ":" + e + ":" + a
177
78
  );
178
79
  }
179
- static triggerFileOpenDialog(e, a, n = {}) {
180
- var s = {
181
- channel: a,
182
- directory: n.directory || "",
183
- filters: n.filters || "*",
184
- openAtLastKnownLocation: n.openAtLastKnownLocation !== void 0 ? n.openAtLastKnownLocation : !0
80
+ /**
81
+ * Trigger a native file open dialog for file selection widgets.
82
+ *
83
+ * @param {Object|null} vscode - VS Code API object (null for plugin mode)
84
+ * @param {string} channel - The associated channel name
85
+ * @param {Object} options - Dialog options
86
+ * @param {string} [options.directory] - Starting directory path
87
+ * @param {string} [options.filters="*"] - File filters (e.g., "*.wav;*.aiff")
88
+ * @param {boolean} [options.openAtLastKnownLocation=true] - Whether to open at last known location
89
+ */
90
+ static triggerFileOpenDialog(o, e, a = {}) {
91
+ var n = {
92
+ channel: e,
93
+ directory: a.directory || "",
94
+ filters: a.filters || "*",
95
+ openAtLastKnownLocation: a.openAtLastKnownLocation !== void 0 ? a.openAtLastKnownLocation : !0
185
96
  };
186
- const o = {
97
+ const s = {
187
98
  command: "fileOpen",
188
- obj: JSON.stringify(s)
99
+ obj: JSON.stringify(n)
189
100
  };
190
- e !== null ? e.postMessage(o) : typeof window.sendMessageFromUI == "function" ? window.sendMessageFromUI(o) : console.error(
101
+ o !== null ? o.postMessage(s) : typeof window.sendMessageFromUI == "function" ? window.sendMessageFromUI(s) : console.error(
191
102
  "Cabbage: window.sendMessageFromUI is not available. Message:",
192
- o
103
+ s
193
104
  );
194
105
  }
195
- static openUrl(e, a, n) {
196
- var s = {
197
- url: a,
198
- file: n
106
+ /**
107
+ * Open a URL or file in the system's default application.
108
+ *
109
+ * @param {Object|null} vscode - VS Code API object (null for plugin mode)
110
+ * @param {string} url - URL to open
111
+ * @param {string} file - File path to open
112
+ */
113
+ static openUrl(o, e, a) {
114
+ var n = {
115
+ url: e,
116
+ file: a
199
117
  };
200
- const o = {
118
+ const s = {
201
119
  command: "openUrl",
202
- obj: JSON.stringify(s)
120
+ obj: JSON.stringify(n)
203
121
  };
204
- e !== null ? e.postMessage(o) : typeof window.sendMessageFromUI == "function" ? window.sendMessageFromUI(o) : console.error(
122
+ o !== null ? o.postMessage(s) : typeof window.sendMessageFromUI == "function" ? window.sendMessageFromUI(s) : console.error(
205
123
  "Cabbage: window.sendMessageFromUI is not available. Message:",
206
- o
124
+ s
207
125
  );
208
126
  }
209
127
  /**
@@ -218,110 +136,185 @@ class f {
218
136
  * The response will be sent via hostMessageCallback with:
219
137
  * {command: "resizeResponse", accepted: boolean, width: number, height: number}
220
138
  */
221
- static requestResize(e, a, n = null) {
222
- const s = {
139
+ static requestResize(o, e, a = null) {
140
+ const n = {
223
141
  command: "requestResize",
224
- width: e,
225
- height: a
142
+ width: o,
143
+ height: e
226
144
  };
227
- if (n !== null) {
145
+ if (a !== null) {
228
146
  console.warn(
229
147
  "Cabbage: requestResize is not supported in VS Code extension mode"
230
148
  );
231
149
  return;
232
150
  } else
233
- typeof window.sendMessageFromUI == "function" ? window.sendMessageFromUI(s) : console.error(
151
+ typeof window.sendMessageFromUI == "function" ? window.sendMessageFromUI(n) : console.error(
234
152
  "Cabbage: window.sendMessageFromUI is not available. Message:",
235
- s
153
+ n
154
+ );
155
+ }
156
+ /**
157
+ * Send channel data directly to Csound without DAW automation involvement.
158
+ *
159
+ * @param {string} channel - The Csound channel name
160
+ * @param {number|string} data - The data to send (number or string)
161
+ * @param {Object|null} vscode - VS Code API object (null for plugin mode)
162
+ */
163
+ static sendChannelData(o, e, a = null) {
164
+ var n = {
165
+ channel: o
166
+ };
167
+ if (typeof e == "string")
168
+ n.stringData = e;
169
+ else if (typeof e == "number")
170
+ n.floatData = e;
171
+ else {
172
+ console.warn(
173
+ "Cabbage: sendChannelData received unsupported data type:",
174
+ typeof e
175
+ );
176
+ return;
177
+ }
178
+ const s = {
179
+ command: "channelData",
180
+ obj: JSON.stringify(n)
181
+ };
182
+ console.log("Cabbage: sending channel data from UI", n), a !== null ? a.postMessage(s) : typeof window.sendMessageFromUI == "function" ? window.sendMessageFromUI(s) : console.error(
183
+ "Cabbage: window.sendMessageFromUI is not available. Message:",
184
+ s
185
+ );
186
+ }
187
+ /**
188
+ * Send a widget state update to the Cabbage backend (used by property panel).
189
+ *
190
+ * @private
191
+ * @param {Object} widget - The widget configuration object to update
192
+ * @param {Object|null} vscode - VS Code API object (null for plugin mode)
193
+ */
194
+ static sendWidgetUpdate(o, e = null) {
195
+ const a = {
196
+ command: "widgetStateUpdate",
197
+ obj: JSON.stringify(CabbageUtils.sanitizeForEditor(o))
198
+ };
199
+ e !== null ? e.postMessage(a) : typeof window.sendMessageFromUI == "function" ? window.sendMessageFromUI(a) : console.error(
200
+ "Cabbage: window.sendMessageFromUI is not available. Message:",
201
+ a
202
+ );
203
+ }
204
+ /**
205
+ * Send a custom command to the Cabbage backend.
206
+ *
207
+ * @param {string} command - The command name to send
208
+ * @param {Object|null} vscode - VS Code API object (null for plugin mode)
209
+ * @param {Object} additionalData - Additional data to include in the command
210
+ */
211
+ static sendCustomCommand(o, e = null, a = {}) {
212
+ const n = {
213
+ command: o,
214
+ text: JSON.stringify(a)
215
+ };
216
+ if (e !== null)
217
+ e.postMessage(n);
218
+ else if (typeof window.sendMessageFromUI == "function") {
219
+ console.log("Cabbage: Calling window.sendMessageFromUI with:", n);
220
+ try {
221
+ const s = window.sendMessageFromUI(n);
222
+ console.log("Cabbage: sendMessageFromUI returned:", s);
223
+ } catch (s) {
224
+ console.error("Cabbage: sendMessageFromUI threw error:", s), console.error("Cabbage: Error stack:", s.stack);
225
+ }
226
+ } else
227
+ console.error(
228
+ "Cabbage: window.sendMessageFromUI is not available yet. Message:",
229
+ n
230
+ ), console.error(
231
+ "Cabbage: typeof window.sendMessageFromUI:",
232
+ typeof window.sendMessageFromUI
233
+ ), console.error(
234
+ "Cabbage: window.sendMessageFromUI value:",
235
+ window.sendMessageFromUI
236
236
  );
237
237
  }
238
238
  }
239
- const U = (d) => {
240
- const [e, a] = p();
239
+ const C = (d) => {
240
+ const [o, e] = u();
241
241
  return M(() => {
242
- const n = (s) => {
243
- const { id: o, widgetJson: l, command: w } = s.data;
244
- if (o === d && l && w === "widgetUpdate") {
242
+ const a = (n) => {
243
+ const { id: s, widgetJson: l, command: w } = n.data;
244
+ if (s === d && l && w === "widgetUpdate") {
245
245
  const t = JSON.parse(l);
246
246
  console.log(
247
- `[Cabbage-React] Received properties for channelId ${o}`,
247
+ `[Cabbage-React] Received properties for channelId ${s}`,
248
248
  t
249
- ), a(t);
249
+ ), e(t);
250
250
  }
251
251
  };
252
- return window.addEventListener("message", n), () => {
253
- window.removeEventListener("message", n);
252
+ return window.addEventListener("message", a), () => {
253
+ window.removeEventListener("message", a);
254
254
  };
255
255
  }, []), {
256
- properties: e
256
+ properties: o
257
257
  };
258
- }, h = (d, e) => {
259
- const { properties: a } = U(d), [n, s] = p(), [o, l] = p(), w = (t) => {
260
- if (s(t), o === void 0) {
261
- console.warn(
262
- `[Cabbage-React] parameterIndex not ready for "${d}"`
263
- );
264
- return;
265
- }
266
- const r = {
267
- paramIdx: o,
258
+ }, y = (d, o = "complete") => {
259
+ const { properties: e } = C(d), [a, n] = u(), [s, l] = u(), w = (t) => {
260
+ n(t);
261
+ const i = {
268
262
  channel: d,
269
263
  value: t,
270
- channelType: "number"
264
+ gesture: o
271
265
  };
272
- f.sendParameterUpdate(r, null);
266
+ I.sendControlData(i, null);
273
267
  };
274
268
  return M(() => {
275
269
  var m;
276
- const t = a == null ? void 0 : a.channels.find(
277
- (i) => i.id === d
270
+ const t = e == null ? void 0 : e.channels.find(
271
+ (r) => r.id === d
278
272
  );
279
273
  if (!t) return;
280
- const r = t == null ? void 0 : t.parameterIndex;
281
- o === void 0 && r !== void 0 && (console.log(
274
+ const i = t == null ? void 0 : t.parameterIndex;
275
+ s === void 0 && i !== void 0 && (console.log(
282
276
  `[Cabbage-React] Received parameterIndex for channelId "${t.id}"`,
283
- r
284
- ), l(r));
277
+ i
278
+ ), l(i));
285
279
  const g = (m = t.range) == null ? void 0 : m.defaultValue;
286
- n === void 0 && g !== void 0 && (console.log(
280
+ a === void 0 && g !== void 0 && (console.log(
287
281
  `[Cabbage-React] Received default value for channelId "${t.id}"`,
288
282
  g
289
- ), s(g));
290
- }, [a]), M(() => {
291
- const t = (r) => {
283
+ ), n(g));
284
+ }, [e]), M(() => {
285
+ const t = (i) => {
292
286
  var m;
293
- const { command: g } = r.data;
287
+ const { command: g } = i.data;
294
288
  if (g === "parameterChange") {
295
- if (e === !0) return;
296
- const { value: i, paramIdx: c } = r.data.data;
297
- if (c !== o || i === null) return;
289
+ const { value: r, paramIdx: c } = i.data.data;
290
+ if (c !== s || r === null) return;
298
291
  console.log(
299
292
  `[Cabbage-React] Received parameterChange for parameterIndex ${c}`,
300
- i
301
- ), s(i);
293
+ r
294
+ ), n(r);
302
295
  } else if (g === "batchWidgetUpdate") {
303
- const i = r.data.widgets, c = i == null ? void 0 : i.find((b) => b.id === d);
296
+ const r = i.data.widgets, c = r == null ? void 0 : r.find((b) => b.id === d);
304
297
  if (!c) return;
305
- const u = JSON.parse(c.widgetJson).channels.find(
298
+ const f = JSON.parse(c.widgetJson).channels.find(
306
299
  (b) => b.id === d
307
- ), I = (m = u == null ? void 0 : u.range) == null ? void 0 : m.value;
300
+ ), p = (m = f == null ? void 0 : f.range) == null ? void 0 : m.value;
308
301
  console.log(
309
302
  `[Cabbage-React] Received batch widget update for channelId ${c.id}`,
310
- I
311
- ), s(I);
303
+ p
304
+ ), n(p);
312
305
  }
313
306
  };
314
307
  return window.addEventListener("message", t), () => {
315
308
  window.removeEventListener("message", t);
316
309
  };
317
- }, [o, e]), {
318
- value: n,
310
+ }, [s]), {
311
+ value: a,
319
312
  setValue: w,
320
- parameterIndex: o
313
+ parameterIndex: s
321
314
  };
322
315
  };
323
316
  export {
324
- f as Cabbage,
325
- U as useCabbageProperties,
326
- h as useCabbageState
317
+ I as Cabbage,
318
+ C as useCabbageProperties,
319
+ y as useCabbageState
327
320
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cabbage-react",
3
- "version": "1.0.24",
3
+ "version": "1.0.25",
4
4
  "type": "module",
5
5
  "keywords": [
6
6
  "cabbage",