@kolkrabbi/kol-hardware 0.6.0 → 0.7.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kolkrabbi/kol-hardware",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Panel controls for instruments",
|
|
6
6
|
"license": "MIT",
|
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
"react-dom": "^18.3.0 || ^19.0.0"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@kolkrabbi/kol-
|
|
24
|
-
"@kolkrabbi/kol-
|
|
25
|
-
"@kolkrabbi/kol-theme": "^0.
|
|
23
|
+
"@kolkrabbi/kol-component": "^0.301.0",
|
|
24
|
+
"@kolkrabbi/kol-icons": "^0.45.0",
|
|
25
|
+
"@kolkrabbi/kol-theme": "^0.189.0"
|
|
26
26
|
},
|
|
27
27
|
"files": [
|
|
28
28
|
"src",
|
package/src/frames/FlipCard.jsx
CHANGED
|
@@ -4,22 +4,30 @@
|
|
|
4
4
|
* to its patch panel. The body turns; everything around it holds still, so the back is the same
|
|
5
5
|
* shaped object as the front.
|
|
6
6
|
*
|
|
7
|
+
* THE FACES SWAP BY BACKFACE, NOT BY A CLOCK (FlipCardFaceSwap, kol-monitor 2026-10-24 — user: *"the card flipps,
|
|
8
|
+
* but the image snaps in once its landed. not believable."*). It swapped `visibility` at a fixed duration / 2, but
|
|
9
|
+
* `ease` crosses edge-on at ~a third of the turn, so from there to the swap neither face showed — a blank card for
|
|
10
|
+
* ~110ms of a 450 — and the back popped in near the end. Both faces hide their backface and the back is pre-rotated
|
|
11
|
+
* 180° in the preserve-3d turner, so the back shows exactly as the card passes edge-on, whatever the easing. The
|
|
12
|
+
* face that is away is `inert` (and aria-hidden), which is what the visibility had been for: out of tab order.
|
|
13
|
+
*
|
|
7
14
|
* @param {boolean} flipped
|
|
8
15
|
* @param {ReactNode} front · back
|
|
9
16
|
* @param {number|string} width the card's width (the back is laid over the front at it)
|
|
10
17
|
* @param {number} duration ms (default 450, mirror's)
|
|
11
18
|
*/
|
|
12
19
|
export default function FlipCard({ flipped = false, front, back, width, duration = 450, className = '' }) {
|
|
13
|
-
const
|
|
20
|
+
const hidden = { backfaceVisibility: 'hidden', WebkitBackfaceVisibility: 'hidden' }
|
|
14
21
|
return (
|
|
15
22
|
<div className={className} style={{ perspective: 1400, position: 'relative', width }}>
|
|
16
23
|
<div style={{ transition: `transform ${duration}ms ease`, transformStyle: 'preserve-3d', transform: flipped ? 'rotateY(180deg)' : 'none' }}>
|
|
17
|
-
<div
|
|
24
|
+
<div aria-hidden={flipped || undefined} inert={flipped || undefined} style={hidden}>
|
|
18
25
|
{front}
|
|
19
26
|
</div>
|
|
20
27
|
<div
|
|
21
|
-
aria-hidden={!flipped}
|
|
22
|
-
|
|
28
|
+
aria-hidden={!flipped || undefined}
|
|
29
|
+
inert={!flipped || undefined}
|
|
30
|
+
style={{ position: 'absolute', inset: 0, transform: 'rotateY(180deg)', overflowY: 'auto', scrollbarWidth: 'none', ...hidden }}
|
|
23
31
|
>
|
|
24
32
|
{back}
|
|
25
33
|
</div>
|
|
@@ -73,7 +73,7 @@ function Tabs({ sections, tabs, saved, onPick, onAppend }) {
|
|
|
73
73
|
<TabsRow tabs={visible} value={current?.id} onChange={setTab} />
|
|
74
74
|
<div className="flex min-h-0 flex-col gap-4 overflow-y-auto">
|
|
75
75
|
{current?.id === 'saved' && !saved?.length
|
|
76
|
-
? <div className="kol-
|
|
76
|
+
? <div className="kol-mono-10 text-fg-48">Nothing saved yet.</div>
|
|
77
77
|
: list.map((s) => <Section key={s.title} section={s} onPick={onPick} onAppend={onAppend} showTitle={list.length > 1} />)}
|
|
78
78
|
</div>
|
|
79
79
|
</div>
|
|
@@ -93,7 +93,7 @@ function Panel({ sections, tabs, saved, onPick, onAppend }) {
|
|
|
93
93
|
</div>
|
|
94
94
|
<div className="flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto p-4" style={{ scrollbarWidth: 'none' }}>
|
|
95
95
|
{current?.id === 'saved' && !saved?.length
|
|
96
|
-
? <div className="kol-
|
|
96
|
+
? <div className="kol-mono-10 text-fg-48">Nothing saved yet.</div>
|
|
97
97
|
: list.map((s) => <Section key={s.title} section={s} onPick={onPick} onAppend={onAppend} showTitle={list.length > 1} />)}
|
|
98
98
|
</div>
|
|
99
99
|
</div>
|
|
@@ -189,7 +189,8 @@ function EnvelopeHandles({ adsr, sec, onDragStart, onDrag, onDragEnd }) {
|
|
|
189
189
|
*
|
|
190
190
|
* TIME is the scope's clock: `bpm` sets its speed (60 BPM = one unit a second, so `t` reads as
|
|
191
191
|
* seconds at the default and as beats at any other). Play / pause stops it where it is. `now`
|
|
192
|
-
* (`() => t`) hands the clock to the host — a desk transport — and the scope reads it instead
|
|
192
|
+
* (`() => t`) hands the clock to the host — a desk transport — and the scope reads it instead;
|
|
193
|
+
* Play/Pause, BPM and Trigger are then not drawn, since the host's transport does their jobs.
|
|
193
194
|
*
|
|
194
195
|
* ADSR: four handles on the scope — A (attack time), D (decay time), S (at the end of the hold:
|
|
195
196
|
* sideways = how long the gate holds, up/down = the sustain level), R (release time). The scope
|
|
@@ -225,14 +226,18 @@ export default function EnvelopeGenerator({ generator, reference = 'panel', clas
|
|
|
225
226
|
const passSec = mode === 'equation' ? sec : envelopeLength(adsr)
|
|
226
227
|
const panel = reference === true || reference === 'panel'
|
|
227
228
|
|
|
229
|
+
/* a host clock (`now`) owns play, speed and restart — the controls for them would do nothing, so
|
|
230
|
+
* they are not drawn (EnvelopeGeneratorHostClockControls, kol-mirror 2026-10-22). Cycle still
|
|
231
|
+
* acts: it decides whether the host's time wraps over the pass or holds at its end. */
|
|
232
|
+
const ownClock = !g.now
|
|
228
233
|
const transport = (
|
|
229
234
|
<div className="flex h-6 shrink-0 items-center gap-3">
|
|
230
|
-
<Button tone="ghost" size="sm" iconOnly={g.playing ? 'pause' : 'play'} aria-label={g.playing ? 'Pause' : 'Play'} onClick={() => g.setPlaying(!g.playing)} />
|
|
231
|
-
<Field label="BPM" value={g.bpm} chars={3} onCommit={(v) => g.setBpm(Math.max(1, Math.min(999, num(v, g.bpm))))} />
|
|
235
|
+
{ownClock && <Button tone="ghost" size="sm" iconOnly={g.playing ? 'pause' : 'play'} aria-label={g.playing ? 'Pause' : 'Play'} onClick={() => g.setPlaying(!g.playing)} />}
|
|
236
|
+
{ownClock && <Field label="BPM" value={g.bpm} chars={3} onCommit={(v) => g.setBpm(Math.max(1, Math.min(999, num(v, g.bpm))))} />}
|
|
232
237
|
{mode === 'adsr' && (
|
|
233
238
|
<>
|
|
234
239
|
<Toggle value={g.cycle} onChange={g.setCycle} label="cycle" />
|
|
235
|
-
<Button tone="ghost" size="sm" onClick={g.fire}>Trigger</Button>
|
|
240
|
+
{ownClock && <Button tone="ghost" size="sm" onClick={g.fire}>Trigger</Button>}
|
|
236
241
|
</>
|
|
237
242
|
)}
|
|
238
243
|
<span className="ml-auto text-fg-48">{mode === 'adsr' ? `${passSec.toFixed(2)} s` : ''}</span>
|