@collabhut/plugin-sdk 0.1.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/LICENSE +21 -0
- package/README.md +1046 -0
- package/dist/helpers/note-utils.d.ts +128 -0
- package/dist/helpers/note-utils.d.ts.map +1 -0
- package/dist/helpers/note-utils.js +155 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/types/audio-effect.d.ts +70 -0
- package/dist/types/audio-effect.d.ts.map +1 -0
- package/dist/types/audio-effect.js +1 -0
- package/dist/types/context.d.ts +39 -0
- package/dist/types/context.d.ts.map +1 -0
- package/dist/types/context.js +1 -0
- package/dist/types/events.d.ts +119 -0
- package/dist/types/events.d.ts.map +1 -0
- package/dist/types/events.js +19 -0
- package/dist/types/instrument.d.ts +83 -0
- package/dist/types/instrument.d.ts.map +1 -0
- package/dist/types/instrument.js +1 -0
- package/dist/types/licensing.d.ts +118 -0
- package/dist/types/licensing.d.ts.map +1 -0
- package/dist/types/licensing.js +27 -0
- package/dist/types/manifest.d.ts +90 -0
- package/dist/types/manifest.d.ts.map +1 -0
- package/dist/types/manifest.js +1 -0
- package/dist/types/midi-effect.d.ts +101 -0
- package/dist/types/midi-effect.d.ts.map +1 -0
- package/dist/types/midi-effect.js +1 -0
- package/dist/types/parameters.d.ts +76 -0
- package/dist/types/parameters.d.ts.map +1 -0
- package/dist/types/parameters.js +1 -0
- package/dist/types/shader.d.ts +110 -0
- package/dist/types/shader.d.ts.map +1 -0
- package/dist/types/shader.js +1 -0
- package/dist/types/vocal-preset.d.ts +149 -0
- package/dist/types/vocal-preset.d.ts.map +1 -0
- package/dist/types/vocal-preset.js +54 -0
- package/package.json +50 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Licensing and collaboration-access types for @collabhut/plugin-sdk.
|
|
3
|
+
*
|
|
4
|
+
* ## Overview
|
|
5
|
+
* CollabDAW verifies plugin licenses server-side on every load. There is
|
|
6
|
+
* **no user-facing license key** — the DAW sends the current user's session
|
|
7
|
+
* token and the plugin's `manifest.id` to the CollabHut API, which returns a
|
|
8
|
+
* `LicenseCheckResult`.
|
|
9
|
+
*
|
|
10
|
+
* ## Collaboration access rules (IMPORTANT)
|
|
11
|
+
*
|
|
12
|
+
* A user who does **not** own a license to a paid plugin may still use that
|
|
13
|
+
* plugin under the following conditions — **all must be true**:
|
|
14
|
+
*
|
|
15
|
+
* 1. They are an active participant in a collaboration (`status !== "completed"`).
|
|
16
|
+
* 2. At least one other participant in that collaboration **does** own a license
|
|
17
|
+
* for the plugin.
|
|
18
|
+
* 3. The plugin's `pricing` field is `"paid"` (free plugins require no license).
|
|
19
|
+
*
|
|
20
|
+
* When the collaboration is marked completed, collab-access is revoked.
|
|
21
|
+
* The user cannot export or render the track if the collab plugin access has
|
|
22
|
+
* lapsed.
|
|
23
|
+
*
|
|
24
|
+
* This policy is enforced both in the DAW runtime (via `LicenseCheckResult`)
|
|
25
|
+
* and in CollabHut's server API.
|
|
26
|
+
*/
|
|
27
|
+
/** ISO-8601 datetime string */
|
|
28
|
+
type IsoDateString = string;
|
|
29
|
+
/**
|
|
30
|
+
* Describes the permission granted through a collaboration partner's license.
|
|
31
|
+
* Only valid while the collaboration is active (not completed).
|
|
32
|
+
*/
|
|
33
|
+
export interface CollaborationAccess {
|
|
34
|
+
/** The CollabHut collaboration ID granting access */
|
|
35
|
+
readonly collaborationId: string;
|
|
36
|
+
/** The user ID of the participant who owns the license */
|
|
37
|
+
readonly ownerUserId: string;
|
|
38
|
+
/**
|
|
39
|
+
* Access expires at this time regardless of collab status.
|
|
40
|
+
* The DAW must re-verify before this timestamp.
|
|
41
|
+
* Null = access is valid for the lifetime of the collaboration.
|
|
42
|
+
*/
|
|
43
|
+
readonly allowedUntil: IsoDateString | null;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* A signed license token returned by the CollabHut API.
|
|
47
|
+
*
|
|
48
|
+
* The DAW must not cache this across sessions — it re-verifies on every
|
|
49
|
+
* project open to handle revocations and collab completions.
|
|
50
|
+
*/
|
|
51
|
+
export interface LicenseToken {
|
|
52
|
+
/** Plugin manifest ID this token covers */
|
|
53
|
+
readonly pluginId: string;
|
|
54
|
+
/** The user ID this token was issued to */
|
|
55
|
+
readonly userId: string;
|
|
56
|
+
/**
|
|
57
|
+
* Type of access:
|
|
58
|
+
* - `"owned"` — user has purchased (or the plugin is free)
|
|
59
|
+
* - `"collab"` — access granted through a collaboration partner
|
|
60
|
+
* - `"free"` — plugin has `pricing: "free"`, no purchase needed
|
|
61
|
+
*/
|
|
62
|
+
readonly accessType: "owned" | "collab" | "free";
|
|
63
|
+
/**
|
|
64
|
+
* Present when `accessType === "collab"`.
|
|
65
|
+
* Contains collab-specific access constraints.
|
|
66
|
+
*/
|
|
67
|
+
readonly collaborationAccess?: CollaborationAccess;
|
|
68
|
+
/**
|
|
69
|
+
* Token expiry — DAW must re-verify after this time.
|
|
70
|
+
* Short-lived (typically 1 hour) to support near-real-time revocation.
|
|
71
|
+
*/
|
|
72
|
+
readonly expiresAt: IsoDateString;
|
|
73
|
+
/**
|
|
74
|
+
* Opaque HMAC signature from the CollabHut server.
|
|
75
|
+
* The DAW validates this against the public key embedded in the app.
|
|
76
|
+
* Do not store or forward this value.
|
|
77
|
+
*/
|
|
78
|
+
readonly signature: string;
|
|
79
|
+
}
|
|
80
|
+
/** The DAW was granted access */
|
|
81
|
+
export interface LicenseGranted {
|
|
82
|
+
readonly granted: true;
|
|
83
|
+
readonly token: LicenseToken;
|
|
84
|
+
}
|
|
85
|
+
/** The DAW was denied access with a human-readable reason */
|
|
86
|
+
export interface LicenseDenied {
|
|
87
|
+
readonly granted: false;
|
|
88
|
+
/**
|
|
89
|
+
* Machine-readable denial code:
|
|
90
|
+
* - `"not-purchased"` — user does not own this plugin
|
|
91
|
+
* - `"collab-completed"` — the collaboration granting access is finished
|
|
92
|
+
* - `"collab-not-member"` — user is no longer in the collaboration
|
|
93
|
+
* - `"revoked"` — license was revoked (e.g., charge-back)
|
|
94
|
+
* - `"expired"` — token is past its `expiresAt` (re-verify required)
|
|
95
|
+
* - `"plugin-delisted"` — plugin was removed from the marketplace
|
|
96
|
+
*/
|
|
97
|
+
readonly code: "not-purchased" | "collab-completed" | "collab-not-member" | "revoked" | "expired" | "plugin-delisted";
|
|
98
|
+
/** Human-readable explanation shown to the user in the DAW */
|
|
99
|
+
readonly reason: string;
|
|
100
|
+
}
|
|
101
|
+
/** Result of a license verification call */
|
|
102
|
+
export type LicenseCheckResult = LicenseGranted | LicenseDenied;
|
|
103
|
+
/**
|
|
104
|
+
* DAW-side interface for performing license checks.
|
|
105
|
+
*
|
|
106
|
+
* This is injected by the DAW runtime — plugin code never calls it directly.
|
|
107
|
+
* It is documented here so host environment authors can implement it correctly.
|
|
108
|
+
*/
|
|
109
|
+
export interface LicenseChecker {
|
|
110
|
+
/**
|
|
111
|
+
* Verify that the current user may load a plugin.
|
|
112
|
+
* @param pluginId The plugin's `manifest.id`
|
|
113
|
+
* @param collaborationId Active collaboration context (if any)
|
|
114
|
+
*/
|
|
115
|
+
check(pluginId: string, collaborationId?: string): Promise<LicenseCheckResult>;
|
|
116
|
+
}
|
|
117
|
+
export {};
|
|
118
|
+
//# sourceMappingURL=licensing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"licensing.d.ts","sourceRoot":"","sources":["../../src/types/licensing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,+BAA+B;AAC/B,KAAK,aAAa,GAAG,MAAM,CAAA;AAE3B;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAChC,qDAAqD;IACrD,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;IAChC,0DAA0D;IAC1D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,YAAY,EAAE,aAAa,GAAG,IAAI,CAAA;CAC9C;AAED;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IACzB,2CAA2C;IAC3C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,2CAA2C;IAC3C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAA;IAChD;;;OAGG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,mBAAmB,CAAA;IAClD;;;OAGG;IACH,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAA;IACjC;;;;OAIG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC7B;AAED,iCAAiC;AACjC,MAAM,WAAW,cAAc;IAC3B,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAA;IACtB,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAA;CAC/B;AAED,6DAA6D;AAC7D,MAAM,WAAW,aAAa;IAC1B,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAA;IACvB;;;;;;;;OAQG;IACH,QAAQ,CAAC,IAAI,EACP,eAAe,GACf,kBAAkB,GAClB,mBAAmB,GACnB,SAAS,GACT,SAAS,GACT,iBAAiB,CAAA;IACvB,8DAA8D;IAC9D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAC1B;AAED,4CAA4C;AAC5C,MAAM,MAAM,kBAAkB,GAAG,cAAc,GAAG,aAAa,CAAA;AAE/D;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC3B;;;;OAIG;IACH,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAA;CACjF"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Licensing and collaboration-access types for @collabhut/plugin-sdk.
|
|
3
|
+
*
|
|
4
|
+
* ## Overview
|
|
5
|
+
* CollabDAW verifies plugin licenses server-side on every load. There is
|
|
6
|
+
* **no user-facing license key** — the DAW sends the current user's session
|
|
7
|
+
* token and the plugin's `manifest.id` to the CollabHut API, which returns a
|
|
8
|
+
* `LicenseCheckResult`.
|
|
9
|
+
*
|
|
10
|
+
* ## Collaboration access rules (IMPORTANT)
|
|
11
|
+
*
|
|
12
|
+
* A user who does **not** own a license to a paid plugin may still use that
|
|
13
|
+
* plugin under the following conditions — **all must be true**:
|
|
14
|
+
*
|
|
15
|
+
* 1. They are an active participant in a collaboration (`status !== "completed"`).
|
|
16
|
+
* 2. At least one other participant in that collaboration **does** own a license
|
|
17
|
+
* for the plugin.
|
|
18
|
+
* 3. The plugin's `pricing` field is `"paid"` (free plugins require no license).
|
|
19
|
+
*
|
|
20
|
+
* When the collaboration is marked completed, collab-access is revoked.
|
|
21
|
+
* The user cannot export or render the track if the collab plugin access has
|
|
22
|
+
* lapsed.
|
|
23
|
+
*
|
|
24
|
+
* This policy is enforced both in the DAW runtime (via `LicenseCheckResult`)
|
|
25
|
+
* and in CollabHut's server API.
|
|
26
|
+
*/
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { PluginParam } from "./parameters";
|
|
2
|
+
/** Semantic version string, e.g. "1.2.3" */
|
|
3
|
+
export type SemVer = `${number}.${number}.${number}`;
|
|
4
|
+
/** Plugin type identifier */
|
|
5
|
+
export type PluginType = "audio-effect" | "midi-effect" | "instrument" | "vocal-preset" | "shader";
|
|
6
|
+
/** Plugin pricing model */
|
|
7
|
+
export type PluginPricing = "free" | "paid";
|
|
8
|
+
/** Author / publisher information */
|
|
9
|
+
export interface PluginAuthor {
|
|
10
|
+
/** Display name */
|
|
11
|
+
readonly name: string;
|
|
12
|
+
/** Homepage or portfolio URL */
|
|
13
|
+
readonly url?: string;
|
|
14
|
+
/** Contact email (never shown to end-users, only used for support) */
|
|
15
|
+
readonly email?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Plugin manifest — the single source of truth for every plugin.
|
|
19
|
+
*
|
|
20
|
+
* Export a value conforming to this interface from your plugin module as
|
|
21
|
+
* `export const manifest: PluginManifest = { ... }`. CollabHut reads the
|
|
22
|
+
* manifest at upload time to populate the marketplace listing and at runtime
|
|
23
|
+
* to validate the loaded plugin against the executing CollabDAW version.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* import type { PluginManifest } from "@collabhut/plugin-sdk"
|
|
28
|
+
*
|
|
29
|
+
* export const manifest: PluginManifest = {
|
|
30
|
+
* id: "com.myorg.warm-compressor",
|
|
31
|
+
* name: "Warm Compressor",
|
|
32
|
+
* version: "1.0.0",
|
|
33
|
+
* type: "audio-effect",
|
|
34
|
+
* description: "Analogue-style VCA compressor with harmonic saturation.",
|
|
35
|
+
* author: { name: "Your Name", url: "https://example.com" },
|
|
36
|
+
* pricing: "paid",
|
|
37
|
+
* minApiVersion: "0.1.0",
|
|
38
|
+
* tags: ["dynamics", "compressor", "vintage"],
|
|
39
|
+
* }
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export interface PluginManifest {
|
|
43
|
+
/**
|
|
44
|
+
* Globally unique identifier in reverse-domain notation.
|
|
45
|
+
* Must be stable — changing this after publication breaks licensing.
|
|
46
|
+
* @example "com.myorg.warm-compressor"
|
|
47
|
+
*/
|
|
48
|
+
readonly id: string;
|
|
49
|
+
/** Human-readable display name shown in the DAW device browser */
|
|
50
|
+
readonly name: string;
|
|
51
|
+
/** Semantic version following semver.org */
|
|
52
|
+
readonly version: SemVer;
|
|
53
|
+
/** Plugin category — determines where it appears in the device browser */
|
|
54
|
+
readonly type: PluginType;
|
|
55
|
+
/**
|
|
56
|
+
* Short description shown in the marketplace (max 280 characters).
|
|
57
|
+
* Write this for musicians, not engineers.
|
|
58
|
+
*/
|
|
59
|
+
readonly description: string;
|
|
60
|
+
/** Author / publisher information */
|
|
61
|
+
readonly author: PluginAuthor;
|
|
62
|
+
/** Optional homepage URL for marketing / documentation */
|
|
63
|
+
readonly homepage?: string;
|
|
64
|
+
/** Tags for search and discoverability in the marketplace */
|
|
65
|
+
readonly tags?: readonly string[];
|
|
66
|
+
/**
|
|
67
|
+
* Parameter definitions exposed to the DAW automation lane and UI.
|
|
68
|
+
* Each param becomes a knob, slider, or toggle in the device editor.
|
|
69
|
+
*/
|
|
70
|
+
readonly params?: readonly PluginParam[];
|
|
71
|
+
/**
|
|
72
|
+
* Pricing model.
|
|
73
|
+
* - `"free"` — no purchase required; runs on all plans
|
|
74
|
+
* - `"paid"` — requires a CollabHut marketplace purchase to use outside
|
|
75
|
+
* of a collaboration where another participant owns a licence
|
|
76
|
+
*/
|
|
77
|
+
readonly pricing: PluginPricing;
|
|
78
|
+
/**
|
|
79
|
+
* Minimum @collabhut/plugin-sdk runtime version the plugin requires.
|
|
80
|
+
* CollabDAW will refuse to load the plugin if the runtime version is lower.
|
|
81
|
+
*/
|
|
82
|
+
readonly minApiVersion: SemVer;
|
|
83
|
+
/**
|
|
84
|
+
* Whether this plugin is compatible with ShaderToy integration.
|
|
85
|
+
* Only relevant for `type: "shader"` plugins.
|
|
86
|
+
* @default false
|
|
87
|
+
*/
|
|
88
|
+
readonly shadertoyCompatible?: boolean;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/types/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAE/C,4CAA4C;AAC5C,MAAM,MAAM,MAAM,GAAG,GAAG,MAAM,IAAI,MAAM,IAAI,MAAM,EAAE,CAAA;AAEpD,6BAA6B;AAC7B,MAAM,MAAM,UAAU,GAChB,cAAc,GACd,aAAa,GACb,YAAY,GACZ,cAAc,GACd,QAAQ,CAAA;AAEd,2BAA2B;AAC3B,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,MAAM,CAAA;AAE3C,qCAAqC;AACrC,MAAM,WAAW,YAAY;IACzB,mBAAmB;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,gCAAgC;IAChC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAA;IACrB,sEAAsE;IACtE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAC1B;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,WAAW,cAAc;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IAEnB,kEAAkE;IAClE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IAErB,4CAA4C;IAC5C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IAExB,0EAA0E;IAC1E,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;IAEzB;;;OAGG;IACH,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAE5B,qCAAqC;IACrC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAA;IAE7B,0DAA0D;IAC1D,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAE1B,6DAA6D;IAC7D,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAEjC;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,WAAW,EAAE,CAAA;IAExC;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAA;IAE/B;;;OAGG;IACH,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAE9B;;;;OAIG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAA;CACzC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import type { PluginContext } from "./context";
|
|
2
|
+
import type { PluginManifest } from "./manifest";
|
|
3
|
+
/** Standard MIDI event types */
|
|
4
|
+
export type MidiEventType = "note-on" | "note-off" | "control-change" | "pitch-bend" | "aftertouch" | "poly-aftertouch" | "program-change";
|
|
5
|
+
/** Base MIDI event — every event carries timing in beats from song start */
|
|
6
|
+
interface MidiEventBase {
|
|
7
|
+
readonly channel: number;
|
|
8
|
+
readonly beatTime: number;
|
|
9
|
+
}
|
|
10
|
+
/** A MIDI note-on event */
|
|
11
|
+
export interface MidiNoteOnEvent extends MidiEventBase {
|
|
12
|
+
readonly type: "note-on";
|
|
13
|
+
readonly note: number;
|
|
14
|
+
readonly velocity: number;
|
|
15
|
+
}
|
|
16
|
+
/** A MIDI note-off event */
|
|
17
|
+
export interface MidiNoteOffEvent extends MidiEventBase {
|
|
18
|
+
readonly type: "note-off";
|
|
19
|
+
readonly note: number;
|
|
20
|
+
readonly velocity: number;
|
|
21
|
+
}
|
|
22
|
+
/** A MIDI continuous controller event */
|
|
23
|
+
export interface MidiControlChangeEvent extends MidiEventBase {
|
|
24
|
+
readonly type: "control-change";
|
|
25
|
+
readonly controller: number;
|
|
26
|
+
readonly value: number;
|
|
27
|
+
}
|
|
28
|
+
/** A MIDI pitch bend event */
|
|
29
|
+
export interface MidiPitchBendEvent extends MidiEventBase {
|
|
30
|
+
readonly type: "pitch-bend";
|
|
31
|
+
readonly value: number;
|
|
32
|
+
}
|
|
33
|
+
/** Channel pressure (aftertouch) */
|
|
34
|
+
export interface MidiAftertouchEvent extends MidiEventBase {
|
|
35
|
+
readonly type: "aftertouch";
|
|
36
|
+
readonly pressure: number;
|
|
37
|
+
}
|
|
38
|
+
/** Poly aftertouch (note-specific pressure) */
|
|
39
|
+
export interface MidiPolyAftertouchEvent extends MidiEventBase {
|
|
40
|
+
readonly type: "poly-aftertouch";
|
|
41
|
+
readonly note: number;
|
|
42
|
+
readonly pressure: number;
|
|
43
|
+
}
|
|
44
|
+
/** Program change */
|
|
45
|
+
export interface MidiProgramChangeEvent extends MidiEventBase {
|
|
46
|
+
readonly type: "program-change";
|
|
47
|
+
readonly program: number;
|
|
48
|
+
}
|
|
49
|
+
/** Discriminated union of all MIDI events */
|
|
50
|
+
export type MidiEvent = MidiNoteOnEvent | MidiNoteOffEvent | MidiControlChangeEvent | MidiPitchBendEvent | MidiAftertouchEvent | MidiPolyAftertouchEvent | MidiProgramChangeEvent;
|
|
51
|
+
/**
|
|
52
|
+
* MIDI effect processor.
|
|
53
|
+
*
|
|
54
|
+
* Receives the list of MIDI events for the current audio block and returns
|
|
55
|
+
* transformed / generated events. Returning an empty array silences all
|
|
56
|
+
* MIDI output for that block.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```ts
|
|
60
|
+
* import type { MidiEffectFactory, MidiEvent } from "@collabhut/plugin-sdk"
|
|
61
|
+
*
|
|
62
|
+
* // Chord generator — fan every note-on into a major chord
|
|
63
|
+
* export const factory: MidiEffectFactory = (_context, params) => ({
|
|
64
|
+
* process(events) {
|
|
65
|
+
* const out: MidiEvent[] = []
|
|
66
|
+
* for (const ev of events) {
|
|
67
|
+
* out.push(ev)
|
|
68
|
+
* if (ev.type === "note-on") {
|
|
69
|
+
* out.push({ ...ev, note: ev.note + 4 }) // major third
|
|
70
|
+
* out.push({ ...ev, note: ev.note + 7 }) // perfect fifth
|
|
71
|
+
* } else if (ev.type === "note-off") {
|
|
72
|
+
* out.push({ ...ev, note: ev.note + 4 })
|
|
73
|
+
* out.push({ ...ev, note: ev.note + 7 })
|
|
74
|
+
* }
|
|
75
|
+
* }
|
|
76
|
+
* return out
|
|
77
|
+
* },
|
|
78
|
+
* dispose() {},
|
|
79
|
+
* })
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
export interface MidiEffectPlugin {
|
|
83
|
+
/**
|
|
84
|
+
* Called once per audio block.
|
|
85
|
+
* @param events Events arriving in this block (sorted by beatTime)
|
|
86
|
+
* @returns Transformed or generated events for downstream
|
|
87
|
+
*/
|
|
88
|
+
process(events: ReadonlyArray<MidiEvent>): MidiEvent[];
|
|
89
|
+
/** Called by the DAW when the plugin is removed or the project closes */
|
|
90
|
+
dispose(): void;
|
|
91
|
+
}
|
|
92
|
+
export type MidiEffectFactory = (context: PluginContext, params: Readonly<Record<string, number | boolean | string>>, manifest: PluginManifest) => MidiEffectPlugin;
|
|
93
|
+
/** Full MIDI-effect module shape */
|
|
94
|
+
export interface MidiEffectModule {
|
|
95
|
+
readonly manifest: PluginManifest & {
|
|
96
|
+
readonly type: "midi-effect";
|
|
97
|
+
};
|
|
98
|
+
readonly factory: MidiEffectFactory;
|
|
99
|
+
}
|
|
100
|
+
export {};
|
|
101
|
+
//# sourceMappingURL=midi-effect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"midi-effect.d.ts","sourceRoot":"","sources":["../../src/types/midi-effect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA;AAE9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAEhD,gCAAgC;AAChC,MAAM,MAAM,aAAa,GACnB,SAAS,GACT,UAAU,GACV,gBAAgB,GAChB,YAAY,GACZ,YAAY,GACZ,iBAAiB,GACjB,gBAAgB,CAAA;AAEtB,4EAA4E;AAC5E,UAAU,aAAa;IACnB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAC5B;AAED,2BAA2B;AAC3B,MAAM,WAAW,eAAgB,SAAQ,aAAa;IAClD,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAC5B;AAED,4BAA4B;AAC5B,MAAM,WAAW,gBAAiB,SAAQ,aAAa;IACnD,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAC5B;AAED,yCAAyC;AACzC,MAAM,WAAW,sBAAuB,SAAQ,aAAa;IACzD,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAA;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACzB;AAED,8BAA8B;AAC9B,MAAM,WAAW,kBAAmB,SAAQ,aAAa;IACrD,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAA;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACzB;AAED,oCAAoC;AACpC,MAAM,WAAW,mBAAoB,SAAQ,aAAa;IACtD,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAA;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAC5B;AAED,+CAA+C;AAC/C,MAAM,WAAW,uBAAwB,SAAQ,aAAa;IAC1D,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAA;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAC5B;AAED,qBAAqB;AACrB,MAAM,WAAW,sBAAuB,SAAQ,aAAa;IACzD,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAA;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAC3B;AAED,6CAA6C;AAC7C,MAAM,MAAM,SAAS,GACf,eAAe,GACf,gBAAgB,GAChB,sBAAsB,GACtB,kBAAkB,GAClB,mBAAmB,GACnB,uBAAuB,GACvB,sBAAsB,CAAA;AAE5B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,WAAW,gBAAgB;IAC7B;;;;OAIG;IACH,OAAO,CAAC,MAAM,EAAE,aAAa,CAAC,SAAS,CAAC,GAAG,SAAS,EAAE,CAAA;IACtD,yEAAyE;IACzE,OAAO,IAAI,IAAI,CAAA;CAClB;AAED,MAAM,MAAM,iBAAiB,GAAG,CAC5B,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC,CAAC,EAC3D,QAAQ,EAAE,cAAc,KACvB,gBAAgB,CAAA;AAErB,oCAAoC;AACpC,MAAM,WAAW,gBAAgB;IAC7B,QAAQ,CAAC,QAAQ,EAAE,cAAc,GAAG;QAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAA;KAAE,CAAA;IACpE,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAA;CACtC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parameter curve controls how raw 0–1 knob values map to parameter values.
|
|
3
|
+
* - `"linear"` — equal steps
|
|
4
|
+
* - `"log"` — logarithmic, good for frequency and time
|
|
5
|
+
* - `"exp"` — exponential, good for gain/amplitude
|
|
6
|
+
*/
|
|
7
|
+
export type ParamCurve = "linear" | "log" | "exp";
|
|
8
|
+
/** Data type for a discrete-choice parameter */
|
|
9
|
+
export interface ChoiceOption {
|
|
10
|
+
readonly value: string;
|
|
11
|
+
readonly label: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Continuous (knob/slider) parameter definition.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* {
|
|
19
|
+
* id: "cutoff",
|
|
20
|
+
* label: "Cutoff",
|
|
21
|
+
* type: "range",
|
|
22
|
+
* min: 20,
|
|
23
|
+
* max: 20000,
|
|
24
|
+
* default: 1000,
|
|
25
|
+
* unit: "Hz",
|
|
26
|
+
* curve: "log",
|
|
27
|
+
* }
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export interface RangeParam {
|
|
31
|
+
readonly type: "range";
|
|
32
|
+
/** Unique identifier within this plugin's parameter set */
|
|
33
|
+
readonly id: string;
|
|
34
|
+
/** Display label shown in the device editor */
|
|
35
|
+
readonly label: string;
|
|
36
|
+
readonly min: number;
|
|
37
|
+
readonly max: number;
|
|
38
|
+
readonly default: number;
|
|
39
|
+
/** Optional unit shown next to the value, e.g. "Hz", "dB", "ms" */
|
|
40
|
+
readonly unit?: string;
|
|
41
|
+
readonly curve?: ParamCurve;
|
|
42
|
+
/** Number of decimal places shown in the UI */
|
|
43
|
+
readonly decimals?: number;
|
|
44
|
+
/** Optional automation grouping label */
|
|
45
|
+
readonly group?: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Boolean (on/off) parameter — rendered as a toggle button.
|
|
49
|
+
*/
|
|
50
|
+
export interface BoolParam {
|
|
51
|
+
readonly type: "bool";
|
|
52
|
+
readonly id: string;
|
|
53
|
+
readonly label: string;
|
|
54
|
+
readonly default: boolean;
|
|
55
|
+
readonly group?: string;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Discrete-choice parameter — rendered as a dropdown.
|
|
59
|
+
*/
|
|
60
|
+
export interface ChoiceParam {
|
|
61
|
+
readonly type: "choice";
|
|
62
|
+
readonly id: string;
|
|
63
|
+
readonly label: string;
|
|
64
|
+
readonly choices: readonly ChoiceOption[];
|
|
65
|
+
readonly default: string;
|
|
66
|
+
readonly group?: string;
|
|
67
|
+
}
|
|
68
|
+
export type PluginParam = RangeParam | BoolParam | ChoiceParam;
|
|
69
|
+
/**
|
|
70
|
+
* Type-safe map from param IDs to values.
|
|
71
|
+
* Produced by the runtime from the current automation state.
|
|
72
|
+
*/
|
|
73
|
+
export type ParamValues<T extends ReadonlyArray<PluginParam>> = {
|
|
74
|
+
readonly [P in T[number] as P["id"]]: P extends RangeParam ? number : P extends BoolParam ? boolean : string;
|
|
75
|
+
};
|
|
76
|
+
//# sourceMappingURL=parameters.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parameters.d.ts","sourceRoot":"","sources":["../../src/types/parameters.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,KAAK,GAAG,KAAK,CAAA;AAEjD,gDAAgD;AAChD,MAAM,WAAW,YAAY;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACzB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,UAAU;IACvB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;IACtB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,+CAA+C;IAC/C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,mEAAmE;IACnE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,UAAU,CAAA;IAC3B,+CAA+C;IAC/C,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,yCAAyC;IACzC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IACxB,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;IACvB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,CAAA;IACzC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAC1B;AAED,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,SAAS,GAAG,WAAW,CAAA;AAE9D;;;GAGG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,aAAa,CAAC,WAAW,CAAC,IAAI;IAC5D,QAAQ,EAAE,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,UAAU,GACpD,MAAM,GACN,CAAC,SAAS,SAAS,GACnB,OAAO,GACP,MAAM;CACf,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type { PluginManifest } from "./manifest";
|
|
2
|
+
/**
|
|
3
|
+
* GLSL uniform value types that a shader plugin may declare.
|
|
4
|
+
* The DAW populates these automatically from session state or from plugin params.
|
|
5
|
+
*/
|
|
6
|
+
export type UniformType = "float" | "int" | "vec2" | "vec3" | "vec4" | "bool" | "sampler2D";
|
|
7
|
+
/** A declared GLSL uniform with its source mapping */
|
|
8
|
+
export interface ShaderUniform {
|
|
9
|
+
/** Uniform name as it appears in the GLSL source */
|
|
10
|
+
readonly name: string;
|
|
11
|
+
readonly type: UniformType;
|
|
12
|
+
/**
|
|
13
|
+
* How this uniform is populated at runtime:
|
|
14
|
+
* - `"param"` — maps to a plugin param by id (must match a `manifest.params` entry)
|
|
15
|
+
* - `"audio-rms"` — root mean square of the current audio block (float, 0–1)
|
|
16
|
+
* - `"audio-spectrum"` — frequency magnitudes as a sampler2D (1×512 texture)
|
|
17
|
+
* - `"beat"` — current fractional beat position (float)
|
|
18
|
+
* - `"bpm"` — session BPM (float)
|
|
19
|
+
* - `"time"` — elapsed playback time in seconds (matches ShaderToy `iTime`)
|
|
20
|
+
* - `"resolution"` — canvas size as vec2 (matches ShaderToy `iResolution`)
|
|
21
|
+
* - `"mouse"` — cursor position as vec2 (matches ShaderToy `iMouse`)
|
|
22
|
+
*/
|
|
23
|
+
readonly source: "param" | "audio-rms" | "audio-spectrum" | "beat" | "bpm" | "time" | "resolution" | "mouse";
|
|
24
|
+
/** Required when source is "param" — must match a manifest.params entry id */
|
|
25
|
+
readonly paramId?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Shader plugin definition.
|
|
29
|
+
*
|
|
30
|
+
* The `glsl` field must be a valid WebGL2 fragment shader.
|
|
31
|
+
*
|
|
32
|
+
* ### ShaderToy Compatibility
|
|
33
|
+
* When `manifest.shadertoyCompatible` is `true`, the DAW automatically injects
|
|
34
|
+
* the standard ShaderToy uniforms (`iTime`, `iResolution`, `iMouse`,
|
|
35
|
+
* `iChannel0`–`iChannel3`) so ShaderToy code can be pasted with minimal changes.
|
|
36
|
+
*
|
|
37
|
+
* You **do not** need to declare those as `uniforms` — they are added
|
|
38
|
+
* automatically. Custom uniforms you declare are added on top.
|
|
39
|
+
*
|
|
40
|
+
* ### Security
|
|
41
|
+
* Shader source is validated server-side before being accepted by the
|
|
42
|
+
* marketplace. Prohibited constructs: infinite loops without exit guards,
|
|
43
|
+
* texture fetches to external URIs, GLSL extensions that could leak device
|
|
44
|
+
* fingerprints.
|
|
45
|
+
*
|
|
46
|
+
* @example Basic audio-reactive shader
|
|
47
|
+
* ```ts
|
|
48
|
+
* import type { ShaderModule } from "@collabhut/plugin-sdk"
|
|
49
|
+
*
|
|
50
|
+
* const mod: ShaderModule = {
|
|
51
|
+
* manifest: {
|
|
52
|
+
* id: "com.myorg.pulse-visualiser",
|
|
53
|
+
* name: "Pulse Visualiser",
|
|
54
|
+
* version: "1.0.0",
|
|
55
|
+
* type: "shader",
|
|
56
|
+
* description: "Circle that pulses with the music",
|
|
57
|
+
* author: { name: "My Org" },
|
|
58
|
+
* pricing: "free",
|
|
59
|
+
* minApiVersion: "0.1.0",
|
|
60
|
+
* shadertoyCompatible: false,
|
|
61
|
+
* },
|
|
62
|
+
* shader: {
|
|
63
|
+
* glsl: `
|
|
64
|
+
* precision mediump float;
|
|
65
|
+
* uniform vec2 uResolution;
|
|
66
|
+
* uniform float uRms;
|
|
67
|
+
* void main() {
|
|
68
|
+
* vec2 uv = (gl_FragCoord.xy / uResolution) * 2.0 - 1.0;
|
|
69
|
+
* float r = length(uv);
|
|
70
|
+
* float circle = smoothstep(0.5 + uRms * 0.4, 0.49 + uRms * 0.4, r);
|
|
71
|
+
* gl_FragColor = vec4(vec3(circle), 1.0);
|
|
72
|
+
* }
|
|
73
|
+
* `,
|
|
74
|
+
* uniforms: [
|
|
75
|
+
* { name: "uResolution", type: "vec2", source: "resolution" },
|
|
76
|
+
* { name: "uRms", type: "float", source: "audio-rms" },
|
|
77
|
+
* ],
|
|
78
|
+
* },
|
|
79
|
+
* }
|
|
80
|
+
*
|
|
81
|
+
* export default mod
|
|
82
|
+
* ```
|
|
83
|
+
*
|
|
84
|
+
* @example ShaderToy port
|
|
85
|
+
* ```ts
|
|
86
|
+
* // manifest.shadertoyCompatible = true
|
|
87
|
+
* // iTime, iResolution, iMouse, iChannel0 are pre-injected
|
|
88
|
+
* const glsl = `
|
|
89
|
+
* void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
90
|
+
* vec2 uv = fragCoord / iResolution.xy;
|
|
91
|
+
* fragColor = vec4(uv, 0.5 + 0.5 * sin(iTime), 1.0);
|
|
92
|
+
* }
|
|
93
|
+
* `
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
export interface ShaderDefinition {
|
|
97
|
+
/** Fragment shader source (WebGL2 / GLSL ES 300 or 100) */
|
|
98
|
+
readonly glsl: string;
|
|
99
|
+
/** Custom uniform declarations */
|
|
100
|
+
readonly uniforms?: readonly ShaderUniform[];
|
|
101
|
+
}
|
|
102
|
+
/** Full shader plugin module shape */
|
|
103
|
+
export interface ShaderModule {
|
|
104
|
+
readonly manifest: PluginManifest & {
|
|
105
|
+
readonly type: "shader";
|
|
106
|
+
readonly shadertoyCompatible?: boolean;
|
|
107
|
+
};
|
|
108
|
+
readonly shader: ShaderDefinition;
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=shader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shader.d.ts","sourceRoot":"","sources":["../../src/types/shader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAEhD;;;GAGG;AACH,MAAM,MAAM,WAAW,GACjB,OAAO,GACP,KAAK,GACL,MAAM,GACN,MAAM,GACN,MAAM,GACN,MAAM,GACN,WAAW,CAAA;AAEjB,sDAAsD;AACtD,MAAM,WAAW,aAAa;IAC1B,oDAAoD;IACpD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAA;IAC1B;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,MAAM,EACT,OAAO,GACP,WAAW,GACX,gBAAgB,GAChB,MAAM,GACN,KAAK,GACL,MAAM,GACN,YAAY,GACZ,OAAO,CAAA;IACb,8EAA8E;IAC9E,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAC5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoEG;AACH,MAAM,WAAW,gBAAgB;IAC7B,2DAA2D;IAC3D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,kCAAkC;IAClC,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,aAAa,EAAE,CAAA;CAC/C;AAED,sCAAsC;AACtC,MAAM,WAAW,YAAY;IACzB,QAAQ,CAAC,QAAQ,EAAE,cAAc,GAAG;QAChC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;QACvB,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAA;KACzC,CAAA;IACD,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAA;CACpC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|