Session handles are one word from a single 218-entry list — instruments, forms, notation and theory, tempo and dynamics — replacing the adjective-noun pair. A batch is shuffled, so it repeats only once the wordlist runs out. Project boxes show the parent directory instead of the full path: the last segment is already the name line above it. The last command is gone from the box, and picking a project no longer pre-selects it — the directory and what runs in it are independent choices. Both changes land in the TUI picker too, which mirrors the web form's rules. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
31 lines
1.1 KiB
TypeScript
31 lines
1.1 KiB
TypeScript
// Path splitting and completion arithmetic for the working-directory input.
|
|
|
|
export function splitPath(value: string): { dir: string; prefix: string } {
|
|
const slash = value.lastIndexOf("/");
|
|
if (slash === -1) return { dir: "", prefix: value };
|
|
return { dir: value.slice(0, slash + 1), prefix: value.slice(slash + 1) };
|
|
}
|
|
|
|
/** Longest common prefix of a list of strings (case-sensitive, preserving the
|
|
* actual on-disk casing). */
|
|
export function longestCommonPrefix(items: string[]): string {
|
|
if (items.length === 0) return "";
|
|
let lcp = items[0];
|
|
for (const s of items.slice(1)) {
|
|
let i = 0;
|
|
while (i < lcp.length && i < s.length && lcp[i] === s[i]) i++;
|
|
lcp = lcp.slice(0, i);
|
|
if (lcp === "") break;
|
|
}
|
|
return lcp;
|
|
}
|
|
|
|
/** Everything above the last segment of an absolute path: `/a/b/c` → `/a/b`.
|
|
* A top-level path keeps `/`, and a trailing slash is ignored. */
|
|
export function parentDir(path: string): string {
|
|
const trimmed = path.length > 1 && path.endsWith("/") ? path.slice(0, -1) : path;
|
|
const slash = trimmed.lastIndexOf("/");
|
|
if (slash <= 0) return slash === 0 ? "/" : "";
|
|
return trimmed.slice(0, slash);
|
|
}
|