@hraness/dawg 0.6.1 → 0.8.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.
Files changed (277) hide show
  1. package/CHANGELOG.md +118 -0
  2. package/DAWG.md +454 -140
  3. package/README.md +30 -26
  4. package/core/autotune.ts +1119 -0
  5. package/core/chords.ts +271 -23
  6. package/core/clips.ts +499 -0
  7. package/core/diff.ts +184 -104
  8. package/core/expression.ts +15 -0
  9. package/core/fx.ts +99 -29
  10. package/core/ids.ts +52 -0
  11. package/core/instruments.ts +19 -0
  12. package/core/keys.ts +3 -3
  13. package/core/loop.ts +8 -0
  14. package/core/lyrics.ts +297 -0
  15. package/core/master.ts +3 -3
  16. package/core/range.ts +581 -0
  17. package/core/resonators.ts +16 -2
  18. package/core/routing.ts +165 -0
  19. package/core/score.ts +955 -14
  20. package/core/sdk/eval-child.ts +7 -2
  21. package/core/sdk/eval.ts +35 -6
  22. package/core/sdk/print.ts +287 -5
  23. package/core/sdk/sync-lyrics.ts +49 -0
  24. package/core/sdk/v1.ts +2132 -46
  25. package/core/sections.ts +480 -35
  26. package/core/sing.ts +815 -0
  27. package/core/style-provenance.ts +80 -0
  28. package/core/styles/africa-mena-southasia.ts +2893 -0
  29. package/core/styles/americas.ts +3810 -0
  30. package/core/styles/art.ts +4993 -0
  31. package/core/styles/base.ts +123 -0
  32. package/core/styles/cycles.ts +106 -0
  33. package/core/styles/electronic.ts +2723 -0
  34. package/core/styles/europe-asia-pacific.ts +2838 -0
  35. package/core/styles/excerpt.ts +29 -0
  36. package/core/styles/gamelan.ts +283 -0
  37. package/core/styles/generate.ts +2199 -0
  38. package/core/styles/index.ts +515 -0
  39. package/core/styles/parts.ts +106 -0
  40. package/core/styles/pop.ts +3189 -0
  41. package/core/styles/rock.ts +2993 -0
  42. package/core/styles/roots.ts +4175 -0
  43. package/core/styles/schema.ts +429 -0
  44. package/core/styles/taxonomy.ts +940 -0
  45. package/core/styles/validate.ts +528 -0
  46. package/core/tempo.ts +32 -2
  47. package/core/tuning.ts +19 -3
  48. package/core/vocoder.ts +524 -0
  49. package/guides/agent.md +29 -0
  50. package/guides/arrange.md +31 -0
  51. package/guides/audio.md +31 -0
  52. package/guides/audition.md +20 -14
  53. package/guides/automation.md +12 -7
  54. package/guides/chords.md +15 -15
  55. package/guides/effects.md +17 -16
  56. package/guides/faders.md +20 -16
  57. package/guides/files.md +13 -9
  58. package/guides/getting-started.md +15 -11
  59. package/guides/keys.md +19 -15
  60. package/guides/media.md +17 -12
  61. package/guides/mix.md +15 -7
  62. package/guides/music.md +23 -8
  63. package/guides/notes.md +15 -9
  64. package/guides/panes.md +32 -0
  65. package/guides/performance.md +15 -13
  66. package/guides/play.md +21 -13
  67. package/guides/project.md +26 -8
  68. package/guides/providers.md +20 -14
  69. package/guides/resample.md +15 -9
  70. package/guides/rhythm.md +18 -13
  71. package/guides/sessions.md +17 -7
  72. package/guides/show-me.md +31 -0
  73. package/guides/sound.md +25 -9
  74. package/guides/sounds.md +15 -13
  75. package/guides/styles.md +31 -0
  76. package/guides/tape.md +32 -0
  77. package/guides/tempo.md +15 -10
  78. package/guides/tracks.md +15 -10
  79. package/guides/tuning.md +32 -0
  80. package/guides/voice.md +31 -0
  81. package/guides/web-search.md +18 -8
  82. package/native/prebuilt/darwin-arm64/libdawg_sink.dylib +0 -0
  83. package/native/prebuilt/darwin-x64/libdawg_sink.dylib +0 -0
  84. package/native/prebuilt/linux-arm64/libdawg_sink.so +0 -0
  85. package/native/prebuilt/linux-x64/libdawg_sink.so +0 -0
  86. package/native/prebuilt/manifest.json +21 -0
  87. package/package.json +6 -2
  88. package/src/agent/agent.ts +130 -18
  89. package/src/agent/calibration-tools.ts +53 -0
  90. package/src/agent/clip-tools.ts +453 -0
  91. package/src/agent/command-agent.ts +378 -0
  92. package/src/agent/drum-tools.ts +2 -2
  93. package/src/agent/expression-tools.ts +1 -1
  94. package/src/agent/gateway.ts +246 -60
  95. package/src/agent/models.ts +53 -12
  96. package/src/agent/ops.ts +27 -1
  97. package/src/agent/pack-tools.ts +1 -1
  98. package/src/agent/planner.ts +25 -0
  99. package/src/agent/portable-schema.ts +80 -0
  100. package/src/agent/preview-tool.ts +4 -1
  101. package/src/agent/provider.ts +22 -8
  102. package/src/agent/range-tools.ts +216 -0
  103. package/src/agent/rhythm-tools.ts +1 -1
  104. package/src/agent/section-tools.ts +1 -1
  105. package/src/agent/show-me.ts +497 -0
  106. package/src/agent/steer.ts +15 -0
  107. package/src/agent/style-tools.ts +217 -0
  108. package/src/agent/tool-error.ts +12 -0
  109. package/src/agent/tools.ts +110 -23
  110. package/src/agent/usage.ts +2 -2
  111. package/src/agent/voice-tools.ts +925 -0
  112. package/src/agent/xcb-agent.ts +13 -10
  113. package/src/argv.ts +38 -0
  114. package/src/audio/analysis.ts +253 -0
  115. package/src/audio/arrange.ts +49 -5
  116. package/src/audio/audio-command.ts +218 -0
  117. package/src/audio/autotune-engine.ts +101 -0
  118. package/src/audio/autotune.ts +640 -0
  119. package/src/audio/clips.ts +240 -0
  120. package/src/audio/devices.ts +264 -0
  121. package/src/audio/doctor.ts +157 -0
  122. package/src/audio/dsp/bandbank.ts +138 -0
  123. package/src/audio/dsp/envelope.ts +10 -0
  124. package/src/audio/dsp/follow.ts +120 -0
  125. package/src/audio/dsp/formant.ts +427 -0
  126. package/src/audio/dsp/glottal.ts +243 -0
  127. package/src/audio/dsp/interp.ts +7 -2
  128. package/src/audio/dsp/lpc.ts +50 -0
  129. package/src/audio/dsp/periodicity.ts +59 -0
  130. package/src/audio/dsp/pitch.ts +995 -0
  131. package/src/audio/dsp/psola.ts +199 -0
  132. package/src/audio/effects/chain.ts +3 -1
  133. package/src/audio/effects/common.ts +43 -0
  134. package/src/audio/effects/convolution.ts +7 -4
  135. package/src/audio/effects/filter.ts +48 -69
  136. package/src/audio/effects/formant.ts +263 -0
  137. package/src/audio/engine.ts +360 -40
  138. package/src/audio/fit.ts +35 -3
  139. package/src/audio/instrument-check.ts +59 -43
  140. package/src/audio/instruments.ts +4 -0
  141. package/src/audio/keys/calibration.ts +56 -0
  142. package/src/audio/keys/electric.ts +8 -1
  143. package/src/audio/keys/engine.ts +13 -1
  144. package/src/audio/keys/piano.ts +22 -2
  145. package/src/audio/kits.ts +135 -6
  146. package/src/audio/live.ts +114 -20
  147. package/src/audio/native.ts +615 -0
  148. package/src/audio/preview.ts +30 -2
  149. package/src/audio/render-worker.ts +2 -0
  150. package/src/audio/renderer.ts +2 -0
  151. package/src/audio/resample.ts +2 -1
  152. package/src/audio/sampler.ts +85 -4
  153. package/src/audio/samples.ts +20 -2
  154. package/src/audio/sing/analysis.ts +193 -0
  155. package/src/audio/sing/engine.ts +949 -0
  156. package/src/audio/strings/bow.ts +48 -5
  157. package/src/audio/strings/engine.ts +8 -2
  158. package/src/audio/synth/oscillators.ts +31 -21
  159. package/src/audio/synth/voice.ts +34 -1
  160. package/src/audio/vocoder/bank.ts +314 -0
  161. package/src/audio/vocoder/carrier.ts +165 -0
  162. package/src/audio/vocoder/control.ts +68 -0
  163. package/src/audio/vocoder/detect.ts +50 -0
  164. package/src/audio/vocoder/index.ts +304 -0
  165. package/src/audio/vocoder/talkbox.ts +143 -0
  166. package/src/audio/wav.ts +500 -77
  167. package/src/audio/winds/engine.ts +5 -1
  168. package/src/audio/winds/trim.ts +28 -4
  169. package/src/audio/winds/trims1.ts +297 -0
  170. package/src/audio/winds/voice.ts +15 -2
  171. package/src/auth/cli.ts +38 -36
  172. package/src/auth/credentials.ts +30 -1
  173. package/src/auth/login.ts +15 -9
  174. package/src/auth/tui.ts +19 -10
  175. package/src/commands/arrange.ts +82 -32
  176. package/src/commands/autotune.ts +421 -0
  177. package/src/commands/calibration.ts +74 -0
  178. package/src/commands/clips.ts +887 -0
  179. package/src/commands/drums.ts +3 -2
  180. package/src/commands/edit.ts +11 -4
  181. package/src/commands/expression.ts +1 -1
  182. package/src/commands/formant.ts +221 -0
  183. package/src/commands/fx.ts +101 -32
  184. package/src/commands/grammar.ts +560 -0
  185. package/src/commands/help.ts +774 -366
  186. package/src/commands/history.ts +139 -10
  187. package/src/commands/keys.ts +25 -8
  188. package/src/commands/modal.ts +1 -1
  189. package/src/commands/music.ts +1 -1
  190. package/src/commands/nearest.ts +53 -0
  191. package/src/commands/pack.ts +9 -2
  192. package/src/commands/param-range.ts +56 -0
  193. package/src/commands/parses.ts +135 -0
  194. package/src/commands/progression.ts +170 -0
  195. package/src/commands/range.ts +763 -0
  196. package/src/commands/resample.ts +13 -10
  197. package/src/commands/rhythm.ts +3 -0
  198. package/src/commands/rig.ts +3 -24
  199. package/src/commands/sample.ts +11 -1
  200. package/src/commands/sing.ts +474 -0
  201. package/src/commands/strum.ts +13 -1
  202. package/src/commands/style.ts +415 -0
  203. package/src/commands/time.ts +6 -3
  204. package/src/commands/tuning.ts +3 -3
  205. package/src/commands/vocal-pitch.ts +617 -0
  206. package/src/commands/vocal.ts +147 -0
  207. package/src/commands/vocoder.ts +627 -0
  208. package/src/commands/wind.ts +2 -2
  209. package/src/fs/durable.ts +50 -0
  210. package/src/identity/actor.ts +127 -0
  211. package/src/lang/glossary.ts +511 -0
  212. package/src/launch-args.ts +267 -0
  213. package/src/main.ts +2508 -308
  214. package/src/media/cli.ts +20 -3
  215. package/src/media/import.ts +3 -1
  216. package/src/project/check.ts +21 -2
  217. package/src/project/clip-pins.ts +72 -0
  218. package/src/project/init.ts +23 -8
  219. package/src/project/sync.ts +418 -86
  220. package/src/render.ts +20 -1
  221. package/src/session/client.ts +107 -3
  222. package/src/session/clipboard.ts +79 -0
  223. package/src/session/daemon.ts +199 -5
  224. package/src/session/live-host.ts +232 -0
  225. package/src/session/meta.ts +14 -0
  226. package/src/session/origin.ts +174 -0
  227. package/src/session/port.ts +109 -14
  228. package/src/session/presence.ts +34 -4
  229. package/src/session/protocol.ts +365 -7
  230. package/src/session/rebase.ts +63 -12
  231. package/src/session/receipt.ts +265 -0
  232. package/src/session/shared-live.ts +131 -0
  233. package/src/session/store.ts +126 -45
  234. package/src/tui/arrange-menu.ts +225 -23
  235. package/src/tui/audition.ts +1 -1
  236. package/src/tui/euclid.ts +113 -27
  237. package/src/tui/fader.ts +282 -42
  238. package/src/tui/granular-menu.ts +2 -4
  239. package/src/tui/knob-fields.ts +107 -0
  240. package/src/tui/knob-map.ts +198 -0
  241. package/src/tui/menu-clips.ts +297 -0
  242. package/src/tui/menu-time.ts +20 -13
  243. package/src/tui/menu-voice.ts +405 -0
  244. package/src/tui/menu.ts +987 -179
  245. package/src/tui/modal-menu.ts +6 -6
  246. package/src/tui/performance-menu.ts +5 -2
  247. package/src/tui/play-chords.ts +4 -2
  248. package/src/tui/play-mode.ts +15 -1
  249. package/src/tui/play-session.ts +243 -28
  250. package/src/tui/sing-menu.ts +278 -0
  251. package/src/tui/style-menu.ts +104 -0
  252. package/src/tui/tape-mode.ts +580 -0
  253. package/src/tui/tape-view.ts +263 -0
  254. package/src/tui/vocoder-menu.ts +244 -0
  255. package/src/tui/wind-menu.ts +3 -3
  256. package/src/version.ts +8 -0
  257. package/src/web/fetch.ts +115 -29
  258. package/tui/activity.ts +180 -9
  259. package/tui/app.ts +491 -84
  260. package/tui/clip-row.ts +132 -0
  261. package/tui/delight.ts +193 -0
  262. package/tui/drawer.ts +233 -27
  263. package/tui/frame-gate.ts +76 -0
  264. package/tui/grammar.ts +143 -65
  265. package/tui/guide.ts +50 -5
  266. package/tui/highway.ts +309 -30
  267. package/tui/hints.ts +197 -0
  268. package/tui/hits.ts +7 -1
  269. package/tui/input.ts +60 -9
  270. package/tui/keys.ts +1 -1
  271. package/tui/knobs.ts +268 -0
  272. package/tui/play-strip.ts +80 -14
  273. package/tui/prompt.ts +1 -1
  274. package/tui/screen.ts +151 -18
  275. package/tui/tape.ts +439 -0
  276. package/tui/text.ts +35 -2
  277. package/tui/theme.ts +46 -1
@@ -0,0 +1,267 @@
1
+ /**
2
+ * The `dawg [options]` launch grammar and the small argv contract the plain
3
+ * subcommands share. Pure: no I/O, so every mistake is rejected before
4
+ * `.dawg/` (or any project file) could be written.
5
+ */
6
+ export { parseSimpleArgv, type SimpleArgv } from "./argv.ts";
7
+ import { parseThemeName, THEME_NAMES, type ThemeName } from "../tui/theme.ts";
8
+
9
+ /** Subcommands that parse their own argv; the launch grammar skips them. */
10
+ export const SUBCOMMANDS: readonly string[] = [
11
+ "login",
12
+ "logout",
13
+ "auth",
14
+ "model",
15
+ "sessions",
16
+ "render",
17
+ "init",
18
+ "check",
19
+ "media",
20
+ ];
21
+ export const VALUE_FLAGS = [
22
+ "--session",
23
+ "--track",
24
+ "--import",
25
+ "--export",
26
+ "--theme",
27
+ ] as const;
28
+ export type ValueFlag = (typeof VALUE_FLAGS)[number];
29
+ export const BOOLEAN_FLAGS: readonly string[] = [
30
+ "--new",
31
+ "--demo",
32
+ "--help",
33
+ "-h",
34
+ "--version",
35
+ "-v",
36
+ "--reduce-motion",
37
+ "--no-mouse",
38
+ ];
39
+
40
+ /** A `--track` value resolved to the id `/track` would use. */
41
+ export type TrackArg = { id: string; name: string };
42
+
43
+ /** Screens a pane can open on (§12.5). */
44
+ export const PANE_SCREENS = ["home", "play", "tape", "sound", "menu"] as const;
45
+ export type PaneScreen = (typeof PANE_SCREENS)[number];
46
+
47
+ /**
48
+ * `dawg pane <screen> [track] [param] [pin | follow [letter]]`: what this
49
+ * terminal shows of the session in the current directory. `sound` opens
50
+ * the drawer on `param` (default volume); `menu` opens `param` as a menu
51
+ * section. `follow` tracks another pane's focus (`*` = the latest).
52
+ */
53
+ export type PaneArgs = {
54
+ screen: PaneScreen;
55
+ param: string | undefined;
56
+ pin: boolean;
57
+ follow: string | undefined;
58
+ };
59
+
60
+ export type LaunchArgs = {
61
+ subcommand: string | undefined;
62
+ /** `dawg pane …` (its track is `track`). */
63
+ pane?: PaneArgs;
64
+ flags: ReadonlySet<string>;
65
+ session: string | undefined;
66
+ track: TrackArg | undefined;
67
+ importPath: string | undefined;
68
+ exportPath: string | undefined;
69
+ theme: ThemeName | undefined;
70
+ };
71
+
72
+ export type LaunchParse =
73
+ { ok: true; args: LaunchArgs } | { ok: false; problem: string };
74
+
75
+ const TRACK_NAME = /^[a-z0-9._ -]{1,64}$/i;
76
+
77
+ /**
78
+ * `--track Bass Guitar` → id `bass-guitar`, name `bass guitar`: the rule
79
+ * `/track` applies (letters, digits, `.`, `_`, `-` and spaces, at most 64),
80
+ * lowercased with runs of spaces collapsed. Undefined when invalid.
81
+ */
82
+ export function normalizeTrackArg(value: string): TrackArg | undefined {
83
+ const name = value.trim().replace(/\s+/g, " ").toLowerCase();
84
+ if (!TRACK_NAME.test(name)) return undefined;
85
+ // `.` and `..` are path segments, never track ids.
86
+ if (/^\.+$/.test(name)) return undefined;
87
+ return { id: name.replace(/ /g, "-"), name };
88
+ }
89
+
90
+ /** The existing track `arg` names (by id or name), else `arg.id`. */
91
+ export function resolveTrackArg(
92
+ tracks: readonly { id: string; name?: string | undefined }[],
93
+ arg: TrackArg,
94
+ ): string {
95
+ const found =
96
+ tracks.find((track) => track.id === arg.id) ??
97
+ tracks.find((track) => track.id.toLowerCase() === arg.id) ??
98
+ tracks.find((track) => (track.name ?? track.id).toLowerCase() === arg.name);
99
+ return found?.id ?? arg.id;
100
+ }
101
+
102
+ /**
103
+ * Parses `dawg [options]`. A subcommand as the first word ends parsing (the
104
+ * subcommand owns the rest). Value flags need a value that does not start
105
+ * with `-`; each may be given once; `--track` and `--theme` are validated
106
+ * here so a bad value is one line and exit 2, never a stack trace.
107
+ */
108
+ export function parseLaunchArgs(argv: readonly string[]): LaunchParse {
109
+ const flags = new Set<string>();
110
+ const values = new Map<ValueFlag, string>();
111
+ const first = argv[0];
112
+ if (first === "pane") return parsePaneArgs(argv.slice(1));
113
+ if (first !== undefined && !first.startsWith("-")) {
114
+ if (SUBCOMMANDS.includes(first))
115
+ return { ok: true, args: emptyArgs(first, flags) };
116
+ return { ok: false, problem: `unknown command · ${clip(first)}` };
117
+ }
118
+ for (let index = 0; index < argv.length; index += 1) {
119
+ const arg = argv[index]!;
120
+ if ((VALUE_FLAGS as readonly string[]).includes(arg)) {
121
+ const flag = arg as ValueFlag;
122
+ const value = argv[index + 1];
123
+ if (value === undefined || value.length === 0 || value.startsWith("-"))
124
+ return { ok: false, problem: `${flag} needs a value` };
125
+ if (values.has(flag))
126
+ return { ok: false, problem: `${flag} given twice` };
127
+ values.set(flag, value);
128
+ index += 1;
129
+ continue;
130
+ }
131
+ if (BOOLEAN_FLAGS.includes(arg)) {
132
+ flags.add(arg);
133
+ continue;
134
+ }
135
+ if (arg.startsWith("-"))
136
+ return { ok: false, problem: `unknown option · ${clip(arg)}` };
137
+ return { ok: false, problem: `unknown command · ${clip(arg)}` };
138
+ }
139
+ let track: TrackArg | undefined;
140
+ const trackValue = values.get("--track");
141
+ if (trackValue !== undefined) {
142
+ track = normalizeTrackArg(trackValue);
143
+ if (!track) return { ok: false, problem: "invalid track name" };
144
+ }
145
+ let theme: ThemeName | undefined;
146
+ const themeValue = values.get("--theme");
147
+ if (themeValue !== undefined) {
148
+ theme = parseThemeName(themeValue);
149
+ if (!theme)
150
+ return {
151
+ ok: false,
152
+ problem: `unknown theme ${clip(themeValue)} · ${THEME_NAMES.join(" | ")}`,
153
+ };
154
+ }
155
+ return {
156
+ ok: true,
157
+ args: {
158
+ subcommand: undefined,
159
+ flags,
160
+ session: values.get("--session"),
161
+ track,
162
+ importPath: values.get("--import"),
163
+ exportPath: values.get("--export"),
164
+ theme,
165
+ },
166
+ };
167
+ }
168
+
169
+ /** Drawer parameters `dawg pane sound <word>` opens (else the word is a track). */
170
+ const SOUND_PARAMS: readonly string[] = [
171
+ "volume",
172
+ "pan",
173
+ "tempo",
174
+ "bpm",
175
+ "bars",
176
+ "meter",
177
+ ];
178
+
179
+ const PANE_USAGE =
180
+ "usage: dawg pane home|play|tape|sound|menu [track] [volume|pan|section] [pin|follow [A-Z]]";
181
+
182
+ export function parsePaneArgs(argv: readonly string[]): LaunchParse {
183
+ const [screenWord, ...rest] = argv;
184
+ const screen = PANE_SCREENS.find(
185
+ (name) => name === screenWord?.toLowerCase(),
186
+ );
187
+ if (!screen)
188
+ return {
189
+ ok: false,
190
+ problem: screenWord
191
+ ? `unknown pane screen ${clip(screenWord)} · ${PANE_USAGE}`
192
+ : PANE_USAGE,
193
+ };
194
+ let pin = false;
195
+ let follow: string | undefined;
196
+ const words: string[] = [];
197
+ for (let index = 0; index < rest.length; index += 1) {
198
+ const word = rest[index]!;
199
+ if (word === "pin") pin = true;
200
+ else if (word === "follow") {
201
+ const letter = rest[index + 1];
202
+ if (letter !== undefined && /^[A-Z]$/i.test(letter)) {
203
+ follow = letter.toUpperCase();
204
+ index += 1;
205
+ } else follow = "*";
206
+ } else if (word.startsWith("-"))
207
+ return {
208
+ ok: false,
209
+ problem: `unknown option · ${clip(word)} · ${PANE_USAGE}`,
210
+ };
211
+ else words.push(word);
212
+ }
213
+ if (pin && follow)
214
+ return { ok: false, problem: `pin or follow, not both · ${PANE_USAGE}` };
215
+ if (words.length > 2)
216
+ return { ok: false, problem: `too many words · ${PANE_USAGE}` };
217
+ let track: TrackArg | undefined;
218
+ // `menu mix`, `sound filter`: one word after these is the section or the
219
+ // parameter; a track then comes first (`sound bass filter`).
220
+ const [first, second] = words;
221
+ let param: string | undefined;
222
+ const trackFirst =
223
+ words.length === 2 ||
224
+ (first !== undefined &&
225
+ (screen === "home" ||
226
+ screen === "play" ||
227
+ (screen === "sound" && !SOUND_PARAMS.includes(first.toLowerCase()))));
228
+ if (trackFirst) {
229
+ track = normalizeTrackArg(first!);
230
+ if (!track) return { ok: false, problem: "invalid track name" };
231
+ param = second;
232
+ } else param = first;
233
+ if (param !== undefined && !/^[a-z][a-z0-9 ._-]{0,63}$/i.test(param))
234
+ return { ok: false, problem: `invalid pane parameter · ${PANE_USAGE}` };
235
+ if (param !== undefined && (screen === "home" || screen === "play"))
236
+ return {
237
+ ok: false,
238
+ problem: `${screen} takes a track only · ${PANE_USAGE}`,
239
+ };
240
+ return {
241
+ ok: true,
242
+ args: {
243
+ ...emptyArgs(undefined, new Set()),
244
+ track,
245
+ pane: { screen, param: param?.toLowerCase(), pin, follow },
246
+ },
247
+ };
248
+ }
249
+
250
+ function emptyArgs(
251
+ subcommand: string | undefined,
252
+ flags: Set<string>,
253
+ ): LaunchArgs {
254
+ return {
255
+ subcommand,
256
+ flags,
257
+ session: undefined,
258
+ track: undefined,
259
+ importPath: undefined,
260
+ exportPath: undefined,
261
+ theme: undefined,
262
+ };
263
+ }
264
+
265
+ function clip(value: string): string {
266
+ return value.length > 40 ? `${value.slice(0, 40)}…` : value;
267
+ }