@m4l-jweb/surface 1.2.1 → 1.3.1
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/package.json +2 -2
- package/src/index.ts +75 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@m4l-jweb/surface",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "m4l-jweb: declare a device's Live parameters as code - the surface Push actually sees - plus a mocked-Live dev harness.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
],
|
|
25
25
|
"peerDependencies": {
|
|
26
26
|
"react": ">=18",
|
|
27
|
-
"@m4l-jweb/bridge": "1.
|
|
27
|
+
"@m4l-jweb/bridge": "1.3.1"
|
|
28
28
|
},
|
|
29
29
|
"peerDependenciesMeta": {
|
|
30
30
|
"react": {
|
package/src/index.ts
CHANGED
|
@@ -593,6 +593,81 @@ export function defineWatch<const W extends Record<string, WatchSpec>>(def: Watc
|
|
|
593
593
|
return { ...def, keys };
|
|
594
594
|
}
|
|
595
595
|
|
|
596
|
+
/* ------------------------------------------------------------------ *
|
|
597
|
+
* defineFiles - declare that a device touches DISK, once, as code.
|
|
598
|
+
*
|
|
599
|
+
* The third sibling of defineSurface and defineWatch, and the one reality asked
|
|
600
|
+
* for. Writing a file is not one feature, it is three that must travel together:
|
|
601
|
+
*
|
|
602
|
+
* the `download` chain it owns [maxurl], and phase three of saveToFile is a
|
|
603
|
+
* `file://` place through it - even for a device that
|
|
604
|
+
* downloads nothing and only writes bytes it already has;
|
|
605
|
+
* the device folder where the files went, which the page cannot know and
|
|
606
|
+
* the wrapper has to tell it (`device_folder`);
|
|
607
|
+
* the selectors `fetch_to_file`, the `save_*` exchange.
|
|
608
|
+
*
|
|
609
|
+
* Get the first one wrong and the failure is SILENT: the bytes are written
|
|
610
|
+
* correctly, the place request leaves on an aux outlet with nothing on the other
|
|
611
|
+
* end, no reply ever comes, and the promise never settles. The UI sits on
|
|
612
|
+
* "Rendering..." next to a scratch file that looks almost right. Nothing in the
|
|
613
|
+
* build or the tests could see it, because nothing knew the device meant to write.
|
|
614
|
+
*
|
|
615
|
+
* Now it does. A device that declares files gets [maxurl] whether or not its
|
|
616
|
+
* manifest remembered to ask, and gets told its own folder; a device that declares
|
|
617
|
+
* none gets neither.
|
|
618
|
+
*
|
|
619
|
+
* NO SUBFOLDER OPTION, deliberately. A save's destination must be a FLAT name in
|
|
620
|
+
* the device folder: Max's [js] File and maxurl (libcurl) resolve a subdirectory
|
|
621
|
+
* differently, so `sub/x.wav` writes the .part where File agrees and the place
|
|
622
|
+
* cannot reach it (see saveToFile in @m4l-jweb/wrapper). An option for a path that
|
|
623
|
+
* does not work is worse than no option.
|
|
624
|
+
* ------------------------------------------------------------------ */
|
|
625
|
+
|
|
626
|
+
export interface FilesDef {
|
|
627
|
+
/**
|
|
628
|
+
* The page holds the bytes and writes them next to the .amxd - a rendered WAV,
|
|
629
|
+
* an export, a bounce. `saveToFile()` in @m4l-jweb/bridge.
|
|
630
|
+
*/
|
|
631
|
+
saves?: boolean;
|
|
632
|
+
/**
|
|
633
|
+
* The page hands Max a URL and Max streams it to disk on its own thread, with
|
|
634
|
+
* nothing crossing the message bridge. `fetchToFile()` in @m4l-jweb/bridge.
|
|
635
|
+
*/
|
|
636
|
+
fetches?: boolean;
|
|
637
|
+
/**
|
|
638
|
+
* Tell the page where its files land, at ui_ready (`device_folder <path>`).
|
|
639
|
+
* On by default: a file the user cannot find is a file that was not written, and
|
|
640
|
+
* the page has no other way to learn its own device's folder. Turn it off for a
|
|
641
|
+
* device whose files are its own business - a cache it fills and reads back.
|
|
642
|
+
*/
|
|
643
|
+
tellPage?: boolean;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
export interface Files extends FilesDef {
|
|
647
|
+
readonly saves: boolean;
|
|
648
|
+
readonly fetches: boolean;
|
|
649
|
+
readonly tellPage: boolean;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* Declare what this device does with disk.
|
|
654
|
+
*
|
|
655
|
+
* Like defineSurface and defineWatch, the checks run HERE, at call time, and
|
|
656
|
+
* throw - the build imports this module to derive the chain and the banner, so a
|
|
657
|
+
* bad declaration fails `pnpm build` and CI.
|
|
658
|
+
*/
|
|
659
|
+
export function defineFiles(def: FilesDef): Files {
|
|
660
|
+
const saves = def.saves ?? false;
|
|
661
|
+
const fetches = def.fetches ?? false;
|
|
662
|
+
// A declaration that claims neither would still pull in [maxurl] and still tell
|
|
663
|
+
// the page a folder, for a device that touches no disk at all. That is not a
|
|
664
|
+
// harmless default, it is a device carrying a chain nobody can explain.
|
|
665
|
+
if (!saves && !fetches) {
|
|
666
|
+
throw new Error(`files: declare at least one of saves / fetches - a device that does neither should not declare files at all`);
|
|
667
|
+
}
|
|
668
|
+
return { saves, fetches, tellPage: def.tellPage ?? true };
|
|
669
|
+
}
|
|
670
|
+
|
|
596
671
|
/** How a value is displayed - the parameter's own `format`, or a sane default. */
|
|
597
672
|
export function formatValue(spec: ParamSpec, value: unknown): string {
|
|
598
673
|
if (spec.kind === "toggle" || spec.kind === "button") return value ? "on" : "off";
|