chore: formatting etc

This commit is contained in:
Brooklyn Nicholson 2026-04-03 20:14:57 -05:00
parent 56a69e519b
commit 5a5d90c85a
14 changed files with 698 additions and 237 deletions

View file

@ -1,4 +1,4 @@
import { existsSync, mkdirSync, readFileSync, appendFileSync } from 'node:fs'
import { appendFileSync, existsSync, mkdirSync, readFileSync } from 'node:fs'
import { homedir } from 'node:os'
import { join } from 'node:path'
@ -17,34 +17,51 @@ function decode(s: string): string {
}
export function load(): string[] {
if (cache) return cache
if (cache) {
return cache
}
try {
if (existsSync(file)) {
cache = readFileSync(file, 'utf8')
.split('\n')
.filter(Boolean)
.map(decode)
.slice(-MAX)
cache = readFileSync(file, 'utf8').split('\n').filter(Boolean).map(decode).slice(-MAX)
} else {
cache = []
}
} catch {
cache = []
}
return cache
}
export function append(line: string): void {
const trimmed = line.trim()
if (!trimmed) return
if (!trimmed) {
return
}
const items = load()
if (items.at(-1) === trimmed) return
if (items.at(-1) === trimmed) {
return
}
items.push(trimmed)
if (items.length > MAX) items.splice(0, items.length - MAX)
if (items.length > MAX) {
items.splice(0, items.length - MAX)
}
try {
if (!existsSync(dir)) mkdirSync(dir, { recursive: true })
if (!existsSync(dir)) {
mkdirSync(dir, { recursive: true })
}
appendFileSync(file, encode(trimmed) + '\n')
} catch { /* ignore */ }
} catch {
/* ignore */
}
}
export function all(): string[] {

124
ui-tui/src/lib/slash.ts Normal file
View file

@ -0,0 +1,124 @@
import type { SlashCatalog } from '../types.js'
/** Match SlashCommandCompleter: command names, subcommands, then skills. */
export function paletteForLine(line: string, c: SlashCatalog | null): [string, string][] {
if (!c || !line.startsWith('/')) {
return []
}
const parts = line.split(/\s+/)
const baseRaw = parts[0]!
const base = baseRaw.toLowerCase()
const inSub = parts.length > 1 || (parts.length === 1 && line.endsWith(' '))
if (inSub) {
const subText = parts.length > 1 ? parts.slice(1).join(' ') : ''
if (subText.includes(' ') || parts.length > 2) {
return []
}
const head = subText.split(/\s+/)[0] ?? ''
if (subText.includes(' ') && head !== subText) {
return []
}
const canonical = c.canon[base] ?? baseRaw
const subs = c.sub[canonical]
if (!subs?.length) {
return []
}
const lo = head.toLowerCase()
return subs
.filter(s => s.toLowerCase().startsWith(lo) && s.toLowerCase() !== lo)
.slice(0, 14)
.map(s => [s, ''])
}
const word = line.slice(1)
return c.pairs
.filter(([k]) => k.slice(1).startsWith(word))
.slice(0, 16)
.map(([k, d]) => [k, d])
}
/** Tab: longest common prefix of palette matches, or first unique completion + space. */
export function tabAdvance(line: string, c: SlashCatalog | null): string | null {
if (!c || !line.startsWith('/')) {
return null
}
const rows = paletteForLine(line, c)
if (!rows.length) {
return null
}
const parts = line.split(/\s+/)
const baseRaw = parts[0]!
const base = baseRaw.toLowerCase()
const inSub = parts.length > 1 || (parts.length === 1 && line.endsWith(' '))
if (inSub) {
const subText = parts.length > 1 ? parts.slice(1).join(' ') : ''
const head = subText.split(/\s+/)[0] ?? ''
const picks = rows.map(([s]) => s)
if (picks.length === 1) {
return `${baseRaw} ${picks[0]!} `
}
const cp = commonPrefix(picks)
if (cp.length > head.length) {
return `${baseRaw} ${cp}`
}
return null
}
const word = line.slice(1)
const names = rows.map(([k]) => k.slice(1))
const cp = commonPrefix(names)
if (names.length === 1) {
return `/${names[0]!} `
}
if (cp.length > word.length) {
return `/${cp}`
}
return null
}
function commonPrefix(xs: string[]): string {
if (!xs.length) {
return ''
}
let n = 0
outer: while (true) {
const ch = xs[0]![n]
if (ch === undefined) {
break
}
for (const x of xs) {
if (x[n] !== ch) {
break outer
}
}
n++
}
return xs[0]!.slice(0, n)
}