feat(desktop): name a draft after what you have typed into it

Every unsent tab was called "New session", so a row of them said nothing
about which was which. Name each one from its composer, using the same
first-line, word-boundary rule the backend's derive_title applies a moment
after the draft is finally sent — so the name the tab already shows is the
name it keeps.

The title moves with the composer, which is far faster than a pane
contribution should be re-registered. Panes can now render a tab label
instead of declaring one, so the label subscribes to its own key and a
rename repaints one string rather than the panes area.
This commit is contained in:
Brooklyn Nicholson
2026-08-09 03:41:55 -05:00
parent 5b3a5ccae6
commit c143ec7f0c
10 changed files with 167 additions and 18 deletions
+5
View File
@@ -36,6 +36,10 @@ export interface PaneMirror<T> {
* self-subscribing component (e.g. a session's status dot) so the strip needn't
* re-sync on status/color change — only `title` drives re-registration. */
tabLead?: (key: string) => ReactNode
/** Custom label NODE for the tile's tab, self-subscribing for the same reason
* as `tabLead` — a name that moves faster than re-registration (see
* PaneChrome.tabTitle). Falls back to `title`. */
tabTitle?: (key: string) => ReactNode
/** Glyph buttons the tile contributes to the strip, after the last tab (where
* "+" sits), while it is the ACTIVE pane — e.g. a preview's console /
* DevTools toggles. DATA, not markup: the strip's `PaneStripGlyph` owns the
@@ -82,6 +86,7 @@ export function paneMirror<T>(cfg: PaneMirror<T>): () => void {
title,
data: {
tabLead: cfg.tabLead ? () => cfg.tabLead!(key) : undefined,
tabTitle: cfg.tabTitle ? () => cfg.tabTitle!(key) : undefined,
stripTools: cfg.stripTools ? () => cfg.stripTools!(key) : undefined,
dock: {
before: cfg.before?.(tile),