refactor(tui): adopt reactive first index and read-only keyed surface

- quark: Collection.first(name) is now a stable Readable<A | undefined>;
  Keyed.ReadOnly names the read surface; useSlot takes plain constant keys;
  Layout.collectionOf<Target>() replaces the content plan double cast
- BackgroundToolHint reads a backgroundRunning first index instead of
  hand-tracking structure plus tool slots
- toolDisplay moves to util/tool-display (content.ts needs it for the index)
This commit is contained in:
Kit Langton 2026-07-18 10:28:36 -04:00
commit c0ce124f8c
12 changed files with 374 additions and 98 deletions

View file

@ -33,7 +33,7 @@ describe("Solid adapter", () => {
createRoot((dispose) => {
const current = useValue(state)
createComputed(() => value = current())
createComputed(() => (value = current()))
state.set(second)
dispose()
})
@ -41,6 +41,21 @@ describe("Solid adapter", () => {
expect(value).toBe(second)
})
it("accepts a plain constant key", () => {
const keyed = Keyed.make<{ readonly id: number; readonly value: string }, number>({ key: (value) => value.id })
keyed.set([{ id: 1, value: "one" }])
const values: Array<string | undefined> = []
createRoot((dispose) => {
const value = useSlot(keyed, 1)
createComputed(() => values.push(value()?.value))
keyed.modify(1, (item) => ({ ...item, value: "ONE" }))
dispose()
})
expect(values).toEqual(["one", "ONE"])
})
it("switches keyed slots and releases obsolete subscriptions", () => {
const keyed = Keyed.make<{ readonly id: number; readonly value: string }, number>({ key: (value) => value.id })
keyed.set([