Repository navigation
Open
Labels
gang-grillDesign topics queued for later group grillingDesign topics queued for later group grilling
Description
Problem
TUI plugin slots currently enumerate fixed positions such as prompt.footer.end. This does not scale when a plugin needs to render relative to existing UI elements. Adding increasingly specific positional slots would keep expanding the API without describing the UI structure plugins attach to.
Proposed API
Publish a small anatomy tree of stable, named host parts and let plugins attach relative to those parts.
context.ui.attach("prompt.footer", {
after: "location",
render: () => <text>content</text>,
})The same operation can support container takeover:
context.ui.attach("prompt.footer", {
replace: "right",
render: () => <text>custom footer</text>,
})Placement is a correlated sum:
type Placement =
| { at: "start" | "end" }
| { before: Part }
| { after: Part }
| { replace: Part }The anatomy should describe the footer that exists today, including its host-owned left side:
prompt.footer
├── left
│ ├── location
│ └── status
└── right
├── editor-context
└── contributions
location and status occupy the same left-side region conditionally: the normal state shows the current location, while active operations show status/progress there.
Proposed host behavior
- Replacing a part suppresses attachments within that subtree.
- Suppression is visible in the plugins UI rather than happening silently.
- Ancestor replacement wins over descendant claims.
- At the same node, the most recently enabled plugin wins.
- User-hidden host parts retain their anchors so attached plugins can still render.
- Under width pressure, guest plugin content is hidden before host content.
- Plugins do not receive width queries; the host owns measurement and pressure policy.
Questions to grill
- What naming and compatibility guarantees do anatomy part IDs need?
- Should
replaceship in the first version or follow relative attachment later? - How should separators and gaps behave when host parts are hidden or replaced?
- Is most-recently-enabled ordering sufficiently predictable for conflict resolution?
- What diagnostics should plugins receive when an attachment is suppressed or references a missing part?
- Does the anatomy tree expose enough structure without coupling plugins too tightly to host layout?
Metadata
Metadata
Assignees
Labels
gang-grillDesign topics queued for later group grillingDesign topics queued for later group grilling
Activity
Dante-dan commentedon Oct 3, 2026
I would make the first slice relative insertion within
prompt.footer, and deferreplace. The useful increment is placement next to a named host part; subtree takeover introduces a separate ownership/conflict model.The current public slot types expose fixed names such as
home_prompt_rightandsession_prompt_right, while the host adapter delegates rendering to the OpenTUI registry. I would keep those APIs working and add anatomy placement at the footer host boundary, rather than change the meaning of existing slot modes.My proposed answers to the six questions:
end.at/before/afterunion only. Reject unknown container/part pairs, including anchors belonging to a different container. Defer replacement, ancestor precedence and winner selection together; insertion does not need a winner.location↔statustransition must preserve attachment order: otherwise a plugin afterlocationcould jump merely because an operation starts.missing_partseparately from temporaryhidden_for_width. Hidden host content is neither of those failures. Dispose attachments with the plugin scope, so disabling a plugin cannot leave ghost entries. Replacement-specific suppression diagnostics can follow replacement.A bounded acceptance matrix would cover two plugins targeting the same anchor, the location/status transition, hidden host content without stray gaps, disable/re-enable and disposal, missing/wrong-container anchors, narrow-width guest suppression, and unchanged legacy slot behavior. These are proposed checks, not results from an implementation.
Could the core team approve this insertion-only footer scope, or identify which semantics need to change first?
moritzWa commentedon Oct 4, 2026
Two plugins I use daily would both be served by the insertion-only slice @Dante-dan describes, so I'd also ship that first and defer
replace.pause - 1.7x + stop) while an answer is being read.#123, coloured by PR state, and opens the PR on click.Both want to sit on the footer's left side next to
location. Today they run on a local build that adds one more fixed slot (session_prompt_footer), which is exactly the slot growth this issue wants to stop. On stock, read-aloud falls back tosession_prompt_right, where it competes with the agent/model row. With the proposed API, each becomesattach("prompt.footer", { after: "location", ... }).On width pressure: without a host policy, every plugin guesses. read-aloud collapses its controls below 80 terminal columns. opencode-session-title measures its own box with
onSizeChangeand hides below a minimum width. Neither knows what else shares the row. "Guest content hides before host content" would replace both hacks.If the insertion-only footer scope is approved, I'm happy to build it.
opencodejr commentedon Oct 28, 2026
Thanks for the detailed write-up. This is a design proposal for a new plugin API rather than a bug, so there was nothing to reproduce. The current fixed-slot API (
prompt.footer,prompt.footer.status,prompt.footer.file, rendered through nested<Slot>s in the prompt component) works as designed today. I'm leaving this for the team to work through the open design questions in the issue.