1import type { TerminalSummary } from "@t3tools/contracts";
2
3function terminalNumber(terminalId: string): string | undefined {
4 return /^term(?:inal)?-(\d+)(?:-[\da-f]{8}(?:-[\da-f]{4}){3}-[\da-f]{12})?$/i.exec(
5 terminalId,
6 )?.[1];
7}
8
9/** Human-readable label for a terminal tab; matches mobile and web sidebars. */
10export function getTerminalLabel(terminalId: string): string {
11 const numericSuffix = terminalNumber(terminalId);
12 if (numericSuffix) {
13 return `Terminal ${numericSuffix}`;
14 }
15
16 return terminalId;
17}
18
19/** Prefer server summary label when present; otherwise fall back to `getTerminalLabel`. */
20export function resolveTerminalSessionLabel(
21 terminalId: string,
22 summary: Pick<TerminalSummary, "label"> | null | undefined,
23): string {
24 const trimmed = summary?.label?.trim();
25 if (trimmed && trimmed.length > 0) {
26 return trimmed;
27 }
28 return getTerminalLabel(terminalId);
29}
30
31/**
32 * Client-side terminal id allocator. Ids are ALWAYS chosen by the client and sent explicitly
33 * on every `terminal.open` / `terminal.attach` call — the server never allocates.
34 *
35 * Returns the lowest unused `term-N` number. When metadata is unavailable,
36 * callers append a UUID so an unseen host session cannot be reused accidentally.
37 */
38export function nextTerminalId(
39 existingTerminalIds: ReadonlyArray<string>,
40 uniqueSuffix?: string,
41): string {
42 const usedNumbers = new Set(existingTerminalIds.map(terminalNumber));
43 let nextIndex = 1;
44 while (usedNumbers.has(String(nextIndex))) {
45 nextIndex += 1;
46 }
47
48 return `term-${nextIndex}${uniqueSuffix === undefined ? "" : `-${uniqueSuffix}`}`;
49}