@hraness/dawg 0.6.1 → 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/CHANGELOG.md +92 -0
- package/DAWG.md +343 -116
- package/README.md +27 -25
- package/core/autotune.ts +1119 -0
- package/core/chords.ts +271 -23
- package/core/clips.ts +499 -0
- package/core/diff.ts +182 -104
- package/core/expression.ts +15 -0
- package/core/fx.ts +99 -29
- package/core/instruments.ts +19 -0
- package/core/keys.ts +3 -3
- package/core/loop.ts +5 -0
- package/core/lyrics.ts +297 -0
- package/core/master.ts +3 -3
- package/core/resonators.ts +16 -2
- package/core/routing.ts +165 -0
- package/core/score.ts +832 -13
- package/core/sdk/eval-child.ts +7 -2
- package/core/sdk/eval.ts +35 -6
- package/core/sdk/print.ts +276 -3
- package/core/sdk/sync-lyrics.ts +49 -0
- package/core/sdk/v1.ts +1849 -42
- package/core/sections.ts +292 -26
- package/core/sing.ts +815 -0
- package/core/style-provenance.ts +80 -0
- package/core/styles/africa-mena-southasia.ts +2893 -0
- package/core/styles/americas.ts +3810 -0
- package/core/styles/art.ts +4993 -0
- package/core/styles/base.ts +123 -0
- package/core/styles/cycles.ts +106 -0
- package/core/styles/electronic.ts +2723 -0
- package/core/styles/europe-asia-pacific.ts +2838 -0
- package/core/styles/excerpt.ts +29 -0
- package/core/styles/gamelan.ts +283 -0
- package/core/styles/generate.ts +2199 -0
- package/core/styles/index.ts +515 -0
- package/core/styles/parts.ts +106 -0
- package/core/styles/pop.ts +3189 -0
- package/core/styles/rock.ts +2993 -0
- package/core/styles/roots.ts +4175 -0
- package/core/styles/schema.ts +429 -0
- package/core/styles/taxonomy.ts +940 -0
- package/core/styles/validate.ts +528 -0
- package/core/tempo.ts +32 -2
- package/core/tuning.ts +19 -3
- package/core/vocoder.ts +524 -0
- package/guides/agent.md +29 -0
- package/guides/arrange.md +30 -0
- package/guides/audition.md +20 -14
- package/guides/automation.md +12 -7
- package/guides/chords.md +15 -15
- package/guides/effects.md +17 -16
- package/guides/faders.md +20 -16
- package/guides/files.md +13 -9
- package/guides/getting-started.md +15 -11
- package/guides/keys.md +19 -15
- package/guides/media.md +17 -12
- package/guides/mix.md +12 -6
- package/guides/music.md +23 -8
- package/guides/notes.md +15 -9
- package/guides/performance.md +15 -13
- package/guides/play.md +21 -13
- package/guides/project.md +25 -8
- package/guides/providers.md +20 -14
- package/guides/resample.md +15 -9
- package/guides/rhythm.md +17 -13
- package/guides/sessions.md +17 -7
- package/guides/show-me.md +31 -0
- package/guides/sound.md +25 -9
- package/guides/sounds.md +15 -13
- package/guides/styles.md +31 -0
- package/guides/tempo.md +15 -10
- package/guides/tracks.md +15 -10
- package/guides/tuning.md +32 -0
- package/guides/voice.md +31 -0
- package/guides/web-search.md +18 -8
- package/native/prebuilt/darwin-arm64/libdawg_sink.dylib +0 -0
- package/native/prebuilt/darwin-x64/libdawg_sink.dylib +0 -0
- package/native/prebuilt/linux-arm64/libdawg_sink.so +0 -0
- package/native/prebuilt/linux-x64/libdawg_sink.so +0 -0
- package/native/prebuilt/manifest.json +21 -0
- package/package.json +5 -2
- package/src/agent/agent.ts +126 -14
- package/src/agent/calibration-tools.ts +53 -0
- package/src/agent/clip-tools.ts +453 -0
- package/src/agent/command-agent.ts +369 -0
- package/src/agent/drum-tools.ts +2 -2
- package/src/agent/expression-tools.ts +1 -1
- package/src/agent/gateway.ts +246 -60
- package/src/agent/models.ts +53 -12
- package/src/agent/ops.ts +12 -1
- package/src/agent/pack-tools.ts +1 -1
- package/src/agent/planner.ts +13 -0
- package/src/agent/portable-schema.ts +80 -0
- package/src/agent/preview-tool.ts +4 -1
- package/src/agent/provider.ts +22 -8
- package/src/agent/rhythm-tools.ts +1 -1
- package/src/agent/section-tools.ts +1 -1
- package/src/agent/show-me.ts +497 -0
- package/src/agent/steer.ts +15 -0
- package/src/agent/style-tools.ts +217 -0
- package/src/agent/tool-error.ts +12 -0
- package/src/agent/tools.ts +108 -23
- package/src/agent/usage.ts +2 -2
- package/src/agent/voice-tools.ts +925 -0
- package/src/agent/xcb-agent.ts +11 -7
- package/src/argv.ts +38 -0
- package/src/audio/analysis.ts +253 -0
- package/src/audio/arrange.ts +37 -3
- package/src/audio/autotune-engine.ts +101 -0
- package/src/audio/autotune.ts +640 -0
- package/src/audio/clips.ts +240 -0
- package/src/audio/doctor.ts +86 -0
- package/src/audio/dsp/bandbank.ts +138 -0
- package/src/audio/dsp/envelope.ts +10 -0
- package/src/audio/dsp/follow.ts +120 -0
- package/src/audio/dsp/formant.ts +427 -0
- package/src/audio/dsp/glottal.ts +243 -0
- package/src/audio/dsp/interp.ts +7 -2
- package/src/audio/dsp/lpc.ts +50 -0
- package/src/audio/dsp/periodicity.ts +59 -0
- package/src/audio/dsp/pitch.ts +995 -0
- package/src/audio/dsp/psola.ts +199 -0
- package/src/audio/effects/chain.ts +3 -1
- package/src/audio/effects/common.ts +43 -0
- package/src/audio/effects/convolution.ts +7 -4
- package/src/audio/effects/filter.ts +48 -69
- package/src/audio/effects/formant.ts +263 -0
- package/src/audio/engine.ts +160 -28
- package/src/audio/fit.ts +35 -3
- package/src/audio/instrument-check.ts +59 -43
- package/src/audio/instruments.ts +4 -0
- package/src/audio/keys/calibration.ts +56 -0
- package/src/audio/keys/electric.ts +8 -1
- package/src/audio/keys/engine.ts +13 -1
- package/src/audio/keys/piano.ts +22 -2
- package/src/audio/kits.ts +135 -6
- package/src/audio/live.ts +114 -20
- package/src/audio/native.ts +615 -0
- package/src/audio/preview.ts +30 -2
- package/src/audio/render-worker.ts +2 -0
- package/src/audio/renderer.ts +2 -0
- package/src/audio/resample.ts +2 -1
- package/src/audio/sampler.ts +85 -4
- package/src/audio/samples.ts +20 -2
- package/src/audio/sing/analysis.ts +193 -0
- package/src/audio/sing/engine.ts +949 -0
- package/src/audio/strings/bow.ts +48 -5
- package/src/audio/strings/engine.ts +8 -2
- package/src/audio/synth/oscillators.ts +31 -21
- package/src/audio/synth/voice.ts +34 -1
- package/src/audio/vocoder/bank.ts +314 -0
- package/src/audio/vocoder/carrier.ts +165 -0
- package/src/audio/vocoder/control.ts +68 -0
- package/src/audio/vocoder/detect.ts +50 -0
- package/src/audio/vocoder/index.ts +304 -0
- package/src/audio/vocoder/talkbox.ts +143 -0
- package/src/audio/wav.ts +500 -77
- package/src/audio/winds/engine.ts +5 -1
- package/src/audio/winds/trim.ts +28 -4
- package/src/audio/winds/trims1.ts +297 -0
- package/src/audio/winds/voice.ts +15 -2
- package/src/auth/cli.ts +38 -36
- package/src/auth/credentials.ts +30 -1
- package/src/auth/login.ts +15 -9
- package/src/auth/tui.ts +19 -10
- package/src/commands/arrange.ts +44 -29
- package/src/commands/autotune.ts +421 -0
- package/src/commands/calibration.ts +74 -0
- package/src/commands/clips.ts +887 -0
- package/src/commands/drums.ts +3 -2
- package/src/commands/edit.ts +11 -4
- package/src/commands/expression.ts +1 -1
- package/src/commands/formant.ts +221 -0
- package/src/commands/fx.ts +101 -32
- package/src/commands/grammar.ts +558 -0
- package/src/commands/help.ts +610 -380
- package/src/commands/history.ts +18 -0
- package/src/commands/keys.ts +8 -8
- package/src/commands/modal.ts +1 -1
- package/src/commands/music.ts +1 -1
- package/src/commands/nearest.ts +53 -0
- package/src/commands/pack.ts +9 -2
- package/src/commands/param-range.ts +56 -0
- package/src/commands/parses.ts +130 -0
- package/src/commands/progression.ts +170 -0
- package/src/commands/rhythm.ts +3 -0
- package/src/commands/rig.ts +3 -24
- package/src/commands/sing.ts +478 -0
- package/src/commands/strum.ts +13 -1
- package/src/commands/style.ts +415 -0
- package/src/commands/time.ts +6 -3
- package/src/commands/tuning.ts +3 -3
- package/src/commands/vocal-pitch.ts +616 -0
- package/src/commands/vocal.ts +147 -0
- package/src/commands/vocoder.ts +627 -0
- package/src/commands/wind.ts +2 -2
- package/src/fs/durable.ts +50 -0
- package/src/lang/glossary.ts +493 -0
- package/src/launch-args.ts +163 -0
- package/src/main.ts +1397 -253
- package/src/media/cli.ts +20 -3
- package/src/media/import.ts +3 -1
- package/src/project/check.ts +21 -2
- package/src/project/clip-pins.ts +72 -0
- package/src/project/init.ts +23 -8
- package/src/project/sync.ts +418 -86
- package/src/render.ts +20 -1
- package/src/session/daemon.ts +3 -0
- package/src/session/meta.ts +14 -0
- package/src/session/origin.ts +154 -0
- package/src/session/port.ts +22 -4
- package/src/session/presence.ts +34 -4
- package/src/session/protocol.ts +5 -1
- package/src/session/rebase.ts +18 -5
- package/src/session/receipt.ts +258 -0
- package/src/session/store.ts +65 -43
- package/src/tui/arrange-menu.ts +65 -31
- package/src/tui/audition.ts +1 -1
- package/src/tui/euclid.ts +18 -13
- package/src/tui/fader.ts +228 -41
- package/src/tui/granular-menu.ts +2 -4
- package/src/tui/menu-clips.ts +297 -0
- package/src/tui/menu-time.ts +20 -13
- package/src/tui/menu-voice.ts +405 -0
- package/src/tui/menu.ts +759 -177
- package/src/tui/modal-menu.ts +6 -6
- package/src/tui/performance-menu.ts +5 -2
- package/src/tui/play-chords.ts +4 -2
- package/src/tui/play-mode.ts +15 -1
- package/src/tui/play-session.ts +47 -7
- package/src/tui/sing-menu.ts +278 -0
- package/src/tui/style-menu.ts +104 -0
- package/src/tui/vocoder-menu.ts +244 -0
- package/src/tui/wind-menu.ts +3 -3
- package/src/version.ts +8 -0
- package/src/web/fetch.ts +115 -29
- package/tui/activity.ts +180 -9
- package/tui/app.ts +274 -40
- package/tui/clip-row.ts +132 -0
- package/tui/delight.ts +144 -0
- package/tui/drawer.ts +70 -22
- package/tui/frame-gate.ts +76 -0
- package/tui/grammar.ts +112 -63
- package/tui/guide.ts +42 -4
- package/tui/highway.ts +269 -25
- package/tui/hints.ts +192 -0
- package/tui/input.ts +60 -9
- package/tui/keys.ts +1 -1
- package/tui/play-strip.ts +68 -14
- package/tui/prompt.ts +1 -1
- package/tui/screen.ts +144 -14
- package/tui/theme.ts +27 -0
package/src/commands/help.ts
CHANGED
|
@@ -1,13 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The command reference, in one place: `/help` renders it in the overlay,
|
|
3
|
-
* `dawg --help` prints it, and the docs mirror it.
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* `dawg --help` prints it, and the docs mirror it. Groups are the ten topic
|
|
4
|
+
* ids (src/lang/glossary.ts), the same ids `/guide` and `/menu` take.
|
|
5
|
+
* A topic may have sub-groups (`sound · performance`) so no group runs
|
|
6
|
+
* past about fifteen rows. Commands are written bare, since the slash is
|
|
7
|
+
* optional; only the free-text window verbs (`/rename`, `/fork`,
|
|
8
|
+
* `/resume`), `/auth`, `/bpm` (a sample's tempo; bare `bpm` is song tempo)
|
|
9
|
+
* and `/play` (play mode; bare `play` is the transport) keep it.
|
|
6
10
|
*/
|
|
7
11
|
import { GUITAR_TUNING_NAMES } from "../../core/chords.ts";
|
|
12
|
+
import { KEYS, type KeyScreen } from "../../tui/grammar.ts";
|
|
13
|
+
import { displayWidth, truncate } from "../../tui/text.ts";
|
|
14
|
+
import {
|
|
15
|
+
resolveTopic,
|
|
16
|
+
topicMiss,
|
|
17
|
+
TOPIC_SUMMARY,
|
|
18
|
+
TOPICS,
|
|
19
|
+
type TopicId,
|
|
20
|
+
} from "../lang/glossary.ts";
|
|
8
21
|
import { EXPRESSION_USAGE } from "./expression.ts";
|
|
22
|
+
import { editDistance } from "./nearest.ts";
|
|
23
|
+
import { VOCAL_VERBS } from "./vocal.ts";
|
|
9
24
|
|
|
10
|
-
|
|
25
|
+
/** A topic id, or a heading of the start page. */
|
|
26
|
+
export type HelpGroup = string;
|
|
11
27
|
|
|
12
28
|
export type HelpEntry = Readonly<{
|
|
13
29
|
/** Canonical form, e.g. `pan <-1..1>`. */
|
|
@@ -23,29 +39,16 @@ export type HelpSection = Readonly<{
|
|
|
23
39
|
|
|
24
40
|
export const HELP_SECTIONS: readonly HelpSection[] = [
|
|
25
41
|
{
|
|
26
|
-
group: "
|
|
42
|
+
group: "sound",
|
|
27
43
|
entries: [
|
|
28
|
-
{ command: "play", summary: "start the transport" },
|
|
29
|
-
{ command: "pause", summary: "stop the transport" },
|
|
30
|
-
{ command: "tempo <bpm>", summary: "20–300 BPM" },
|
|
31
|
-
{ command: "add <note> at <beat> [for <beats>]", summary: "add C4 at 0" },
|
|
32
|
-
{ command: "remove <id>", summary: "delete a note" },
|
|
33
|
-
{ command: "move <id> to <beat>", summary: "shift a note" },
|
|
34
|
-
{ command: "length <id> <beats>", summary: "resize a note" },
|
|
35
|
-
{ command: "velocity <id> <0..1>", summary: "note loudness" },
|
|
36
|
-
{ command: "bars <count>", summary: "loop length, 1–256" },
|
|
37
|
-
{ command: "extend <count> bars", summary: "lengthen the loop" },
|
|
38
44
|
{
|
|
39
45
|
command: "instrument <name>",
|
|
40
|
-
summary: "sine piano pluck bass
|
|
46
|
+
summary: "sine piano pluck bass sawtooth square triangle wavetable kit",
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
command: "synth <param> <value> | preset <name>",
|
|
50
|
+
summary: "the synth · synth lpf 1200 · synth alone lists every param",
|
|
41
51
|
},
|
|
42
|
-
{ command: "volume <0..1>", summary: "track level" },
|
|
43
|
-
{ command: "pan <-1..1>", summary: "left … right" },
|
|
44
|
-
{ command: "mute", summary: "silence this track" },
|
|
45
|
-
{ command: "unmute", summary: "hear it again" },
|
|
46
|
-
{ command: "solo", summary: "only this track" },
|
|
47
|
-
{ command: "unsolo", summary: "every track again" },
|
|
48
|
-
{ command: "clear", summary: "remove this track's notes" },
|
|
49
52
|
{
|
|
50
53
|
command: "wt <table> | wt <0..1> | wt list",
|
|
51
54
|
summary: "wavetable synth · wt basic · wt wt_digital:2 · wt 0.5",
|
|
@@ -56,92 +59,68 @@ export const HELP_SECTIONS: readonly HelpSection[] = [
|
|
|
56
59
|
},
|
|
57
60
|
{
|
|
58
61
|
command:
|
|
59
|
-
"grain <preset> | <param> <value> | on [voice V] | src synth:<name>|voice V
|
|
62
|
+
"grain <preset> | <param> <value> | on [voice V] | src synth:<name>|voice V",
|
|
60
63
|
summary:
|
|
61
64
|
"granular cloud · grain cloud · grain scan 0.2 · track swarm-2 · grain presets",
|
|
62
65
|
},
|
|
63
66
|
{
|
|
64
|
-
command: "
|
|
65
|
-
summary: "
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
command: "synth <param> <value> | preset <name>",
|
|
69
|
-
summary: "synth voice · synth lpf 1200 · synth lists every param",
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
command: "string <preset> | <param> <value> | presets | reset | off",
|
|
73
|
-
summary:
|
|
74
|
-
"plucked strings · string sitar · string buzz 0.8 · string ring 6",
|
|
67
|
+
command: "piano [<preset>] | grand | upright | felt | honkytonk",
|
|
68
|
+
summary: "modeled piano · piano ballad · piano lofi · piano prepared",
|
|
75
69
|
},
|
|
76
70
|
{
|
|
77
|
-
command: "
|
|
78
|
-
summary:
|
|
79
|
-
"bowed strings · bowed violin · bowed cellos · bowed pressure 0.7 · bowed sord 1",
|
|
71
|
+
command: "keys <param> <value> | preset <name> | reset",
|
|
72
|
+
summary: "piano params · keys hardness 0.3 decay 1.5 · keys lists them",
|
|
80
73
|
},
|
|
81
74
|
{
|
|
82
|
-
command: "
|
|
75
|
+
command: "epiano|wurli|clav [preset <name>] | <param> <value>",
|
|
83
76
|
summary:
|
|
84
|
-
"
|
|
77
|
+
"electric keys · suitcase dyno funkclav · epiano vibe 0.6 · clav pickup bridge",
|
|
85
78
|
},
|
|
86
79
|
{
|
|
87
80
|
command:
|
|
88
|
-
"
|
|
89
|
-
summary: "guitar fretting · guitar tune dadgad · guitar capo 2",
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
command: "strum [chords] [pattern] [strokes D-DU-UDU] [speed 22ms]",
|
|
81
|
+
"tonewheel [<drawbars>] | combo [<tabs>] [<register>] | pipe [<stops>]",
|
|
93
82
|
summary:
|
|
94
|
-
"
|
|
83
|
+
"organs · tonewheel 888800008 perc 3rd · gospel · combo 08880 flute · pipe plenum · keys perc 3rd",
|
|
95
84
|
},
|
|
96
85
|
{
|
|
97
|
-
command: "
|
|
86
|
+
command: "rotary slow|fast|stop [at <beat>]",
|
|
98
87
|
summary:
|
|
99
|
-
"
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
command: "track jangle|punk|funk|ragged|gtr-lead|gtr-metal|bachata",
|
|
103
|
-
summary: "new guitar track with that rig",
|
|
88
|
+
"organ rotary speaker speed · rotary fast at 16 writes the keys-rotary lane",
|
|
104
89
|
},
|
|
105
90
|
{
|
|
106
|
-
command: "
|
|
91
|
+
command: "string <preset> | <param> <value> | presets | reset | off",
|
|
107
92
|
summary:
|
|
108
|
-
"
|
|
109
|
-
},
|
|
110
|
-
{
|
|
111
|
-
command: "piano [<preset>] | grand | upright | felt | honkytonk",
|
|
112
|
-
summary: "modelled piano · piano ballad · piano lofi · piano prepared",
|
|
113
|
-
},
|
|
114
|
-
{
|
|
115
|
-
command: "keys <param> <value> | preset <name> | reset",
|
|
116
|
-
summary: "piano params · keys hardness 0.3 decay 1.5 · keys lists them",
|
|
93
|
+
"plucked strings · string sitar · string buzz 0.8 · string ring 6",
|
|
117
94
|
},
|
|
118
95
|
{
|
|
119
|
-
command: "
|
|
96
|
+
command: "bowed [<preset>] | <param> <value> | presets",
|
|
120
97
|
summary:
|
|
121
|
-
"
|
|
98
|
+
"bowed strings · bowed violin · bowed cellos · bowed pressure 0.7 · bowed sord 1",
|
|
122
99
|
},
|
|
123
100
|
{
|
|
124
101
|
command:
|
|
125
|
-
"
|
|
126
|
-
summary:
|
|
127
|
-
"organs · tonewheel 888800008 perc 3rd · gospel · combo 08880 flute · pipe plenum · keys perc 3rd",
|
|
128
|
-
},
|
|
129
|
-
{
|
|
130
|
-
command: "rotary slow|fast|stop [at <beat>]",
|
|
131
|
-
summary:
|
|
132
|
-
"organ rotary speaker speed · rotary fast at 16 writes the keys-rotary lane",
|
|
102
|
+
"guitar tune <name|notes> | capo | hand | ring | position | reset",
|
|
103
|
+
summary: "guitar fretting · guitar tune dadgad · guitar capo 2",
|
|
133
104
|
},
|
|
134
105
|
{
|
|
135
106
|
command:
|
|
136
107
|
"modal <preset> | <param> <value> | mallet <name> | reset | off",
|
|
137
|
-
summary:
|
|
138
|
-
"mallets and bells · modal vibes · modal hardness 0.8 · modal presets",
|
|
108
|
+
summary: "mallets · modal vibes · modal hardness 0.8 · modal presets",
|
|
139
109
|
},
|
|
140
110
|
{
|
|
141
111
|
command: "wind <preset> | <param> <value> | mute <name> | reset | off",
|
|
142
112
|
summary:
|
|
143
113
|
"winds and brass · wind flute · wind sax · wind players 4 · wind presets",
|
|
144
114
|
},
|
|
115
|
+
{
|
|
116
|
+
command: "/try <sound command>",
|
|
117
|
+
summary: "hear it on a loop first · a A/B · enter keep",
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
group: "sound · performance",
|
|
123
|
+
entries: [
|
|
145
124
|
{
|
|
146
125
|
command: "art <articulation>|off [target]",
|
|
147
126
|
summary:
|
|
@@ -179,375 +158,511 @@ export const HELP_SECTIONS: readonly HelpSection[] = [
|
|
|
179
158
|
"seeded feel at render · humanize 10 8 5 · humanize 20 bars 2-3",
|
|
180
159
|
},
|
|
181
160
|
{ command: "expression", summary: "this track's performance settings" },
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
161
|
+
],
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
group: "sound · samples",
|
|
165
|
+
entries: [
|
|
185
166
|
{
|
|
186
|
-
command: "
|
|
187
|
-
summary: "
|
|
167
|
+
command: "/sample [<path> [as <sample>]]",
|
|
168
|
+
summary: "add a sample · alone lists the samples",
|
|
188
169
|
},
|
|
189
170
|
{
|
|
190
|
-
command: "
|
|
191
|
-
summary: "
|
|
171
|
+
command: "/bpm <n> [<sample>]",
|
|
172
|
+
summary: "a sample's own tempo (the slash matters: bare bpm is tempo)",
|
|
192
173
|
},
|
|
193
174
|
{
|
|
194
|
-
command: "
|
|
195
|
-
summary: "
|
|
175
|
+
command: "fitmode [repitch|beats|tones|auto] [<sample>]",
|
|
176
|
+
summary: "how it fits · alone suggests one from the sound",
|
|
196
177
|
},
|
|
197
|
-
{ command: "clear [<lane>] automation", summary: "drop a lane's points" },
|
|
198
178
|
{
|
|
199
|
-
command: "
|
|
200
|
-
summary:
|
|
201
|
-
"song master · eq glue tape width limiter · master glue ratio 4",
|
|
179
|
+
command: "len <beats> [<sample>]",
|
|
180
|
+
summary: "the sample lasts n beats of the song",
|
|
202
181
|
},
|
|
203
182
|
{
|
|
204
|
-
command: "
|
|
183
|
+
command: "shift <semitones> [formant keep|follow|<n>] [<sample>]",
|
|
205
184
|
summary:
|
|
206
|
-
"
|
|
185
|
+
"a sample's pitch without changing its length · shift 0 clears",
|
|
207
186
|
},
|
|
208
|
-
{ command: "track name <text>", summary: "rename this track" },
|
|
209
|
-
{ command: "meter <1..16>", summary: "beats per bar" },
|
|
210
187
|
{
|
|
211
|
-
command: "
|
|
212
|
-
summary: "
|
|
188
|
+
command: "fade [in|out] <seconds> [<sample>]",
|
|
189
|
+
summary: "a sample's fade in and out (Strudel fadeInTime/fadeTime)",
|
|
213
190
|
},
|
|
214
191
|
{
|
|
215
|
-
command:
|
|
216
|
-
|
|
192
|
+
command:
|
|
193
|
+
"resample <track>|orbit <n>|master [section <name>|bars a-b] [grain]",
|
|
194
|
+
summary: "render to a pinned WAV on a new sampler (or granular) track",
|
|
217
195
|
},
|
|
196
|
+
],
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
group: "voice",
|
|
200
|
+
entries: [
|
|
218
201
|
{
|
|
219
|
-
command: "
|
|
220
|
-
summary:
|
|
202
|
+
command: "sing [preset] [param value]",
|
|
203
|
+
summary:
|
|
204
|
+
"built-in singing voice: aah ooh choir chorale airy glass lament soprano basso, throat: drone khoomei sygyt kargyraa",
|
|
221
205
|
},
|
|
222
206
|
{
|
|
223
|
-
command: "
|
|
224
|
-
summary:
|
|
207
|
+
command: "sing vowels <v> …",
|
|
208
|
+
summary:
|
|
209
|
+
"vowels for the track's notes in order, cycled · sing vowels a e i o",
|
|
225
210
|
},
|
|
226
211
|
{
|
|
227
|
-
command: "
|
|
228
|
-
summary: "
|
|
212
|
+
command: "note vowel <v|a>u> [target]",
|
|
213
|
+
summary: "sung vowel for selected notes · note vowel o bar 2",
|
|
229
214
|
},
|
|
230
215
|
{
|
|
231
|
-
command: "
|
|
232
|
-
summary: "
|
|
216
|
+
command: "lyrics [bar] sun-lit morn-ing",
|
|
217
|
+
summary: "syllables onto the notes (- splits, _ holds, ~ skips)",
|
|
233
218
|
},
|
|
234
219
|
{
|
|
235
|
-
command: "
|
|
236
|
-
summary:
|
|
220
|
+
command: "autotune [preset] [field value …] | off | presets",
|
|
221
|
+
summary:
|
|
222
|
+
"pitch correction · autotune hard · autotune gentle · autotune to notes melody · autotune key D bayati",
|
|
237
223
|
},
|
|
238
224
|
{
|
|
239
|
-
command:
|
|
240
|
-
|
|
225
|
+
command:
|
|
226
|
+
"/formant <-12..12> [mix] | deep | giant | bright | tiny | on | off",
|
|
227
|
+
summary:
|
|
228
|
+
"formant shift at constant pitch · /formant -4 deeper · /formant 3 smaller",
|
|
241
229
|
},
|
|
242
230
|
{
|
|
243
|
-
command:
|
|
244
|
-
|
|
231
|
+
command:
|
|
232
|
+
"/vowel <v> [<to> [<morph>]] | ee | to <v>|off | morph <0..1> | off",
|
|
233
|
+
summary: "vowel filter · /vowel a · /vowel a o 0.5 morphs a toward o",
|
|
245
234
|
},
|
|
246
235
|
{
|
|
247
|
-
command:
|
|
248
|
-
|
|
236
|
+
command:
|
|
237
|
+
"vocoder [preset] | src <track> | <param> <value|reset> | off | presets",
|
|
238
|
+
summary:
|
|
239
|
+
"vocode the focused voice onto a synth (classic robot talkbox choir glass whisper smear lofi) · help vocoder lists params",
|
|
249
240
|
},
|
|
241
|
+
],
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
group: "voice · clips",
|
|
245
|
+
entries: [
|
|
246
|
+
{ command: "vocal", summary: "voice tools: lists every verb" },
|
|
250
247
|
{
|
|
251
|
-
command:
|
|
252
|
-
|
|
248
|
+
command:
|
|
249
|
+
"clip [id] gain -3 | gain by -3 | fade .01 .2 | fade in .01 | split 7",
|
|
250
|
+
summary: "edit an audio clip (dB, seconds, 1-based bars)",
|
|
253
251
|
},
|
|
254
252
|
{
|
|
255
|
-
command:
|
|
256
|
-
|
|
253
|
+
command:
|
|
254
|
+
"clip [id] trim offset 1 dur 4|end | rev | repeat 2 [to 32] | mute | rm",
|
|
255
|
+
summary: "trim, reverse, repeat, mute or remove a clip",
|
|
257
256
|
},
|
|
257
|
+
...VOCAL_VERBS.map((verb) => ({
|
|
258
|
+
command: `vocal ${verb.usage}`,
|
|
259
|
+
summary: verb.summary,
|
|
260
|
+
})),
|
|
261
|
+
],
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
group: "effects",
|
|
265
|
+
entries: [
|
|
258
266
|
{
|
|
259
|
-
command: "
|
|
260
|
-
summary:
|
|
267
|
+
command: "fx <effect> <param> <value> | on | off | preset <name>",
|
|
268
|
+
summary:
|
|
269
|
+
"effects · fx delay mix 0.3 · fx reverb on · fx alone lists them",
|
|
261
270
|
},
|
|
262
271
|
{
|
|
263
|
-
command: "
|
|
264
|
-
summary:
|
|
272
|
+
command: "filter <hz> [res] · delay <beats> [fb] [mix] · reverb <mix>",
|
|
273
|
+
summary:
|
|
274
|
+
"shortcuts for fx filter, delay and reverb · filter 1200 · delay 0.5 · reverb 0.3",
|
|
265
275
|
},
|
|
266
276
|
{
|
|
267
|
-
command: "
|
|
268
|
-
summary:
|
|
277
|
+
command: "rig <preset> | reset",
|
|
278
|
+
summary:
|
|
279
|
+
"guitar amp and pedals · rig crunch · rig jangle · rig metal · rig alone lists them",
|
|
269
280
|
},
|
|
270
281
|
{
|
|
271
|
-
command: "
|
|
272
|
-
summary:
|
|
282
|
+
command: "stomp|head|cab <type> | <param> <value>",
|
|
283
|
+
summary:
|
|
284
|
+
"pedal, amp, cabinet · stomp rat · head gain 7 gate -55 · cab 4x12",
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
command: "fx wobble|bloom|swell|double [param value]",
|
|
288
|
+
summary:
|
|
289
|
+
"shoegaze · fx wobble depth 30 · fx double · fx reverb ir builtin:reverse",
|
|
273
290
|
},
|
|
291
|
+
],
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
group: "rhythm",
|
|
295
|
+
entries: [
|
|
274
296
|
{
|
|
275
|
-
command: "hit <
|
|
276
|
-
summary: "kit
|
|
297
|
+
command: "hit <drum> at <beat>",
|
|
298
|
+
summary: "one hit on a kit track · hit kick at 0",
|
|
277
299
|
},
|
|
278
300
|
{
|
|
279
|
-
command: "pattern <
|
|
280
|
-
summary: "pattern kick every 1",
|
|
301
|
+
command: "pattern <drum> <beats...> | every <step>",
|
|
302
|
+
summary: "a drum on beats · pattern kick every 1",
|
|
281
303
|
},
|
|
282
|
-
{ command: "clear <
|
|
304
|
+
{ command: "clear <drum>", summary: "remove one drum's hits" },
|
|
283
305
|
{
|
|
284
|
-
command: "euclid <
|
|
306
|
+
command: "euclid <drum> <pulses> [<steps>] [rotate <n>]",
|
|
285
307
|
summary: "generated rhythm · euclid hat 7 16 rotate 2",
|
|
286
308
|
},
|
|
287
309
|
{
|
|
288
|
-
command: "euclid <
|
|
310
|
+
command: "euclid <drum> <field> <value> | off | freeze",
|
|
289
311
|
summary: "repeats pace accent prob swing …",
|
|
290
312
|
},
|
|
291
|
-
{ command: "grid <
|
|
313
|
+
{ command: "grid <drum> <x.X.>", summary: "explicit steps · X accent" },
|
|
292
314
|
{
|
|
293
|
-
command: "
|
|
294
|
-
summary:
|
|
295
|
-
"a sample's pitch without changing its length · shift 0 clears",
|
|
315
|
+
command: "pattern [name]",
|
|
316
|
+
summary: "groove picker · hear each groove move",
|
|
296
317
|
},
|
|
297
318
|
{
|
|
298
|
-
command: "
|
|
299
|
-
summary: "
|
|
319
|
+
command: "/kit [name]",
|
|
320
|
+
summary: "kit picker · synth kits, then sample kits",
|
|
300
321
|
},
|
|
301
322
|
{
|
|
302
|
-
command:
|
|
303
|
-
|
|
304
|
-
|
|
323
|
+
command: "pack list|info|use|add",
|
|
324
|
+
summary: "sample packs · pack use 909/bd",
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
command: "euclid [drum]",
|
|
328
|
+
summary: "euclid rhythm editor · Ctrl-K › Rhythm",
|
|
305
329
|
},
|
|
306
|
-
{ command: "undo", summary: "step back · Ctrl-Z" },
|
|
307
|
-
{ command: "redo", summary: "step forward · Ctrl-Y" },
|
|
308
330
|
],
|
|
309
331
|
},
|
|
310
332
|
{
|
|
311
|
-
group: "
|
|
333
|
+
group: "chords",
|
|
312
334
|
entries: [
|
|
313
|
-
{ command: "/sessions", summary: "list sessions in this workspace" },
|
|
314
|
-
{ command: "/resume [<n>|<name>|<id>]", summary: "switch session" },
|
|
315
|
-
{ command: "/rename <name>|--auto", summary: "name this session" },
|
|
316
|
-
{ command: "/fork [<name>]", summary: "copy into a new session" },
|
|
317
|
-
{ command: "/status", summary: "name · revision · digest · storage" },
|
|
318
335
|
{
|
|
319
|
-
command: "
|
|
320
|
-
summary: "
|
|
336
|
+
command: "key <tonic> <mode> | none",
|
|
337
|
+
summary: "song key · key A minor · key F# dorian",
|
|
321
338
|
},
|
|
322
|
-
{ command: "/import <file>", summary: "replace the score from a file" },
|
|
323
339
|
{
|
|
324
|
-
command: "
|
|
325
|
-
summary: "
|
|
340
|
+
command: "scale [<tonic>] <name> | list",
|
|
341
|
+
summary: "song scale · scale D hijaz · scale yaman · scale list",
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
command: "tuning <name> | edo <n> | scl <file> | off",
|
|
345
|
+
summary: "song tuning · tuning 19-edo · tuning just · tuning list",
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
command: "tuning ref <hz> | root <note> | map linear|nearest",
|
|
349
|
+
summary: "A4 reference · degree-0 key · keys per step",
|
|
326
350
|
},
|
|
327
351
|
{
|
|
328
|
-
command: "
|
|
352
|
+
command: "tuning track <…> | track off",
|
|
353
|
+
summary: "this track's tuning · off follows the song",
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
command: "cents <id> <±c>",
|
|
357
|
+
summary: "detune one note · cents n3 -14 · add E4-14c at 0",
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
command: "progression <chords> [each 4] [at 0] [bass]",
|
|
329
361
|
summary:
|
|
330
|
-
"
|
|
362
|
+
"sustained voice-led chords · progression i7 IV7 each 8 · progression Am7 D9 bass",
|
|
331
363
|
},
|
|
332
364
|
{
|
|
333
|
-
command: "
|
|
334
|
-
summary:
|
|
365
|
+
command: "strum [chords] [pattern] [strokes D-DU-UDU] [speed 22ms]",
|
|
366
|
+
summary:
|
|
367
|
+
"strummed guitar chords · strum G D Em C folk · strum I V vi IV · strum alone strums the track's chords",
|
|
335
368
|
},
|
|
336
369
|
{
|
|
337
|
-
command: "/
|
|
338
|
-
summary: "
|
|
370
|
+
command: "/chords auto|manual|off",
|
|
371
|
+
summary: "chords in play mode · /chords alone shows settings",
|
|
372
|
+
},
|
|
373
|
+
{
|
|
374
|
+
command: "play degrees|in-key|chromatic",
|
|
375
|
+
summary:
|
|
376
|
+
"in play mode the home row plays the key's degrees (any tuning)",
|
|
339
377
|
},
|
|
340
378
|
],
|
|
341
379
|
},
|
|
342
380
|
{
|
|
343
|
-
group: "
|
|
381
|
+
group: "mix",
|
|
344
382
|
entries: [
|
|
383
|
+
{ command: "volume <0..1>", summary: "track level" },
|
|
384
|
+
{ command: "pan <-1..1>", summary: "left … right" },
|
|
385
|
+
{ command: "mute", summary: "silence this track" },
|
|
386
|
+
{ command: "unmute", summary: "hear it again" },
|
|
387
|
+
{ command: "solo", summary: "only this track" },
|
|
388
|
+
{ command: "unsolo", summary: "every track again" },
|
|
345
389
|
{
|
|
346
|
-
command: "
|
|
347
|
-
summary: "
|
|
348
|
-
},
|
|
349
|
-
{ command: "/tracks", summary: "list tracks" },
|
|
350
|
-
{ command: "/view focus|all", summary: "one track or every track" },
|
|
351
|
-
{ command: "/transcript", summary: "scrollable log · Ctrl-O" },
|
|
352
|
-
{ command: "/theme default|high-contrast|mono", summary: "colors" },
|
|
353
|
-
{ command: "/motion on|off", summary: "animation" },
|
|
354
|
-
{ command: "/model [alias]", summary: "pick a model · cost per prompt" },
|
|
355
|
-
{
|
|
356
|
-
command: "/login [gateway|openrouter|codex|claude]",
|
|
357
|
-
summary: "sign in to a provider",
|
|
390
|
+
command: "automate <lane> at <beat> <value>",
|
|
391
|
+
summary: "volume pan filter resonance delay-feedback delay-mix wt",
|
|
358
392
|
},
|
|
359
393
|
{
|
|
360
|
-
command: "
|
|
361
|
-
summary: "
|
|
394
|
+
command: "automate <lane> points <b:v>...",
|
|
395
|
+
summary: "several points · automate pan points 0:-1 4:1",
|
|
362
396
|
},
|
|
363
|
-
{ command: "
|
|
364
|
-
{ command: "
|
|
397
|
+
{ command: "automate <lane> remove <beat>", summary: "drop one point" },
|
|
398
|
+
{ command: "clear [<lane>] automation", summary: "drop a lane's points" },
|
|
365
399
|
{
|
|
366
|
-
command: "
|
|
367
|
-
summary:
|
|
400
|
+
command: "master <unit> on|off | preset <name> | <param> <value>",
|
|
401
|
+
summary:
|
|
402
|
+
"song master · eq glue tape width limiter · master glue ratio 4",
|
|
368
403
|
},
|
|
369
404
|
{
|
|
370
|
-
command: "
|
|
371
|
-
summary:
|
|
405
|
+
command: "master <target> | target <lufs> | measure | off",
|
|
406
|
+
summary:
|
|
407
|
+
"loudness · master streaming · master target -9 · master measure",
|
|
372
408
|
},
|
|
409
|
+
],
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
group: "arrange",
|
|
413
|
+
entries: [
|
|
414
|
+
{ command: "add <note> at <beat> [for <beats>]", summary: "add C4 at 0" },
|
|
415
|
+
{ command: "remove <id>", summary: "delete a note" },
|
|
416
|
+
{ command: "move <id> to <beat>", summary: "shift a note" },
|
|
417
|
+
{ command: "length <id> <beats>", summary: "resize a note" },
|
|
418
|
+
{ command: "velocity <id> <0..1>", summary: "note loudness" },
|
|
419
|
+
{ command: "clear", summary: "remove this track's notes" },
|
|
420
|
+
{ command: "track <name>", summary: "focus a track, creating it if new" },
|
|
421
|
+
{ command: "tracks", summary: "list tracks" },
|
|
373
422
|
{
|
|
374
|
-
command: "
|
|
375
|
-
summary:
|
|
423
|
+
command: "track rm|move <name> [<position>]",
|
|
424
|
+
summary:
|
|
425
|
+
"remove a track (Ctrl-Z undoes) or move it · rm and delete are aliases",
|
|
376
426
|
},
|
|
427
|
+
{ command: "track name <text>", summary: "rename this track" },
|
|
377
428
|
{
|
|
378
|
-
command: "
|
|
379
|
-
summary: "
|
|
429
|
+
command: "track rate|phase|loop <n> | off",
|
|
430
|
+
summary: "polytempo · track rate 3/2 · track loop 3",
|
|
380
431
|
},
|
|
381
432
|
{
|
|
382
|
-
command: "
|
|
383
|
-
summary: "
|
|
433
|
+
command: "track phasing <beats> [over <beats>|hold <n>]",
|
|
434
|
+
summary: "Reich phasing · track phasing 3 hold 8 · track time off",
|
|
384
435
|
},
|
|
436
|
+
],
|
|
437
|
+
},
|
|
438
|
+
{
|
|
439
|
+
group: "arrange · song",
|
|
440
|
+
entries: [
|
|
385
441
|
{
|
|
386
|
-
command: "
|
|
387
|
-
summary:
|
|
442
|
+
command: "section <name> <a>-<b> | add | dup | move | rename | remove",
|
|
443
|
+
summary:
|
|
444
|
+
"song sections · section chorus 9-16 · section alone lists them",
|
|
388
445
|
},
|
|
389
446
|
{
|
|
390
|
-
command: "
|
|
391
|
-
summary: "
|
|
447
|
+
command: "section loop | jump | mute | vary <name>",
|
|
448
|
+
summary: "section loop chorus · section mute verse drums",
|
|
392
449
|
},
|
|
393
450
|
{
|
|
394
|
-
command: "
|
|
395
|
-
summary: "
|
|
451
|
+
command: "form <section…> | off | bake",
|
|
452
|
+
summary: "song form · form intro verse chorus*2 outro",
|
|
396
453
|
},
|
|
397
454
|
{
|
|
398
|
-
command: "
|
|
399
|
-
summary: "
|
|
455
|
+
command: "build | drop | fill [<section> | <a>-<b>]",
|
|
456
|
+
summary: "riser, roll, sweep · pre-drop cut and impact · drum fill",
|
|
400
457
|
},
|
|
401
458
|
{
|
|
402
|
-
command: "
|
|
403
|
-
summary: "
|
|
459
|
+
command: "style <id> [bars] [seed]",
|
|
460
|
+
summary: "a whole song in a style · style bebop 16 3 · style again",
|
|
404
461
|
},
|
|
405
462
|
{
|
|
406
|
-
command: "
|
|
407
|
-
summary: "
|
|
463
|
+
command: "style list|search|info · style blend <a> <b> [w]",
|
|
464
|
+
summary: "the style tree · style search maqam",
|
|
408
465
|
},
|
|
466
|
+
{ command: "bars <count>", summary: "loop length, 1–256" },
|
|
467
|
+
{ command: "extend <count> bars", summary: "lengthen the loop" },
|
|
409
468
|
],
|
|
410
469
|
},
|
|
411
470
|
{
|
|
412
|
-
group: "
|
|
471
|
+
group: "project",
|
|
413
472
|
entries: [
|
|
414
|
-
{ command: "
|
|
415
|
-
{ command: "
|
|
416
|
-
{ command: "Alt-Enter", summary: "queue the request" },
|
|
417
|
-
{ command: "Ctrl-Q", summary: "toggle queue mode" },
|
|
418
|
-
{ command: "Ctrl-Z / Ctrl-Y", summary: "undo / redo" },
|
|
419
|
-
{ command: "Ctrl-O", summary: "transcript" },
|
|
420
|
-
{ command: "Esc", summary: "cancel the agent · back one level" },
|
|
421
|
-
{ command: "?", summary: "keys for the screen you are on" },
|
|
422
|
-
{ command: "Space", summary: "play/pause on an empty prompt" },
|
|
473
|
+
{ command: "play", summary: "start the transport" },
|
|
474
|
+
{ command: "pause", summary: "stop the transport" },
|
|
423
475
|
{
|
|
424
|
-
command: "
|
|
425
|
-
summary: "play mode
|
|
476
|
+
command: "/play [on|off]",
|
|
477
|
+
summary: "play mode: the computer keyboard plays notes · ctrl-p",
|
|
426
478
|
},
|
|
479
|
+
{ command: "tempo <bpm>", summary: "20–300 BPM" },
|
|
427
480
|
{
|
|
428
|
-
command: "
|
|
429
|
-
summary: "
|
|
481
|
+
command: "tempo <bpm> at <beat>|bar <n> [ramp|exp]",
|
|
482
|
+
summary: "tempo change · tempo 90 at bar 9 ramp · tempo clear",
|
|
430
483
|
},
|
|
431
|
-
{ command: "Ctrl-C", summary: "exit" },
|
|
432
|
-
],
|
|
433
|
-
},
|
|
434
|
-
];
|
|
435
|
-
|
|
436
|
-
/**
|
|
437
|
-
* `/help` with no topic: a short, task-first guide. Each row is something to
|
|
438
|
-
* type or press, then what it does. The full reference is `/help all` (or one
|
|
439
|
-
* group: `/help music`).
|
|
440
|
-
*/
|
|
441
|
-
export const HELP_GUIDE: readonly HelpSection[] = [
|
|
442
|
-
{
|
|
443
|
-
group: "start here" as HelpGroup,
|
|
444
|
-
entries: [
|
|
445
|
-
{ command: "type a request", summary: "“add a walking bass in A minor”" },
|
|
446
|
-
{ command: "ctrl-p", summary: "play notes on the computer keyboard" },
|
|
447
484
|
{
|
|
448
|
-
command: "
|
|
449
|
-
summary: "
|
|
485
|
+
command: "rit|accel [<n> bars] [to <bpm>] [at bar <n>]",
|
|
486
|
+
summary: "gradual · rit 4 bars to 80 · a tempo · tempo primo",
|
|
450
487
|
},
|
|
451
|
-
{ command: "? · ctrl-z · ctrl-y", summary: "keys here · undo · redo" },
|
|
452
|
-
],
|
|
453
|
-
},
|
|
454
|
-
{
|
|
455
|
-
group: "play notes" as HelpGroup,
|
|
456
|
-
entries: [
|
|
457
488
|
{
|
|
458
|
-
command: "
|
|
459
|
-
summary: "
|
|
489
|
+
command: "fermata [at <beat>|at bar <n>|at end] [<extra beats>]",
|
|
490
|
+
summary: "hold a beat · fermata at 31 2 · fermata clear",
|
|
460
491
|
},
|
|
492
|
+
{ command: "meter <1..16>", summary: "beats per bar" },
|
|
461
493
|
{
|
|
462
|
-
command: "
|
|
463
|
-
summary: "
|
|
494
|
+
command: "meter <n>/<d> [at bar <n>]",
|
|
495
|
+
summary: "meter change · meter 7/8 at bar 5 · meter clear",
|
|
464
496
|
},
|
|
465
|
-
],
|
|
466
|
-
},
|
|
467
|
-
{
|
|
468
|
-
group: "make drums" as HelpGroup,
|
|
469
|
-
entries: [
|
|
470
497
|
{
|
|
471
|
-
command: "/
|
|
472
|
-
summary: "
|
|
498
|
+
command: "/click on|off|<volume>",
|
|
499
|
+
summary: "metronome · /count-in 0-2 · grid 1/16",
|
|
473
500
|
},
|
|
474
|
-
{ command: "/euclid", summary: "rhythm editor: pulses, steps, rotation" },
|
|
475
|
-
],
|
|
476
|
-
},
|
|
477
|
-
{
|
|
478
|
-
group: "shape the sound" as HelpGroup,
|
|
479
|
-
entries: [
|
|
480
501
|
{
|
|
481
|
-
command: "
|
|
482
|
-
summary:
|
|
502
|
+
command: "export <file> [stems]",
|
|
503
|
+
summary:
|
|
504
|
+
"JSON, MIDI for .mid, WAV for .wav · export song.wav · export mix.wav stems",
|
|
483
505
|
},
|
|
484
|
-
{ command: "
|
|
506
|
+
{ command: "import <file>", summary: "replace the score from a file" },
|
|
485
507
|
{
|
|
486
|
-
command: "
|
|
487
|
-
summary: "
|
|
508
|
+
command: "calibration [0|1|latest|off]",
|
|
509
|
+
summary: "sound fixes · 1 chokes hats, levels keys · 0 legacy",
|
|
488
510
|
},
|
|
489
|
-
{ command: "
|
|
511
|
+
{ command: "undo", summary: "step back · Ctrl-Z" },
|
|
512
|
+
{ command: "redo", summary: "step forward · Ctrl-Y" },
|
|
513
|
+
{ command: "/status", summary: "name · revision · digest · storage" },
|
|
490
514
|
],
|
|
491
515
|
},
|
|
492
516
|
{
|
|
493
|
-
group: "
|
|
517
|
+
group: "project · window",
|
|
494
518
|
entries: [
|
|
519
|
+
{ command: "/view focus|all", summary: "one track or every track" },
|
|
520
|
+
{ command: "/transcript", summary: "scrollable log · Ctrl-O" },
|
|
521
|
+
{ command: "/theme default|high-contrast|mono", summary: "colors" },
|
|
522
|
+
{ command: "/motion on|off", summary: "animation" },
|
|
523
|
+
{
|
|
524
|
+
command: "/menu [section]",
|
|
525
|
+
summary: "every setting by hand · Ctrl-K · /menu <topic>",
|
|
526
|
+
},
|
|
495
527
|
{
|
|
496
|
-
command: "
|
|
497
|
-
summary: "
|
|
528
|
+
command: "help [topic]",
|
|
529
|
+
summary: "start here · help <topic> · help all for everything",
|
|
498
530
|
},
|
|
531
|
+
{ command: "/guide [topic]", summary: "short how-to guides · F1" },
|
|
499
532
|
],
|
|
500
533
|
},
|
|
501
534
|
{
|
|
502
|
-
group: "
|
|
535
|
+
group: "project · session",
|
|
503
536
|
entries: [
|
|
504
|
-
{ command: "
|
|
505
|
-
{
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
},
|
|
537
|
+
{ command: "/sessions", summary: "list sessions in this workspace" },
|
|
538
|
+
{ command: "/resume [<n>|<name>|<id>]", summary: "switch session" },
|
|
539
|
+
{ command: "/rename <name>|--auto", summary: "name this session" },
|
|
540
|
+
{ command: "/fork [<name>]", summary: "copy into a new session" },
|
|
509
541
|
],
|
|
510
542
|
},
|
|
511
543
|
{
|
|
512
|
-
group: "
|
|
544
|
+
group: "agent",
|
|
513
545
|
entries: [
|
|
514
546
|
{
|
|
515
|
-
command: "
|
|
516
|
-
summary: "
|
|
547
|
+
command: "/model [alias]",
|
|
548
|
+
summary: "pick the agent's model · /model lists them · /model fast",
|
|
517
549
|
},
|
|
518
550
|
{
|
|
519
|
-
command: "
|
|
520
|
-
summary: "
|
|
551
|
+
command: "/model key [gateway|openrouter|codex|claude]",
|
|
552
|
+
summary: "add an agent key (optional) · finds existing setups first",
|
|
553
|
+
},
|
|
554
|
+
{
|
|
555
|
+
command: "/logout [provider]",
|
|
556
|
+
summary: "forget keys and the saved choice",
|
|
557
|
+
},
|
|
558
|
+
{ command: "/auth [--check]", summary: "provider and audio status" },
|
|
559
|
+
{
|
|
560
|
+
command: "/showme on|quiet|off",
|
|
561
|
+
summary:
|
|
562
|
+
"the agent types its commands in your prompt bar as it streams",
|
|
521
563
|
},
|
|
522
564
|
],
|
|
523
565
|
},
|
|
566
|
+
];
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* `/help` with no topic: start here, then the ten topics. Each row is
|
|
570
|
+
* something to type or press, then what it does. The full reference is
|
|
571
|
+
* `/help all`; one topic is `/help <topic>`.
|
|
572
|
+
*/
|
|
573
|
+
export const HELP_GUIDE: readonly HelpSection[] = [
|
|
524
574
|
{
|
|
525
|
-
group: "
|
|
575
|
+
group: "start here",
|
|
526
576
|
entries: [
|
|
527
577
|
{
|
|
528
|
-
command: "
|
|
529
|
-
summary: "
|
|
578
|
+
command: "type a request",
|
|
579
|
+
summary: "“add a walking bass in A minor” (with an agent)",
|
|
530
580
|
},
|
|
531
581
|
{
|
|
532
|
-
command: "
|
|
533
|
-
summary: "
|
|
582
|
+
command: "style deep-house",
|
|
583
|
+
summary: "a whole song in a style · style list",
|
|
534
584
|
},
|
|
585
|
+
{
|
|
586
|
+
command: "space · ctrl-p",
|
|
587
|
+
summary: "play · play mode: the keyboard plays notes",
|
|
588
|
+
},
|
|
589
|
+
{ command: "ctrl-k", summary: "menu: every setting by hand" },
|
|
590
|
+
{ command: "? · ctrl-z · ctrl-y", summary: "keys here · undo · redo" },
|
|
591
|
+
],
|
|
592
|
+
},
|
|
593
|
+
{
|
|
594
|
+
group: "topics · help <topic>",
|
|
595
|
+
entries: [
|
|
596
|
+
...TOPICS.map((id) => ({ command: id, summary: TOPIC_SUMMARY[id] })),
|
|
597
|
+
{ command: "all", summary: "every command · /guide <topic> · F1" },
|
|
535
598
|
],
|
|
536
599
|
},
|
|
537
600
|
];
|
|
538
601
|
|
|
539
|
-
/** Topics `/help <topic>` takes, besides `all
|
|
540
|
-
export const HELP_TOPICS =
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
]
|
|
602
|
+
/** Topics `/help <topic>` takes, besides `all` (aliases resolve too). */
|
|
603
|
+
export const HELP_TOPICS = TOPICS;
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* `/help keys`: every screen's `?` panel, straight from tui/grammar.ts
|
|
607
|
+
* KEYS, so the reference and the panels never disagree.
|
|
608
|
+
*/
|
|
609
|
+
export function keysLines(width = 80): string[] {
|
|
610
|
+
const seen = new Set<string>();
|
|
611
|
+
const sections = (Object.keys(KEYS) as KeyScreen[]).flatMap((screen) =>
|
|
612
|
+
KEYS[screen].map((section) => ({
|
|
613
|
+
title:
|
|
614
|
+
section.title === screen || screen === "prompt"
|
|
615
|
+
? section.title
|
|
616
|
+
: `${screen} · ${section.title}`,
|
|
617
|
+
rows: section.rows,
|
|
618
|
+
})),
|
|
619
|
+
);
|
|
620
|
+
const column =
|
|
621
|
+
Math.min(
|
|
622
|
+
24,
|
|
623
|
+
Math.max(
|
|
624
|
+
...sections.flatMap((s) => s.rows.map(([keys]) => displayWidth(keys))),
|
|
625
|
+
),
|
|
626
|
+
) + 2;
|
|
627
|
+
const lines = ["── keys"];
|
|
628
|
+
for (const section of sections) {
|
|
629
|
+
const key = JSON.stringify(section.rows);
|
|
630
|
+
if (seen.has(key)) continue;
|
|
631
|
+
seen.add(key);
|
|
632
|
+
lines.push("", truncate(`── ${section.title}`, width));
|
|
633
|
+
for (const [keys, action] of section.rows)
|
|
634
|
+
lines.push(
|
|
635
|
+
truncate(
|
|
636
|
+
`${keys}${" ".repeat(Math.max(1, column - displayWidth(keys)))}${action}`,
|
|
637
|
+
width,
|
|
638
|
+
),
|
|
639
|
+
);
|
|
640
|
+
}
|
|
641
|
+
return lines;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
/** The last rows of a topic page: the same id in the other two doors. */
|
|
645
|
+
function doors(id: TopicId, width: number): string[] {
|
|
646
|
+
return ["", truncate(`guide ${id} · menu ${id} · help all`, width)];
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
/** A topic's groups: `sound`, then `sound · performance`, `sound · samples`. */
|
|
650
|
+
export function topicSections(id: TopicId): readonly HelpSection[] {
|
|
651
|
+
return HELP_SECTIONS.filter(
|
|
652
|
+
(section) => section.group === id || section.group.startsWith(`${id} · `),
|
|
653
|
+
);
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
/** One topic's page: its groups, then where else the topic opens. */
|
|
657
|
+
function topicLines(id: TopicId, width: number): string[] {
|
|
658
|
+
if (id === "keys") return [...keysLines(width), ...doors(id, width)];
|
|
659
|
+
return [...sectionLines(topicSections(id), width), ...doors(id, width)];
|
|
660
|
+
}
|
|
547
661
|
|
|
548
662
|
/**
|
|
549
|
-
* Rows for `/help [topic]`: the
|
|
550
|
-
* `all`,
|
|
663
|
+
* Rows for `/help [topic]`: the start page with no topic, the full
|
|
664
|
+
* reference for `all`, a topic for its id or an alias, a command's rows
|
|
665
|
+
* for its verb (`/help vocoder`); undefined for an unknown word.
|
|
551
666
|
*/
|
|
552
667
|
export function helpTopicLines(
|
|
553
668
|
topic: string | undefined,
|
|
@@ -557,34 +672,91 @@ export function helpTopicLines(
|
|
|
557
672
|
if (!name) return sectionLines(HELP_GUIDE, width, 20);
|
|
558
673
|
if (name === "all" || name === "commands" || name === "reference")
|
|
559
674
|
return helpLines(width);
|
|
560
|
-
if (
|
|
561
|
-
return
|
|
562
|
-
const
|
|
563
|
-
|
|
675
|
+
if ((TOPICS as readonly string[]).includes(name))
|
|
676
|
+
return topicLines(name as TopicId, width);
|
|
677
|
+
const spelled = HELP_COMMAND_ALIASES[name];
|
|
678
|
+
if (spelled) return commandTopic(spelled, width, name);
|
|
679
|
+
const alias = resolveTopic(name);
|
|
680
|
+
// A topic alias wins unless a reference row starts with the word:
|
|
681
|
+
// `/help sections` is the arrange topic, `/help scale` the command.
|
|
682
|
+
const command =
|
|
683
|
+
alias && !hasRow(name) ? undefined : commandTopic(name, width);
|
|
684
|
+
if (command)
|
|
685
|
+
return alias
|
|
686
|
+
? [...command, "", truncate(`see also help ${alias}`, width)]
|
|
687
|
+
: command;
|
|
688
|
+
return alias ? topicLines(alias, width) : undefined;
|
|
564
689
|
}
|
|
565
690
|
|
|
566
|
-
/**
|
|
567
|
-
function
|
|
568
|
-
const
|
|
569
|
-
return
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
691
|
+
/** True when a reference row starts with `name` (`/scale`, `scale …`). */
|
|
692
|
+
function hasRow(name: string): boolean {
|
|
693
|
+
const verb = name.replace(/[^a-z0-9-]/g, "");
|
|
694
|
+
return HELP_SECTIONS.some((section) =>
|
|
695
|
+
section.entries.some((entry) =>
|
|
696
|
+
new RegExp(`^/?${verb}(\\s|$)`, "i").test(entry.command),
|
|
697
|
+
),
|
|
698
|
+
);
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
/**
|
|
702
|
+
* `/help <command>` (`/help vocoder`, `/help clip`): the command's rows
|
|
703
|
+
* from every group, then its full usage wrapped; undefined when no command
|
|
704
|
+
* has that name.
|
|
705
|
+
*/
|
|
706
|
+
function commandTopic(
|
|
707
|
+
name: string,
|
|
708
|
+
width: number,
|
|
709
|
+
usageKey = name,
|
|
710
|
+
): string[] | undefined {
|
|
711
|
+
const pattern = name
|
|
712
|
+
.replace(/[^a-z0-9 -]/g, "")
|
|
713
|
+
.trim()
|
|
714
|
+
.replace(/ +/g, "\\s+");
|
|
715
|
+
const own = (command: string) =>
|
|
716
|
+
new RegExp(`^/?${pattern}(\\s|$)`, "i").test(command);
|
|
717
|
+
const rows = HELP_SECTIONS.flatMap((section) =>
|
|
718
|
+
section.entries.filter((entry) => own(entry.command)),
|
|
719
|
+
);
|
|
720
|
+
const usage = USAGE[usageKey] ?? USAGE[name];
|
|
721
|
+
if (rows.length === 0 && !usage) return undefined;
|
|
722
|
+
const wrap = Math.max(30, width - 2);
|
|
723
|
+
// A usage line that repeats a row keeps only what it adds (examples).
|
|
724
|
+
const extra = usage
|
|
725
|
+
? rows.reduce((text, entry) => {
|
|
726
|
+
const bare = (value: string) =>
|
|
727
|
+
value.replace(/^\//, "").replace(/\s*\|\s*/g, "|");
|
|
728
|
+
return bare(text).startsWith(bare(entry.command))
|
|
729
|
+
? bare(text)
|
|
730
|
+
.slice(bare(entry.command).length)
|
|
731
|
+
.replace(/^\s*·\s*/, "")
|
|
732
|
+
: text;
|
|
733
|
+
}, usage)
|
|
734
|
+
: "";
|
|
735
|
+
// Examples a row summary already shows are not repeated.
|
|
736
|
+
const shown = rows.map((entry) => entry.summary).join(" · ");
|
|
737
|
+
const added = extra
|
|
738
|
+
.split(" · ")
|
|
739
|
+
.filter((part) => part && !shown.split(" · ").includes(part))
|
|
740
|
+
.join(" · ");
|
|
741
|
+
return [
|
|
742
|
+
`── ${name}`,
|
|
743
|
+
...rows.flatMap((entry) => [
|
|
744
|
+
...wrapWords(entry.command, wrap),
|
|
745
|
+
...wrapWords(entry.summary, wrap - 2).map((line) => ` ${line}`),
|
|
746
|
+
]),
|
|
747
|
+
...(added ? ["", ...wrapWords(added, wrap)] : []),
|
|
748
|
+
];
|
|
586
749
|
}
|
|
587
750
|
|
|
751
|
+
/**
|
|
752
|
+
* Typed aliases that /help shows under their one word (design §8.2): the
|
|
753
|
+
* alias opens the page and the title, heading and rows name the canonical
|
|
754
|
+
* command.
|
|
755
|
+
*/
|
|
756
|
+
export const HELP_COMMAND_ALIASES: Readonly<Record<string, string>> = {
|
|
757
|
+
login: "model key",
|
|
758
|
+
};
|
|
759
|
+
|
|
588
760
|
function wrapWords(text: string, width: number): string[] {
|
|
589
761
|
const rows: string[] = [];
|
|
590
762
|
let row = "";
|
|
@@ -614,18 +786,21 @@ function sectionLines(
|
|
|
614
786
|
);
|
|
615
787
|
for (const section of sections) {
|
|
616
788
|
if (lines.length > 0) lines.push("");
|
|
617
|
-
lines.push(`── ${section.group}
|
|
789
|
+
lines.push(truncate(`── ${section.group}`, width));
|
|
618
790
|
for (const entry of section.entries) {
|
|
619
791
|
const pad = Math.max(1, column - entry.command.length);
|
|
620
792
|
lines.push(
|
|
621
|
-
`${entry.command}${" ".repeat(pad)}${entry.summary}
|
|
793
|
+
truncate(`${entry.command}${" ".repeat(pad)}${entry.summary}`, width),
|
|
622
794
|
);
|
|
623
795
|
}
|
|
624
796
|
}
|
|
625
797
|
return lines;
|
|
626
798
|
}
|
|
627
799
|
|
|
628
|
-
/**
|
|
800
|
+
/**
|
|
801
|
+
* `/help all`: a heading per topic, then `command summary` lines, then
|
|
802
|
+
* the keys of every screen.
|
|
803
|
+
*/
|
|
629
804
|
export function helpLines(width = 80): string[] {
|
|
630
805
|
const lines: string[] = [];
|
|
631
806
|
const column = Math.min(
|
|
@@ -636,27 +811,25 @@ export function helpLines(width = 80): string[] {
|
|
|
636
811
|
);
|
|
637
812
|
for (const section of HELP_SECTIONS) {
|
|
638
813
|
if (lines.length > 0) lines.push("");
|
|
639
|
-
lines.push(`── ${section.group}
|
|
814
|
+
lines.push(truncate(`── ${section.group}`, width));
|
|
640
815
|
for (const entry of section.entries) {
|
|
641
816
|
const pad = Math.max(1, column - entry.command.length);
|
|
642
817
|
lines.push(
|
|
643
|
-
`${entry.command}${" ".repeat(pad)}${entry.summary}
|
|
818
|
+
truncate(`${entry.command}${" ".repeat(pad)}${entry.summary}`, width),
|
|
644
819
|
);
|
|
645
820
|
}
|
|
646
821
|
}
|
|
647
|
-
return lines;
|
|
822
|
+
return [...lines, "", ...keysLines(width)];
|
|
648
823
|
}
|
|
649
824
|
|
|
650
825
|
/** The `Commands:` block of `dawg --help`. */
|
|
651
826
|
export function helpText(): string {
|
|
652
|
-
return HELP_SECTIONS.
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
)
|
|
659
|
-
.join("\n");
|
|
827
|
+
return HELP_SECTIONS.map(
|
|
828
|
+
(section) =>
|
|
829
|
+
`${section.group}:\n${section.entries
|
|
830
|
+
.map((entry) => ` ${entry.command}`)
|
|
831
|
+
.join("\n")}`,
|
|
832
|
+
).join("\n");
|
|
660
833
|
}
|
|
661
834
|
|
|
662
835
|
/** Usage for a known verb, shown instead of sending a near-miss to the agent. */
|
|
@@ -669,12 +842,15 @@ export const USAGE: Readonly<Record<string, string>> = {
|
|
|
669
842
|
scale: "scale [<tonic>] <name> | list · scale D hijaz",
|
|
670
843
|
tuning:
|
|
671
844
|
"tuning <name> | edo <n> | ratios … | cents … | scl <file> [kbm <file>] | ref <hz> | root <note> | map linear|nearest | track … | off · tuning 19-edo",
|
|
672
|
-
|
|
845
|
+
calibration: "calibration [0|1|latest|off] · calibration latest",
|
|
846
|
+
tune: "tuning <name> | edo <n> | scl <file> | off · tuning list · pitch correction is autotune",
|
|
847
|
+
autotune:
|
|
848
|
+
"autotune [hard|robot|warble|trap|pop|natural|gentle|guided|locked] | to scale|chromatic|chord|notes [track] | key <key> | speed hold glide <ms> | relax amount center drift <0..1> | flex <0..100> | vib <Hz> vibmod <st> | voice auto|bass|tenor|alto|soprano | reset | off | presets · autotune hard · autotune natural flex 40",
|
|
673
849
|
cents: "cents <id> <±cents> · cents n3 -14",
|
|
674
|
-
grid: "
|
|
850
|
+
grid: "grid 1/4|1/8|1/8T|1/16|1/16T|1/32",
|
|
675
851
|
tempo:
|
|
676
852
|
"tempo takes 20…300 · tempo 120 · tempo 90 at bar 9 [ramp|exp] · tempo remove bar 9 · tempo clear · tempo map",
|
|
677
|
-
bpm: "tempo takes 20…300 · tempo 120 · a sample's own tempo: /bpm 174 [<
|
|
853
|
+
bpm: "tempo takes 20…300 · tempo 120 · a sample's own tempo: /bpm 174 [<sample>] · /bpm off",
|
|
678
854
|
rit: "rit [<n> bars|beats] [to <bpm>] [at bar <n>|<beat>] [exp] · rit 4 bars to 80",
|
|
679
855
|
ritardando:
|
|
680
856
|
"rit [<n> bars|beats] [to <bpm>] [at bar <n>|<beat>] [exp] · rit 4 bars to 80",
|
|
@@ -696,7 +872,7 @@ export const USAGE: Readonly<Record<string, string>> = {
|
|
|
696
872
|
bars: "bars takes 1…256 · bars 8",
|
|
697
873
|
extend: "extend <count> bars · extend 4 bars",
|
|
698
874
|
instrument:
|
|
699
|
-
"instrument <name> · sine piano pluck bass
|
|
875
|
+
"instrument <name> · sine piano pluck bass sawtooth square triangle wavetable kit · pianos: grand upright felt honkytonk prepared · electric: epiano suitcase dyno wurli clav funkclav · synth: supersaw pulse white pink z_square · voices: vocal aah ooh choir chorale khoomei sygyt kargyraa vocoder…",
|
|
700
876
|
volume: "volume takes 0…1 · volume 0.8",
|
|
701
877
|
vol: "volume takes 0…1 · volume 0.8",
|
|
702
878
|
pan: "pan takes -1…1 · pan -0.5",
|
|
@@ -712,28 +888,28 @@ export const USAGE: Readonly<Record<string, string>> = {
|
|
|
712
888
|
reverb: "reverb <mix> [size] · reverb 0.3 0.6 · reverb off",
|
|
713
889
|
automate: "automate <lane> at <beat> <value> · automate volume at 0 0.5",
|
|
714
890
|
automation: "automate <lane> at <beat> <value> · automate volume at 0 0.5",
|
|
715
|
-
hit: "hit <
|
|
716
|
-
pattern: "pattern <
|
|
717
|
-
clear: "clear · clear <
|
|
891
|
+
hit: "hit <drum> at <beat> · hit kick at 0",
|
|
892
|
+
pattern: "pattern <drum> <beats...> | every <step> · pattern kick every 1",
|
|
893
|
+
clear: "clear · clear <drum> · clear [<lane>] automation",
|
|
718
894
|
track:
|
|
719
|
-
"
|
|
720
|
-
tracks: "
|
|
895
|
+
"track <name> · track drums · track rm <name> · track move <name> <position> · track rate <0.125..8>|<a>/<b>|off · track phase <beats> · track loop <beats> · track phasing <beats> [over <beats>]",
|
|
896
|
+
tracks: "tracks",
|
|
721
897
|
sessions: "/sessions",
|
|
722
898
|
resume: "/resume [<n>|<name>|<id>]",
|
|
723
899
|
rename: "/rename <name> | --auto",
|
|
724
900
|
fork: "/fork [<name>]",
|
|
725
901
|
status: "/status",
|
|
726
|
-
export: "
|
|
727
|
-
import: "
|
|
902
|
+
export: "export <file> · export loop.track.json",
|
|
903
|
+
import: "import <file> · import loop.track.json",
|
|
728
904
|
sample:
|
|
729
|
-
"/sample <path> [as <
|
|
730
|
-
samples: "/sample · lists the focused track's
|
|
905
|
+
"/sample <path> [as <sample>] · /sample set <sample> <control> <value>… · /sample set brk fit on clip 1 · /sample set soft vel 0-63 rr a",
|
|
906
|
+
samples: "/sample · lists the focused track's samples",
|
|
731
907
|
fitmode:
|
|
732
|
-
"
|
|
733
|
-
len: "
|
|
908
|
+
"fitmode [repitch|beats|tones|auto|off] [<sample>] · fitmode beats · fitmode auto brk",
|
|
909
|
+
len: "len <beats> [<sample>] · len 16 · len off",
|
|
734
910
|
shift:
|
|
735
|
-
"shift <semitones> [formant keep|follow|<n>] [<
|
|
736
|
-
fade: "fade [in|out] <seconds> [<
|
|
911
|
+
"shift <semitones> [formant keep|follow|<n>] [<sample>] · shift 7 formant keep · shift 0",
|
|
912
|
+
fade: "fade [in|out] <seconds> [<sample>] · fade out 0.5 · fade in 0.05 · fade off",
|
|
737
913
|
resample:
|
|
738
914
|
"resample <track>|orbit <n>|master [section <name>|bars a-b] [post] [grain] [as <id>] · resample lead · resample drums bars 1-2 grain · resample master section chorus",
|
|
739
915
|
bounce: "resample <track>|orbit <n>|master [section <name>|bars a-b] [grain]",
|
|
@@ -742,12 +918,14 @@ export const USAGE: Readonly<Record<string, string>> = {
|
|
|
742
918
|
log: "/transcript",
|
|
743
919
|
theme: "/theme default | high-contrast | mono",
|
|
744
920
|
motion: "/motion on | off",
|
|
745
|
-
model:
|
|
746
|
-
|
|
921
|
+
model:
|
|
922
|
+
"/model [fast | alias | vendor/model] · /model key [gateway | openrouter | codex | claude]",
|
|
923
|
+
models: "/model [fast | alias | vendor/model] · /model key [provider]",
|
|
924
|
+
login: "/model key [gateway | openrouter | codex | claude]",
|
|
747
925
|
logout: "/logout [provider]",
|
|
748
926
|
auth: "/auth [--check]",
|
|
749
|
-
help: "
|
|
750
|
-
guide: "/guide [topic] · /guide
|
|
927
|
+
help: "help [topic] · help all · help sound|voice|effects|rhythm|chords|mix|arrange|project|keys|agent",
|
|
928
|
+
guide: "/guide [topic] · /guide voice · F1 · the same topics as help",
|
|
751
929
|
fx: "fx <effect> <param> <value> | on | off | preset <name> · fx delay mix 0.3",
|
|
752
930
|
synth: "synth <param> <value> | preset <name> · synth lpf 1200",
|
|
753
931
|
string:
|
|
@@ -756,6 +934,8 @@ export const USAGE: Readonly<Record<string, string>> = {
|
|
|
756
934
|
"bowed [violin|viola|cello|contrabass|fiddle|erhu|kamancheh|violins|violas|cellos|contrabasses|pizz|trem] | <param> <value> | presets · bowed violin · bowed pressure 0.7",
|
|
757
935
|
rig: "rig clean|crunch|punk|ragged|lead|metal|fuzz|octave|funk|wah|bachata|spring|bassdrive|reese|jangle|alt|shoegaze|glide|dreampop|swell|ebow | reset",
|
|
758
936
|
guitar: `guitar tune ${GUITAR_TUNING_NAMES.join("|")} | E A D G B E · capo 0..12 · hand 3..6 · ring 0..1 · position · reset`,
|
|
937
|
+
progression:
|
|
938
|
+
"progression i7 IV7 i7 IV7 [each 8] [at 0] [bass] · numerals in the song key or symbols (Am7 D9) · block chords that hold for each span",
|
|
759
939
|
strum:
|
|
760
940
|
"strum G D Em C [folk|pop|punk|…] [strokes D-DU-UDU] [speed 22ms] [each 4] [at 0] · strum alone strums the track's chords",
|
|
761
941
|
stomp:
|
|
@@ -778,14 +958,25 @@ export const USAGE: Readonly<Record<string, string>> = {
|
|
|
778
958
|
modal:
|
|
779
959
|
"modal <preset> | <body> | <param> <value> | mallet <name> | pair <track> | gamelan | reset | off | presets · modal vibes · modal gangsa · modal ring 3",
|
|
780
960
|
wind: "wind <preset> | <param> <value> | mute <name> | reset | off | presets · wind flute · wind trumpet mute harmon · wind players 4",
|
|
781
|
-
|
|
961
|
+
formant:
|
|
962
|
+
"/formant <-12..12> [mix] | deep|giant|bright|tiny | on | off · /formant -4 · /formant 3 0.5",
|
|
963
|
+
vowel:
|
|
964
|
+
"/vowel <v> [<to> [<morph 0..1>]] | ee | to <v>|off | morph <0..1> | mix <0..1> | off · /vowel a o 0.5",
|
|
965
|
+
lyrics:
|
|
966
|
+
'lyrics [bar] <syllables> | clear [bar] · lyrics "sun-lit morn-ing" (- splits, _ holds, ~ skips)',
|
|
967
|
+
sing: "sing <preset> | <param> <value> | drone <D3> | vowels a e i … | reset | off | presets · sing choir · sing khoomei drone D3 · sing vowel o voices 6",
|
|
968
|
+
vocoder:
|
|
969
|
+
"vocoder [preset] | src <track> | <param> <value|reset> | reset | off | presets · vocoder talkbox · vocoder src vox · vocoder formant +3 · vocoder gate auto · params: tap mode carrier follow root spread bands lo hi width attack release formant unvoiced sens hiss gate enhance depth freeze mix gain seed",
|
|
970
|
+
pack: "pack list | info <name> | use <pack>/<sound> | add <url>",
|
|
782
971
|
kit: "/kit [name] · /kit syn909",
|
|
783
|
-
euclid: "
|
|
784
|
-
menu: "/menu [sound|effects|rhythm|chords|mix|
|
|
972
|
+
euclid: "euclid [drum] · euclid hat 7 16",
|
|
973
|
+
menu: "/menu [sound|voice|effects|rhythm|chords|mix|arrange|project|keys|agent] or any row name · /menu tuning · Ctrl-K",
|
|
974
|
+
style:
|
|
975
|
+
"style [list [id]|search <words>|info <id>|<id> [bars] [seed]|blend <a> <b> [w] [bars] [seed]|again] · style deep-house 16 · style blend bebop bossa-nova 0.3",
|
|
785
976
|
master:
|
|
786
977
|
"master <unit> on|off|preset <name>|<param> <value> · master streaming|club|loud · master target -14 · master measure · master off",
|
|
787
978
|
try: "/try <sound command> · /try fx reverb mix 0.6",
|
|
788
|
-
play: "/play [on|off|degrees|chromatic] ·
|
|
979
|
+
play: "/play [on|off|degrees|in-key|chromatic] · ctrl-p play mode · i toggles degrees",
|
|
789
980
|
meter:
|
|
790
981
|
"meter <1..16> · meter 3 · meter 7/8 [at bar <n>] · meter remove bar <n> · meter clear",
|
|
791
982
|
art: EXPRESSION_USAGE.art,
|
|
@@ -810,37 +1001,32 @@ export const USAGE: Readonly<Record<string, string>> = {
|
|
|
810
1001
|
redo: "redo · Ctrl-Y",
|
|
811
1002
|
};
|
|
812
1003
|
|
|
813
|
-
/** Verbs a typo can be matched against,
|
|
1004
|
+
/** Verbs a typo can be matched against, without their slash. */
|
|
814
1005
|
const KNOWN_VERBS: readonly string[] = [
|
|
815
1006
|
...new Set(
|
|
816
1007
|
[
|
|
817
1008
|
...HELP_SECTIONS.flatMap((section) =>
|
|
818
|
-
section.
|
|
819
|
-
? []
|
|
820
|
-
: section.entries.map((entry) => entry.command.split(/[\s|[]/)[0]!),
|
|
1009
|
+
section.entries.map((entry) => entry.command.split(/[\s|[]/)[0]!),
|
|
821
1010
|
),
|
|
822
1011
|
...Object.values(USAGE).map((usage) => usage.split(/[\s|[]/)[0]!),
|
|
823
|
-
]
|
|
1012
|
+
]
|
|
1013
|
+
.map((verb) => verb.replace(/^\//, ""))
|
|
1014
|
+
.filter((verb) => /^[a-z][\w-]*$/i.test(verb)),
|
|
824
1015
|
),
|
|
825
1016
|
];
|
|
826
1017
|
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
previous = current;
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
|
-
return row[b.length]!;
|
|
843
|
-
}
|
|
1018
|
+
/** Window verbs whose free text would otherwise read as a typo fix. */
|
|
1019
|
+
const SLASH_ONLY: ReadonlySet<string> = new Set([
|
|
1020
|
+
"rename",
|
|
1021
|
+
"fork",
|
|
1022
|
+
"resume",
|
|
1023
|
+
"login",
|
|
1024
|
+
"logout",
|
|
1025
|
+
"auth",
|
|
1026
|
+
"bpm",
|
|
1027
|
+
]);
|
|
1028
|
+
|
|
1029
|
+
export { editDistance } from "./nearest.ts";
|
|
844
1030
|
|
|
845
1031
|
/**
|
|
846
1032
|
* The known command nearest to the first word of `command` (`/clik` →
|
|
@@ -848,19 +1034,25 @@ function editDistance(a: string, b: string): number {
|
|
|
848
1034
|
*/
|
|
849
1035
|
export function nearestCommand(command: string): string | undefined {
|
|
850
1036
|
const word = command.trim().split(/\s+/)[0]?.toLowerCase() ?? "";
|
|
1037
|
+
const slash = word.startsWith("/");
|
|
851
1038
|
const bare = word.replace(/^\//, "");
|
|
852
1039
|
if (!bare) return undefined;
|
|
853
1040
|
let best: { verb: string; distance: number; score: number } | undefined;
|
|
854
1041
|
for (const verb of KNOWN_VERBS) {
|
|
855
|
-
const distance = editDistance(bare, verb
|
|
856
|
-
if (distance === 0 &&
|
|
857
|
-
// Ties go to
|
|
858
|
-
|
|
859
|
-
const
|
|
1042
|
+
const distance = editDistance(bare, verb);
|
|
1043
|
+
if (distance === 0 && !slash) continue;
|
|
1044
|
+
// Ties go to a dropped letter (`/clik` → `/click`, not `/clip`), the
|
|
1045
|
+
// commonest slip, then a changed one, then an extra one.
|
|
1046
|
+
const shape =
|
|
1047
|
+
verb.length > bare.length ? 0 : verb.length === bare.length ? 1 : 2;
|
|
1048
|
+
const score = distance * 3 + shape;
|
|
860
1049
|
if (!best || score < best.score) best = { verb, distance, score };
|
|
861
1050
|
}
|
|
862
1051
|
const limit = bare.length <= 4 ? 1 : 2;
|
|
863
|
-
|
|
1052
|
+
if (!best || best.distance > limit) return undefined;
|
|
1053
|
+
// Answer in the form typed (`/clik` → `/click`), except an exact word
|
|
1054
|
+
// that failed with a slash (`/fx delay on` → `fx`): its slash was wrong.
|
|
1055
|
+
return slash && best.distance > 0 ? `/${best.verb}` : best.verb;
|
|
864
1056
|
}
|
|
865
1057
|
|
|
866
1058
|
/**
|
|
@@ -877,7 +1069,7 @@ export function typoFix(
|
|
|
877
1069
|
const [first, ...rest] = text.split(/\s+/);
|
|
878
1070
|
const word = first!.toLowerCase();
|
|
879
1071
|
for (const verb of KNOWN_VERBS) {
|
|
880
|
-
if (
|
|
1072
|
+
if (SLASH_ONLY.has(verb) || verb === word) continue;
|
|
881
1073
|
if (editDistance(word, verb) !== 1) continue;
|
|
882
1074
|
const candidate = [verb, ...rest].join(" ");
|
|
883
1075
|
if (parses(candidate)) return candidate;
|
|
@@ -917,7 +1109,18 @@ export function usageHint(command: string): string | undefined {
|
|
|
917
1109
|
!words.slice(1).some((word) => /\d/.test(word) || ARRANGE_WORDS.has(word))
|
|
918
1110
|
)
|
|
919
1111
|
return undefined;
|
|
920
|
-
|
|
1112
|
+
const usage = USAGE[verb];
|
|
1113
|
+
return usage === undefined ? undefined : usageForm(usage);
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
/**
|
|
1117
|
+
* Every usage line reads `usage · <cmd> <args>`; a range refusal keeps
|
|
1118
|
+
* its own template (`pan takes -1…1 · pan -0.5`).
|
|
1119
|
+
*/
|
|
1120
|
+
export function usageForm(usage: string): string {
|
|
1121
|
+
return / takes /.test(usage) || usage.startsWith("usage · ")
|
|
1122
|
+
? usage
|
|
1123
|
+
: `usage · ${usage}`;
|
|
921
1124
|
}
|
|
922
1125
|
|
|
923
1126
|
const ARRANGE_VERBS: ReadonlySet<string> = new Set([
|
|
@@ -944,3 +1147,30 @@ const ARRANGE_WORDS: ReadonlySet<string> = new Set([
|
|
|
944
1147
|
"bake",
|
|
945
1148
|
"off",
|
|
946
1149
|
]);
|
|
1150
|
+
|
|
1151
|
+
/** The overlay title for `/help [topic]`: an alias shows its topic. */
|
|
1152
|
+
export function helpTitle(topic: string | undefined): string {
|
|
1153
|
+
const name = topic?.trim().toLowerCase().replace(/^\//, "");
|
|
1154
|
+
if (!name) return "help";
|
|
1155
|
+
const spelled = HELP_COMMAND_ALIASES[name];
|
|
1156
|
+
if (spelled) return `help · ${spelled}`;
|
|
1157
|
+
const id = (TOPICS as readonly string[]).includes(name)
|
|
1158
|
+
? name
|
|
1159
|
+
: USAGE[name] || commandHelpExists(name)
|
|
1160
|
+
? name
|
|
1161
|
+
: (resolveTopic(name) ?? name);
|
|
1162
|
+
return `help · ${id}`;
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
function commandHelpExists(name: string): boolean {
|
|
1166
|
+
return HELP_SECTIONS.some((section) =>
|
|
1167
|
+
section.entries.some((entry) =>
|
|
1168
|
+
entry.command.replace(/^\//, "").startsWith(`${name} `),
|
|
1169
|
+
),
|
|
1170
|
+
);
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
/** `no topic X · did you mean Y · /help`, for `/help X` with no page. */
|
|
1174
|
+
export function helpMiss(topic: string): string {
|
|
1175
|
+
return topicMiss(topic, ["all"]);
|
|
1176
|
+
}
|